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