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