001/* 002 * Copyright (c) 2006-2011 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 * Jens Huebel, Open Text 011 * Florent Guillaume, Nuxeo 012 */ 013package org.nuxeo.ecm.core.opencmis.impl.util; 014 015import java.math.BigInteger; 016import java.util.ArrayList; 017import java.util.Collection; 018import java.util.HashMap; 019import java.util.List; 020import java.util.Map; 021import java.util.Map.Entry; 022 023import org.apache.chemistry.opencmis.commons.definitions.MutableTypeDefinition; 024import org.apache.chemistry.opencmis.commons.definitions.PropertyDefinition; 025import org.apache.chemistry.opencmis.commons.definitions.TypeDefinition; 026import org.apache.chemistry.opencmis.commons.definitions.TypeDefinitionContainer; 027import org.apache.chemistry.opencmis.commons.definitions.TypeDefinitionList; 028import org.apache.chemistry.opencmis.commons.enums.BaseTypeId; 029import org.apache.chemistry.opencmis.commons.exceptions.CmisInvalidArgumentException; 030import org.apache.chemistry.opencmis.commons.exceptions.CmisNotSupportedException; 031import org.apache.chemistry.opencmis.commons.impl.WSConverter; 032import org.apache.chemistry.opencmis.commons.impl.dataobjects.AbstractPropertyDefinition; 033import org.apache.chemistry.opencmis.commons.impl.dataobjects.TypeDefinitionContainerImpl; 034import org.apache.chemistry.opencmis.commons.impl.dataobjects.TypeDefinitionListImpl; 035import org.apache.chemistry.opencmis.server.support.TypeDefinitionFactory; 036import org.apache.chemistry.opencmis.server.support.TypeManager; 037 038/** 039 * Manages a type system for a repository. 040 * <p> 041 * Types can be added, the inheritance can be managed and type can be retrieved for a given type id. 042 * <p> 043 * Structures are not copied when returned. 044 */ 045public class TypeManagerImpl implements TypeManager { 046 047 public static final int DEFAULT_MAX_TYPE_CHILDREN = 100; 048 049 protected Map<String, TypeDefinitionContainer> typesMap = new HashMap<String, TypeDefinitionContainer>(); 050 051 protected Map<String, String> propQueryNameToId = new HashMap<String, String>(); 052 053 @Override 054 public TypeDefinitionContainer getTypeById(String typeId) { 055 return typesMap.get(typeId); 056 } 057 058 /** 059 * Checks if a type is known. 060 * 061 * @param typeId the type id 062 * @return {@code true} if known 063 * @since 5.9.3 064 */ 065 public boolean hasType(String typeId) { 066 return typesMap.containsKey(typeId); 067 } 068 069 @Override 070 public TypeDefinition getTypeByQueryName(String typeQueryName) { 071 for (Entry<String, TypeDefinitionContainer> entry : typesMap.entrySet()) { 072 TypeDefinition type = entry.getValue().getTypeDefinition(); 073 if (type.getQueryName().equals(typeQueryName)) { 074 return type; 075 } 076 } 077 return null; 078 } 079 080 public TypeDefinitionList getTypeChildren(String typeId, Boolean includePropertyDefinitions, BigInteger maxItems, 081 BigInteger skipCount) { 082 TypeDefinitionContainer typec; 083 if (typeId == null) { 084 // return root types 085 typec = null; 086 } else { 087 typec = typesMap.get(typeId); 088 if (typec == null) { 089 throw new CmisInvalidArgumentException("No such type: " + typeId); 090 } 091 } 092 List<TypeDefinitionContainer> types; 093 if (typec == null) { 094 // return root types 095 // TODO maintain pre-computed root types 096 types = new ArrayList<TypeDefinitionContainer>(4); 097 for (TypeDefinitionContainer tc : typesMap.values()) { 098 if (tc.getTypeDefinition().getParentTypeId() == null) { 099 types.add(tc); 100 } 101 } 102 } else { 103 types = typec.getChildren(); 104 } 105 List<TypeDefinition> list = new ArrayList<TypeDefinition>(types.size()); 106 for (TypeDefinitionContainer tdc : types) { 107 TypeDefinition type = tdc.getTypeDefinition(); 108 if (!Boolean.TRUE.equals(includePropertyDefinitions)) { 109 type = WSConverter.convert(WSConverter.convert(type)); // clone 110 // TODO avoid recomputing type-without-properties 111 type.getPropertyDefinitions().clear(); 112 } 113 list.add(type); 114 } 115 list = ListUtils.batchList(list, maxItems, skipCount, DEFAULT_MAX_TYPE_CHILDREN); 116 return new TypeDefinitionListImpl(list); 117 } 118 119 public List<TypeDefinitionContainer> getTypeDescendants(String typeId, int depth, Boolean includePropertyDefinitions) { 120 List<TypeDefinitionContainer> types; 121 boolean includeProps = Boolean.TRUE.equals(includePropertyDefinitions); 122 if (typeId == null) { 123 // return all types, unlimited depth 124 types = new ArrayList<TypeDefinitionContainer>(4); 125 for (TypeDefinitionContainer tc : typesMap.values()) { 126 if (tc.getTypeDefinition().getParentTypeId() == null) { 127 types.add(tc); 128 } 129 } 130 if (!includeProps) { 131 // remove props 132 types = cloneTypes(types, -1, false); 133 } 134 } else { 135 TypeDefinitionContainer typec = typesMap.get(typeId); 136 if (typec == null) { 137 throw new CmisInvalidArgumentException("No such type: " + typeId); 138 } 139 if (depth == 0 || depth < -1) { 140 throw new CmisInvalidArgumentException("Invalid depth: " + depth); 141 } 142 if (depth == -1) { 143 types = typec.getChildren(); 144 if (!includeProps) { 145 // remove props 146 types = cloneTypes(types, -1, false); 147 } 148 } else { 149 types = typec.getChildren(); 150 // truncate tree 151 types = cloneTypes(types, depth - 1, includeProps); 152 } 153 } 154 return types; 155 } 156 157 @Override 158 public Collection<TypeDefinitionContainer> getTypeDefinitionList() { 159 List<TypeDefinitionContainer> typeRoots = new ArrayList<TypeDefinitionContainer>(); 160 // iterate types map and return a list collecting the root types: 161 for (TypeDefinitionContainer typeCont : typesMap.values()) { 162 if (typeCont.getTypeDefinition().getParentTypeId() == null) { 163 typeRoots.add(typeCont); 164 } 165 } 166 return typeRoots; 167 } 168 169 @Override 170 public List<TypeDefinitionContainer> getRootTypes() { 171 List<TypeDefinitionContainer> rootTypes = new ArrayList<TypeDefinitionContainer>(); 172 for (TypeDefinitionContainer type : typesMap.values()) { 173 String id = type.getTypeDefinition().getId(); 174 if (BaseTypeId.CMIS_DOCUMENT.value().equals(id) || BaseTypeId.CMIS_FOLDER.value().equals(id) 175 || BaseTypeId.CMIS_RELATIONSHIP.value().equals(id) || BaseTypeId.CMIS_POLICY.value().equals(id)) { 176 rootTypes.add(type); 177 } 178 } 179 return rootTypes; 180 } 181 182 /** 183 * Add a type to the type system. Add type to children of parent types. If specified, add all properties from 184 * inherited types., 185 * 186 * @param type new type to add 187 * @param addInheritedProperties 188 */ 189 @Override 190 public void addTypeDefinition(TypeDefinition type, boolean addInheritedProperties) { 191 String id = type.getId(); 192 if (typesMap.containsKey(id)) { 193 throw new RuntimeException("Type already exists: " + id); 194 } 195 196 TypeDefinitionContainer typeContainer = new TypeDefinitionContainerImpl(type); 197 // add type to type map 198 typesMap.put(id, typeContainer); 199 200 String parentId = type.getParentTypeId(); 201 if (parentId != null) { 202 if (!typesMap.containsKey(parentId)) { 203 throw new RuntimeException("Cannot add type " + id + ", parent does not exist: " + parentId); 204 } 205 TypeDefinitionContainer parentTypeContainer = typesMap.get(parentId); 206 // add new type to children of parent types 207 parentTypeContainer.getChildren().add(typeContainer); 208 if (addInheritedProperties) { 209 // recursively add inherited properties 210 Map<String, PropertyDefinition<?>> propDefs = typeContainer.getTypeDefinition().getPropertyDefinitions(); 211 addInheritedProperties(propDefs, parentTypeContainer.getTypeDefinition()); 212 } 213 } 214 215 // prop query names 216 for (PropertyDefinition<?> pd : type.getPropertyDefinitions().values()) { 217 String propQueryName = pd.getQueryName(); 218 String propId = pd.getId(); 219 String old = propQueryNameToId.put(propQueryName, propId); 220 if (old != null && !old.equals(propId)) { 221 throw new RuntimeException("Cannot add type " + id + ", query name " + propQueryName 222 + " already used for property id " + old); 223 } 224 } 225 } 226 227 public void addTypeDefinition(TypeDefinition type) { 228 addTypeDefinition(type, true); 229 } 230 231 @Override 232 public String getPropertyIdForQueryName(TypeDefinition typeDefinition, String propQueryName) { 233 for (PropertyDefinition<?> pd : typeDefinition.getPropertyDefinitions().values()) { 234 if (pd.getQueryName().equals(propQueryName)) { 235 return pd.getId(); 236 } 237 } 238 return null; 239 } 240 241 public String getPropertyIdForQueryName(String propQueryName) { 242 return propQueryNameToId.get(propQueryName); 243 } 244 245 protected void addInheritedProperties(Map<String, PropertyDefinition<?>> propDefs, TypeDefinition type) { 246 if (type.getPropertyDefinitions() != null) { 247 addInheritedPropertyDefinitions(propDefs, type.getPropertyDefinitions()); 248 } 249 TypeDefinitionContainer parentTypeContainer = typesMap.get(type.getParentTypeId()); 250 if (parentTypeContainer != null) { 251 addInheritedProperties(propDefs, parentTypeContainer.getTypeDefinition()); 252 } 253 } 254 255 protected void addInheritedPropertyDefinitions(Map<String, PropertyDefinition<?>> propDefs, 256 Map<String, PropertyDefinition<?>> superPropDefs) { 257 for (PropertyDefinition<?> superPropDef : superPropDefs.values()) { 258 PropertyDefinition<?> clone = WSConverter.convert(WSConverter.convert(superPropDef)); 259 ((AbstractPropertyDefinition<?>) clone).setIsInherited(Boolean.TRUE); 260 propDefs.put(superPropDef.getId(), clone); 261 } 262 } 263 264 /** 265 * Returns a clone of a types tree. 266 * <p> 267 * Removes properties on the clone if requested, cuts the children of the clone if the depth is exceeded. 268 */ 269 protected static List<TypeDefinitionContainer> cloneTypes(List<TypeDefinitionContainer> types, int depth, 270 boolean includePropertyDefinitions) { 271 List<TypeDefinitionContainer> res = new ArrayList<TypeDefinitionContainer>(types.size()); 272 TypeDefinitionFactory tdFactory = TypeDefinitionFactory.newInstance(); 273 for (TypeDefinitionContainer tc : types) { 274 MutableTypeDefinition td = tdFactory.copy(tc.getTypeDefinition(), includePropertyDefinitions); 275 TypeDefinitionContainerImpl clone = new TypeDefinitionContainerImpl(td); 276 if (depth != 0) { 277 clone.setChildren(cloneTypes(tc.getChildren(), depth - 1, includePropertyDefinitions)); 278 } 279 res.add(clone); 280 } 281 return res; 282 } 283 284 @Override 285 public void updateTypeDefinition(TypeDefinition typeDefinition) { 286 throw new CmisNotSupportedException(); 287 } 288 289 @Override 290 public void deleteTypeDefinition(String typeId) { 291 throw new CmisNotSupportedException(); 292 } 293 294}