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 */ 018package org.nuxeo.ecm.core.opencmis.impl.server; 019 020import java.util.HashMap; 021import java.util.Map; 022 023import org.apache.chemistry.opencmis.commons.PropertyIds; 024import org.apache.chemistry.opencmis.commons.definitions.PropertyDefinition; 025import org.apache.chemistry.opencmis.commons.definitions.TypeDefinition; 026import org.apache.chemistry.opencmis.commons.enums.BaseTypeId; 027import org.apache.chemistry.opencmis.commons.enums.Cardinality; 028import org.apache.chemistry.opencmis.commons.enums.CmisVersion; 029import org.apache.chemistry.opencmis.commons.enums.ContentStreamAllowed; 030import org.apache.chemistry.opencmis.commons.enums.PropertyType; 031import org.apache.chemistry.opencmis.commons.enums.Updatability; 032import org.apache.chemistry.opencmis.commons.impl.dataobjects.AbstractPropertyDefinition; 033import org.apache.chemistry.opencmis.commons.impl.dataobjects.AbstractTypeDefinition; 034import org.apache.chemistry.opencmis.commons.impl.dataobjects.DocumentTypeDefinitionImpl; 035import org.apache.chemistry.opencmis.commons.impl.dataobjects.FolderTypeDefinitionImpl; 036import org.apache.chemistry.opencmis.commons.impl.dataobjects.PropertyBooleanDefinitionImpl; 037import org.apache.chemistry.opencmis.commons.impl.dataobjects.PropertyDateTimeDefinitionImpl; 038import org.apache.chemistry.opencmis.commons.impl.dataobjects.PropertyDecimalDefinitionImpl; 039import org.apache.chemistry.opencmis.commons.impl.dataobjects.PropertyHtmlDefinitionImpl; 040import org.apache.chemistry.opencmis.commons.impl.dataobjects.PropertyIdDefinitionImpl; 041import org.apache.chemistry.opencmis.commons.impl.dataobjects.PropertyIntegerDefinitionImpl; 042import org.apache.chemistry.opencmis.commons.impl.dataobjects.PropertyStringDefinitionImpl; 043import org.apache.chemistry.opencmis.commons.impl.dataobjects.PropertyUriDefinitionImpl; 044import org.apache.chemistry.opencmis.commons.impl.dataobjects.RelationshipTypeDefinitionImpl; 045import org.apache.chemistry.opencmis.commons.impl.dataobjects.SecondaryTypeDefinitionImpl; 046import org.apache.commons.logging.Log; 047import org.apache.commons.logging.LogFactory; 048import org.nuxeo.common.utils.Path; 049import org.nuxeo.ecm.core.api.DocumentModel; 050import org.nuxeo.ecm.core.api.blobholder.BlobHolder; 051import org.nuxeo.ecm.core.api.impl.DocumentModelImpl; 052import org.nuxeo.ecm.core.schema.DocumentType; 053import org.nuxeo.ecm.core.schema.FacetNames; 054import org.nuxeo.ecm.core.schema.Namespace; 055import org.nuxeo.ecm.core.schema.SchemaManager; 056import org.nuxeo.ecm.core.schema.types.CompositeType; 057import org.nuxeo.ecm.core.schema.types.Field; 058import org.nuxeo.ecm.core.schema.types.ListType; 059import org.nuxeo.ecm.core.schema.types.Schema; 060import org.nuxeo.ecm.core.schema.types.SimpleType; 061import org.nuxeo.ecm.core.schema.types.Type; 062import org.nuxeo.ecm.core.schema.types.primitives.BooleanType; 063import org.nuxeo.ecm.core.schema.types.primitives.DateType; 064import org.nuxeo.ecm.core.schema.types.primitives.DoubleType; 065import org.nuxeo.ecm.core.schema.types.primitives.LongType; 066import org.nuxeo.ecm.core.schema.types.primitives.StringType; 067import org.nuxeo.runtime.api.Framework; 068 069/** 070 * Nuxeo Type Utilities. 071 * <p> 072 * Maps Nuxeo types to CMIS types using the following rules: 073 * <ul> 074 * <li>Only types containing dublincore are exposed,</li> 075 * <li>cmis:document and cmis:folder expose dublincore, and are not creatable,</li> 076 * <li>The Document type is not exposed,</li> 077 * <li>Types inheriting from Document are exposed as inheriting cmis:document,</li> 078 * <li>The Folder type is mapped to a concrete subtype of cmis:folder,</li> 079 * <li>Other folderish types directly under Folder are mapped to subtypes of cmis:folder as well.</li> 080 * </ul> 081 */ 082public class NuxeoTypeHelper { 083 084 private static final Log log = LogFactory.getLog(NuxeoTypeHelper.class); 085 086 public static final String NUXEO_DOCUMENT = "Document"; 087 088 public static final String NUXEO_FOLDER = "Folder"; 089 090 public static final String NUXEO_RELATION = "Relation"; 091 092 public static final String NUXEO_RELATION_DEFAULT = "DefaultRelation"; 093 094 public static final String NUXEO_FILE = "File"; 095 096 public static final String NUXEO_ORDERED_FOLDER = "OrderedFolder"; 097 098 public static final String FACET_TYPE_PREFIX = "facet:"; 099 100 public static final String NX_DUBLINCORE = "dublincore"; 101 102 public static final String NX_DC_TITLE = "dc:title"; 103 104 public static final String NX_DC_DESCRIPTION = "dc:description"; 105 106 public static final String NX_DC_CREATED = "dc:created"; 107 108 public static final String NX_DC_CREATOR = "dc:creator"; 109 110 public static final String NX_DC_MODIFIED = "dc:modified"; 111 112 public static final String NX_DC_LAST_CONTRIBUTOR = "dc:lastContributor"; 113 114 public static final String NX_ICON = "common:icon"; 115 116 public static final String NX_REL_SOURCE = "relation:source"; 117 118 public static final String NX_REL_TARGET = "relation:target"; 119 120 public static final String NX_DIGEST = "nuxeo:contentStreamDigest"; 121 122 public static final String NX_ISVERSION = "nuxeo:isVersion"; 123 124 public static final String NX_ISCHECKEDIN = "nuxeo:isCheckedIn"; 125 126 /** 127 * @since 10.2 128 */ 129 public static final String NX_ISTRASHED = "nuxeo:isTrashed"; 130 131 public static final String NX_FACETS = "nuxeo:secondaryObjectTypeIds"; 132 133 public static final String NX_LIFECYCLE_STATE = "nuxeo:lifecycleState"; 134 135 public static final String NX_PARENT_ID = "nuxeo:parentId"; 136 137 public static final String NX_PATH_SEGMENT = "nuxeo:pathSegment"; 138 139 public static final String ENABLE_COMPLEX_PROPERTIES = "org.nuxeo.cmis.enableComplexProperties"; 140 141 /** @since 6.0 */ 142 public static final String NX_POS = "nuxeo:pos"; 143 144 private static final String NAMESPACE = "http://ns.nuxeo.org/cmis/type/"; 145 146 private static final String NAMESPACE_FACET = "http://ns.nuxeo.org/cmis/facet/"; 147 148 protected static boolean isComplexPropertiesEnabled() { 149 return !Framework.isBooleanPropertyFalse(ENABLE_COMPLEX_PROPERTIES); 150 } 151 152 protected AbstractTypeDefinition t; 153 154 // used to track down and log duplicates 155 protected Map<String, String> propertyToSchema; 156 157 protected CmisVersion cmisVersion; 158 159 /** 160 * Helper to construct one CMIS type from a {@link DocumentType}. 161 */ 162 protected NuxeoTypeHelper(String id, String parentId, BaseTypeId baseTypeId, DocumentType documentType, 163 String nuxeoTypeId, boolean creatable, CmisVersion cmisVersion) { 164 propertyToSchema = new HashMap<String, String>(); 165 this.cmisVersion = cmisVersion; 166 constructBaseDocumentType(id, parentId, baseTypeId, documentType, nuxeoTypeId, creatable); 167 } 168 169 /** 170 * Helper to construct one CMIS type from a secondary type. 171 */ 172 protected NuxeoTypeHelper(String id, String nuxeoTypeId, CmisVersion cmisVersion) { 173 propertyToSchema = new HashMap<String, String>(); 174 this.cmisVersion = cmisVersion; 175 constructBaseSecondaryType(id, nuxeoTypeId); 176 } 177 178 /** 179 * Gets the remapped parent type id, or {@code null} if the type is to be ignored. 180 */ 181 public static String getParentTypeId(DocumentType documentType) { 182 if (!documentType.hasSchema(NX_DUBLINCORE)) { 183 // ignore type without dublincore 184 return null; 185 } 186 if (documentType.getFacets().contains(FacetNames.HIDDEN_IN_NAVIGATION)) { 187 // ignore hiddeninnavigation type except if it's a relation 188 if (getBaseTypeId(documentType) != BaseTypeId.CMIS_RELATIONSHIP) { 189 return null; 190 } 191 } 192 String nuxeoTypeId = documentType.getName(); 193 // NUXEO_DOCUMENT already excluded be previous checks 194 if (NUXEO_FOLDER.equals(nuxeoTypeId)) { 195 // Folder has artificial parent cmis:folder 196 return BaseTypeId.CMIS_FOLDER.value(); 197 } 198 if (NUXEO_RELATION.equals(nuxeoTypeId)) { 199 // Relation has artificial parent cmis:relationship 200 return BaseTypeId.CMIS_RELATIONSHIP.value(); 201 } 202 DocumentType superType = (DocumentType) documentType.getSuperType(); 203 if (superType == null) { 204 return null; 205 } 206 String parentId = mappedId(superType.getName()); 207 if (NUXEO_FOLDER.equals(parentId)) { 208 // reparent Folder child under cmis:folder 209 parentId = BaseTypeId.CMIS_FOLDER.value(); 210 } 211 if (NUXEO_RELATION.equals(parentId)) { 212 // reparent Relation child under cmis:relationship 213 parentId = BaseTypeId.CMIS_RELATIONSHIP.value(); 214 } 215 if (NUXEO_DOCUMENT.equals(parentId)) { 216 // reparent Document child under cmis:document 217 parentId = BaseTypeId.CMIS_DOCUMENT.value(); 218 } 219 return parentId; 220 } 221 222 public static TypeDefinition constructDocumentType(DocumentType documentType, String parentId, 223 CmisVersion cmisVersion) { 224 String nuxeoTypeId = documentType.getName(); 225 String id = mappedId(nuxeoTypeId); 226 NuxeoTypeHelper h = new NuxeoTypeHelper(id, parentId, getBaseTypeId(documentType), documentType, nuxeoTypeId, 227 true, cmisVersion); 228 // Nuxeo Property Definitions 229 for (Schema schema : documentType.getSchemas()) { 230 h.addSchemaPropertyDefinitions(schema); 231 } 232 return h.t; 233 } 234 235 public static TypeDefinition constructSecondaryType(CompositeType type, CmisVersion cmisVersion) { 236 String nuxeoTypeId = type.getName(); 237 String id = FACET_TYPE_PREFIX + nuxeoTypeId; 238 NuxeoTypeHelper h = new NuxeoTypeHelper(id, nuxeoTypeId, cmisVersion); 239 // Nuxeo Property Definitions 240 for (Schema schema : type.getSchemas()) { 241 h.addSchemaPropertyDefinitions(schema); 242 } 243 return h.t; 244 } 245 246 /** 247 * Constructs a base type, not mapped to a Nuxeo type. If not a secondary, it has the dublincore schema. 248 */ 249 public static TypeDefinition constructCmisBase(BaseTypeId baseTypeId, SchemaManager schemaManager, 250 CmisVersion cmisVersion) { 251 NuxeoTypeHelper h; 252 if (baseTypeId != BaseTypeId.CMIS_SECONDARY) { 253 h = new NuxeoTypeHelper(baseTypeId.value(), null, baseTypeId, null, null, true, cmisVersion); 254 DocumentType dt = schemaManager.getDocumentType(NUXEO_FOLDER); // has dc 255 h.addSchemaPropertyDefinitions(dt.getSchema(NX_DUBLINCORE)); 256 } else { 257 h = new NuxeoTypeHelper(baseTypeId.value(), null, cmisVersion); 258 } 259 return h.t; 260 } 261 262 protected void addSchemaPropertyDefinitions(Schema schema) { 263 for (Field field : schema.getFields()) { 264 PropertyType propertyType; 265 Cardinality cardinality; 266 Type fieldType = field.getType(); 267 String schemaName = schema.getName(); 268 boolean queryable; 269 boolean orderable; 270 if (fieldType.isComplexType()) { 271 if (isComplexPropertiesEnabled()) { 272 // content is specifically excluded from the properties 273 if ("content".equals(fieldType.getName())) { 274 log.debug("Ignoring complex type: " + schemaName + '/' + field.getName() + " in type: " 275 + t.getId()); 276 continue; 277 } 278 // complex types get exposed to CMIS as a single string value; 279 // the NuxeoPropertyData class will marshal/unmarshal them as JSON. 280 cardinality = Cardinality.SINGLE; 281 propertyType = PropertyType.STRING; 282 queryable = false; 283 orderable = false; 284 } else { 285 log.debug("Ignoring complex type: " + schemaName + '/' + field.getName() + " in type: " + t.getId()); 286 continue; 287 } 288 } else { 289 if (fieldType.isListType()) { 290 Type listFieldType = ((ListType) fieldType).getFieldType(); 291 if (!listFieldType.isSimpleType()) { 292 if (isComplexPropertiesEnabled()) { 293 // complex lists get exposed to CMIS as a list of string values; 294 // the NuxeoPropertyData class will marshal/unmarshal them as JSON. 295 cardinality = Cardinality.MULTI; 296 propertyType = PropertyType.STRING; 297 queryable = false; 298 orderable = false; 299 } else { 300 log.debug("Ignoring complex type: " + schemaName + '/' + field.getName() + "in type: " 301 + t.getId()); 302 continue; 303 } 304 } else { 305 // array: use a collection table 306 cardinality = Cardinality.MULTI; 307 propertyType = getPropertType((SimpleType) listFieldType); 308 queryable = false; 309 orderable = false; 310 } 311 } else { 312 // primitive type 313 cardinality = Cardinality.SINGLE; 314 propertyType = getPropertType((SimpleType) fieldType); 315 queryable = true; 316 orderable = true; 317 } 318 } 319 String name = field.getName().getPrefixedName(); 320 PropertyDefinition<?> pd = newPropertyDefinition(name, name, propertyType, cardinality, 321 Updatability.READWRITE, false, false, queryable, orderable); 322 if (t.getPropertyDefinitions().containsKey(pd.getId())) { 323 log.error("Type '" + t.getId() + "' has duplicate property '" + name + "' in schemas '" 324 + propertyToSchema.get(pd.getId()) + "' and '" + schemaName + "', ignoring the one in '" 325 + schemaName + "'"); 326 continue; 327 } 328 propertyToSchema.put(pd.getId(), schemaName); 329 t.addPropertyDefinition(pd); 330 } 331 } 332 333 /** 334 * Constructs the base for a {@link DocumentType}. 335 */ 336 protected void constructBaseDocumentType(String id, String parentId, BaseTypeId baseTypeId, DocumentType documentType, 337 String nuxeoTypeId, boolean creatable) { 338 if (baseTypeId == BaseTypeId.CMIS_FOLDER) { 339 t = new FolderTypeDefinitionImpl(); 340 } else if (baseTypeId == BaseTypeId.CMIS_RELATIONSHIP) { 341 t = new RelationshipTypeDefinitionImpl(); 342 } else { 343 t = new DocumentTypeDefinitionImpl(); 344 } 345 t.setBaseTypeId(baseTypeId); 346 t.setId(id); 347 t.setParentTypeId(parentId); 348 t.setDescription(id); 349 t.setDisplayName(id); 350 t.setLocalName(nuxeoTypeId == null ? id : nuxeoTypeId); 351 Namespace ns = documentType == null ? null : documentType.getNamespace(); 352 t.setLocalNamespace(ns == null ? NAMESPACE : ns.uri); 353 t.setQueryName(id); 354 t.setIsCreatable(Boolean.valueOf(creatable)); 355 t.setIsQueryable(Boolean.TRUE); 356 t.setIsIncludedInSupertypeQuery(Boolean.TRUE); 357 t.setIsFulltextIndexed(Boolean.TRUE); 358 t.setIsControllableAcl(Boolean.TRUE); 359 t.setIsControllablePolicy(Boolean.FALSE); 360 addBasePropertyDefinitions(); 361 if (t instanceof FolderTypeDefinitionImpl) { 362 t.setIsFileable(Boolean.TRUE); 363 FolderTypeDefinitionImpl ft = (FolderTypeDefinitionImpl) t; 364 addFolderPropertyDefinitions(ft); 365 } else if (t instanceof RelationshipTypeDefinitionImpl) { 366 RelationshipTypeDefinitionImpl rt = (RelationshipTypeDefinitionImpl) t; 367 rt.setAllowedSourceTypes(null); 368 rt.setAllowedTargetTypes(null); 369 addRelationshipPropertyDefinitions(rt); 370 t.setIsFileable(Boolean.FALSE); 371 } else { 372 DocumentTypeDefinitionImpl dt = (DocumentTypeDefinitionImpl) t; 373 boolean versionable = documentType == null ? false : documentType.getFacets().contains( 374 FacetNames.VERSIONABLE); 375 dt.setIsVersionable(Boolean.valueOf(versionable)); 376 t.setIsFileable(Boolean.TRUE); 377 ContentStreamAllowed csa = (documentType != null && supportsBlobHolder(documentType)) ? ContentStreamAllowed.ALLOWED 378 : ContentStreamAllowed.NOTALLOWED; 379 dt.setContentStreamAllowed(csa); 380 addDocumentPropertyDefinitions(dt); 381 } 382 } 383 384 /** 385 * Constructs the base for a secondary type. 386 */ 387 protected void constructBaseSecondaryType(String id, String nuxeoTypeId) { 388 t = new SecondaryTypeDefinitionImpl(); 389 t.setBaseTypeId(BaseTypeId.CMIS_SECONDARY); 390 t.setId(id); 391 t.setParentTypeId(nuxeoTypeId == null ? null : BaseTypeId.CMIS_SECONDARY.value()); 392 t.setDescription(id); 393 t.setDisplayName(id); 394 t.setLocalName(nuxeoTypeId == null ? id : nuxeoTypeId); 395 t.setLocalNamespace(NAMESPACE_FACET); 396 t.setQueryName(id); 397 t.setIsCreatable(Boolean.FALSE); 398 t.setIsQueryable(Boolean.TRUE); 399 t.setIsIncludedInSupertypeQuery(Boolean.TRUE); 400 t.setIsFulltextIndexed(Boolean.TRUE); 401 t.setIsControllableAcl(Boolean.FALSE); 402 t.setIsControllablePolicy(Boolean.FALSE); 403 t.setIsFileable(Boolean.FALSE); 404 } 405 406 protected void addBasePropertyDefinitions() { 407 t.addPropertyDefinition(newPropertyDefinition(PropertyIds.OBJECT_ID, "Object ID", PropertyType.ID, 408 Cardinality.SINGLE, Updatability.READONLY, false, false, true, true)); 409 t.addPropertyDefinition(newPropertyDefinition(PropertyIds.OBJECT_TYPE_ID, "Type ID", PropertyType.ID, 410 Cardinality.SINGLE, Updatability.ONCREATE, false, true, false, false)); 411 t.addPropertyDefinition(newPropertyDefinition(PropertyIds.BASE_TYPE_ID, "Base Type ID", PropertyType.ID, 412 Cardinality.SINGLE, Updatability.READONLY, false, false, false, false)); 413 if (cmisVersion != CmisVersion.CMIS_1_0) { 414 t.addPropertyDefinition(newPropertyDefinition(PropertyIds.SECONDARY_OBJECT_TYPE_IDS, "Secondary Type IDs", 415 PropertyType.ID, Cardinality.MULTI, Updatability.READONLY, false, false, false, false)); 416 } 417 t.addPropertyDefinition(newPropertyDefinition(PropertyIds.NAME, "Name", PropertyType.STRING, 418 Cardinality.SINGLE, Updatability.READWRITE, false, true, true, true)); 419 if (cmisVersion != CmisVersion.CMIS_1_0) { 420 t.addPropertyDefinition(newPropertyDefinition(PropertyIds.DESCRIPTION, "Description", PropertyType.STRING, 421 Cardinality.SINGLE, Updatability.READWRITE, false, false, true, true)); 422 } 423 t.addPropertyDefinition(newPropertyDefinition(PropertyIds.CREATED_BY, "Created By", PropertyType.STRING, 424 Cardinality.SINGLE, Updatability.READONLY, false, false, true, true)); 425 t.addPropertyDefinition(newPropertyDefinition(PropertyIds.CREATION_DATE, "Creation Date", 426 PropertyType.DATETIME, Cardinality.SINGLE, Updatability.READONLY, false, false, true, true)); 427 t.addPropertyDefinition(newPropertyDefinition(PropertyIds.LAST_MODIFIED_BY, "Last Modified By", 428 PropertyType.STRING, Cardinality.SINGLE, Updatability.READONLY, false, false, true, true)); 429 t.addPropertyDefinition(newPropertyDefinition(PropertyIds.LAST_MODIFICATION_DATE, "Last Modification Date", 430 PropertyType.DATETIME, Cardinality.SINGLE, Updatability.READONLY, false, false, true, true)); 431 t.addPropertyDefinition(newPropertyDefinition(PropertyIds.CHANGE_TOKEN, "Change Token", PropertyType.STRING, 432 Cardinality.SINGLE, Updatability.READONLY, false, false, false, false)); 433 434 // Nuxeo system properties 435 t.addPropertyDefinition(newPropertyDefinition(NX_LIFECYCLE_STATE, "Lifecycle State", PropertyType.STRING, 436 Cardinality.SINGLE, Updatability.READONLY, false, false, true, true)); 437 t.addPropertyDefinition(newPropertyDefinition(NX_FACETS, "Facets", PropertyType.ID, Cardinality.MULTI, 438 Updatability.READONLY, false, false, true, false)); 439 t.addPropertyDefinition(newPropertyDefinition(NX_PARENT_ID, "Nuxeo Parent ID", PropertyType.ID, 440 Cardinality.SINGLE, Updatability.READONLY, false, false, true, true)); 441 t.addPropertyDefinition(newPropertyDefinition(NX_PATH_SEGMENT, "Path Segment", PropertyType.STRING, 442 Cardinality.SINGLE, Updatability.READONLY, false, false, true, true)); 443 t.addPropertyDefinition(newPropertyDefinition(NX_POS, "Position", PropertyType.INTEGER, Cardinality.SINGLE, 444 Updatability.READONLY, false, false, true, true)); 445 } 446 447 protected static void addFolderPropertyDefinitions(FolderTypeDefinitionImpl t) { 448 t.addPropertyDefinition(newPropertyDefinition(PropertyIds.PARENT_ID, "Parent ID", PropertyType.ID, 449 Cardinality.SINGLE, Updatability.READONLY, false, false, true, true)); 450 t.addPropertyDefinition(newPropertyDefinition(PropertyIds.PATH, "Path", PropertyType.STRING, 451 Cardinality.SINGLE, Updatability.READONLY, false, false, false, false)); 452 t.addPropertyDefinition(newPropertyDefinition(PropertyIds.ALLOWED_CHILD_OBJECT_TYPE_IDS, 453 "Allowed Child Object Type IDs", PropertyType.ID, Cardinality.MULTI, Updatability.READONLY, false, 454 false, false, false)); 455 } 456 457 protected static void addRelationshipPropertyDefinitions(RelationshipTypeDefinitionImpl t) { 458 t.addPropertyDefinition(newPropertyDefinition(PropertyIds.SOURCE_ID, "Source ID", PropertyType.ID, 459 Cardinality.SINGLE, Updatability.READWRITE, false, true, true, true)); 460 t.addPropertyDefinition(newPropertyDefinition(PropertyIds.TARGET_ID, "Target ID", PropertyType.ID, 461 Cardinality.SINGLE, Updatability.READWRITE, false, true, true, true)); 462 } 463 464 protected void addDocumentPropertyDefinitions(DocumentTypeDefinitionImpl t) { 465 t.addPropertyDefinition(newPropertyDefinition(PropertyIds.IS_IMMUTABLE, "Is Immutable", PropertyType.BOOLEAN, 466 Cardinality.SINGLE, Updatability.READONLY, false, false, false, false)); 467 t.addPropertyDefinition(newPropertyDefinition(PropertyIds.IS_LATEST_VERSION, "Is Latest Version", 468 PropertyType.BOOLEAN, Cardinality.SINGLE, Updatability.READONLY, false, false, true, true)); 469 t.addPropertyDefinition(newPropertyDefinition(PropertyIds.IS_MAJOR_VERSION, "Is Major Version", 470 PropertyType.BOOLEAN, Cardinality.SINGLE, Updatability.READONLY, false, false, false, false)); 471 t.addPropertyDefinition(newPropertyDefinition(PropertyIds.IS_LATEST_MAJOR_VERSION, "Is Latest Major Version", 472 PropertyType.BOOLEAN, Cardinality.SINGLE, Updatability.READONLY, false, false, true, true)); 473 t.addPropertyDefinition(newPropertyDefinition(PropertyIds.VERSION_LABEL, "Version Label", PropertyType.STRING, 474 Cardinality.SINGLE, Updatability.READONLY, false, false, true, true)); 475 t.addPropertyDefinition(newPropertyDefinition(PropertyIds.VERSION_SERIES_ID, "Version Series ID", 476 PropertyType.ID, Cardinality.SINGLE, Updatability.READONLY, false, false, false, false)); 477 t.addPropertyDefinition(newPropertyDefinition(PropertyIds.IS_VERSION_SERIES_CHECKED_OUT, 478 "Is Version Series Checked Out", PropertyType.BOOLEAN, Cardinality.SINGLE, Updatability.READONLY, 479 false, false, false, false)); 480 t.addPropertyDefinition(newPropertyDefinition(PropertyIds.VERSION_SERIES_CHECKED_OUT_BY, 481 "Version Series Checked Out By", PropertyType.STRING, Cardinality.SINGLE, Updatability.READONLY, false, 482 false, false, false)); 483 t.addPropertyDefinition(newPropertyDefinition(PropertyIds.VERSION_SERIES_CHECKED_OUT_ID, 484 "Version Series Checked Out ID", PropertyType.ID, Cardinality.SINGLE, Updatability.READONLY, false, 485 false, false, false)); 486 t.addPropertyDefinition(newPropertyDefinition(PropertyIds.CHECKIN_COMMENT, "Checkin Comment", 487 PropertyType.STRING, Cardinality.SINGLE, Updatability.READONLY, false, false, false, false)); 488 // mandatory properties even when content stream not allowed 489 t.addPropertyDefinition(newPropertyDefinition(PropertyIds.CONTENT_STREAM_LENGTH, "Content Stream Length", 490 PropertyType.INTEGER, Cardinality.SINGLE, Updatability.READONLY, false, false, false, false)); 491 t.addPropertyDefinition(newPropertyDefinition(PropertyIds.CONTENT_STREAM_MIME_TYPE, "MIME Type", 492 PropertyType.STRING, Cardinality.SINGLE, Updatability.READONLY, false, false, false, false)); 493 t.addPropertyDefinition(newPropertyDefinition(PropertyIds.CONTENT_STREAM_FILE_NAME, "Filename", 494 PropertyType.STRING, Cardinality.SINGLE, Updatability.READONLY, false, false, false, false)); 495 t.addPropertyDefinition(newPropertyDefinition(PropertyIds.CONTENT_STREAM_ID, "Content Stream ID", 496 PropertyType.ID, Cardinality.SINGLE, Updatability.READONLY, false, false, false, false)); 497 t.addPropertyDefinition(newPropertyDefinition(PropertyIds.CONTENT_STREAM_HASH, "Content Stream Hashes", 498 PropertyType.STRING, Cardinality.MULTI, Updatability.READONLY, false, false, false, false)); 499 // Nuxeo system properties 500 // TODO: make digest queryable at some point 501 t.addPropertyDefinition(newPropertyDefinition(NX_DIGEST, "Content Stream Digest", PropertyType.STRING, 502 Cardinality.SINGLE, Updatability.READONLY, false, false, false, false)); 503 t.addPropertyDefinition(newPropertyDefinition(NX_ISVERSION, "Is Version", PropertyType.BOOLEAN, 504 Cardinality.SINGLE, Updatability.READONLY, false, false, true, true)); 505 t.addPropertyDefinition(newPropertyDefinition(NX_ISCHECKEDIN, "Is Checked In PWC", PropertyType.BOOLEAN, 506 Cardinality.SINGLE, Updatability.READONLY, false, false, true, true)); 507 t.addPropertyDefinition(newPropertyDefinition(NX_ISTRASHED, "Is Trashed", PropertyType.BOOLEAN, 508 Cardinality.SINGLE, Updatability.READONLY, false, false, true, true)); 509 if (cmisVersion != CmisVersion.CMIS_1_0) { 510 t.addPropertyDefinition(newPropertyDefinition(PropertyIds.IS_PRIVATE_WORKING_COPY, "Is PWC", 511 PropertyType.BOOLEAN, Cardinality.SINGLE, Updatability.READONLY, false, false, false, false)); 512 } 513 } 514 515 protected static PropertyDefinition<?> newPropertyDefinition(String id, String displayName, 516 PropertyType propertyType, Cardinality cardinality, Updatability updatability, boolean inherited, 517 boolean required, boolean queryable, boolean orderable) { 518 AbstractPropertyDefinition<?> p; 519 switch (propertyType) { 520 case BOOLEAN: 521 p = new PropertyBooleanDefinitionImpl(); 522 break; 523 case DATETIME: 524 p = new PropertyDateTimeDefinitionImpl(); 525 break; 526 case DECIMAL: 527 p = new PropertyDecimalDefinitionImpl(); 528 break; 529 case HTML: 530 p = new PropertyHtmlDefinitionImpl(); 531 break; 532 case ID: 533 p = new PropertyIdDefinitionImpl(); 534 break; 535 case INTEGER: 536 p = new PropertyIntegerDefinitionImpl(); 537 break; 538 case STRING: 539 p = new PropertyStringDefinitionImpl(); 540 break; 541 case URI: 542 p = new PropertyUriDefinitionImpl(); 543 break; 544 default: 545 throw new RuntimeException(propertyType.toString()); 546 } 547 p.setId(id); 548 p.setDescription(displayName); 549 p.setDisplayName(displayName); 550 p.setLocalName(id); 551 p.setLocalNamespace(null); // TODO 552 p.setQueryName(id); 553 p.setPropertyType(propertyType); 554 p.setCardinality(cardinality); 555 p.setUpdatability(updatability); 556 p.setIsInherited(Boolean.valueOf(inherited)); 557 p.setIsRequired(Boolean.valueOf(required)); 558 p.setIsQueryable(Boolean.valueOf(queryable)); 559 p.setIsOrderable(Boolean.valueOf(orderable)); 560 return p; 561 } 562 563 // TODO update BlobHolderAdapterService to be able to do this 564 // without constructing a fake document 565 protected static boolean supportsBlobHolder(DocumentType documentType) { 566 DocumentModel doc = new DocumentModelImpl(null, documentType.getName(), null, new Path("/"), null, null, null, 567 documentType.getSchemaNames(), documentType.getFacets(), null, "default"); 568 return doc.getAdapter(BlobHolder.class) != null; 569 } 570 571 /** 572 * Turns a Nuxeo type into a CMIS type. 573 */ 574 protected static String mappedId(String id) { 575 // we don't map any Nuxeo type anymore to cmis:document or cmis:folder 576 return id; 577 } 578 579 protected static PropertyType getPropertType(SimpleType type) { 580 SimpleType primitive = type.getPrimitiveType(); 581 if (primitive == StringType.INSTANCE) { 582 return PropertyType.STRING; 583 } else if (primitive == BooleanType.INSTANCE) { 584 return PropertyType.BOOLEAN; 585 } else if (primitive == DateType.INSTANCE) { 586 return PropertyType.DATETIME; 587 } else if (primitive == LongType.INSTANCE) { 588 return PropertyType.INTEGER; 589 } else if (primitive == DoubleType.INSTANCE) { 590 return PropertyType.DECIMAL; 591 } else { 592 return PropertyType.STRING; 593 } 594 } 595 596 public static BaseTypeId getBaseTypeId(DocumentType type) { 597 if (type.isFolder()) { 598 return BaseTypeId.CMIS_FOLDER; 599 } 600 DocumentType t = type; 601 do { 602 if (NUXEO_RELATION.equals(t.getName())) { 603 return BaseTypeId.CMIS_RELATIONSHIP; 604 } 605 t = (DocumentType) t.getSuperType(); 606 } while (t != null); 607 return BaseTypeId.CMIS_DOCUMENT; 608 } 609 610 public static BaseTypeId getBaseTypeId(DocumentModel doc) { 611 return getBaseTypeId(doc.getDocumentType()); 612 } 613 614}