001/*
002 * (C) Copyright 2006-2011 Nuxeo SA (http://nuxeo.com/) and others.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *     http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 *
016 * Contributors:
017 *     Florent Guillaume
018 */
019
020package org.nuxeo.ecm.core.storage.sql;
021
022import java.io.Serializable;
023import java.lang.reflect.Array;
024import java.util.Calendar;
025import java.util.Collection;
026import java.util.Date;
027import java.util.GregorianCalendar;
028
029import org.nuxeo.ecm.core.api.model.DeltaLong;
030import org.nuxeo.ecm.core.schema.types.SimpleTypeImpl;
031import org.nuxeo.ecm.core.schema.types.Type;
032import org.nuxeo.ecm.core.schema.types.primitives.BinaryType;
033import org.nuxeo.ecm.core.schema.types.primitives.BooleanType;
034import org.nuxeo.ecm.core.schema.types.primitives.DateType;
035import org.nuxeo.ecm.core.schema.types.primitives.DoubleType;
036import org.nuxeo.ecm.core.schema.types.primitives.IntegerType;
037import org.nuxeo.ecm.core.schema.types.primitives.LongType;
038import org.nuxeo.ecm.core.schema.types.primitives.StringType;
039
040/**
041 * @author Florent Guillaume
042 */
043public enum PropertyType {
044    STRING(String.class), //
045    BOOLEAN(Boolean.class), //
046    LONG(Long.class), //
047    DOUBLE(Double.class), //
048    DATETIME(Calendar.class), //
049    BINARY(String.class), //
050    ACL(ACLRow.class), //
051    ARRAY_STRING(STRING, new String[0]), //
052    ARRAY_BOOLEAN(BOOLEAN, new Boolean[0]), //
053    ARRAY_LONG(LONG, new Long[0]), //
054    ARRAY_DOUBLE(DOUBLE, new Double[0]), //
055    ARRAY_DATETIME(DATETIME, new Calendar[0]), //
056    ARRAY_BINARY(BINARY, new String[0]), //
057    COLL_ACL(ACL, new ACLRow[0]);
058
059    private final Class<?> klass;
060
061    private final PropertyType arrayBaseType;
062
063    private final Serializable[] emptyArray;
064
065    PropertyType(Class<?> klass) {
066        this.klass = klass;
067        arrayBaseType = null;
068        emptyArray = null;
069    }
070
071    PropertyType(PropertyType arrayBaseType, Serializable[] emptyArray) {
072        klass = null;
073        this.arrayBaseType = arrayBaseType;
074        this.emptyArray = emptyArray;
075    }
076
077    public boolean isArray() {
078        return arrayBaseType != null;
079    }
080
081    public PropertyType getArrayBaseType() {
082        return arrayBaseType;
083    }
084
085    public Serializable[] getEmptyArray() {
086        return emptyArray;
087    }
088
089    public Serializable[] collectionToArray(Collection<Serializable> collection) {
090        // contrary to list.toArray(), this creates an array
091        // of the property type instead of an Object[]
092        Serializable[] array = (Serializable[]) Array.newInstance(klass, collection.size());
093        return collection.toArray(array);
094    }
095
096    /**
097     * Normalizes a scalar value of this type.
098     *
099     * @param value the value to normalize
100     * @return the normalized value
101     * @throws IllegalArgumentException
102     */
103    public Serializable normalize(Object value) {
104        if (value == null) {
105            return null;
106        }
107        switch (this) {
108        case STRING:
109            if (value instanceof String) {
110                return (String) value;
111            }
112            throw new IllegalArgumentException("value is not a String: " + value);
113        case BOOLEAN:
114            if (value instanceof Boolean) {
115                return (Boolean) value;
116            }
117            throw new IllegalArgumentException("value is not a Boolean: " + value);
118        case LONG:
119            if (value instanceof Long) {
120                return (Long) value;
121            }
122            if (value instanceof DeltaLong) {
123                return (DeltaLong) value;
124            }
125            throw new IllegalArgumentException("value is not a Long: " + value);
126        case DOUBLE:
127            if (value instanceof Double) {
128                return (Double) value;
129            }
130            throw new IllegalArgumentException("value is not a Double: " + value);
131        case DATETIME:
132            if (value instanceof Calendar) {
133                return (Calendar) value;
134            }
135            if (value instanceof Date) {
136                Calendar cal = new GregorianCalendar(); // XXX timezone
137                cal.setTime((Date) value);
138                return cal;
139            }
140            throw new IllegalArgumentException("value is not a Calendar: " + value);
141        case BINARY:
142            if (value instanceof String) {
143                return (String) value;
144            }
145            throw new IllegalArgumentException("value is not a binary String: " + value);
146        case ACL:
147            if (value instanceof ACLRow) {
148                return (ACLRow) value;
149            }
150            throw new IllegalArgumentException("value is not a ACLRow: " + value);
151        default:
152            throw new RuntimeException(this.toString());
153        }
154    }
155
156    /**
157     * Normalizes an array value of this type.
158     * <p>
159     * A {@code null} value will be normalized to an empty array.
160     *
161     * @param value the array to normalize
162     * @return the normalized array
163     * @throws IllegalArgumentException
164     */
165    public Serializable[] normalize(Object[] value) {
166        if (value == null) {
167            return emptyArray;
168        }
169        Serializable[] newValue;
170        if (value instanceof Serializable[]) {
171            // update in place
172            newValue = (Serializable[]) value;
173        } else {
174            newValue = new Serializable[value.length];
175        }
176        for (int i = 0; i < value.length; i++) {
177            newValue[i] = arrayBaseType.normalize(value[i]);
178        }
179        return newValue;
180    }
181
182    /**
183     * Converts a Nuxeo core schema field type into a property type.
184     *
185     * @param fieldType the field type to convert
186     * @param array {@code true} if an array type is required
187     * @return
188     */
189    public static PropertyType fromFieldType(Type fieldType, boolean array) {
190        if (fieldType instanceof StringType) {
191            return array ? ARRAY_STRING : STRING;
192        } else if (fieldType instanceof BooleanType) {
193            return array ? ARRAY_BOOLEAN : BOOLEAN;
194        } else if (fieldType instanceof LongType) {
195            return array ? ARRAY_LONG : LONG;
196        } else if (fieldType instanceof DoubleType) {
197            return array ? ARRAY_DOUBLE : DOUBLE;
198        } else if (fieldType instanceof DateType) {
199            return array ? ARRAY_DATETIME : DATETIME;
200        } else if (fieldType instanceof BinaryType) {
201            return array ? ARRAY_BINARY : BINARY;
202        } else if (fieldType instanceof IntegerType) {
203            throw new RuntimeException("Unimplemented primitive type: " + fieldType.getClass().getName());
204        } else if (fieldType instanceof SimpleTypeImpl) {
205            // simple type with constraints -- ignore constraints XXX
206            return fromFieldType(fieldType.getSuperType(), array);
207        } else {
208            throw new RuntimeException("Invalid primitive type: " + fieldType.getClass().getName());
209        }
210    }
211
212}