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