/* * 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: * Jens Huebel, Open Text * Florent Guillaume, Nuxeo */ package org.eclipse.ecr.opencmis.impl.util; import java.math.BigInteger; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import org.apache.chemistry.opencmis.commons.definitions.PropertyDefinition; import org.apache.chemistry.opencmis.commons.definitions.TypeDefinition; import org.apache.chemistry.opencmis.commons.definitions.TypeDefinitionContainer; import org.apache.chemistry.opencmis.commons.definitions.TypeDefinitionList; import org.apache.chemistry.opencmis.commons.enums.BaseTypeId; import org.apache.chemistry.opencmis.commons.exceptions.CmisInvalidArgumentException; import org.apache.chemistry.opencmis.commons.impl.Converter; import org.apache.chemistry.opencmis.commons.impl.dataobjects.AbstractPropertyDefinition; import org.apache.chemistry.opencmis.commons.impl.dataobjects.AbstractTypeDefinition; import org.apache.chemistry.opencmis.commons.impl.dataobjects.TypeDefinitionContainerImpl; import org.apache.chemistry.opencmis.commons.impl.dataobjects.TypeDefinitionListImpl; import org.apache.chemistry.opencmis.server.support.TypeManager; /** * Manages a type system for a repository. * <p> * Types can be added, the inheritance can be managed and type can be retrieved * for a given type id. * <p> * Structures are not copied when returned. */ public class TypeManagerImpl implements TypeManager { public static final int DEFAULT_MAX_TYPE_CHILDREN = 100; protected Map<String, TypeDefinitionContainer> typesMap = new HashMap<String, TypeDefinitionContainer>(); @Override public TypeDefinitionContainer getTypeById(String typeId) { return typesMap.get(typeId); } @Override public TypeDefinition getTypeByQueryName(String typeQueryName) { for (Entry<String, TypeDefinitionContainer> entry : typesMap.entrySet()) { TypeDefinition type = entry.getValue().getTypeDefinition(); if (type.getQueryName().equals(typeQueryName)) return type; } return null; } public TypeDefinitionList getTypeChildren(String typeId, Boolean includePropertyDefinitions, BigInteger maxItems, BigInteger skipCount) { TypeDefinitionContainer typec; if (typeId == null) { // return root types typec = null; } else { typec = typesMap.get(typeId); if (typec == null) { throw new CmisInvalidArgumentException("No such type: " + typeId); } } List<TypeDefinitionContainer> types; if (typec == null) { // return root types // TODO maintain pre-computed root types types = new ArrayList<TypeDefinitionContainer>(4); for (TypeDefinitionContainer tc : typesMap.values()) { if (tc.getTypeDefinition().getParentTypeId() == null) types.add(tc); } } else { types = typec.getChildren(); } List<TypeDefinition> list = new ArrayList<TypeDefinition>(types.size()); for (TypeDefinitionContainer tdc : types) { TypeDefinition type = tdc.getTypeDefinition(); if (!Boolean.TRUE.equals(includePropertyDefinitions)) { type = Converter.convert(Converter.convert(type)); // clone // TODO avoid recomputing type-without-properties type.getPropertyDefinitions().clear(); } list.add(type); } list = ListUtils.batchList(list, maxItems, skipCount, DEFAULT_MAX_TYPE_CHILDREN); return new TypeDefinitionListImpl(list); } public List<TypeDefinitionContainer> getTypeDescendants(String typeId, int depth, Boolean includePropertyDefinitions) { List<TypeDefinitionContainer> types; boolean includeProps = Boolean.TRUE.equals(includePropertyDefinitions); if (typeId == null) { // return all types, unlimited depth types = new ArrayList<TypeDefinitionContainer>(4); for (TypeDefinitionContainer tc : typesMap.values()) { if (tc.getTypeDefinition().getParentTypeId() == null) types.add(tc); } if (!includeProps) { // remove props types = cloneTypes(types, -1, false); } } else { TypeDefinitionContainer typec = typesMap.get(typeId); if (typec == null) { throw new CmisInvalidArgumentException("No such type: " + typeId); } if (depth == 0 || depth < -1) { throw new CmisInvalidArgumentException("Invalid depth: " + depth); } if (depth == -1) { types = typec.getChildren(); if (!includeProps) { // remove props types = cloneTypes(types, -1, false); } } else { types = typec.getChildren(); // truncate tree types = cloneTypes(types, depth - 1, includeProps); } } return types; } @Override public Collection<TypeDefinitionContainer> getTypeDefinitionList() { List<TypeDefinitionContainer> typeRoots = new ArrayList<TypeDefinitionContainer>(); // iterate types map and return a list collecting the root types: for (TypeDefinitionContainer typeCont : typesMap.values()) { if (typeCont.getTypeDefinition().getParentTypeId() == null) typeRoots.add(typeCont); } return typeRoots; } @Override public List<TypeDefinitionContainer> getRootTypes() { List<TypeDefinitionContainer> rootTypes = new ArrayList<TypeDefinitionContainer>(); for (TypeDefinitionContainer type : typesMap.values()) { String id = type.getTypeDefinition().getId(); if (BaseTypeId.CMIS_DOCUMENT.value().equals(id) || BaseTypeId.CMIS_FOLDER.value().equals(id) || BaseTypeId.CMIS_RELATIONSHIP.value().equals(id) || BaseTypeId.CMIS_POLICY.value().equals(id)) { rootTypes.add(type); } } return rootTypes; } /** * Add a type to the type system. Add all properties from inherited types, * add type to children of parent types. * * @param type new type to add */ public void addTypeDefinition(TypeDefinition type) { String id = type.getId(); if (typesMap.containsKey(id)) { throw new RuntimeException("Type already exists: " + id); } TypeDefinitionContainer typeContainer = new TypeDefinitionContainerImpl( type); // add type to type map typesMap.put(id, typeContainer); String parentId = type.getParentTypeId(); if (parentId != null) { if (!typesMap.containsKey(parentId)) { throw new RuntimeException("Cannot add type " + id + ", parent does not exist: " + parentId); } TypeDefinitionContainer parentTypeContainer = typesMap.get(parentId); // add new type to children of parent types parentTypeContainer.getChildren().add(typeContainer); // recursively add inherited properties Map<String, PropertyDefinition<?>> propDefs = typeContainer.getTypeDefinition().getPropertyDefinitions(); addInheritedProperties(propDefs, parentTypeContainer.getTypeDefinition()); } } @Override public String getPropertyIdForQueryName(TypeDefinition typeDefinition, String propQueryName) { for (PropertyDefinition<?> pd : typeDefinition.getPropertyDefinitions().values()) { if (pd.getQueryName().equals(propQueryName)) return pd.getId(); } return null; } protected void addInheritedProperties( Map<String, PropertyDefinition<?>> propDefs, TypeDefinition type) { if (type.getPropertyDefinitions() != null) { addInheritedPropertyDefinitions(propDefs, type.getPropertyDefinitions()); } TypeDefinitionContainer parentTypeContainer = typesMap.get(type.getParentTypeId()); if (parentTypeContainer != null) { addInheritedProperties(propDefs, parentTypeContainer.getTypeDefinition()); } } protected void addInheritedPropertyDefinitions( Map<String, PropertyDefinition<?>> propDefs, Map<String, PropertyDefinition<?>> superPropDefs) { for (PropertyDefinition<?> superPropDef : superPropDefs.values()) { PropertyDefinition<?> clone = Converter.convert(Converter.convert(superPropDef)); ((AbstractPropertyDefinition<?>) clone).setIsInherited(Boolean.TRUE); propDefs.put(superPropDef.getId(), clone); } } /** * Returns a clone of a types tree. * <p> * Removes properties on the clone if requested, cuts the children of the * clone if the depth is exceeded. */ protected static List<TypeDefinitionContainer> cloneTypes( List<TypeDefinitionContainer> types, int depth, boolean includePropertyDefinitions) { List<TypeDefinitionContainer> res = new ArrayList<TypeDefinitionContainer>( types.size()); for (TypeDefinitionContainer tc : types) { AbstractTypeDefinition td = ((AbstractTypeDefinition) tc.getTypeDefinition()).clone(); if (!includePropertyDefinitions) { td.setPropertyDefinitions(null); } TypeDefinitionContainerImpl clone = new TypeDefinitionContainerImpl( td); if (depth != 0) { clone.setChildren(cloneTypes(tc.getChildren(), depth - 1, includePropertyDefinitions)); } res.add(clone); } return res; } }