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}