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.util.ArrayList; 020import java.util.Collections; 021import java.util.List; 022 023import org.apache.commons.logging.Log; 024import org.apache.commons.logging.LogFactory; 025import org.custommonkey.xmlunit.Difference; 026import org.custommonkey.xmlunit.DifferenceConstants; 027import org.custommonkey.xmlunit.NodeDetail; 028import org.nuxeo.ecm.core.api.NuxeoException; 029import org.nuxeo.ecm.core.io.ExportConstants; 030import org.nuxeo.ecm.diff.model.DifferenceType; 031import org.nuxeo.ecm.diff.model.DocumentDiff; 032import org.nuxeo.ecm.diff.model.PropertyDiff; 033import org.nuxeo.ecm.diff.model.PropertyType; 034import org.nuxeo.ecm.diff.model.SchemaDiff; 035import org.nuxeo.ecm.diff.model.impl.ComplexPropertyDiff; 036import org.nuxeo.ecm.diff.model.impl.ContentProperty; 037import org.nuxeo.ecm.diff.model.impl.ContentPropertyDiff; 038import org.nuxeo.ecm.diff.model.impl.ListPropertyDiff; 039import org.nuxeo.ecm.diff.model.impl.PropertyHierarchyNode; 040import org.nuxeo.ecm.diff.model.impl.SimplePropertyDiff; 041import org.w3c.dom.NamedNodeMap; 042import org.w3c.dom.Node; 043import org.w3c.dom.NodeList; 044 045/** 046 * Helper for computing a field diff. 047 * 048 * @author <a href="mailto:ataillefer@nuxeo.com">Antoine Taillefer</a> 049 * @since 5.6 050 */ 051public final class FieldDiffHelper { 052 053 private static final Log LOGGER = LogFactory.getLog(FieldDiffHelper.class); 054 055 public static final String FACET_ELEMENT = "facet"; 056 057 public static final String SCHEMA_ELEMENT = "schema"; 058 059 public static final String NAME_ATTRIBUTE = "name"; 060 061 public static final String TYPE_ATTRIBUTE = "type"; 062 063 /** 064 * Computes a field diff. 065 * <p> 066 * First gets all needed elements to compute the field diff: 067 * <ul> 068 * <li>propertyHierarchy: list holding the property hierarchy 069 * 070 * <pre> 071 * Every time we encounter a list, a complex or a content node going up in the DOM tree 072 * from the property node to the prefixed field node, we add it to the property 073 * hierarchy. 074 * If it is a list item node, we set its index in the hierarchy. 075 * If it is a complex item node, we set its name in the hierarchy. 076 * If it is a content item node (ie. "encoding", "mime-type", "filename" or "digest"), 077 * we set its name in the hierarchy. 078 * 079 * Example: complex list 080 * 081 * The "true" property's hierarchy is: 082 * [{list,"0"},{complex, "complexBoolean"}] 083 * 084 * The "jack" property's hierarchy is: 085 * [{list,"1"},{complex, "complexString"}] 086 * 087 * The "UTF-8" property's hierarchy is: 088 * [{list,"0"},{complex, "complexString"},{content, "encoding"}] 089 * 090 * <list type="complexList"> 091 * <complexItem type="complex"> 092 * <complexString type="string">joe</complexString> 093 * <complexBoolean type="boolean">true</complexBoolean> 094 * <complexContent type="content"> 095 * <encoding>UTF-8</encoding> 096 * <mime-type>text/plain</mime-type> 097 * <filename>My_file.txt</filename> 098 * <digest>5dafdabf966043c8c8cef20011e939a2</digest> 099 * </complexContent> 100 * </complexItem> 101 * <complexItem type="complex"> 102 * <complexString type="string">jack</complexString> 103 * <complexBoolean type="boolean">false</complexBoolean> 104 * </complexItem> 105 * </list> 106 * </pre> 107 * 108 * </li> 109 * </ul> 110 * 111 * @param docDiff the doc diff 112 * @param controlNodeDetail the control node detail 113 * @param testNodeDetail the test node detail 114 * @param fieldDifferenceCount the field difference countadd 115 * @param difference the difference 116 * @return true if a field diff has been found 117 */ 118 public static boolean computeFieldDiff(DocumentDiff docDiff, NodeDetail controlNodeDetail, 119 NodeDetail testNodeDetail, int fieldDifferenceCount, Difference difference) { 120 121 // Use control node or if null test node to detect schema and 122 // field elements 123 Node currentNode = controlNodeDetail.getNode(); 124 if (currentNode == null) { 125 currentNode = testNodeDetail.getNode(); 126 } 127 if (currentNode != null) { 128 129 String field = null; 130 String currentNodeName = currentNode.getNodeName(); 131 List<PropertyHierarchyNode> propertyHierarchy = new ArrayList<PropertyHierarchyNode>(); 132 133 // Detect a schema element, 134 // for instance: <schema name="dublincore" xmlns:dc="...">. 135 // Otherwise build the property hierarchy. 136 // For a content type property (blob) don't take into account a 137 // difference on the "data" field, since what makes the difference 138 // between 2 blobs is either the filename or the digest. 139 Node parentNode = currentNode.getParentNode(); 140 while (parentNode != null && !SCHEMA_ELEMENT.equals(currentNodeName) 141 && !ExportConstants.BLOB_DATA.equals(parentNode.getNodeName())) { 142 143 // Get property type 144 String propertyType = getPropertyType(currentNode); 145 String parentPropertyType = getPropertyType(parentNode); 146 147 // Fill in property hierarchy 148 if (PropertyType.isListType(parentPropertyType)) { 149 int currentNodePosition = getNodePosition(currentNode); 150 propertyHierarchy.add(new PropertyHierarchyNode(parentPropertyType, 151 String.valueOf(currentNodePosition))); 152 } else if (PropertyType.isComplexType(parentPropertyType) 153 || PropertyType.isContentType(parentPropertyType)) { 154 propertyHierarchy.add(new PropertyHierarchyNode(parentPropertyType, currentNodeName)); 155 } 156 157 // Detect a field element, ie. an element that has a 158 // prefix, for instance: <dc:title>. 159 if (SCHEMA_ELEMENT.equals(parentNode.getNodeName())) { 160 String currentNodeLocalName = currentNode.getLocalName(); 161 // TODO: manage better the facet case 162 if (!FACET_ELEMENT.equals(currentNodeLocalName)) { 163 field = currentNodeLocalName; 164 if (PropertyType.isSimpleType(propertyType) || PropertyType.isListType(propertyType) 165 && propertyHierarchy.isEmpty() || PropertyType.isComplexType(propertyType) 166 && propertyHierarchy.isEmpty() || PropertyType.isContentType(propertyType) 167 && propertyHierarchy.isEmpty()) { 168 propertyHierarchy.add(new PropertyHierarchyNode(propertyType, null)); 169 } 170 } 171 } 172 currentNode = parentNode; 173 currentNodeName = currentNode.getNodeName(); 174 parentNode = parentNode.getParentNode(); 175 } 176 177 // If we found a schema element (ie. we did not 178 // reached the root element, ie. parentNode != null) and a 179 // nested field element, we can compute the diff for this 180 // field. 181 if (parentNode != null && field != null && !propertyHierarchy.isEmpty()) { 182 String schema = currentNodeName; 183 // Get schema name 184 NamedNodeMap attr = currentNode.getAttributes(); 185 if (attr != null && attr.getLength() > 0) { 186 Node nameAttr = attr.getNamedItem(NAME_ATTRIBUTE); 187 if (nameAttr != null) { 188 schema = nameAttr.getNodeValue(); 189 } 190 } 191 192 // Reverse property hierarchy 193 Collections.reverse(propertyHierarchy); 194 195 // Pretty log field difference 196 LOGGER.debug(String.format( 197 "Found field difference #%d on [%s]/[%s] with hierarchy %s: [%s (%s)] {%s --> %s}", 198 fieldDifferenceCount + 1, schema, field, propertyHierarchy, difference.getDescription(), 199 difference.getId(), controlNodeDetail.getValue(), testNodeDetail.getValue())); 200 201 // Compute field diff 202 computeFieldDiff(docDiff, schema, field, propertyHierarchy, difference.getId(), controlNodeDetail, 203 testNodeDetail); 204 // Return true since a field diff has been found 205 return true; 206 207 } else {// Non-field difference 208 LOGGER.debug(String.format("Found non-field difference: [%s (%s)] {%s --> %s}", 209 difference.getDescription(), difference.getId(), controlNodeDetail.getValue(), 210 testNodeDetail.getValue())); 211 } 212 } 213 return false; 214 } 215 216 /** 217 * Gets the node property type. 218 * 219 * @param node the node 220 * @return the property diff type 221 */ 222 public static String getPropertyType(Node node) { 223 224 // Default: undefined 225 String propertyType = PropertyType.UNDEFINED; 226 227 NamedNodeMap nodeAttr = node.getAttributes(); 228 if (nodeAttr != null) { 229 Node type = nodeAttr.getNamedItem(TYPE_ATTRIBUTE); 230 if (type != null) { 231 propertyType = type.getNodeValue(); 232 } 233 } 234 235 return propertyType; 236 } 237 238 /** 239 * Gets the node position. 240 * 241 * @param node the node 242 * @return the node position 243 */ 244 private static int getNodePosition(Node node) { 245 246 int nodePos = 0; 247 Node previousSibling = node.getPreviousSibling(); 248 while (previousSibling != null) { 249 nodePos++; 250 previousSibling = previousSibling.getPreviousSibling(); 251 } 252 return nodePos; 253 } 254 255 /** 256 * Sets the property diff hierarchy. 257 * 258 * @param firstPropertyDiff the first property diff 259 * @param propertyHierarchy the property hierarchy 260 * @return the property diff 261 */ 262 public static PropertyDiff applyPropertyHierarchyToDiff(PropertyDiff firstPropertyDiff, 263 List<PropertyHierarchyNode> propertyHierarchy) { 264 265 if (propertyHierarchy.isEmpty()) { 266 throw new NuxeoException("Empty property hierarchy."); 267 } 268 269 // Get first property hierarchy node 270 PropertyHierarchyNode propertyHierarchyNode = propertyHierarchy.get(0); 271 String firstPropertyType = propertyHierarchyNode.getNodeType(); 272 String firstPropertyValue = propertyHierarchyNode.getNodeValue(); 273 274 if ((PropertyType.isSimpleType(firstPropertyType) || PropertyType.isContentType(firstPropertyType)) 275 && propertyHierarchy.size() > 1) { 276 throw new NuxeoException(String.format("Inconsistant property hierarchy %s.", propertyHierarchy)); 277 } 278 279 // Go through the property hierarchy 280 PropertyDiff propertyDiff = firstPropertyDiff; 281 String propertyType = firstPropertyType; 282 String propertyValue = firstPropertyValue; 283 for (int i = 1; i < propertyHierarchy.size(); i++) { 284 285 PropertyDiff childPropertyDiff = null; 286 PropertyHierarchyNode childPropertyHierarchyNode = propertyHierarchy.get(i); 287 String childPropertyType = childPropertyHierarchyNode.getNodeType(); 288 String childPropertyValue = childPropertyHierarchyNode.getNodeValue(); 289 290 // Simple or content type 291 if (PropertyType.isSimpleType(propertyType) || PropertyType.isContentType(propertyType)) { 292 // Nothing to do here (should never happen) 293 } 294 // List type 295 else if (PropertyType.isListType(propertyType)) { 296 int propertyIndex = Integer.parseInt(propertyValue); 297 // Get list diff, if null create a new one 298 childPropertyDiff = ((ListPropertyDiff) propertyDiff).getDiff(propertyIndex); 299 if (childPropertyDiff == null) { 300 childPropertyDiff = newPropertyDiff(childPropertyType); 301 ((ListPropertyDiff) propertyDiff).putDiff(propertyIndex, childPropertyDiff); 302 } 303 propertyDiff = childPropertyDiff; 304 } 305 // Complex type 306 else { 307 // Get complex diff, initialize it if null 308 childPropertyDiff = ((ComplexPropertyDiff) propertyDiff).getDiff(propertyValue); 309 if (childPropertyDiff == null) { 310 childPropertyDiff = newPropertyDiff(childPropertyType); 311 ((ComplexPropertyDiff) propertyDiff).putDiff(propertyValue, childPropertyDiff); 312 } 313 propertyDiff = childPropertyDiff; 314 } 315 316 propertyType = childPropertyType; 317 propertyValue = childPropertyValue; 318 } 319 return propertyDiff; 320 } 321 322 /** 323 * Computes a field diff. 324 * 325 * @param docDiff the doc diff 326 * @param schema the schema 327 * @param field the field 328 * @param propertyHierarchy the property hierarchy 329 * @param differenceId the difference id 330 * @param controlNodeDetail the control node detail 331 * @param testNodeDetail the test node detail 332 */ 333 private static void computeFieldDiff(DocumentDiff docDiff, String schema, String field, 334 List<PropertyHierarchyNode> propertyHierarchy, int differenceId, NodeDetail controlNodeDetail, 335 NodeDetail testNodeDetail) { 336 337 if (propertyHierarchy.isEmpty()) { 338 throw new NuxeoException("Empty property hierarchy."); 339 } 340 341 // Get first property hierarchy node 342 PropertyHierarchyNode propertyHierarchyNode = propertyHierarchy.get(0); 343 String firstPropertyType = propertyHierarchyNode.getNodeType(); 344 345 // Get schema diff, initialize it if null 346 SchemaDiff schemaDiff = docDiff.getSchemaDiff(schema); 347 if (schemaDiff == null) { 348 schemaDiff = docDiff.initSchemaDiff(schema); 349 } 350 351 // Get field diff, initialize it if null 352 PropertyDiff fieldDiff = schemaDiff.getFieldDiff(field); 353 if (fieldDiff == null) { 354 fieldDiff = newPropertyDiff(firstPropertyType); 355 } 356 357 PropertyDiff endPropertyDiff = fieldDiff; 358 // Apply property hierarchy to diff if first property type in hierarchy 359 // is list or complex 360 if (!(PropertyType.isSimpleType(firstPropertyType))) { 361 endPropertyDiff = applyPropertyHierarchyToDiff(fieldDiff, propertyHierarchy); 362 } 363 364 // Compute field diff depending on difference type. 365 switch (differenceId) { 366 case DifferenceConstants.TEXT_VALUE_ID: 367 computeTextValueDiff(endPropertyDiff, controlNodeDetail, testNodeDetail); 368 break; 369 case DifferenceConstants.CHILD_NODE_NOT_FOUND_ID: 370 computeChildNodeNotFoundDiff(endPropertyDiff, controlNodeDetail, testNodeDetail); 371 break; 372 case DifferenceConstants.HAS_CHILD_NODES_ID: 373 computeHasChildNodesDiff(endPropertyDiff, controlNodeDetail, testNodeDetail); 374 break; 375 default: 376 computeTextValueDiff(endPropertyDiff, controlNodeDetail, testNodeDetail); 377 } 378 379 schemaDiff.putFieldDiff(field, fieldDiff); 380 } 381 382 /** 383 * New property diff. 384 * 385 * @param propertyType the property type 386 * @return the property diff 387 */ 388 private static PropertyDiff newPropertyDiff(String propertyType) { 389 390 if (PropertyType.isSimpleType(propertyType)) { 391 return new SimplePropertyDiff(propertyType); 392 } else if (PropertyType.isListType(propertyType)) { 393 return new ListPropertyDiff(propertyType); 394 } else if (PropertyType.isComplexType(propertyType)) { 395 return new ComplexPropertyDiff(); 396 } else { // Content type 397 return new ContentPropertyDiff(); 398 } 399 } 400 401 /** 402 * Computes a TEXT_VALUE diff. 403 * 404 * @param fieldDiff the field diff 405 * @param controlNodeDetail the control node detail 406 * @param testNodeDetail the test node detail 407 */ 408 private static void computeTextValueDiff(PropertyDiff fieldDiff, NodeDetail controlNodeDetail, 409 NodeDetail testNodeDetail) { 410 411 String leftValue = controlNodeDetail.getValue(); 412 String rightValue = testNodeDetail.getValue(); 413 414 Node controlNode = controlNodeDetail.getNode(); 415 if (controlNode == null) { 416 throw new NuxeoException("Control node should never be null."); 417 } 418 419 Node controlParentNode = controlNode.getParentNode(); 420 if (controlParentNode == null) { 421 throw new NuxeoException("Control parent node should never be null."); 422 } 423 424 String controlParentNodePropertyType = getPropertyType(controlParentNode); 425 String fieldDiffPropertyType = fieldDiff.getPropertyType(); 426 // Simple type 427 if (PropertyType.isSimpleType(fieldDiffPropertyType)) { 428 ((SimplePropertyDiff) fieldDiff).setLeftValue(leftValue); 429 ((SimplePropertyDiff) fieldDiff).setRightValue(rightValue); 430 } 431 // List type 432 else if (PropertyType.isListType(fieldDiffPropertyType)) { 433 ((ListPropertyDiff) fieldDiff).putDiff(getNodePosition(controlParentNode), new SimplePropertyDiff( 434 controlParentNodePropertyType, leftValue, rightValue)); 435 } 436 // Complex type 437 else if (PropertyType.isComplexType(fieldDiffPropertyType)) { 438 ((ComplexPropertyDiff) fieldDiff).putDiff(controlParentNode.getNodeName(), new SimplePropertyDiff( 439 controlParentNodePropertyType, leftValue, rightValue)); 440 } 441 // Content type 442 else { 443 ContentPropertyDiff contentPropertyDiff = ((ContentPropertyDiff) fieldDiff); 444 setContentSubPropertyDiff(contentPropertyDiff, controlParentNode.getNodeName(), leftValue, rightValue); 445 } 446 } 447 448 /** 449 * Computes a CHILD_NODE_NOT_FOUND diff. 450 * 451 * @param fieldDiff the field diff 452 * @param controlNodeDetail the control node detail 453 * @param testNodeDetail the test node detail 454 */ 455 private static void computeChildNodeNotFoundDiff(PropertyDiff fieldDiff, NodeDetail controlNodeDetail, 456 NodeDetail testNodeDetail) { 457 458 Node childNode; 459 boolean isTestNodeNotFound = "null".equals(testNodeDetail.getValue()); 460 if (!isTestNodeNotFound) { 461 childNode = testNodeDetail.getNode(); 462 } else { 463 childNode = controlNodeDetail.getNode(); 464 } 465 466 if (childNode == null) { 467 throw new NuxeoException("Child node should never be null."); 468 } 469 470 String propertyType = fieldDiff.getPropertyType(); 471 // Simple type 472 if (PropertyType.isSimpleType(propertyType)) { 473 // Should never happen as then it would be marked as a 474 // HAS_CHILD_NODES difference. 475 throw new NuxeoException("A CHILD_NODE_NOT_FOUND difference should never be found within a simple type."); 476 } 477 // List type 478 else if (PropertyType.isListType(propertyType)) { 479 PropertyDiff childNodeDiff = getChildNodePropertyDiff(childNode, isTestNodeNotFound); 480 ((ListPropertyDiff) fieldDiff).putDiff(getNodePosition(childNode), childNodeDiff); 481 } 482 // Complex type 483 else if (PropertyType.isComplexType(propertyType)) { // Complex type 484 throw new NuxeoException("A CHILD_NODE_NOT_FOUND difference should never be found within a complex type."); 485 } 486 // Content type 487 else { 488 throw new NuxeoException("A CHILD_NODE_NOT_FOUND difference should never be found within a content type."); 489 } 490 } 491 492 /** 493 * Computes a HAS_CHILD_NODES diff. 494 * 495 * @param fieldDiff the field diff 496 * @param controlNodeDetail the control node detail 497 * @param testNodeDetail the test node detail 498 */ 499 private static void computeHasChildNodesDiff(PropertyDiff fieldDiff, NodeDetail controlNodeDetail, 500 NodeDetail testNodeDetail) { 501 502 Node nodeWithChildren; 503 boolean hasControlNodeChildNodes = Boolean.valueOf(controlNodeDetail.getValue()); 504 if (hasControlNodeChildNodes) { 505 nodeWithChildren = controlNodeDetail.getNode(); 506 } else { 507 nodeWithChildren = testNodeDetail.getNode(); 508 } 509 510 if (nodeWithChildren == null) { 511 throw new NuxeoException("Node with children should never be null."); 512 } 513 514 String propertyType = fieldDiff.getPropertyType(); 515 // Simple type 516 if (PropertyType.isSimpleType(propertyType)) { 517 setSimplePropertyDiff((SimplePropertyDiff) fieldDiff, nodeWithChildren, hasControlNodeChildNodes); 518 } 519 // List type 520 else if (PropertyType.isListType(propertyType)) { 521 PropertyDiff childNodeDiff = getChildNodePropertyDiff(nodeWithChildren, hasControlNodeChildNodes); 522 if (PropertyType.isListType(getPropertyType(nodeWithChildren))) { 523 ((ListPropertyDiff) fieldDiff).putAllDiff((ListPropertyDiff) childNodeDiff); 524 } else { 525 ((ListPropertyDiff) fieldDiff).putDiff(getNodePosition(nodeWithChildren), childNodeDiff); 526 } 527 } 528 // Complex type 529 else if (PropertyType.isComplexType(propertyType)) { 530 PropertyDiff childNodeDiff = getChildNodePropertyDiff(nodeWithChildren, hasControlNodeChildNodes); 531 if (PropertyType.isComplexType(getPropertyType(nodeWithChildren))) { 532 ((ComplexPropertyDiff) fieldDiff).putAllDiff((ComplexPropertyDiff) childNodeDiff); 533 } else { 534 ((ComplexPropertyDiff) fieldDiff).putDiff(nodeWithChildren.getNodeName(), childNodeDiff); 535 } 536 } 537 // Content type 538 else { 539 if (PropertyType.isContentType(getPropertyType(nodeWithChildren))) { 540 PropertyDiff childNodeDiff = getChildNodePropertyDiff(nodeWithChildren, hasControlNodeChildNodes); 541 ((ContentPropertyDiff) fieldDiff).setLeftContent(((ContentPropertyDiff) childNodeDiff).getLeftContent()); 542 ((ContentPropertyDiff) fieldDiff).setRightContent(((ContentPropertyDiff) childNodeDiff).getRightContent()); 543 } else { 544 setContentPropertyDiff((ContentPropertyDiff) fieldDiff, nodeWithChildren, hasControlNodeChildNodes); 545 } 546 } 547 } 548 549 /** 550 * Gets the child node property diff. 551 * 552 * @param node the node 553 * @param hasControlNodeChildNodes the test node was not found 554 */ 555 private static PropertyDiff getChildNodePropertyDiff(Node node, boolean hasControlNodeChildNodes) 556 { 557 558 PropertyDiff propertyDiff; 559 560 String nodePropertyType = getPropertyType(node); 561 562 // Simple type 563 if (PropertyType.isSimpleType(nodePropertyType)) { 564 propertyDiff = new SimplePropertyDiff(nodePropertyType); 565 setSimplePropertyDiff((SimplePropertyDiff) propertyDiff, node, hasControlNodeChildNodes); 566 } 567 // List type 568 else if (PropertyType.isListType(nodePropertyType)) { 569 propertyDiff = new ListPropertyDiff(nodePropertyType); 570 NodeList childNodes = node.getChildNodes(); 571 for (int i = 0; i < childNodes.getLength(); i++) { 572 ((ListPropertyDiff) propertyDiff).putDiff(i, 573 getChildNodePropertyDiff(childNodes.item(i), hasControlNodeChildNodes)); 574 } 575 } 576 // Complex type 577 else if (PropertyType.isComplexType(nodePropertyType)) { 578 propertyDiff = new ComplexPropertyDiff(); 579 NodeList childNodes = node.getChildNodes(); 580 for (int i = 0; i < childNodes.getLength(); i++) { 581 Node childNode = childNodes.item(i); 582 ((ComplexPropertyDiff) propertyDiff).putDiff(childNode.getNodeName(), 583 getChildNodePropertyDiff(childNode, hasControlNodeChildNodes)); 584 } 585 } 586 // Content type 587 else { 588 propertyDiff = new ContentPropertyDiff(); 589 NodeList childNodes = node.getChildNodes(); 590 for (int i = 0; i < childNodes.getLength(); i++) { 591 Node childNode = childNodes.item(i); 592 setContentPropertyDiff((ContentPropertyDiff) propertyDiff, childNode, hasControlNodeChildNodes); 593 } 594 } 595 return propertyDiff; 596 } 597 598 /** 599 * Sets the text content of textNode on {@link SimplePropertyDiff} field diff. 600 * 601 * @param fieldDiff the field diff 602 * @param textNode the text node 603 * @param hasControlNodeContent the has control node content 604 */ 605 private static void setSimplePropertyDiff(SimplePropertyDiff fieldDiff, Node textNode, boolean hasControlNodeContent) { 606 607 String textNodeValue = textNode.getTextContent(); 608 609 String leftValue = hasControlNodeContent ? textNodeValue : null; 610 String rightValue = hasControlNodeContent ? null : textNodeValue; 611 612 fieldDiff.setLeftValue(leftValue); 613 fieldDiff.setRightValue(rightValue); 614 } 615 616 /** 617 * Sets the text content of textNode on a {@link ContentPropertyDiff} field diff. 618 * 619 * @param fieldDiff the field diff 620 * @param textNode the text node 621 * @param hasControlNodeContent the has control node content 622 */ 623 private static void setContentPropertyDiff(ContentPropertyDiff fieldDiff, Node textNode, 624 boolean hasControlNodeContent) { 625 626 String textNodeValue = textNode.getTextContent(); 627 628 String leftValue = hasControlNodeContent ? textNodeValue : null; 629 String rightValue = hasControlNodeContent ? null : textNodeValue; 630 631 setContentSubPropertyDiff(fieldDiff, textNode.getNodeName(), leftValue, rightValue); 632 } 633 634 protected static void setContentSubPropertyDiff(ContentPropertyDiff fieldDiff, String subPropertyName, 635 String leftSubPropertyValue, String rightSubPropertyValue) { 636 637 // Get or initialize left and right content 638 ContentProperty leftContent = fieldDiff.getLeftContent(); 639 ContentProperty rightContent = fieldDiff.getRightContent(); 640 if (leftContent == null) { 641 leftContent = new ContentProperty(); 642 fieldDiff.setLeftContent(leftContent); 643 } 644 if (rightContent == null) { 645 rightContent = new ContentProperty(); 646 fieldDiff.setRightContent(rightContent); 647 } 648 649 // Set sub property on left and right content 650 leftContent.setSubProperty(subPropertyName, leftSubPropertyValue); 651 rightContent.setSubProperty(subPropertyName, rightSubPropertyValue); 652 653 // Set difference type on content property diff 654 if (ExportConstants.BLOB_FILENAME.equals(subPropertyName)) { 655 if (DifferenceType.differentDigest.equals(fieldDiff.getDifferenceType())) { 656 fieldDiff.setDifferenceType(DifferenceType.different); 657 } else { 658 fieldDiff.setDifferenceType(DifferenceType.differentFilename); 659 } 660 } else if (ExportConstants.BLOB_DIGEST.equals(subPropertyName)) { 661 if (DifferenceType.differentFilename.equals(fieldDiff.getDifferenceType())) { 662 fieldDiff.setDifferenceType(DifferenceType.different); 663 } else { 664 fieldDiff.setDifferenceType(DifferenceType.differentDigest); 665 } 666 } 667 } 668}