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