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