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<String, TypeDefinitionContainer>(); 057 058 protected Map<String, String> propQueryNameToId = new HashMap<String, String>(); 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<TypeDefinitionContainer>(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<TypeDefinition>(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<TypeDefinitionContainer>(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<TypeDefinitionContainer>(); 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<TypeDefinitionContainer>(); 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 * @param addInheritedProperties 200 */ 201 @Override 202 public void addTypeDefinition(TypeDefinition type, boolean addInheritedProperties) { 203 String id = type.getId(); 204 if (typesMap.containsKey(id)) { 205 throw new RuntimeException("Type already exists: " + id); 206 } 207 208 TypeDefinitionContainer typeContainer = new TypeDefinitionContainerImpl(type); 209 // add type to type map 210 typesMap.put(id, typeContainer); 211 212 String parentId = type.getParentTypeId(); 213 if (parentId != null) { 214 if (!typesMap.containsKey(parentId)) { 215 throw new RuntimeException("Cannot add type " + id + ", parent does not exist: " + parentId); 216 } 217 TypeDefinitionContainer parentTypeContainer = typesMap.get(parentId); 218 // add new type to children of parent types 219 parentTypeContainer.getChildren().add(typeContainer); 220 if (addInheritedProperties) { 221 // recursively add inherited properties 222 Map<String, PropertyDefinition<?>> propDefs = typeContainer.getTypeDefinition().getPropertyDefinitions(); 223 addInheritedProperties(propDefs, parentTypeContainer.getTypeDefinition()); 224 } 225 } 226 227 // prop query names 228 for (PropertyDefinition<?> pd : type.getPropertyDefinitions().values()) { 229 String propQueryName = pd.getQueryName(); 230 String propId = pd.getId(); 231 String old = propQueryNameToId.put(propQueryName, propId); 232 if (old != null && !old.equals(propId)) { 233 throw new RuntimeException("Cannot add type " + id + ", query name " + propQueryName 234 + " already used for property id " + old); 235 } 236 } 237 } 238 239 public void addTypeDefinition(TypeDefinition type) { 240 addTypeDefinition(type, true); 241 } 242 243 @Override 244 public String getPropertyIdForQueryName(TypeDefinition typeDefinition, String propQueryName) { 245 for (PropertyDefinition<?> pd : typeDefinition.getPropertyDefinitions().values()) { 246 if (pd.getQueryName().equals(propQueryName)) { 247 return pd.getId(); 248 } 249 } 250 return null; 251 } 252 253 public String getPropertyIdForQueryName(String propQueryName) { 254 return propQueryNameToId.get(propQueryName); 255 } 256 257 protected void addInheritedProperties(Map<String, PropertyDefinition<?>> propDefs, TypeDefinition type) { 258 if (type.getPropertyDefinitions() != null) { 259 addInheritedPropertyDefinitions(propDefs, type.getPropertyDefinitions()); 260 } 261 TypeDefinitionContainer parentTypeContainer = typesMap.get(type.getParentTypeId()); 262 if (parentTypeContainer != null) { 263 addInheritedProperties(propDefs, parentTypeContainer.getTypeDefinition()); 264 } 265 } 266 267 protected void addInheritedPropertyDefinitions(Map<String, PropertyDefinition<?>> propDefs, 268 Map<String, PropertyDefinition<?>> superPropDefs) { 269 for (PropertyDefinition<?> superPropDef : superPropDefs.values()) { 270 PropertyDefinition<?> clone = WSConverter.convert(WSConverter.convert(superPropDef)); 271 ((AbstractPropertyDefinition<?>) clone).setIsInherited(Boolean.TRUE); 272 propDefs.put(superPropDef.getId(), clone); 273 } 274 } 275 276 /** 277 * Returns a clone of a types tree. 278 * <p> 279 * Removes properties on the clone if requested, cuts the children of the clone if the depth is exceeded. 280 */ 281 protected static List<TypeDefinitionContainer> cloneTypes(List<TypeDefinitionContainer> types, int depth, 282 boolean includePropertyDefinitions) { 283 List<TypeDefinitionContainer> res = new ArrayList<TypeDefinitionContainer>(types.size()); 284 TypeDefinitionFactory tdFactory = TypeDefinitionFactory.newInstance(); 285 for (TypeDefinitionContainer tc : types) { 286 MutableTypeDefinition td = tdFactory.copy(tc.getTypeDefinition(), includePropertyDefinitions); 287 TypeDefinitionContainerImpl clone = new TypeDefinitionContainerImpl(td); 288 if (depth != 0) { 289 clone.setChildren(cloneTypes(tc.getChildren(), depth - 1, includePropertyDefinitions)); 290 } 291 res.add(clone); 292 } 293 return res; 294 } 295 296 @Override 297 public void updateTypeDefinition(TypeDefinition typeDefinition) { 298 throw new CmisNotSupportedException(); 299 } 300 301 @Override 302 public void deleteTypeDefinition(String typeId) { 303 throw new CmisNotSupportedException(); 304 } 305 306}