/* * (C) Copyright 2006-2011 Nuxeo SA (http://nuxeo.com/) and others. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * Contributors: * Jens Huebel, Open Text * Florent Guillaume, Nuxeo */ package org.nuxeo.ecm.core.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.MutableTypeDefinition; 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.exceptions.CmisNotSupportedException; import org.apache.chemistry.opencmis.commons.impl.WSConverter; import org.apache.chemistry.opencmis.commons.impl.dataobjects.AbstractPropertyDefinition; 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.TypeDefinitionFactory; 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>(); protected Map<String, String> propQueryNameToId = new HashMap<String, String>(); @Override public TypeDefinitionContainer getTypeById(String typeId) { return typesMap.get(typeId); } public TypeDefinition getTypeDefinition(String typeId) { TypeDefinitionContainer typec = getTypeById(typeId); return typec == null ? null : typec.getTypeDefinition(); } /** * Checks if a type is known. * * @param typeId the type id * @return {@code true} if known * @since 5.9.3 */ public boolean hasType(String typeId) { return typesMap.containsKey(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 = WSConverter.convert(WSConverter.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 type to children of parent types. If specified, add all properties from * inherited types., * * @param type new type to add * @param addInheritedProperties */ @Override public void addTypeDefinition(TypeDefinition type, boolean addInheritedProperties) { 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); if (addInheritedProperties) { // recursively add inherited properties Map<String, PropertyDefinition<?>> propDefs = typeContainer.getTypeDefinition().getPropertyDefinitions(); addInheritedProperties(propDefs, parentTypeContainer.getTypeDefinition()); } } // prop query names for (PropertyDefinition<?> pd : type.getPropertyDefinitions().values()) { String propQueryName = pd.getQueryName(); String propId = pd.getId(); String old = propQueryNameToId.put(propQueryName, propId); if (old != null && !old.equals(propId)) { throw new RuntimeException("Cannot add type " + id + ", query name " + propQueryName + " already used for property id " + old); } } } public void addTypeDefinition(TypeDefinition type) { addTypeDefinition(type, true); } @Override public String getPropertyIdForQueryName(TypeDefinition typeDefinition, String propQueryName) { for (PropertyDefinition<?> pd : typeDefinition.getPropertyDefinitions().values()) { if (pd.getQueryName().equals(propQueryName)) { return pd.getId(); } } return null; } public String getPropertyIdForQueryName(String propQueryName) { return propQueryNameToId.get(propQueryName); } 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 = WSConverter.convert(WSConverter.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()); TypeDefinitionFactory tdFactory = TypeDefinitionFactory.newInstance(); for (TypeDefinitionContainer tc : types) { MutableTypeDefinition td = tdFactory.copy(tc.getTypeDefinition(), includePropertyDefinitions); TypeDefinitionContainerImpl clone = new TypeDefinitionContainerImpl(td); if (depth != 0) { clone.setChildren(cloneTypes(tc.getChildren(), depth - 1, includePropertyDefinitions)); } res.add(clone); } return res; } @Override public void updateTypeDefinition(TypeDefinition typeDefinition) { throw new CmisNotSupportedException(); } @Override public void deleteTypeDefinition(String typeId) { throw new CmisNotSupportedException(); } }