001/* 002 * (C) Copyright 2012-2018 Nuxeo (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 * Antoine Taillefer 018 */ 019package org.nuxeo.ecm.diff.service.impl; 020 021import java.io.Serializable; 022import java.util.ArrayList; 023import java.util.Arrays; 024import java.util.Calendar; 025import java.util.HashMap; 026import java.util.Iterator; 027import java.util.List; 028import java.util.Map; 029import java.util.Set; 030 031import org.apache.commons.collections.CollectionUtils; 032import org.apache.commons.lang3.StringUtils; 033import org.apache.commons.logging.Log; 034import org.apache.commons.logging.LogFactory; 035import org.nuxeo.ecm.core.api.DocumentModel; 036import org.nuxeo.ecm.core.api.NuxeoException; 037import org.nuxeo.ecm.core.schema.SchemaManager; 038import org.nuxeo.ecm.core.schema.types.Field; 039import org.nuxeo.ecm.core.schema.types.FieldImpl; 040import org.nuxeo.ecm.core.schema.types.QName; 041import org.nuxeo.ecm.core.schema.types.Type; 042import org.nuxeo.ecm.diff.content.ContentDiffHelper; 043import org.nuxeo.ecm.diff.model.DiffBlockDefinition; 044import org.nuxeo.ecm.diff.model.DiffComplexFieldDefinition; 045import org.nuxeo.ecm.diff.model.DiffDisplayBlock; 046import org.nuxeo.ecm.diff.model.DiffFieldDefinition; 047import org.nuxeo.ecm.diff.model.DiffFieldItemDefinition; 048import org.nuxeo.ecm.diff.model.DifferenceType; 049import org.nuxeo.ecm.diff.model.DocumentDiff; 050import org.nuxeo.ecm.diff.model.PropertyDiff; 051import org.nuxeo.ecm.diff.model.PropertyDiffDisplay; 052import org.nuxeo.ecm.diff.model.PropertyType; 053import org.nuxeo.ecm.diff.model.SchemaDiff; 054import org.nuxeo.ecm.diff.model.impl.ComplexPropertyDiff; 055import org.nuxeo.ecm.diff.model.impl.ContentDiffDisplayImpl; 056import org.nuxeo.ecm.diff.model.impl.ContentProperty; 057import org.nuxeo.ecm.diff.model.impl.ContentPropertyDiff; 058import org.nuxeo.ecm.diff.model.impl.DiffBlockDefinitionImpl; 059import org.nuxeo.ecm.diff.model.impl.DiffDisplayBlockImpl; 060import org.nuxeo.ecm.diff.model.impl.DiffFieldDefinitionImpl; 061import org.nuxeo.ecm.diff.model.impl.DiffFieldItemDefinitionImpl; 062import org.nuxeo.ecm.diff.model.impl.ListPropertyDiff; 063import org.nuxeo.ecm.diff.model.impl.PropertyDiffDisplayImpl; 064import org.nuxeo.ecm.diff.model.impl.SimplePropertyDiff; 065import org.nuxeo.ecm.diff.service.ComplexPropertyHelper; 066import org.nuxeo.ecm.diff.service.DiffDisplayService; 067import org.nuxeo.ecm.platform.forms.layout.api.BuiltinModes; 068import org.nuxeo.ecm.platform.forms.layout.api.FieldDefinition; 069import org.nuxeo.ecm.platform.forms.layout.api.LayoutDefinition; 070import org.nuxeo.ecm.platform.forms.layout.api.LayoutRowDefinition; 071import org.nuxeo.ecm.platform.forms.layout.api.WidgetDefinition; 072import org.nuxeo.ecm.platform.forms.layout.api.WidgetReference; 073import org.nuxeo.ecm.platform.forms.layout.api.impl.FieldDefinitionImpl; 074import org.nuxeo.ecm.platform.forms.layout.api.impl.LayoutDefinitionImpl; 075import org.nuxeo.ecm.platform.forms.layout.api.impl.LayoutRowDefinitionImpl; 076import org.nuxeo.ecm.platform.forms.layout.api.impl.WidgetDefinitionImpl; 077import org.nuxeo.ecm.platform.forms.layout.api.impl.WidgetReferenceImpl; 078import org.nuxeo.ecm.platform.forms.layout.api.service.LayoutStore; 079import org.nuxeo.runtime.api.Framework; 080import org.nuxeo.runtime.model.ComponentInstance; 081import org.nuxeo.runtime.model.DefaultComponent; 082 083/** 084 * Default implementation of the {@link DiffDisplayService}. 085 * 086 * @author Antoine Taillefer ([email protected]) 087 * @since 5.6 088 */ 089public class DiffDisplayServiceImpl extends DefaultComponent implements DiffDisplayService { 090 091 private static final long serialVersionUID = 6608445970773402827L; 092 093 private static final Log LOGGER = LogFactory.getLog(DiffDisplayServiceImpl.class); 094 095 protected static final String DIFF_DISPLAY_EXTENSION_POINT = "diffDisplay"; 096 097 protected static final String DIFF_DEFAULT_DISPLAY_EXTENSION_POINT = "diffDefaultDisplay"; 098 099 protected static final String DIFF_BLOCK_EXTENSION_POINT = "diffBlock"; 100 101 protected static final String DIFF_WIDGET_CATEGORY = "diff"; 102 103 protected static final String DIFF_BLOCK_DEFAULT_TEMPLATE_PATH = "/layouts/layout_diff_template.xhtml"; 104 105 protected static final String DIFF_BLOCK_LABEL_PROPERTY_NAME = "label"; 106 107 protected static final String DIFF_BLOCK_DEFAULT_LABEL_PREFIX = "label.diffBlock."; 108 109 protected static final String DIFF_WIDGET_LABEL_PREFIX = "label."; 110 111 protected static final String CONTENT_DIFF_LINKS_WIDGET_NAME = "contentDiffLinks"; 112 113 protected static final String CONTENT_DIFF_LINKS_WIDGET_NAME_SUFFIX = "_contentDiffLinks"; 114 115 protected static final String DIFF_WIDGET_FIELD_DEFINITION_VALUE = "value"; 116 117 protected static final String DIFF_WIDGET_FIELD_DEFINITION_DIFFERENCE_TYPE = "differenceType"; 118 119 protected static final String DIFF_WIDGET_FIELD_DEFINITION_STYLE_CLASS = "styleClass"; 120 121 protected static final String DIFF_WIDGET_FIELD_DEFINITION_FILENAME = "filename"; 122 123 protected static final String DIFF_WIDGET_FIELD_DEFINITION_DISPLAY_HTML_CONVERSION = "displayHtmlConversion"; 124 125 protected static final String DIFF_WIDGET_FIELD_DEFINITION_DISPLAY_TEXT_CONVERSION = "displayTextConversion"; 126 127 protected static final String DIFF_WIDGET_PROPERTY_DISPLAY_ALL_ITEMS = "displayAllItems"; 128 129 protected static final String DIFF_WIDGET_PROPERTY_DISPLAY_ITEM_INDEXES = "displayItemIndexes"; 130 131 // TODO: refactor name (not related to widget) 132 protected static final String DIFF_LIST_WIDGET_INDEX_SUBWIDGET_FIELD = "index"; 133 134 protected static final String DIFF_LIST_WIDGET_INDEX_SUBWIDGET_TYPE = "int"; 135 136 protected static final String DIFF_LIST_WIDGET_INDEX_SUBWIDGET_LABEL = "label.list.index"; 137 138 protected static final String DIFF_LIST_WIDGET_VALUE_SUBWIDGET_FIELD = "value"; 139 140 /** Diff excluded fields contributions. */ 141 protected Map<String, List<String>> diffExcludedFieldsContribs = new HashMap<>(); 142 143 /** Diff complex fields contributions. */ 144 protected Map<String, Map<String, DiffComplexFieldDefinition>> diffComplexFieldsContribs = new HashMap<>(); 145 146 /** Diff display contributions. */ 147 protected Map<String, List<String>> diffDisplayContribs = new HashMap<>(); 148 149 /** Diff block contributions. */ 150 protected Map<String, DiffBlockDefinition> diffBlockContribs = new HashMap<>(); 151 152 @Override 153 public void registerContribution(Object contribution, String extensionPoint, ComponentInstance contributor) { 154 155 if (DIFF_DEFAULT_DISPLAY_EXTENSION_POINT.equals(extensionPoint)) { 156 if (contribution instanceof DiffExcludedFieldsDescriptor) { 157 registerDiffExcludedFields((DiffExcludedFieldsDescriptor) contribution); 158 } else if (contribution instanceof DiffComplexFieldDescriptor) { 159 registerDiffComplexField((DiffComplexFieldDescriptor) contribution); 160 } 161 } else if (DIFF_DISPLAY_EXTENSION_POINT.equals(extensionPoint)) { 162 if (contribution instanceof DiffDisplayDescriptor) { 163 registerDiffDisplay((DiffDisplayDescriptor) contribution); 164 } 165 } else if (DIFF_BLOCK_EXTENSION_POINT.equals(extensionPoint)) { 166 if (contribution instanceof DiffBlockDescriptor) { 167 registerDiffBlock((DiffBlockDescriptor) contribution); 168 } 169 } 170 super.registerContribution(contribution, extensionPoint, contributor); 171 } 172 173 @Override 174 public Map<String, List<String>> getDiffExcludedSchemas() { 175 return diffExcludedFieldsContribs; 176 } 177 178 @Override 179 public List<String> getDiffExcludedFields(String schemaName) { 180 return diffExcludedFieldsContribs.get(schemaName); 181 } 182 183 @Override 184 public List<DiffComplexFieldDefinition> getDiffComplexFields() { 185 List<DiffComplexFieldDefinition> diffComplexFields = new ArrayList<>(); 186 for (Map<String, DiffComplexFieldDefinition> diffComplexFieldsBySchema : diffComplexFieldsContribs.values()) { 187 for (DiffComplexFieldDefinition diffComplexField : diffComplexFieldsBySchema.values()) { 188 diffComplexFields.add(diffComplexField); 189 } 190 } 191 return diffComplexFields; 192 } 193 194 @Override 195 public DiffComplexFieldDefinition getDiffComplexField(String schemaName, String fieldName) { 196 Map<String, DiffComplexFieldDefinition> diffComplexFieldsBySchema = diffComplexFieldsContribs.get(schemaName); 197 if (diffComplexFieldsBySchema != null) { 198 return diffComplexFieldsBySchema.get(fieldName); 199 } 200 return null; 201 } 202 203 @Override 204 public Map<String, List<String>> getDiffDisplays() { 205 return diffDisplayContribs; 206 } 207 208 @Override 209 public List<String> getDiffDisplay(String docType) { 210 return diffDisplayContribs.get(docType); 211 212 } 213 214 @Override 215 public Map<String, DiffBlockDefinition> getDiffBlockDefinitions() { 216 return diffBlockContribs; 217 } 218 219 @Override 220 public DiffBlockDefinition getDiffBlockDefinition(String name) { 221 return diffBlockContribs.get(name); 222 } 223 224 @Override 225 public List<DiffDisplayBlock> getDiffDisplayBlocks(DocumentDiff docDiff, DocumentModel leftDoc, 226 DocumentModel rightDoc) { 227 228 String leftDocType = leftDoc.getType(); 229 String rightDocType = rightDoc.getType(); 230 if (leftDocType.equals(rightDocType)) { 231 LOGGER.info(String.format( 232 "The 2 documents have the same type '%s' => looking for a diffDisplay contribution defined for this type or the nearest super type.", 233 leftDocType)); 234 List<String> diffBlockRefs = getNearestSuperTypeDiffDisplay(leftDocType); 235 if (diffBlockRefs != null) { 236 LOGGER.info(String.format( 237 "Found a diffDisplay contribution defined for the type '%s' or one of its super type => using it to display the diff.", 238 leftDocType)); 239 return getDiffDisplayBlocks(getDiffBlockDefinitions(diffBlockRefs), docDiff, leftDoc, rightDoc); 240 } else { 241 LOGGER.info(String.format( 242 "No diffDisplay contribution was defined for the type '%s' or one of its super type => using default diff display.", 243 leftDocType)); 244 } 245 } else { 246 LOGGER.info(String.format( 247 "The 2 documents don't have the same type: '%s'/'%s' => looking for a diffDisplay contribution defined for the nearest common super type.", 248 leftDocType, rightDocType)); 249 List<String> diffBlockRefs = getNearestSuperTypeDiffDisplay(leftDocType, rightDocType); 250 if (diffBlockRefs != null) { 251 LOGGER.info(String.format( 252 "Found a diffDisplay contribution defined for a common super type of the types '%s'/'%s' => using it to display the diff.", 253 leftDocType, rightDocType)); 254 return getDiffDisplayBlocks(getDiffBlockDefinitions(diffBlockRefs), docDiff, leftDoc, rightDoc); 255 } else { 256 LOGGER.info(String.format( 257 "No diffDisplay contribution was defined for any of the common super types of the types '%s'/'%s' => using default diff display.", 258 leftDocType, rightDocType)); 259 } 260 } 261 return getDefaultDiffDisplayBlocks(docDiff, leftDoc, rightDoc); 262 } 263 264 public List<DiffDisplayBlock> getDefaultDiffDisplayBlocks(DocumentDiff docDiff, DocumentModel leftDoc, 265 DocumentModel rightDoc) { 266 267 return getDiffDisplayBlocks(getDefaultDiffBlockDefinitions(docDiff), docDiff, leftDoc, rightDoc); 268 } 269 270 protected List<String> getNearestSuperTypeDiffDisplay(String docTypeName) { 271 272 List<String> diffDisplay = getDiffDisplay(docTypeName); 273 Type docType = getSchemaManager().getDocumentType(docTypeName); 274 while (diffDisplay == null && docType != null) { 275 Type superType = docType.getSuperType(); 276 if (superType != null) { 277 diffDisplay = getDiffDisplay(superType.getName()); 278 } 279 docType = superType; 280 } 281 return diffDisplay; 282 } 283 284 protected List<String> getNearestSuperTypeDiffDisplay(String leftDocTypeName, String rightDocTypeName) { 285 List<String> leftDocSuperTypeNames = new ArrayList<>(); 286 List<String> rightDocSuperTypeNames = new ArrayList<>(); 287 leftDocSuperTypeNames.add(leftDocTypeName); 288 rightDocSuperTypeNames.add(rightDocTypeName); 289 290 Type leftDocType = getSchemaManager().getDocumentType(leftDocTypeName); 291 Type rightDocType = getSchemaManager().getDocumentType(rightDocTypeName); 292 if (leftDocType != null && rightDocType != null) { 293 Type[] leftDocTypeHierarchy = leftDocType.getTypeHierarchy(); 294 for (Type type : leftDocTypeHierarchy) { 295 leftDocSuperTypeNames.add(type.getName()); 296 } 297 Type[] rightDocTypeHierarchy = rightDocType.getTypeHierarchy(); 298 for (Type type : rightDocTypeHierarchy) { 299 rightDocSuperTypeNames.add(type.getName()); 300 } 301 } 302 303 for (String superTypeName : leftDocSuperTypeNames) { 304 if (rightDocSuperTypeNames.contains(superTypeName)) { 305 return getNearestSuperTypeDiffDisplay(superTypeName); 306 } 307 } 308 309 return null; 310 } 311 312 /** 313 * Registers a diff excluded fields contrib. 314 */ 315 protected final void registerDiffExcludedFields(DiffExcludedFieldsDescriptor descriptor) { 316 317 String schemaName = descriptor.getSchema(); 318 if (!StringUtils.isEmpty(schemaName)) { 319 boolean enabled = descriptor.isEnabled(); 320 // Check existing diffExcludedFields contrib for this schema 321 List<String> diffExcludedFields = diffExcludedFieldsContribs.get(schemaName); 322 if (diffExcludedFields != null) { 323 // If !enabled remove contrib 324 if (!enabled) { 325 diffExcludedFieldsContribs.remove(schemaName); 326 } 327 // Else override contrib (no merge) 328 // TODO: implement merge 329 else { 330 diffExcludedFieldsContribs.put(schemaName, getDiffExcludedFieldRefs(descriptor.getFields())); 331 } 332 } 333 // No existing diffExcludedFields contrib for this 334 // schema and enabled => add contrib 335 else if (enabled) { 336 diffExcludedFieldsContribs.put(schemaName, getDiffExcludedFieldRefs(descriptor.getFields())); 337 } 338 } 339 } 340 341 /** 342 * Registers a diff complex field contrib. 343 */ 344 protected final void registerDiffComplexField(DiffComplexFieldDescriptor descriptor) { 345 346 String schemaName = descriptor.getSchema(); 347 String fieldName = descriptor.getName(); 348 if (!StringUtils.isEmpty(schemaName) && !StringUtils.isEmpty(fieldName)) { 349 // Check existing diffComplexField contrib for this schema/field 350 DiffComplexFieldDefinition diffComplexField = getDiffComplexField(schemaName, fieldName); 351 if (diffComplexField != null) { 352 // Override contrib (no merge) 353 // TODO: implement merge 354 diffComplexFieldsContribs.get(schemaName).put(fieldName, descriptor.getDiffComplexFieldDefinition()); 355 } 356 // No existing diffComplexField contrib for this 357 // schema/field => add contrib 358 else { 359 diffComplexFieldsContribs.computeIfAbsent(schemaName, k -> new HashMap<>()) // 360 .put(fieldName, descriptor.getDiffComplexFieldDefinition()); 361 } 362 } 363 } 364 365 /** 366 * Registers a diff display contrib. 367 * 368 * @param descriptor the contribution 369 */ 370 protected final void registerDiffDisplay(DiffDisplayDescriptor descriptor) { 371 372 String docType = descriptor.getType(); 373 if (!StringUtils.isEmpty(docType)) { 374 boolean enabled = descriptor.isEnabled(); 375 // Check existing diffDisplay contrib for this type 376 List<String> diffDisplay = diffDisplayContribs.get(docType); 377 if (diffDisplay != null) { 378 // If !enabled remove contrib 379 if (!enabled) { 380 diffDisplayContribs.remove(docType); 381 } 382 // Else override contrib (no merge) 383 // TODO: implement merge 384 else { 385 diffDisplayContribs.put(docType, getDiffBlockRefs(descriptor.getDiffBlocks())); 386 } 387 } 388 // No existing diffDisplay contrib for this 389 // type and enabled => add contrib 390 else if (enabled) { 391 diffDisplayContribs.put(docType, getDiffBlockRefs(descriptor.getDiffBlocks())); 392 } 393 } 394 } 395 396 protected final List<String> getDiffBlockRefs(List<DiffBlockReferenceDescriptor> diffBlocks) { 397 398 List<String> diffBlockRefs = new ArrayList<>(); 399 for (DiffBlockReferenceDescriptor diffBlockRef : diffBlocks) { 400 diffBlockRefs.add(diffBlockRef.getName()); 401 } 402 return diffBlockRefs; 403 } 404 405 protected final List<String> getDiffExcludedFieldRefs(List<DiffFieldDescriptor> diffExcludedFields) { 406 407 List<String> diffExcludedFieldRefs = new ArrayList<>(); 408 for (DiffFieldDescriptor diffExcludedFieldRef : diffExcludedFields) { 409 diffExcludedFieldRefs.add(diffExcludedFieldRef.getName()); 410 } 411 return diffExcludedFieldRefs; 412 } 413 414 protected final void registerDiffBlock(DiffBlockDescriptor descriptor) { 415 416 String diffBlockName = descriptor.getName(); 417 if (!StringUtils.isEmpty(diffBlockName)) { 418 List<DiffFieldDescriptor> fieldDescriptors = descriptor.getFields(); 419 // No field descriptors => don't take diff block into account. 420 if (fieldDescriptors == null || fieldDescriptors.isEmpty()) { 421 LOGGER.warn(String.format( 422 "The diffBlock contribution named '%s' has no fields, it won't be taken into account.", 423 diffBlockName)); 424 } else { 425 List<DiffFieldDefinition> fields = new ArrayList<>(); 426 // Some field descriptors were found => use them to add the 427 // described fields, taking their order into account. 428 for (DiffFieldDescriptor fieldDescriptor : fieldDescriptors) { 429 String category = fieldDescriptor.getCategory(); 430 String schema = fieldDescriptor.getSchema(); 431 String name = fieldDescriptor.getName(); 432 boolean displayContentDiffLinks = fieldDescriptor.isDisplayContentDiffLinks(); 433 List<DiffFieldItemDescriptor> fieldItemDescriptors = fieldDescriptor.getItems(); 434 if (!StringUtils.isEmpty(schema) && !StringUtils.isEmpty(name)) { 435 List<DiffFieldItemDefinition> items = new ArrayList<>(); 436 for (DiffFieldItemDescriptor fieldItemDescriptor : fieldItemDescriptors) { 437 items.add(new DiffFieldItemDefinitionImpl(fieldItemDescriptor.getName(), 438 fieldItemDescriptor.isDisplayContentDiffLinks())); 439 } 440 fields.add(new DiffFieldDefinitionImpl(category, schema, name, displayContentDiffLinks, items)); 441 } 442 } 443 // TODO: implement merge 444 diffBlockContribs.put(diffBlockName, new DiffBlockDefinitionImpl(diffBlockName, 445 descriptor.getTemplates(), fields, descriptor.getProperties())); 446 } 447 } 448 } 449 450 protected final List<DiffBlockDefinition> getDefaultDiffBlockDefinitions(DocumentDiff docDiff) { 451 452 List<DiffBlockDefinition> diffBlockDefs = new ArrayList<>(); 453 454 for (String schemaName : docDiff.getSchemaNames()) { 455 List<String> diffExcludedFields = getDiffExcludedFields(schemaName); 456 // Only add the schema fields if the whole schema is not excluded 457 if (diffExcludedFields == null || diffExcludedFields.size() > 0) { 458 SchemaDiff schemaDiff = docDiff.getSchemaDiff(schemaName); 459 List<DiffFieldDefinition> fieldDefs = new ArrayList<>(); 460 for (String fieldName : schemaDiff.getFieldNames()) { 461 // Only add the field if it is not excluded 462 if (diffExcludedFields == null || !diffExcludedFields.contains(fieldName)) { 463 List<DiffFieldItemDefinition> fieldItems = new ArrayList<>(); 464 DiffComplexFieldDefinition complexFieldDef = getDiffComplexField(schemaName, fieldName); 465 if (complexFieldDef != null) { 466 List<DiffFieldItemDefinition> includedItems = complexFieldDef.getIncludedItems(); 467 List<DiffFieldItemDefinition> excludedItems = complexFieldDef.getExcludedItems(); 468 // Check included field items 469 if (!CollectionUtils.isEmpty(includedItems)) { 470 fieldItems.addAll(includedItems); 471 } 472 // Check excluded field items 473 else if (!CollectionUtils.isEmpty(excludedItems)) { 474 Field complexField = ComplexPropertyHelper.getField(schemaName, fieldName); 475 if (complexField.getType().isListType()) { 476 complexField = ComplexPropertyHelper.getListFieldItem(complexField); 477 } 478 if (!complexField.getType().isComplexType()) { 479 throw new NuxeoException(String.format( 480 "Cannot compute field items for [%s:%s] since it is not a complex nor a complex list property.", 481 schemaName, fieldName)); 482 } 483 List<Field> complexFieldItems = ComplexPropertyHelper.getComplexFieldItems( 484 complexField); 485 for (Field complexFieldItem : complexFieldItems) { 486 String complexFieldItemName = complexFieldItem.getName().getLocalName(); 487 boolean isFieldItem = true; 488 for (DiffFieldItemDefinition fieldItemDef : excludedItems) { 489 if (fieldItemDef.getName().equals(complexFieldItemName)) { 490 isFieldItem = false; 491 break; 492 } 493 } 494 if (isFieldItem) { 495 fieldItems.add(new DiffFieldItemDefinitionImpl(complexFieldItemName)); 496 } 497 } 498 } 499 } 500 fieldDefs.add( 501 new DiffFieldDefinitionImpl(DIFF_WIDGET_CATEGORY, schemaName, fieldName, fieldItems)); 502 } 503 } 504 505 Map<String, String> defaultDiffBlockTemplates = new HashMap<>(); 506 defaultDiffBlockTemplates.put(BuiltinModes.ANY, DIFF_BLOCK_DEFAULT_TEMPLATE_PATH); 507 508 Map<String, Map<String, Serializable>> defaultDiffBlockProperties = new HashMap<>(); 509 Map<String, Serializable> labelProperty = new HashMap<>(); 510 labelProperty.put(DIFF_BLOCK_LABEL_PROPERTY_NAME, DIFF_BLOCK_DEFAULT_LABEL_PREFIX + schemaName); 511 defaultDiffBlockProperties.put(BuiltinModes.ANY, labelProperty); 512 513 diffBlockDefs.add(new DiffBlockDefinitionImpl(schemaName, defaultDiffBlockTemplates, fieldDefs, 514 defaultDiffBlockProperties)); 515 } 516 } 517 518 return diffBlockDefs; 519 } 520 521 protected final List<DiffBlockDefinition> getDiffBlockDefinitions(List<String> diffBlockRefs) { 522 523 List<DiffBlockDefinition> diffBlockDefinitions = new ArrayList<>(); 524 for (String diffBlockRef : diffBlockRefs) { 525 diffBlockDefinitions.add(getDiffBlockDefinition(diffBlockRef)); 526 } 527 return diffBlockDefinitions; 528 } 529 530 protected final List<DiffDisplayBlock> getDiffDisplayBlocks(List<DiffBlockDefinition> diffBlockDefinitions, 531 DocumentDiff docDiff, DocumentModel leftDoc, DocumentModel rightDoc) { 532 533 List<DiffDisplayBlock> diffDisplayBlocks = new ArrayList<>(); 534 535 for (DiffBlockDefinition diffBlockDef : diffBlockDefinitions) { 536 if (diffBlockDef != null) { 537 DiffDisplayBlock diffDisplayBlock = getDiffDisplayBlock(diffBlockDef, docDiff, leftDoc, rightDoc); 538 if (!diffDisplayBlock.isEmpty()) { 539 diffDisplayBlocks.add(diffDisplayBlock); 540 } 541 } 542 } 543 544 return diffDisplayBlocks; 545 } 546 547 protected final DiffDisplayBlock getDiffDisplayBlock(DiffBlockDefinition diffBlockDefinition, DocumentDiff docDiff, 548 DocumentModel leftDoc, DocumentModel rightDoc) { 549 550 Map<String, Map<String, PropertyDiffDisplay>> leftValue = new HashMap<>(); 551 Map<String, Map<String, PropertyDiffDisplay>> rightValue = new HashMap<>(); 552 Map<String, Map<String, PropertyDiffDisplay>> contentDiffValue = new HashMap<>(); 553 554 List<LayoutRowDefinition> layoutRowDefinitions = new ArrayList<>(); 555 List<WidgetDefinition> widgetDefinitions = new ArrayList<>(); 556 557 List<DiffFieldDefinition> fieldDefinitions = diffBlockDefinition.getFields(); 558 for (DiffFieldDefinition fieldDefinition : fieldDefinitions) { 559 560 String category = fieldDefinition.getCategory(); 561 if (StringUtils.isEmpty(category)) { 562 category = DIFF_WIDGET_CATEGORY; 563 } 564 String schemaName = fieldDefinition.getSchema(); 565 String fieldName = fieldDefinition.getName(); 566 boolean displayContentDiffLinks = fieldDefinition.isDisplayContentDiffLinks(); 567 List<DiffFieldItemDefinition> fieldItemDefs = fieldDefinition.getItems(); 568 569 SchemaDiff schemaDiff = docDiff.getSchemaDiff(schemaName); 570 if (schemaDiff != null) { 571 PropertyDiff fieldDiff = schemaDiff.getFieldDiff(fieldName); 572 if (fieldDiff != null) { 573 574 Serializable leftProperty = (Serializable) leftDoc.getProperty(schemaName, fieldName); 575 Serializable rightProperty = (Serializable) rightDoc.getProperty(schemaName, fieldName); 576 577 // Only include field diff if it is significant 578 if (isFieldDiffSignificant(leftProperty, rightProperty)) { 579 580 String propertyName = getPropertyName(schemaName, fieldName); 581 List<WidgetReference> widgetReferences = new ArrayList<>(); 582 583 // Set property widget definition 584 WidgetDefinition propertyWidgetDefinition = getWidgetDefinition(category, propertyName, 585 fieldDiff.getPropertyType(), null, fieldItemDefs, false); 586 widgetDefinitions.add(propertyWidgetDefinition); 587 // Set property widget ref 588 WidgetReferenceImpl propertyWidgetRef = new WidgetReferenceImpl(category, propertyName); 589 widgetReferences.add(propertyWidgetRef); 590 591 // Check if must display the content diff links widget 592 if (!displayContentDiffLinks) { 593 for (DiffFieldItemDefinition fieldItemDef : fieldItemDefs) { 594 if (fieldItemDef.isDisplayContentDiffLinks()) { 595 displayContentDiffLinks = true; 596 break; 597 } 598 } 599 } 600 // Set content diff links widget definition and ref if 601 // needed 602 if (displayContentDiffLinks) { 603 WidgetDefinition contentDiffLinksWidgetDefinition = getWidgetDefinition(category, 604 propertyName, fieldDiff.getPropertyType(), null, fieldItemDefs, true); 605 widgetDefinitions.add(contentDiffLinksWidgetDefinition); 606 WidgetReferenceImpl contentDiffLinksWidgetRef = new WidgetReferenceImpl(category, 607 propertyName + CONTENT_DIFF_LINKS_WIDGET_NAME_SUFFIX); 608 widgetReferences.add(contentDiffLinksWidgetRef); 609 } 610 611 // Set layout row definition 612 LayoutRowDefinition layoutRowDefinition = new LayoutRowDefinitionImpl(propertyName, null, 613 widgetReferences, false, true); 614 layoutRowDefinitions.add(layoutRowDefinition); 615 616 // Set diff display field value 617 boolean isDisplayAllItems = isDisplayAllItems(propertyWidgetDefinition); 618 boolean isDisplayItemIndexes = isDisplayItemIndexes(propertyWidgetDefinition); 619 620 // Left diff display 621 setFieldDiffDisplay(leftProperty, fieldDiff, isDisplayAllItems, isDisplayItemIndexes, leftValue, 622 schemaName, fieldName, leftDoc, PropertyDiffDisplay.RED_BACKGROUND_STYLE_CLASS); 623 624 // Right diff display 625 setFieldDiffDisplay(rightProperty, fieldDiff, isDisplayAllItems, isDisplayItemIndexes, 626 rightValue, schemaName, fieldName, rightDoc, 627 PropertyDiffDisplay.GREEN_BACKGROUND_STYLE_CLASS); 628 629 // Content diff display 630 if (displayContentDiffLinks) { 631 PropertyDiffDisplay contentDiffDisplay = getFieldXPaths(propertyName, fieldDiff, 632 leftProperty, rightProperty, isDisplayAllItems, isDisplayItemIndexes, 633 fieldItemDefs); 634 contentDiffValue.computeIfAbsent(schemaName, k -> new HashMap<>()) // 635 .put(fieldName, contentDiffDisplay); 636 } 637 } 638 } 639 } 640 } 641 642 // Build layout definition 643 LayoutDefinition layoutDefinition = new LayoutDefinitionImpl(diffBlockDefinition.getName(), 644 diffBlockDefinition.getProperties(), diffBlockDefinition.getTemplates(), layoutRowDefinitions, 645 widgetDefinitions); 646 647 // Build diff display block 648 Map<String, Serializable> diffBlockProperties = diffBlockDefinition.getProperties(BuiltinModes.ANY); 649 DiffDisplayBlock diffDisplayBlock = new DiffDisplayBlockImpl( 650 (String) diffBlockProperties.get(DIFF_BLOCK_LABEL_PROPERTY_NAME), leftValue, rightValue, 651 contentDiffValue, layoutDefinition); 652 653 return diffDisplayBlock; 654 } 655 656 /** 657 * Checks if the difference between the two specified properties is significant. 658 * <p> 659 * For example in the case of a date property, checks if the difference is greater than 1 minute since we don't 660 * display seconds in the default date widget. 661 */ 662 protected boolean isFieldDiffSignificant(Serializable leftProperty, Serializable rightProperty) { 663 664 if (leftProperty instanceof Calendar && rightProperty instanceof Calendar) { 665 Calendar leftDate = (Calendar) leftProperty; 666 Calendar rightDate = (Calendar) rightProperty; 667 if (Math.abs(leftDate.getTimeInMillis() - rightDate.getTimeInMillis()) <= 60000) { 668 return false; 669 } 670 } 671 return true; 672 } 673 674 protected final boolean isDisplayAllItems(WidgetDefinition wDef) { 675 676 // Check 'displayAllItems' widget property 677 return getBooleanProperty(wDef, BuiltinModes.ANY, DIFF_WIDGET_PROPERTY_DISPLAY_ALL_ITEMS); 678 } 679 680 protected final boolean isDisplayItemIndexes(WidgetDefinition wDef) { 681 682 // Check 'displayItemIndexes' widget property 683 return getBooleanProperty(wDef, BuiltinModes.ANY, DIFF_WIDGET_PROPERTY_DISPLAY_ITEM_INDEXES); 684 } 685 686 protected final boolean getBooleanProperty(WidgetDefinition wDef, String mode, String property) { 687 688 Map<String, Map<String, Serializable>> props = wDef.getProperties(); 689 if (props != null) { 690 Map<String, Serializable> modeProps = props.get(mode); 691 if (modeProps != null) { 692 Serializable propertyValue = modeProps.get(property); 693 if (propertyValue instanceof String) { 694 return Boolean.parseBoolean((String) propertyValue); 695 } 696 } 697 } 698 return false; 699 } 700 701 /** 702 * Sets the field diff display. 703 * 704 * @param property the property 705 * @param fieldDiff the field diff 706 * @param isDisplayAllItems the is display all items 707 * @param isDisplayItemIndexes the is display item indexes 708 * @param value the value 709 * @param schemaName the schema name 710 * @param fieldName the field name 711 * @param doc the doc 712 * @param styleClass the style class 713 */ 714 protected void setFieldDiffDisplay(Serializable property, PropertyDiff fieldDiff, boolean isDisplayAllItems, 715 boolean isDisplayItemIndexes, Map<String, Map<String, PropertyDiffDisplay>> value, String schemaName, 716 String fieldName, DocumentModel doc, String styleClass) { 717 718 PropertyDiffDisplay fieldDiffDisplay = getFieldDiffDisplay(property, fieldDiff, isDisplayAllItems, 719 isDisplayItemIndexes, false, styleClass); 720 Map<String, PropertyDiffDisplay> schemaMap = value.computeIfAbsent(schemaName, k -> new HashMap<>()); 721 schemaMap.put(fieldName, fieldDiffDisplay); 722 // Handle mime type for the note:note property 723 putMimeTypeDiffDisplay(schemaName, fieldName, schemaMap, doc); 724 } 725 726 protected final void putMimeTypeDiffDisplay(String schemaName, String fieldName, 727 Map<String, PropertyDiffDisplay> schemaMap, DocumentModel doc) { 728 729 if ("note".equals(schemaName) && "note".equals(fieldName) && !schemaMap.containsKey("mime_type")) { 730 schemaMap.put("mime_type", 731 new PropertyDiffDisplayImpl((Serializable) doc.getProperty("note", "mime_type"))); 732 } 733 } 734 735 protected final PropertyDiffDisplay getFieldDiffDisplay(Serializable property, PropertyDiff propertyDiff, 736 boolean isDisplayAllItems, boolean isDisplayItemIndexes, boolean mustApplyStyleClass, String styleClass) { 737 738 if (property == null) { 739 String fieldDiffDisplayStyleClass = PropertyDiffDisplay.DEFAULT_STYLE_CLASS; 740 if (mustApplyStyleClass && propertyDiff != null) { 741 fieldDiffDisplayStyleClass = styleClass; 742 } 743 // TODO: add DifferenceType.removed? 744 return new PropertyDiffDisplayImpl(null, fieldDiffDisplayStyleClass); 745 } 746 747 // List type 748 if (isListType(property)) { 749 List<Serializable> listProperty = getListProperty(property); 750 return getListFieldDiffDisplay(listProperty, (ListPropertyDiff) propertyDiff, isDisplayAllItems, 751 isDisplayItemIndexes, styleClass); 752 } 753 // Other types (scalar, complex, content) 754 else { 755 return getFinalFieldDiffDisplay(property, propertyDiff, mustApplyStyleClass, styleClass); 756 } 757 } 758 759 protected final PropertyDiffDisplay getFinalFieldDiffDisplay(Serializable fieldDiffDisplay, 760 PropertyDiff propertyDiff, boolean mustApplyStyleClass, String styleClass) { 761 762 String finalFieldDiffDisplayStyleClass = PropertyDiffDisplay.DEFAULT_STYLE_CLASS; 763 if (mustApplyStyleClass && propertyDiff != null) { 764 finalFieldDiffDisplayStyleClass = styleClass; 765 } 766 PropertyDiffDisplay finalFieldDiffDisplay; 767 if (isComplexType(fieldDiffDisplay)) { 768 ComplexPropertyDiff complexPropertyDiff = null; 769 if (propertyDiff != null) { 770 if (!propertyDiff.isComplexType()) { 771 throw new NuxeoException( 772 "'fieldDiffDisplay' is of complex type whereas 'propertyDiff' is not, this is inconsistent"); 773 } 774 complexPropertyDiff = (ComplexPropertyDiff) propertyDiff; 775 } 776 Map<String, Serializable> complexFieldDiffDisplay = getComplexProperty(fieldDiffDisplay); 777 for (String complexItemName : complexFieldDiffDisplay.keySet()) { 778 PropertyDiff complexItemPropertyDiff = null; 779 if (complexPropertyDiff != null) { 780 complexItemPropertyDiff = complexPropertyDiff.getDiff(complexItemName); 781 } 782 complexFieldDiffDisplay.put(complexItemName, 783 // TODO: shouldn't we call getFieldDiffDisplay in case 784 // of an embedded list? 785 getFinalFieldDiffDisplay(complexFieldDiffDisplay.get(complexItemName), complexItemPropertyDiff, 786 true, styleClass)); 787 } 788 finalFieldDiffDisplay = new PropertyDiffDisplayImpl((Serializable) complexFieldDiffDisplay); 789 } else if (fieldDiffDisplay instanceof Calendar) { 790 // TODO: add propertyDiff.getDifferenceType()? 791 finalFieldDiffDisplay = new PropertyDiffDisplayImpl(((Calendar) fieldDiffDisplay).getTime(), 792 finalFieldDiffDisplayStyleClass); 793 } else { 794 // TODO: add propertyDiff.getDifferenceType()? 795 finalFieldDiffDisplay = new PropertyDiffDisplayImpl(fieldDiffDisplay, finalFieldDiffDisplayStyleClass); 796 } 797 return finalFieldDiffDisplay; 798 } 799 800 /** 801 * Gets the list field diff display. 802 * 803 * @param listProperty the list property 804 * @param listPropertyDiff the list property diff 805 * @param isDisplayAllItems the is display all items 806 * @param isDisplayItemIndexes the is display item indexes 807 * @param styleClass the style class 808 * @return the list field diff display 809 */ 810 protected final PropertyDiffDisplay getListFieldDiffDisplay(List<Serializable> listProperty, 811 ListPropertyDiff listPropertyDiff, boolean isDisplayAllItems, boolean isDisplayItemIndexes, 812 String styleClass) { 813 814 // Get list property indexes 815 // By default: only items that are different (ie. held by the 816 // propertyDiff) 817 List<Integer> listPropertyIndexes = new ArrayList<>(); 818 if (isDisplayAllItems) { 819 // All items 820 for (int index = 0; index < listProperty.size(); index++) { 821 listPropertyIndexes.add(index); 822 } 823 } else { 824 if (listPropertyDiff != null) { 825 listPropertyIndexes = listPropertyDiff.getDiffIndexes(); 826 } 827 } 828 829 return getComplexListFieldDiffDisplay(listProperty, listPropertyIndexes, listPropertyDiff, isDisplayAllItems, 830 isDisplayItemIndexes, styleClass); 831 } 832 833 protected final PropertyDiffDisplay getComplexListFieldDiffDisplay(List<Serializable> listProperty, 834 List<Integer> listPropertyIndexes, ListPropertyDiff listPropertyDiff, boolean isDisplayAllItems, 835 boolean isDisplayItemIndexes, String styleClass) { 836 837 if (listPropertyIndexes.isEmpty()) { 838 // TODO: add differenceType? 839 return new PropertyDiffDisplayImpl(new ArrayList<Serializable>()); 840 } 841 boolean isComplexListWidget = isDisplayItemIndexes 842 || (listPropertyDiff != null && listPropertyDiff.isComplexListType()); 843 844 if (isComplexListWidget) { 845 List<Map<String, Serializable>> listFieldDiffDisplay = new ArrayList<>(); 846 Set<String> complexPropertyItemNames = null; 847 for (int index : listPropertyIndexes) { 848 849 Map<String, Serializable> listItemDiffDisplay = new HashMap<>(); 850 // Put item index if wanted 851 if (isDisplayItemIndexes) { 852 listItemDiffDisplay.put(DIFF_LIST_WIDGET_INDEX_SUBWIDGET_FIELD, index + 1); 853 } 854 // Only put value if index is in list range 855 if (index < listProperty.size()) { 856 Serializable listPropertyItem = listProperty.get(index); 857 PropertyDiff listItemPropertyDiff = null; 858 if (listPropertyDiff != null) { 859 listItemPropertyDiff = listPropertyDiff.getDiff(index); 860 } 861 if (isComplexType(listPropertyItem)) { // Complex 862 // list 863 ComplexPropertyDiff complexPropertyDiff = null; 864 if (listItemPropertyDiff != null && listItemPropertyDiff.isComplexType()) { 865 complexPropertyDiff = (ComplexPropertyDiff) listItemPropertyDiff; 866 } 867 Map<String, Serializable> complexProperty = getComplexProperty(listPropertyItem); 868 complexPropertyItemNames = complexProperty.keySet(); 869 for (String complexPropertyItemName : complexPropertyItemNames) { 870 Serializable complexPropertyItem = complexProperty.get(complexPropertyItemName); 871 // TODO: take into account subwidget properties 872 // 'displayAllItems' and 'displayItemIndexes' 873 // instead of inheriting them from the parent 874 // widget. 875 PropertyDiff complexItemPropertyDiff = null; 876 if (complexPropertyDiff != null) { 877 complexItemPropertyDiff = complexPropertyDiff.getDiff(complexPropertyItemName); 878 } 879 listItemDiffDisplay.put(complexPropertyItemName, 880 getFieldDiffDisplay(complexPropertyItem, complexItemPropertyDiff, isDisplayAllItems, 881 isDisplayItemIndexes, true, styleClass)); 882 } 883 } else { // Scalar or content list 884 listItemDiffDisplay.put(DIFF_LIST_WIDGET_VALUE_SUBWIDGET_FIELD, getFinalFieldDiffDisplay( 885 listPropertyItem, listItemPropertyDiff, isDisplayAllItems, styleClass)); 886 } 887 } else {// Index not in list range => put null value 888 if (complexPropertyItemNames != null) { 889 for (String complexPropertyItemName : complexPropertyItemNames) { 890 // TODO: add DifferenceType.removed? 891 listItemDiffDisplay.put(complexPropertyItemName, 892 new PropertyDiffDisplayImpl(null, styleClass)); 893 } 894 } else { 895 // TODO: add DifferenceType.removed? 896 listItemDiffDisplay.put(DIFF_LIST_WIDGET_VALUE_SUBWIDGET_FIELD, new PropertyDiffDisplayImpl( 897 null, isDisplayAllItems ? styleClass : PropertyDiffDisplay.DEFAULT_STYLE_CLASS)); 898 } 899 } 900 listFieldDiffDisplay.add(listItemDiffDisplay); 901 } 902 return new PropertyDiffDisplayImpl((Serializable) listFieldDiffDisplay); 903 } else { 904 List<Serializable> listFieldDiffDisplay = new ArrayList<>(); 905 for (int index : listPropertyIndexes) { 906 // Only put value if index is in list range 907 if (index < listProperty.size()) { 908 PropertyDiff listItemPropertyDiff = null; 909 if (listPropertyDiff != null) { 910 listItemPropertyDiff = listPropertyDiff.getDiff(index); 911 } 912 listFieldDiffDisplay.add(getFinalFieldDiffDisplay(listProperty.get(index), listItemPropertyDiff, 913 isDisplayAllItems, styleClass)); 914 } else {// Index not in list range => put null value 915 // TODO: add DifferenceType.removed? 916 listFieldDiffDisplay.add(new PropertyDiffDisplayImpl(null, 917 isDisplayAllItems ? styleClass : PropertyDiffDisplay.DEFAULT_STYLE_CLASS)); 918 } 919 } 920 return new PropertyDiffDisplayImpl((Serializable) listFieldDiffDisplay); 921 } 922 } 923 924 protected final PropertyDiffDisplay getFieldXPaths(String propertyName, PropertyDiff propertyDiff, 925 Serializable leftProperty, Serializable rightProperty, boolean isDisplayAllItems, 926 boolean isDisplayItemIndexes, List<DiffFieldItemDefinition> complexFieldItemDefs) { 927 928 PropertyDiffDisplay fieldXPaths = null; 929 if (propertyDiff == null) { 930 throw new NuxeoException("The 'propertyDiff' parameter cannot be null."); 931 } 932 933 boolean isDisplayHtmlConversion = ContentDiffHelper.isDisplayHtmlConversion(leftProperty) 934 && ContentDiffHelper.isDisplayHtmlConversion(rightProperty); 935 boolean isDisplayTextConversion = ContentDiffHelper.isDisplayTextConversion(leftProperty) 936 && ContentDiffHelper.isDisplayTextConversion(rightProperty); 937 938 // Simple type 939 if (propertyDiff.isSimpleType()) { 940 SimplePropertyDiff simplePropertyDiff = (SimplePropertyDiff) propertyDiff; 941 // Keep fieldXPaths null if one of the left or right properties is 942 // empty 943 if (!StringUtils.isEmpty(simplePropertyDiff.getLeftValue()) 944 && !StringUtils.isEmpty(simplePropertyDiff.getRightValue())) { 945 fieldXPaths = new ContentDiffDisplayImpl(propertyName, simplePropertyDiff.getDifferenceType(), 946 isDisplayHtmlConversion, isDisplayTextConversion); 947 } 948 } 949 // Content type 950 else if (propertyDiff.isContentType()) { 951 ContentPropertyDiff contentPropertyDiff = (ContentPropertyDiff) propertyDiff; 952 ContentProperty leftContent = contentPropertyDiff.getLeftContent(); 953 ContentProperty rightContent = contentPropertyDiff.getRightContent(); 954 // Keep fieldXPaths null if one of the left or right properties is 955 // empty 956 if (leftContent != null && rightContent != null 957 && ((StringUtils.isNotEmpty(leftContent.getFilename()) 958 && StringUtils.isNotEmpty(rightContent.getFilename())) 959 || (StringUtils.isNotEmpty(leftContent.getDigest()) 960 && StringUtils.isNotEmpty(rightContent.getDigest())))) { 961 fieldXPaths = new ContentDiffDisplayImpl(propertyName, contentPropertyDiff.getDifferenceType(), 962 isDisplayHtmlConversion, isDisplayTextConversion); 963 } 964 } 965 // Complex type 966 else if (propertyDiff.isComplexType()) { 967 968 Map<String, Serializable> leftComplexProperty = getComplexPropertyIfNotNull(leftProperty); 969 Map<String, Serializable> rightComplexProperty = getComplexPropertyIfNotNull(rightProperty); 970 971 // TODO (maybe): take into account subwidget properties 972 // 'displayAllItems' and 'displayItemIndexes' 973 // instead of inheriting them from the parent 974 // widget. 975 Map<String, PropertyDiff> complexPropertyDiffMap = ((ComplexPropertyDiff) propertyDiff).getDiffMap(); 976 Map<String, Serializable> complexPropertyXPaths = new HashMap<>(); 977 if (CollectionUtils.isEmpty(complexFieldItemDefs)) { 978 Iterator<String> complexFieldItemNamesIt = complexPropertyDiffMap.keySet().iterator(); 979 while (complexFieldItemNamesIt.hasNext()) { 980 String complexFieldItemName = complexFieldItemNamesIt.next(); 981 setComplexPropertyXPaths(complexPropertyXPaths, complexFieldItemName, 982 getSubPropertyFullName(propertyName, complexFieldItemName), complexPropertyDiffMap, 983 leftComplexProperty, rightComplexProperty, isDisplayAllItems, isDisplayItemIndexes); 984 } 985 } else { 986 for (DiffFieldItemDefinition complexFieldItemDef : complexFieldItemDefs) { 987 if (complexFieldItemDef.isDisplayContentDiffLinks()) { 988 String complexFieldItemName = complexFieldItemDef.getName(); 989 if (complexPropertyDiffMap.containsKey(complexFieldItemName)) { 990 setComplexPropertyXPaths(complexPropertyXPaths, complexFieldItemName, 991 getSubPropertyFullName(propertyName, complexFieldItemName), complexPropertyDiffMap, 992 leftComplexProperty, rightComplexProperty, isDisplayAllItems, isDisplayItemIndexes); 993 } 994 } 995 } 996 } 997 fieldXPaths = new ContentDiffDisplayImpl((Serializable) complexPropertyXPaths); 998 } 999 // List type 1000 else { 1001 List<Serializable> leftListProperty = getListPropertyIfNotNull(leftProperty); 1002 List<Serializable> rightListProperty = getListPropertyIfNotNull(rightProperty); 1003 1004 ListPropertyDiff listPropertyDiff = (ListPropertyDiff) propertyDiff; 1005 1006 // Get list property indexes 1007 // By default: only items that are different (ie. held by the 1008 // propertyDiff) 1009 List<Integer> listPropertyIndexes = new ArrayList<>(); 1010 if (isDisplayAllItems) { 1011 // All items 1012 int listPropertySize = Math.min(leftListProperty.size(), rightListProperty.size()); 1013 1014 for (int index = 0; index < listPropertySize; index++) { 1015 listPropertyIndexes.add(index); 1016 } 1017 } else { 1018 listPropertyIndexes = listPropertyDiff.getDiffIndexes(); 1019 } 1020 fieldXPaths = getComplexListXPaths(propertyName, listPropertyIndexes, listPropertyDiff, leftListProperty, 1021 rightListProperty, isDisplayAllItems, isDisplayItemIndexes); 1022 } 1023 return fieldXPaths; 1024 } 1025 1026 protected final PropertyDiffDisplay getComplexListXPaths(String propertyName, List<Integer> listPropertyIndexes, 1027 ListPropertyDiff listPropertyDiff, List<Serializable> leftListProperty, 1028 List<Serializable> rightListProperty, boolean isDisplayAllItems, boolean isDisplayItemIndexes) { 1029 1030 if (listPropertyIndexes.isEmpty()) { 1031 // TODO: add differenceType? 1032 return new ContentDiffDisplayImpl(new ArrayList<Serializable>()); 1033 } 1034 boolean isComplexListWidget = isDisplayItemIndexes 1035 || (listPropertyDiff != null && listPropertyDiff.isComplexListType()); 1036 1037 if (isComplexListWidget) { 1038 List<Map<String, Serializable>> listFieldXPaths = new ArrayList<>(); 1039 for (int index : listPropertyIndexes) { 1040 1041 Map<String, Serializable> listItemXPaths = new HashMap<>(); 1042 // Put item index if wanted 1043 if (isDisplayItemIndexes) { 1044 listItemXPaths.put(DIFF_LIST_WIDGET_INDEX_SUBWIDGET_FIELD, index + 1); 1045 } 1046 PropertyDiff listItemPropertyDiff = listPropertyDiff.getDiff(index); 1047 if (listItemPropertyDiff != null) { 1048 1049 Serializable leftListPropertyItem = null; 1050 Serializable rightListPropertyItem = null; 1051 if (index < leftListProperty.size()) { 1052 leftListPropertyItem = leftListProperty.get(index); 1053 } 1054 if (index < rightListProperty.size()) { 1055 rightListPropertyItem = rightListProperty.get(index); 1056 } 1057 Map<String, Serializable> leftComplexProperty = null; 1058 Map<String, Serializable> rightComplexProperty = null; 1059 if (isComplexType(leftListPropertyItem)) { 1060 leftComplexProperty = getComplexProperty(leftListPropertyItem); 1061 } 1062 if (isComplexType(rightListPropertyItem)) { 1063 rightComplexProperty = getComplexProperty(rightListPropertyItem); 1064 } 1065 1066 // Complex list 1067 if (listItemPropertyDiff.isComplexType()) { 1068 Map<String, PropertyDiff> complexPropertyDiffMap = ((ComplexPropertyDiff) listItemPropertyDiff).getDiffMap(); 1069 Iterator<String> complexPropertyItemNamesIt = complexPropertyDiffMap.keySet().iterator(); 1070 while (complexPropertyItemNamesIt.hasNext()) { 1071 String complexPropertyItemName = complexPropertyItemNamesIt.next(); 1072 // TODO: take into account subwidget properties 1073 // 'displayAllItems' and 'displayItemIndexes' 1074 // instead of inheriting them from the parent 1075 // widget. 1076 setComplexPropertyXPaths(listItemXPaths, complexPropertyItemName, 1077 getSubPropertyFullName(propertyName, 1078 getSubPropertyFullName(String.valueOf(index), complexPropertyItemName)), 1079 complexPropertyDiffMap, leftComplexProperty, rightComplexProperty, 1080 isDisplayAllItems, isDisplayItemIndexes); 1081 } 1082 } 1083 // Scalar or content list 1084 else { 1085 String listItemXPath = null; 1086 // Keep listItemXPath null if one of the left or right 1087 // properties is empty 1088 if (leftListPropertyItem != null && rightListPropertyItem != null) { 1089 listItemXPath = getSubPropertyFullName(propertyName, String.valueOf(index)); 1090 } 1091 boolean isDisplayHtmlConversion = ContentDiffHelper.isDisplayHtmlConversion( 1092 leftListPropertyItem) 1093 && ContentDiffHelper.isDisplayHtmlConversion(rightListPropertyItem); 1094 boolean isDisplayTextConversion = ContentDiffHelper.isDisplayTextConversion( 1095 leftListPropertyItem) 1096 && ContentDiffHelper.isDisplayTextConversion(rightListPropertyItem); 1097 listItemXPaths.put(DIFF_LIST_WIDGET_VALUE_SUBWIDGET_FIELD, 1098 new ContentDiffDisplayImpl(listItemXPath, listItemPropertyDiff.getDifferenceType(), 1099 isDisplayHtmlConversion, isDisplayTextConversion)); 1100 } 1101 } 1102 listFieldXPaths.add(listItemXPaths); 1103 } 1104 return new ContentDiffDisplayImpl((Serializable) listFieldXPaths); 1105 } else { 1106 List<PropertyDiffDisplay> listFieldXPaths = new ArrayList<>(); 1107 for (int index : listPropertyIndexes) { 1108 PropertyDiffDisplay listItemXPath = null; 1109 // Keep listItemXPath null if one of the left or right 1110 // properties is empty 1111 if (index < leftListProperty.size() && index < rightListProperty.size()) { 1112 PropertyDiff listItemPropertyDiff = null; 1113 if (listPropertyDiff != null) { 1114 listItemPropertyDiff = listPropertyDiff.getDiff(index); 1115 } 1116 DifferenceType differenceType = DifferenceType.different; 1117 if (listItemPropertyDiff != null) { 1118 differenceType = listItemPropertyDiff.getDifferenceType(); 1119 } 1120 Serializable leftListPropertyItem = leftListProperty.get(index); 1121 Serializable rightListPropertyItem = rightListProperty.get(index); 1122 boolean isDisplayHtmlConversion = ContentDiffHelper.isDisplayHtmlConversion(leftListPropertyItem) 1123 && ContentDiffHelper.isDisplayHtmlConversion(rightListPropertyItem); 1124 boolean isDisplayTextConversion = ContentDiffHelper.isDisplayTextConversion(leftListPropertyItem) 1125 && ContentDiffHelper.isDisplayTextConversion(rightListPropertyItem); 1126 listItemXPath = new ContentDiffDisplayImpl( 1127 getSubPropertyFullName(propertyName, String.valueOf(index)), differenceType, 1128 isDisplayHtmlConversion, isDisplayTextConversion); 1129 } 1130 listFieldXPaths.add(listItemXPath); 1131 } 1132 return new ContentDiffDisplayImpl((Serializable) listFieldXPaths); 1133 } 1134 } 1135 1136 /** 1137 * Sets the complex property xpaths. 1138 * 1139 * @param complexPropertyXPaths the complex property xpaths 1140 * @param complexFieldItemName the complex field item name 1141 * @param subPropertyFullName the sub property full name 1142 * @param complexPropertyDiffMap the complex property diff map 1143 * @param leftComplexProperty the left complex property 1144 * @param rightComplexProperty the right complex property 1145 * @param isDisplayAllItems the is display all items 1146 * @param isDisplayItemIndexes the is display item indexes 1147 */ 1148 protected void setComplexPropertyXPaths(Map<String, Serializable> complexPropertyXPaths, 1149 String complexFieldItemName, String subPropertyFullName, Map<String, PropertyDiff> complexPropertyDiffMap, 1150 Map<String, Serializable> leftComplexProperty, Map<String, Serializable> rightComplexProperty, 1151 boolean isDisplayAllItems, boolean isDisplayItemIndexes) { 1152 1153 Serializable leftComplexPropertyItemValue = null; 1154 Serializable rightComplexPropertyItemValue = null; 1155 if (leftComplexProperty != null) { 1156 leftComplexPropertyItemValue = leftComplexProperty.get(complexFieldItemName); 1157 } 1158 if (rightComplexProperty != null) { 1159 rightComplexPropertyItemValue = rightComplexProperty.get(complexFieldItemName); 1160 } 1161 complexPropertyXPaths.put(complexFieldItemName, 1162 getFieldXPaths(subPropertyFullName, complexPropertyDiffMap.get(complexFieldItemName), 1163 leftComplexPropertyItemValue, rightComplexPropertyItemValue, isDisplayAllItems, 1164 isDisplayItemIndexes, null)); 1165 } 1166 1167 protected boolean isListType(Serializable property) { 1168 1169 return property instanceof List<?> || property instanceof Serializable[]; 1170 } 1171 1172 protected boolean isComplexType(Serializable property) { 1173 1174 return property instanceof Map<?, ?>; 1175 } 1176 1177 /** 1178 * Casts or convert a {@link Serializable} property to {@link List <Serializable>}. 1179 * 1180 * @param property the property 1181 * @return the list property 1182 * @throws ClassCastException if the {@code property} is not a {@link List <Serializable>} nor an array. 1183 */ 1184 @SuppressWarnings("unchecked") 1185 protected List<Serializable> getListProperty(Serializable property) { 1186 List<Serializable> listProperty; 1187 if (property instanceof List<?>) { // List 1188 listProperty = (List<Serializable>) property; 1189 } else { // Array 1190 listProperty = Arrays.asList((Serializable[]) property); 1191 } 1192 return listProperty; 1193 } 1194 1195 /** 1196 * Gets the list property if the {@code property} is not null. 1197 * 1198 * @param property the property 1199 * @return the list property if the {@code property} is not null, null otherwise 1200 * @throws NuxeoException if the {@code property} is not a list. 1201 */ 1202 protected List<Serializable> getListPropertyIfNotNull(Serializable property) { 1203 1204 if (property != null) { 1205 if (!isListType(property)) { 1206 throw new NuxeoException( 1207 "Tryed to get a list property from a Serializable property that is not a list, this is inconsistent."); 1208 } 1209 return getListProperty(property); 1210 } 1211 return null; 1212 } 1213 1214 /** 1215 * Casts a {@link Serializable} property to {@link Map<String, Serializable>}. 1216 * 1217 * @param property the property 1218 * @return the complex property 1219 * @throws ClassCastException if the {@code property} is not a {@link Map <String, Serializable>}. 1220 */ 1221 @SuppressWarnings("unchecked") 1222 protected Map<String, Serializable> getComplexProperty(Serializable property) { 1223 return (Map<String, Serializable>) property; 1224 } 1225 1226 /** 1227 * Gets the complex property if the {@code property} is not null. 1228 * 1229 * @param property the property 1230 * @return the complex property if the {@code property} is not null, null otherwise 1231 * @throws NuxeoException if the {@code property} is not a list. 1232 */ 1233 protected Map<String, Serializable> getComplexPropertyIfNotNull(Serializable property) { 1234 1235 if (property != null) { 1236 if (!isComplexType(property)) { 1237 throw new NuxeoException( 1238 "Tryed to get a complex property from a Serializable property that is not a map, this is inconsistent."); 1239 } 1240 return getComplexProperty(property); 1241 } 1242 return null; 1243 } 1244 1245 // TODO: better separate regular and contentDiffLinksWidget cases (call // submethods) 1246 protected final WidgetDefinition getWidgetDefinition(String category, String propertyName, String propertyType, 1247 Field field, List<DiffFieldItemDefinition> complexFieldItemDefs, boolean isContentDiffLinksWidget) { 1248 1249 boolean isGeneric = false; 1250 boolean isCloned = false; 1251 1252 WidgetDefinition wDef; 1253 if (!isContentDiffLinksWidget) { 1254 // Look for a specific widget in the "diff" category named with the 1255 // property name 1256 wDef = getLayoutStore().getWidgetDefinition(category, propertyName); 1257 if (wDef == null) { 1258 isGeneric = true; 1259 // Fallback on a generic widget in the "diff" category named 1260 // with the property type 1261 wDef = getLayoutStore().getWidgetDefinition(category, propertyType); 1262 if (wDef == null) { 1263 throw new NuxeoException(String.format( 1264 "Could not find any specific widget named '%s', nor any generic widget named '%s'. Please make sure at least a generic widget is defined for this type.", 1265 propertyName, propertyType)); 1266 } 1267 } 1268 } else { 1269 isGeneric = true; 1270 if (PropertyType.isSimpleType(propertyType) || PropertyType.isContentType(propertyType)) { 1271 wDef = getLayoutStore().getWidgetDefinition(category, CONTENT_DIFF_LINKS_WIDGET_NAME); 1272 if (wDef == null) { 1273 throw new NuxeoException(String.format( 1274 "Could not find any generic widget named '%s'. Please make sure a generic widget is defined with this name.", 1275 CONTENT_DIFF_LINKS_WIDGET_NAME)); 1276 } 1277 } else { 1278 // Get the generic widget in the "diff" category named with 1279 // the property type 1280 wDef = getLayoutStore().getWidgetDefinition(category, propertyType); 1281 if (wDef == null) { 1282 throw new NuxeoException(String.format( 1283 "Could not find any generic widget named '%s'. Please make sure a generic widget is defined for this type.", 1284 propertyType)); 1285 } 1286 } 1287 } 1288 1289 if (isGeneric) { 1290 // Clone widget definition 1291 wDef = wDef.clone(); 1292 isCloned = true; 1293 // Set widget name 1294 String widgetName = propertyName; 1295 if (isContentDiffLinksWidget) { 1296 widgetName += CONTENT_DIFF_LINKS_WIDGET_NAME_SUFFIX; 1297 } 1298 wDef.setName(widgetName); 1299 1300 // Set labels 1301 Map<String, String> labels = new HashMap<>(); 1302 labels.put(BuiltinModes.ANY, DIFF_WIDGET_LABEL_PREFIX + getPropertyLabel(propertyName)); 1303 wDef.setLabels(labels); 1304 1305 // Set translated 1306 wDef.setTranslated(true); 1307 } 1308 1309 // Set field definitions if generic or specific and not already set in 1310 // widget definition 1311 if (isGeneric || !isFieldDefinitions(wDef)) { 1312 1313 String fieldName = propertyName; 1314 if (field != null) { 1315 fieldName = field.getName().getLocalName(); 1316 } 1317 1318 FieldDefinition[] fieldDefinitions; 1319 if (isContentDiffLinksWidget) { 1320 fieldDefinitions = new FieldDefinition[4]; 1321 fieldDefinitions[0] = new FieldDefinitionImpl(null, 1322 getFieldDefinition(fieldName, DIFF_WIDGET_FIELD_DEFINITION_VALUE)); 1323 fieldDefinitions[1] = new FieldDefinitionImpl(null, 1324 getFieldDefinition(fieldName, DIFF_WIDGET_FIELD_DEFINITION_DIFFERENCE_TYPE)); 1325 fieldDefinitions[2] = new FieldDefinitionImpl(null, 1326 getFieldDefinition(fieldName, DIFF_WIDGET_FIELD_DEFINITION_DISPLAY_HTML_CONVERSION)); 1327 fieldDefinitions[3] = new FieldDefinitionImpl(null, 1328 getFieldDefinition(fieldName, DIFF_WIDGET_FIELD_DEFINITION_DISPLAY_TEXT_CONVERSION)); 1329 } else { 1330 int fieldCount = 2; 1331 if (PropertyType.isContentType(propertyType) || ("note:note".equals(propertyName))) { 1332 fieldCount = 3; 1333 } 1334 fieldDefinitions = new FieldDefinition[fieldCount]; 1335 fieldDefinitions[0] = new FieldDefinitionImpl(null, 1336 getFieldDefinition(fieldName, DIFF_WIDGET_FIELD_DEFINITION_VALUE)); 1337 1338 FieldDefinition styleClassFieldDef = new FieldDefinitionImpl(null, 1339 getFieldDefinition(fieldName, DIFF_WIDGET_FIELD_DEFINITION_STYLE_CLASS)); 1340 if (PropertyType.isContentType(propertyType)) { 1341 fieldDefinitions[1] = new FieldDefinitionImpl(null, 1342 getFieldDefinition(getFieldDefinition(fieldName, DIFF_WIDGET_FIELD_DEFINITION_VALUE), 1343 DIFF_WIDGET_FIELD_DEFINITION_FILENAME)); 1344 fieldDefinitions[2] = styleClassFieldDef; 1345 } else if ("note:note".equals(propertyName)) { 1346 fieldDefinitions[1] = new FieldDefinitionImpl(null, 1347 getFieldDefinition("note:mime_type", DIFF_WIDGET_FIELD_DEFINITION_VALUE)); 1348 fieldDefinitions[2] = styleClassFieldDef; 1349 } else { 1350 fieldDefinitions[1] = styleClassFieldDef; 1351 } 1352 } 1353 1354 // Clone if needed 1355 if (!isCloned) { 1356 wDef = wDef.clone(); 1357 isCloned = true; 1358 } 1359 wDef.setFieldDefinitions(fieldDefinitions); 1360 } 1361 1362 // Set subwidgets if not already set 1363 if (!isSubWidgets(wDef)) { 1364 if (PropertyType.isListType(propertyType) || PropertyType.isComplexType(propertyType)) { 1365 1366 Field declaringField = field; 1367 if (declaringField == null) { 1368 declaringField = ComplexPropertyHelper.getField(getPropertySchema(propertyName), 1369 getPropertyField(propertyName)); 1370 } 1371 // Clone if needed 1372 if (!isCloned) { 1373 wDef = wDef.clone(); 1374 isCloned = true; 1375 } 1376 wDef.setSubWidgetDefinitions(getSubWidgetDefinitions(category, propertyName, propertyType, 1377 declaringField, complexFieldItemDefs, isDisplayItemIndexes(wDef), isContentDiffLinksWidget)); 1378 } 1379 } 1380 1381 return wDef; 1382 } 1383 1384 protected final boolean isSubWidgets(WidgetDefinition wDef) { 1385 1386 WidgetDefinition[] subWidgetDefs = wDef.getSubWidgetDefinitions(); 1387 return subWidgetDefs != null && subWidgetDefs.length > 0; 1388 } 1389 1390 protected final boolean isFieldDefinitions(WidgetDefinition wDef) { 1391 1392 FieldDefinition[] fieldDefs = wDef.getFieldDefinitions(); 1393 return fieldDefs != null && fieldDefs.length > 0; 1394 } 1395 1396 protected final String getFieldDefinition(String fieldName, String subPropertyName) { 1397 1398 return fieldName + "/" + subPropertyName; 1399 } 1400 1401 protected final WidgetDefinition[] getSubWidgetDefinitions(String category, String propertyName, 1402 String propertyType, Field field, List<DiffFieldItemDefinition> complexFieldItemDefs, 1403 boolean isDisplayItemIndexes, boolean isContentDiffLinks) { 1404 1405 WidgetDefinition[] subWidgetDefs = null; 1406 // Complex 1407 if (PropertyType.isComplexType(propertyType)) { 1408 subWidgetDefs = getComplexSubWidgetDefinitions(category, propertyName, field, complexFieldItemDefs, false, 1409 isContentDiffLinks); 1410 } 1411 // Scalar or content list 1412 else if (PropertyType.isScalarListType(propertyType) || PropertyType.isContentListType(propertyType)) { 1413 Field listFieldItem = ComplexPropertyHelper.getListFieldItem(field); 1414 subWidgetDefs = initSubWidgetDefinitions(isDisplayItemIndexes, 1); 1415 subWidgetDefs[subWidgetDefs.length - 1] = getWidgetDefinition(category, 1416 getSubPropertyFullName(propertyName, listFieldItem.getName().getLocalName()), 1417 ComplexPropertyHelper.getFieldType(listFieldItem), 1418 new FieldImpl(new QName(DIFF_LIST_WIDGET_VALUE_SUBWIDGET_FIELD), field.getType(), 1419 listFieldItem.getType()), 1420 null, isContentDiffLinks); 1421 } 1422 // Complex list 1423 else if (PropertyType.isComplexListType(propertyType)) { 1424 Field listFieldItem = ComplexPropertyHelper.getListFieldItem(field); 1425 subWidgetDefs = getComplexSubWidgetDefinitions(category, propertyName, listFieldItem, complexFieldItemDefs, 1426 isDisplayItemIndexes, isContentDiffLinks); 1427 } 1428 return subWidgetDefs; 1429 } 1430 1431 protected final WidgetDefinition[] getComplexSubWidgetDefinitions(String category, String propertyName, Field field, 1432 List<DiffFieldItemDefinition> complexFieldItemDefs, boolean isDisplayItemIndexes, 1433 boolean isContentDiffLinks) { 1434 1435 WidgetDefinition[] subWidgetDefs; 1436 int subWidgetIndex = isDisplayItemIndexes ? 1 : 0; 1437 1438 if (CollectionUtils.isEmpty(complexFieldItemDefs)) { 1439 List<Field> complexFieldItems = ComplexPropertyHelper.getComplexFieldItems(field); 1440 subWidgetDefs = initSubWidgetDefinitions(isDisplayItemIndexes, complexFieldItems.size()); 1441 1442 for (Field complexFieldItem : complexFieldItems) { 1443 subWidgetDefs[subWidgetIndex] = getWidgetDefinition(category, 1444 getSubPropertyFullName(propertyName, complexFieldItem.getName().getLocalName()), 1445 ComplexPropertyHelper.getFieldType(complexFieldItem), complexFieldItem, null, 1446 isContentDiffLinks); 1447 subWidgetIndex++; 1448 } 1449 } else { 1450 int subWidgetCount = complexFieldItemDefs.size(); 1451 // Only add a subwidget for the items marked to display the content 1452 // diff links 1453 if (isContentDiffLinks) { 1454 subWidgetCount = 0; 1455 for (DiffFieldItemDefinition complexFieldItemDef : complexFieldItemDefs) { 1456 if (complexFieldItemDef.isDisplayContentDiffLinks()) { 1457 subWidgetCount++; 1458 } 1459 } 1460 } 1461 subWidgetDefs = initSubWidgetDefinitions(isDisplayItemIndexes, subWidgetCount); 1462 1463 for (DiffFieldItemDefinition complexFieldItemDef : complexFieldItemDefs) { 1464 if (!isContentDiffLinks || complexFieldItemDef.isDisplayContentDiffLinks()) { 1465 String complexFieldItemName = complexFieldItemDef.getName(); 1466 Field complexFieldItem = ComplexPropertyHelper.getComplexFieldItem(field, complexFieldItemName); 1467 if (complexFieldItem != null) { 1468 subWidgetDefs[subWidgetIndex] = getWidgetDefinition(category, 1469 getSubPropertyFullName(propertyName, complexFieldItemName), 1470 ComplexPropertyHelper.getFieldType(complexFieldItem), complexFieldItem, null, 1471 isContentDiffLinks); 1472 subWidgetIndex++; 1473 } 1474 } 1475 } 1476 } 1477 return subWidgetDefs; 1478 } 1479 1480 protected final WidgetDefinition[] initSubWidgetDefinitions(boolean isDisplayItemIndexes, int subWidgetCount) { 1481 1482 WidgetDefinition[] subWidgetDefs; 1483 if (isDisplayItemIndexes) { 1484 subWidgetDefs = new WidgetDefinition[subWidgetCount + 1]; 1485 subWidgetDefs[0] = getIndexSubwidgetDefinition(); 1486 } else { 1487 subWidgetDefs = new WidgetDefinition[subWidgetCount]; 1488 } 1489 1490 return subWidgetDefs; 1491 } 1492 1493 protected final WidgetDefinition getIndexSubwidgetDefinition() { 1494 1495 FieldDefinition[] fieldDefinitions = { new FieldDefinitionImpl(null, DIFF_LIST_WIDGET_INDEX_SUBWIDGET_FIELD) }; 1496 1497 return new WidgetDefinitionImpl(DIFF_LIST_WIDGET_INDEX_SUBWIDGET_FIELD, DIFF_LIST_WIDGET_INDEX_SUBWIDGET_TYPE, 1498 DIFF_LIST_WIDGET_INDEX_SUBWIDGET_LABEL, null, true, null, Arrays.asList(fieldDefinitions), null, null); 1499 } 1500 1501 /** 1502 * Gets the property name. 1503 * 1504 * @param schema the schema 1505 * @param field the field 1506 * @return the property name 1507 */ 1508 protected final String getPropertyName(String schema, String field) { 1509 1510 StringBuilder sb = new StringBuilder(); 1511 if (!StringUtils.isEmpty(schema)) { 1512 sb.append(schema); 1513 sb.append(":"); 1514 } 1515 sb.append(field); 1516 return sb.toString(); 1517 } 1518 1519 protected final String getSubPropertyFullName(String basePropertyName, String subPropertyName) { 1520 1521 if (StringUtils.isEmpty(subPropertyName)) { 1522 return basePropertyName; 1523 } 1524 return basePropertyName + '/' + subPropertyName; 1525 } 1526 1527 protected final String getPropertySchema(String propertyName) { 1528 1529 int indexOfColon = propertyName.indexOf(':'); 1530 if (indexOfColon > -1) { 1531 return propertyName.substring(0, indexOfColon); 1532 } 1533 return null; 1534 } 1535 1536 protected final String getPropertyField(String propertyName) { 1537 1538 int indexOfColon = propertyName.indexOf(':'); 1539 if (indexOfColon > -1 && indexOfColon < propertyName.length() - 1) { 1540 return propertyName.substring(indexOfColon + 1); 1541 } 1542 return propertyName; 1543 } 1544 1545 protected final String getPropertyLabel(String propertyName) { 1546 1547 return propertyName.replaceAll(":", ".").replaceAll("/", "."); 1548 } 1549 1550 /** 1551 * Gets the schema manager. 1552 * 1553 * @return the schema manager 1554 */ 1555 protected final SchemaManager getSchemaManager() { 1556 SchemaManager schemaManager = Framework.getService(SchemaManager.class); 1557 if (schemaManager == null) { 1558 throw new NuxeoException("SchemaManager service is null."); 1559 } 1560 return schemaManager; 1561 } 1562 1563 /** 1564 * Gets the layout store service. 1565 * 1566 * @return the layout store service 1567 */ 1568 protected final LayoutStore getLayoutStore() { 1569 LayoutStore layoutStore = Framework.getService(LayoutStore.class); 1570 if (layoutStore == null) { 1571 throw new NuxeoException("LayoutStore service is null."); 1572 } 1573 return layoutStore; 1574 } 1575}