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