/* * Copyright (c) 2006-2011 Nuxeo SA (http://nuxeo.com/) and others. * * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Nuxeo - initial API and implementation * * $Id$ */ package org.eclipse.ecr.core.schema.types; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.Map; import org.eclipse.ecr.core.schema.Namespace; import org.eclipse.ecr.core.schema.TypeConstants; import org.eclipse.ecr.core.schema.TypeRef; /** * @author <a href="mailto:bs@nuxeo.com">Bogdan Stefanescu</a> * */ public class ComplexTypeImpl extends AbstractType implements ComplexType { public static final int F_UNSTRUCT_DEFAULT = 0; public static final int F_UNSTRUCT_FALSE = 1; public static final int F_UNSTRUCT_TRUE = 2; private static final long serialVersionUID = 294207320373332155L; // fields map by their qname protected final Map<QName, Field> fields = new HashMap<QName, Field>(); // the cache used to lookup fields by string keys protected final Map<String, Field> fieldsCache = new HashMap<String, Field>(); protected final Namespace ns; protected int unstructured; // 0 - inherit, 1 - structured, 2 - unstructured public ComplexTypeImpl(TypeRef<? extends ComplexType> superType, String schema, String name, Namespace ns, int struct) { super(superType, schema, name); ComplexType stype = (ComplexType) getSuperType(); if (stype != null) { Collection<Field> fields = stype.getFields(); for (Field field : fields) { this.fields.put(field.getName(), field); } } unstructured = struct; this.ns = ns; } public ComplexTypeImpl(ComplexType superType, String schema, String name) { this(superType == null ? null : superType.getRef(), schema, name, Namespace.DEFAULT_NS, F_UNSTRUCT_DEFAULT); } public ComplexTypeImpl(ComplexType superType, String schema, String name, Namespace ns) { this(superType == null ? null : superType.getRef(), schema, name, ns, F_UNSTRUCT_DEFAULT); } public ComplexTypeImpl(TypeRef<? extends ComplexType> superType, String schema, String name) { this(superType, schema, name, Namespace.DEFAULT_NS, F_UNSTRUCT_DEFAULT); } public ComplexTypeImpl(TypeRef<? extends ComplexType> superType, String schema, String name, Namespace ns) { this(superType, schema, name, Namespace.DEFAULT_NS, F_UNSTRUCT_DEFAULT); } @Override public Namespace getNamespace() { return ns; } @Override public boolean isUnstructured() { if (unstructured == F_UNSTRUCT_DEFAULT) { unstructured = hasFields() ? F_UNSTRUCT_FALSE : F_UNSTRUCT_TRUE; } return unstructured == F_UNSTRUCT_TRUE; } @Override public Field getField(String name) { Field field = fieldsCache.get(name); if (field == null) { QName qname = QName.valueOf(name, ns.prefix); field = getField(qname); if (field != null) { fieldsCache.put(name, field); } } return field; } @Override public Field getField(QName name) { return fields.get(name); } @Override public Collection<Field> getFields() { return Collections.unmodifiableCollection(fields.values()); } @Override public int getFieldsCount() { return fields.size(); } @Override public Field addField(String name, TypeRef<? extends Type> type) { return addField(QName.valueOf(name, ns.prefix), type, null, 0); } @Override public Field addField(QName name, TypeRef<? extends Type> type) { return addField(name, type, null, 0); } @Override public Field addField(String name, TypeRef<? extends Type> type, String defaultValue, int flags) { return addField(QName.valueOf(name, ns.prefix), type, defaultValue, flags); } @Override public Field addField(QName name, TypeRef<? extends Type> type, String defaultValue, int flags) { FieldImpl field = new FieldImpl(name, getRef(), type, defaultValue, flags); fields.put(name, field); return field; } @Override public boolean hasField(String name) { if (fieldsCache.containsKey(name)) { return true; } return null != getField(name); } @Override public boolean hasField(QName name) { return fields.containsKey(name); } @Override public boolean hasFields() { return !fields.isEmpty(); } @Override public boolean isComplexType() { return true; } @Override public boolean validate(Object object) throws TypeException { if (object == null && isNotNull()) { return false; } if (object == null) { return true; } if (object instanceof Map) { return validateMap((Map) object); } return false; } protected boolean validateMap(Map map) { return true; } @Override public String toString() { return getClass().getSimpleName() + '(' + name + ')'; } @Override public Map<String, Object> newInstance() { if (TypeConstants.isContentType(this)) { // NXP-912: should return null for a blob. Since there is no // pluggable adapter mechanism on types, and since document model // properties consider that every complex property named "content" // should be dealt with a BlobProperty, this is hardcoded here. return null; } Map<String, Object> map = new HashMap<String, Object>(); for (Field field : fields.values()) { Object value; Type type = field.getType(); if (type.isComplexType()) { value = type.newInstance(); } else if (type.isListType()) { value = new ArrayList<Object>(); } else { value = field.getDefaultValue(); } map.put(field.getName().getLocalName(), value); } return map; } @Override @SuppressWarnings("unchecked") public Object convert(Object object) throws TypeException { if (object instanceof Map) { Map<Object, Object> map = (Map<Object, Object>) object; for (Map.Entry entry : map.entrySet()) { String key = entry.getKey().toString(); Field field = getField(key); if (field == null) { throw new IllegalArgumentException("Field " + key + " is not defined for the complex type " + getName()); } entry.setValue(field.getType().convert(entry.getValue())); } return object; } throw new TypeException("Incompatible object: " + object.getClass() + " for type " + this); } @Override public TypeRef<? extends ComplexType> getRef() { return new TypeRef<ComplexType>(schema, name, this); } }