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