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