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     */
102    public Serializable normalize(Object value) {
103        if (value == null) {
104            return null;
105        }
106        switch (this) {
107        case STRING:
108            if (value instanceof String) {
109                return (String) value;
110            }
111            throw new IllegalArgumentException("value is not a String: " + value);
112        case BOOLEAN:
113            if (value instanceof Boolean) {
114                return (Boolean) value;
115            }
116            throw new IllegalArgumentException("value is not a Boolean: " + value);
117        case LONG:
118            if (value instanceof Long) {
119                return (Long) value;
120            }
121            if (value instanceof DeltaLong) {
122                return (DeltaLong) value;
123            }
124            throw new IllegalArgumentException("value is not a Long: " + value);
125        case DOUBLE:
126            if (value instanceof Double) {
127                return (Double) value;
128            }
129            throw new IllegalArgumentException("value is not a Double: " + value);
130        case DATETIME:
131            if (value instanceof Calendar) {
132                return (Calendar) value;
133            }
134            if (value instanceof Date) {
135                Calendar cal = new GregorianCalendar(); // XXX timezone
136                cal.setTime((Date) value);
137                return cal;
138            }
139            throw new IllegalArgumentException("value is not a Calendar: " + value);
140        case BINARY:
141            if (value instanceof String) {
142                return (String) value;
143            }
144            throw new IllegalArgumentException("value is not a binary String: " + value);
145        case ACL:
146            if (value instanceof ACLRow) {
147                return (ACLRow) value;
148            }
149            throw new IllegalArgumentException("value is not a ACLRow: " + value);
150        default:
151            throw new RuntimeException(this.toString());
152        }
153    }
154
155    /**
156     * Normalizes an array value of this type.
157     * <p>
158     * A {@code null} value will be normalized to an empty array.
159     *
160     * @param value the array to normalize
161     * @return the normalized array
162     */
163    public Serializable[] normalize(Object[] value) {
164        if (value == null) {
165            return emptyArray;
166        }
167        Serializable[] newValue;
168        if (value instanceof Serializable[]) {
169            // update in place
170            newValue = (Serializable[]) value;
171        } else {
172            newValue = new Serializable[value.length];
173        }
174        for (int i = 0; i < value.length; i++) {
175            newValue[i] = arrayBaseType.normalize(value[i]);
176        }
177        return newValue;
178    }
179
180    /**
181     * Converts a Nuxeo core schema field type into a property type.
182     *
183     * @param fieldType the field type to convert
184     * @param array {@code true} if an array type is required
185     */
186    public static PropertyType fromFieldType(Type fieldType, boolean array) {
187        if (fieldType instanceof StringType) {
188            return array ? ARRAY_STRING : STRING;
189        } else if (fieldType instanceof BooleanType) {
190            return array ? ARRAY_BOOLEAN : BOOLEAN;
191        } else if (fieldType instanceof LongType) {
192            return array ? ARRAY_LONG : LONG;
193        } else if (fieldType instanceof DoubleType) {
194            return array ? ARRAY_DOUBLE : DOUBLE;
195        } else if (fieldType instanceof DateType) {
196            return array ? ARRAY_DATETIME : DATETIME;
197        } else if (fieldType instanceof BinaryType) {
198            return array ? ARRAY_BINARY : BINARY;
199        } else if (fieldType instanceof IntegerType) {
200            throw new RuntimeException("Unimplemented primitive type: " + fieldType.getClass().getName());
201        } else if (fieldType instanceof SimpleTypeImpl) {
202            // simple type with constraints -- ignore constraints XXX
203            return fromFieldType(fieldType.getSuperType(), array);
204        } else {
205            throw new RuntimeException("Invalid primitive type: " + fieldType.getClass().getName());
206        }
207    }
208
209}