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