001/*
002 * (C) Copyright 2006-2012 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 *     Bogdan Stefanescu
018 *     Florent Guillaume
019 */
020package org.nuxeo.ecm.core.schema.types;
021
022import java.util.ArrayList;
023import java.util.Collection;
024import java.util.HashSet;
025import java.util.List;
026import java.util.Set;
027
028import org.nuxeo.ecm.core.schema.types.constraints.Constraint;
029
030/**
031 * The implementation for a List type.
032 */
033public class ListTypeImpl extends AbstractType implements ListType {
034
035    private static final long serialVersionUID = 1L;
036
037    protected static final String DEFAULT_VALUE_SEPARATOR = " ";
038
039    protected final Type type;
040
041    protected final Field field;
042
043    // TODO: should be removed. use field.defaultvalue instead
044    protected String defaultValue;
045
046    protected int minOccurs;
047
048    protected int maxOccurs;
049
050    protected boolean isArray = false;
051
052    public ListTypeImpl(String schema, String name, Type type, String fieldName, String defaultValue, int flags,
053            Set<Constraint> constraints, int minOccurs, int maxOccurs) {
054        super(null, schema, name);
055        if (fieldName == null) {
056            isArray = true;
057            fieldName = "item";
058        }
059        this.type = type;
060        // if the list is an array, there's no field constraint (notnull)
061        Collection<Constraint> computedConstraints = isArray ? type.getConstraints() : constraints;
062        field = new FieldImpl(QName.valueOf(fieldName), this, type, defaultValue, flags, computedConstraints);
063        this.minOccurs = minOccurs;
064        this.maxOccurs = maxOccurs;
065        this.defaultValue = defaultValue;
066    }
067
068    public ListTypeImpl(String schema, String name, Type type, String fieldName, String defaultValue, int minOccurs,
069            int maxOccurs) {
070        this(schema, name, type, fieldName, defaultValue, 0, new HashSet<Constraint>(), minOccurs, maxOccurs);
071    }
072
073    public ListTypeImpl(String schema, String name, Type type) {
074        this(schema, name, type, null, null, 0, -1);
075    }
076
077    @Override
078    public void setLimits(int minOccurs, int maxOccurs) {
079        this.minOccurs = minOccurs;
080        this.maxOccurs = maxOccurs;
081    }
082
083    @Override
084    public void setDefaultValue(String value) {
085        defaultValue = value;
086    }
087
088    @Override
089    public String getFieldName() {
090        return field.getName().getLocalName();
091    }
092
093    @Override
094    public Type getFieldType() {
095        return field.getType();
096    }
097
098    @Override
099    public Field getField() {
100        return field;
101    }
102
103    @Override
104    public Object getDefaultValue() {
105        return type.decode(defaultValue);
106    }
107
108    public Type getType() {
109        return type;
110    }
111
112    @Override
113    public int getMinCount() {
114        return minOccurs;
115    }
116
117    @Override
118    public int getMaxCount() {
119        return maxOccurs;
120    }
121
122    @Override
123    public boolean isListType() {
124        return true;
125    }
126
127    @Override
128    public Object decode(String string) {
129        // XXX: OG: I do not really know how this is suppose to work
130        // I need this to decode default values and I could
131        // not find how XMLSchema defines default values for sequences thus the
132        // following naive splitting of the string representation of the default
133        // value
134        if (string != null) {
135            List<Object> decoded = new ArrayList<Object>();
136            for (String component : string.split(DEFAULT_VALUE_SEPARATOR)) {
137                decoded.add(type.decode(component));
138            }
139            return decoded;
140        } else {
141            return null;
142        }
143    }
144
145    @Override
146    @SuppressWarnings("rawtypes")
147    public boolean validate(Object object) throws TypeException {
148        if (object == null) {
149            return true;
150        }
151        if (object instanceof Collection) {
152            return validateCollection((Collection) object);
153        } else if (object.getClass().isArray()) {
154            return validateArray((Object[]) object);
155        }
156        return false;
157    }
158
159    protected boolean validateArray(Object[] array) {
160        return true; // TODO
161    }
162
163    @SuppressWarnings("rawtypes")
164    protected boolean validateCollection(Collection col) {
165        return true; // TODO
166    }
167
168    @Override
169    public Object newInstance() {
170        Object defaultValue = this.defaultValue;
171        if (defaultValue != null) {
172            return defaultValue;
173        } else {
174            // XXX AT: maybe use the type to be more specific on list elements
175            return new ArrayList<Object>();
176        }
177    }
178
179    @Override
180    @SuppressWarnings("unchecked")
181    public Object convert(Object object) throws TypeException {
182        if (object instanceof List) {
183            List<Object> list = (List<Object>) object;
184            for (int i = 0, len = list.size(); i < len; i++) {
185                Object value = list.get(i);
186                list.set(i, type.convert(value));
187            }
188            return object;
189        }
190        throw new TypeException("Incompatible object: " + object.getClass() + " for type " + getName());
191    }
192
193    @Override
194    public boolean isArray() {
195        return isArray;
196    }
197
198    @Override
199    public boolean isScalarList() {
200        return field.getType().isSimpleType();
201    }
202}