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