All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.seleniumtests.xmldog.Comparator Maven / Gradle / Ivy

/*
 * Copyright 2021 www.seleniumtests.com
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * http://www.apache.org/licenses/LICENSE-2.0
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.seleniumtests.xmldog;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.w3c.dom.Attr;
import org.w3c.dom.CDATASection;
import org.w3c.dom.Comment;
import org.w3c.dom.Document;
import org.w3c.dom.DocumentType;
import org.w3c.dom.Element;
import org.w3c.dom.EntityReference;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.w3c.dom.Text;

/**
 * Comparator class containing the Core of XML Comparison Engine.
 *
 * 

Comparison Engine takes each Test Document Node and tries and find the Control/Golden * *

Document Node which is a Best fit */ public class Comparator implements XMLDogConstants { private Node _controlNode = null; private Node _testNode = null; private Config _config = null; // List of listeners private List _listeners = new ArrayList(); private Set _elist = new HashSet(); private boolean _ignoringWhitespace = true; private boolean _includeNodeValueInXPath = true; /** * Default Contructor. */ public Comparator() { this(null, null, new Config()); } /** * Constructor. * * @param controlDoc the Control Document to compare test Document against * @param testDoc the test Document to be compared for differences */ public Comparator(final Node controlNode, final Node testNode, final Config config) { /* * * if ((controlNode == null) || (testNode == null)) * * throw new IllegalArgumentException("Cannot compare null node"); * */ _config = config; _controlNode = controlNode; _testNode = testNode; _ignoringWhitespace = config.isIgnoringWhitespace(); _includeNodeValueInXPath = config.includesNodeValuesInXPath(); _listeners.clear(); _listeners.add(new Differences()); } /** * Adds DifferenceListener. */ public void addDifferenceListener(final DifferenceListener listener) { if (listener != null) { _listeners.add(listener); } } /** * Overrides DifferenceListener.
* *

Method will clear all the DifferenceListeners and put only one * *

DifferenceListener passed as an argument */ public void overrideDifferenceListener(final DifferenceListener listener) { if (listener != null) { _listeners.clear(); _listeners.add(listener); } } /** * Notifies all the DifferenceListeners of the events. */ private void notifyDifferenceListeners(final int type, final Node controlNode, final Node testNode, final String msg) { if (type == XMLDogConstants.EVENT_NODE_IDENTICAL) { for (int i = 0; i < _listeners.size(); i++) { ((DifferenceListener) _listeners.get(i)).identicalNodeFound(controlNode, testNode, msg); } } else if (type == XMLDogConstants.EVENT_NODE_SIMILAR) { for (int i = 0; i < _listeners.size(); i++) { ((DifferenceListener) _listeners.get(i)).similarNodeFound(controlNode, testNode, msg); } } else if (type == XMLDogConstants.EVENT_NODE_MISMATCH) { for (int i = 0; i < _listeners.size(); i++) { ((DifferenceListener) _listeners.get(i)).nodeNotFound(controlNode, testNode, msg); } } else { // do nothing } } /** * Sets Control Node. */ public void setControlNode(final Node controlNode) { if (controlNode == null) { throw new IllegalArgumentException("Cannot compare null node"); } _controlNode = controlNode; } /** * Gets Control Node. */ public Node getControlNode() { return _controlNode; } /** * Sets Test Node. */ public void setTestNode(final Node testNode) { if (testNode == null) { throw new IllegalArgumentException("Cannot compare null node"); } _testNode = testNode; } /** * Gets Test Node. */ public Node getTestNode() { return _testNode; } /** * Convenience method to check if the whitespace is being ignored. */ protected boolean isIgnoringWhitespace() { return _config.isIgnoringWhitespace(); } /** * Compares Control and Test nodes. */ public Differences compare() { log("IN comparator compare method"); return compare(getControlNode(), getTestNode()); } /** * Compares nodes and its children recursively. * * @param control the control Node * @param test the test Node * * @return the Differences between two Nodes * * @see Differences */ public Differences compare(final Node controlNode, final Node testNode) { Differences differences = new Differences(); log("IN the compare(node, node) method"); Node parent = null; XNode xControlNode = new XNode(controlNode, XMLUtil.generateXPath(controlNode, _ignoringWhitespace, _includeNodeValueInXPath, false)); XNode xTestNode = new XNode(testNode, XMLUtil.generateXPath(testNode, _ignoringWhitespace, _includeNodeValueInXPath, false)); xControlNode.setDepth(0); xTestNode.setDepth(0); NodeResult nodeResult = new NodeResult(xControlNode, xTestNode, differences); // For Document type Node if (testNode.getNodeType() == Node.DOCUMENT_NODE) { log("Test Node is Document Node"); Element controlRoot = ((Document) controlNode).getDocumentElement(); // Element testRoot = ((Document)testNode).getDocumentElement(); if (testNode.hasChildNodes()) { if (!controlNode.hasChildNodes()) { if (!_config.isCustomDifference()) { differences.add("/" + testNode.getNodeName() + " is entirely new document"); } else { Difference diff = new Difference(DifferenceConstants.NEW_DOCUMENT, null, xTestNode); differences.add(diff); } return differences; } else { // Get all the Document Children and compare them differences.add(compareChildNodes(xControlNode, xTestNode)); } } else { if (!_config.isCustomDifference()) { differences.add("/" + testNode.getNodeName() + " is an empty document"); } else { Difference diff = new Difference(DifferenceConstants.EMPTY_DOCUMENT, xControlNode, xTestNode); differences.add(diff); } return differences; } } else { nodeResult = compareSimilarNodes(xControlNode, xTestNode, new OrderedMap(OrderedMap.TYPE_UNSYNCHRONIZED_MOV)); differences.add(nodeResult); } return differences; } /** * Compares Nodes similar to the ones which are input and returns the one. * *

that is the BEST fit * *

*
* OrderedMap is used for information such as which control Nodes are already * *

visited and which test Nodes match a given control Node * *

*
* For a given test Node and control Node set, similar Nodes are found using the parent * *

of the control Node which closely match a given test Node and only the Node matching most * *

closely to the test Node is returned * * @param control the control Node * @param test the test Node * @param nodeTracker the OrderedMap which contains control Nodes already visited as keys and * *

all the matching test Nodes as the values * * @return the NodeResult containing the Differences between two Nodes * * @see NodeResult */ protected NodeResult compareSimilarNodes(final XNode xControl, final XNode xTest, final OrderedMap nodeTracker) { Differences differences = new Differences(); boolean noSimilarNodes = true; // to indicate no nodes of the type found boolean unset = true; // to indicate bestfit is uninitialzed Node control = xControl.getNode(); Node test = xTest.getNode(); XNode xSimilarNode = null; String similarNodeXPath = null; NodeResult bestfitNodeResult = new NodeResult(xControl, xTest, differences); Node parent = control.getParentNode(); log("Parent XPath " + XMLUtil.generateXPath(xControl.getXPath())); if (parent != null) { List similarNodes = XMLUtil.getSimilarChildXNodes(parent, test, isIgnoringWhitespace()); Node similarNode = null; NodeResult nr = null; if (similarNodes.size() > 0) { noSimilarNodes = false; } // FIXME FIXME FIXME!! // Later control can be transferred to the App User code, to give them // control over comparing 2 similar nodes for (int i = 0; i < similarNodes.size(); i++) { xSimilarNode = (XNode) similarNodes.get(i); similarNodeXPath = XMLUtil.generateXPath(xSimilarNode.getNode(), XMLUtil.generateXPath(xControl.getXPath()), _ignoringWhitespace, _includeNodeValueInXPath, false); xSimilarNode.setXPath(similarNodeXPath); // For Element type Node if (test.getNodeType() == Node.ELEMENT_NODE) { log("compareSimilarNodes: Test node is ELEMENT type"); nr = compareElements(xSimilarNode, xTest); } else // For Entity Reference Node if (test.getNodeType() == Node.ENTITY_REFERENCE_NODE) { log("compareSimilarNodes: Test node is Entity Reference type"); } else // For Text Node if (test.getNodeType() == Node.TEXT_NODE) { log("compareSimilarNodes: Test node is TEXT type"); if ((StringUtil.isWhitespaceStr(test.getNodeValue())) && (isIgnoringWhitespace())) { log("compareSimilarNodes: Ignoring WHITE space node"); } else { nr = compareText(xSimilarNode, xTest); } } else // For Document Type Node if (test.getNodeType() == Node.DOCUMENT_TYPE_NODE) { log("compareSimilarNodes: Test node is DOCUMENT TYPE type"); nr = compareDocumentType(xSimilarNode, xTest); } else // For Comment Node if (test.getNodeType() == Node.COMMENT_NODE) { log("compareSimilarNodes: Test node is COMMENT type"); nr = compareComments(xSimilarNode, xTest); } else // For CDATA section Node if (test.getNodeType() == Node.CDATA_SECTION_NODE) { log("compareSimilarNodes: Test node is CDATA SECTION type"); nr = compareCDATA(xSimilarNode, xTest); } // Make sure bestfitNodeResult has the best result so far if (nr != null) { if ((nr.isExactMatch()) || (nr.isUniqueAttrMatch())) { bestfitNodeResult = nr; // Continue only if this similarNode (controlNode) has NOT // been taken already Object ntObject = null; boolean shouldBreak = false; /* * * if(((ntObject = nodeTracker.getElement(similarNode)) instanceof NodeResult) && * * (!((NodeResult)ntObject).isMatch())) * */ if ((ntObject = nodeTracker.getElement(similarNode)) == null) { shouldBreak = true; } else { if (ntObject instanceof List) { List l = (List) ntObject; for (int j = 0; j < l.size(); j++) { if (((NodeResult) l.get(j)).isMatch()) { shouldBreak = true; break; } } } if ((ntObject instanceof NodeResult) && (!((NodeResult) ntObject).isMatch())) { shouldBreak = true; } } if (shouldBreak) { break; } } else { // Assign first NR as the Best Fit and then compare if (unset) { bestfitNodeResult = nr; unset = false; } // whichever has minimum differences is the best fit else { if (nr.getDifferences().size() < bestfitNodeResult.getDifferences().size()) { bestfitNodeResult = nr; } else // If they are equal take the one which is not present in the nodeTracker if ((nr.getDifferences().size() == bestfitNodeResult.getDifferences().size()) && (nodeTracker.getElement(bestfitNodeResult.getControlNode().getNode()) != null)) { bestfitNodeResult = nr; } } } } } // end for all the similar nodes } // If no similar Nodes found and if its ignoring white space if (noSimilarNodes) { if ((isIgnoringWhitespace()) && (XMLUtil.isWhitespaceTextNode(test))) { // ignore } else { // BUG FIX // Explicitly set control node to NULL to indicate NO MATCHES found bestfitNodeResult.setControlNode(new XNode(null, null)); if (!_config.isCustomDifference()) { differences.add("Added Node: Test Node " + xTest.getXPath()); } else { Difference diff = new Difference(DifferenceConstants.NODE_NOT_FOUND, xControl, xTest); differences.add(diff); } } } log("Start==================================================================="); log("compareSimilarNodes: BestFitNode is " + bestfitNodeResult.toString()); log("End==================================================================="); return bestfitNodeResult; } /** * Compares DocumentType Node. * * @param control the control DOCTYPE Node * @param test the test DOCTYPE Node * * @return the NodeResult containing the Differences between two DOCTYPE Nodes * * @see NodeResult */ protected NodeResult compareDocumentType(final XNode xControl, final XNode xTest) { Differences differences = new Differences(); NodeResult nodeResult = new NodeResult(xControl, xTest, differences); boolean diffName = false; boolean diffPublicId = false; boolean diffSysId = false; DocumentType control = (DocumentType) xControl.getNode(); DocumentType test = (DocumentType) xTest.getNode(); if ((diffName = XMLUtil.areNullorEqual(control.getPublicId(), test.getPublicId(), _ignoringWhitespace, _includeNodeValueInXPath)) && (diffSysId = XMLUtil.areNullorEqual(control.getSystemId(), test.getSystemId(), _ignoringWhitespace, _includeNodeValueInXPath)) && (diffPublicId = XMLUtil.areNullorEqual(control.getName(), test.getName(), _ignoringWhitespace, _includeNodeValueInXPath))) { // do nothing } else { if (!_config.isCustomDifference()) { differences.add("Different DocumentType Node: Current Node " + xTest.getXPath() + " --> Golden Node " + xControl.getXPath()); } else { Difference diff = null; if (diffName) { diff = new Difference(DifferenceConstants.DOCTYPE_NAME, xControl, xTest); } else if (diffSysId) { diff = new Difference(DifferenceConstants.DOCTYPE_SYSTEM_ID, xControl, xTest); } else if (diffPublicId) { diff = new Difference(DifferenceConstants.DOCTYPE_PUBLIC_ID, xControl, xTest); } differences.add(diff); } } return nodeResult; } /** * Compares Comment Nodes. * * @param control the control Comment Node * @param test the test Comment Node * * @return the NodeResult containing the Differences between two Comment Nodes * * @see NodeResult */ protected NodeResult compareComments(final XNode xControl, final XNode xTest) { Differences differences = new Differences(); NodeResult nodeResult = new NodeResult(xControl, xTest, differences); Comment control = (Comment) xControl.getNode(); Comment test = (Comment) xTest.getNode(); if (!XMLUtil.nodesEqual(control, test, isIgnoringWhitespace())) { if (!_config.isCustomDifference()) { differences.add("Different Comment Node: Current Node" + xTest.getXPath() + " --> Golden Node " + xControl.getXPath()); } else { Difference diff = new Difference(DifferenceConstants.COMMENT_VALUE, xControl, xTest); differences.add(diff); } } else { nodeResult.setIfExactMatch(true); } return nodeResult; } /** * Compares CDATASECTION Nodes. * * @param control the control CDATA Node * @param test the test CDATA Node * * @return the NodeResult containing the Differences between two CDATA Nodes * * @see NodeResult */ protected NodeResult compareCDATA(final XNode xControl, final XNode xTest) { Differences differences = new Differences(); CDATASection control = (CDATASection) xControl.getNode(); CDATASection test = (CDATASection) xTest.getNode(); NodeResult nodeResult = new NodeResult(xControl, xTest, differences); if (!XMLUtil.nodesEqual(control, test, isIgnoringWhitespace())) { if (!_config.isCustomDifference()) { differences.add("Different CDATA Node : Current Node " + xTest.getXPath() + " --> Golden Node " + xControl.getXPath()); } else { Difference diff = new Difference(DifferenceConstants.CDATA_VALUE, xControl, xTest); differences.add(diff); } } else { nodeResult.setIfExactMatch(true); } return nodeResult; } /** * Compares Entity Reference Nodes. * * @param control the control EntityRef Node * @param test the test EntityRef Node * * @return the NodeResult containing the Differences between two EntityRef Nodes * * @see NodeResult */ protected NodeResult compareEntityRefs(final XNode xControl, final XNode xTest) { Differences differences = new Differences(); EntityReference control = (EntityReference) xControl.getNode(); EntityReference test = (EntityReference) xTest.getNode(); NodeResult nodeResult = new NodeResult(xControl, xTest, differences); if (!XMLUtil.nodesEqual(control, test, isIgnoringWhitespace())) { differences.add("Different Comment Node : Current Node " + xTest.getXPath() + " --> Golden Node " + xControl.getXPath()); } else { nodeResult.setIfExactMatch(true); } return nodeResult; } /** * Compares Text Nodes. * * @param control the control Text Node * @param test the test Text Node * * @return the NodeResult containing the Differences between two Text Nodes * * @see NodeResult */ protected NodeResult compareText(final XNode xControl, final XNode xTest) { Differences differences = new Differences(); Text control = (Text) xControl.getNode(); Text test = (Text) xTest.getNode(); NodeResult nodeResult = new NodeResult(xControl, xTest, differences); if (!XMLUtil.nodesEqual(control, test, isIgnoringWhitespace())) { if (DEBUG) { System.out.println("===> Compare Text is ignoring whitespace " + isIgnoringWhitespace()); System.out.println("=====> Text nodes Control and test "); XMLUtil.printNodeBasics(control); XMLUtil.printNodeBasics(test); } if (!_config.isCustomDifference()) { differences.add("Different Text Node: Current Node " + xTest.getXPath() + " --> Golden Node " + xControl.getXPath()); } else { Difference diff = new Difference(DifferenceConstants.TEXT_VALUE, xControl, xTest); differences.add(diff); } } else { nodeResult.setIfExactMatch(true); } return nodeResult; } /** * Compares Control and Test Element nodes. * *

*
* While comparing Element nodes, all the Attributes as well as Children of the Element nodes * *

are compared recursively * * @param control the control Element Node * @param test the test Element Node * * @return the NodeResult containing the Differences between two Element Node subtree * * @see NodeResult */ protected NodeResult compareElements(final XNode xControl, final XNode xTest) { Differences differences = new Differences(); Element control = (Element) xControl.getNode(); Element test = (Element) xTest.getNode(); NodeResult nodeResult = new NodeResult(xControl, xTest, differences); log("Comparing Elements at Test " + xTest.getXPath() + " Control " + xControl.getXPath()); /* * * Element controlNode = (Element)control.cloneNode(true); * * Element testNode = (Element)test.cloneNode(true); * * * * String uniqueAttrName = (String)_config.getUniqueAttributeMap().get(testNode.getTagName()); * */ String uniqueAttrName = (String) _config.getUniqueAttributeMap().get(test.getTagName()); // Special case if the nodes are same in the first shot itself // based on the unique attribute // if((uniqueAttrName != null) && (XMLUtil.nodesEqual(controlNode, testNode, isIgnoringWhitespace()))) if ((uniqueAttrName != null) && (XMLUtil.nodesEqual(control, test, isIgnoringWhitespace()))) { /* Next block is identical except I am NOT USING CLONED NODE * * if((controlNode.hasAttributes()) && (testNode.hasAttributes())) * * { * * String testAttrValue = testNode.getAttribute(uniqueAttrName); * * String controlAttrValue = controlNode.getAttribute(uniqueAttrName); * * * * if ((!testAttrValue.trim().equals("")) && (controlAttrValue.equals(testAttrValue))) * * { * * nodeResult.setUniqueAttrMatch(true); * * return nodeResult; * * } * * } * */ if ((control.hasAttributes()) && (test.hasAttributes())) { String testAttrValue = test.getAttribute(uniqueAttrName); String controlAttrValue = control.getAttribute(uniqueAttrName); if ((!testAttrValue.trim().equals("")) && (controlAttrValue.equals(testAttrValue))) { nodeResult.setUniqueAttrMatch(true); return nodeResult; } } } reportMissingAttrs(xControl, xTest, differences); // Compare all the Child Nodes differences.add(compareChildNodes(xControl, xTest)); // if no differences, its an exact match if (differences.size() == 0) { log(" Exact match for Test ELEMENT node " + XMLUtil.getNodeBasics(test) + " with control node " + XMLUtil.getNodeBasics(control)); nodeResult.setIfExactMatch(true); } return nodeResult; } private Differences reportMissingAttrs(final XNode xControl, final XNode xTest, final Differences differences) { boolean includedEmpty = false; boolean excludedEmpty = false; List excludedAttrs = null; List includedAttrs = null; Element control = (Element) xControl.getNode(); Element test = (Element) xTest.getNode(); String controlNodeXPath = xControl.getXPath(); String testNodeXPath = xTest.getXPath(); log("Comparing Attributes for test " + testNodeXPath + " control " + controlNodeXPath); Element controlNode = (Element) control.cloneNode(true); Element testNode = (Element) test.cloneNode(true); // Since the elementList contains elements with the same name, we only // need to get this once excludedAttrs = (List) _config.getExcludedAttributesMap().get(testNode.getTagName()); includedAttrs = (List) _config.getIncludedAttributesMap().get(testNode.getTagName()); if ((includedAttrs == null) || (includedAttrs.size() == 0)) { includedEmpty = true; } if ((excludedAttrs == null) || (excludedAttrs.size() == 0)) { excludedEmpty = true; } NamedNodeMap testAttrs = testNode.getAttributes(); NamedNodeMap controlAttrs = controlNode.getAttributes(); NamedNodeMap testAttrsForXPath = test.getAttributes(); NamedNodeMap controlAttrsForXPath = control.getAttributes(); if (DEBUG) { System.out.println(" ********** " + test.getNodeName() + " test node has " + testAttrs.getLength() + " attrs "); System.out.println(" ********** " + control.getNodeName() + " control node has " + controlAttrs.getLength() + " attrs "); } // Remove all the excluded attributes, no need to compare them if (!excludedEmpty) { for (int i = 0; i < excludedAttrs.size(); i++) { if (testAttrs != null) { testAttrs.removeNamedItem((String) excludedAttrs.get(i)); } if (controlAttrs != null) { controlAttrs.removeNamedItem((String) excludedAttrs.get(i)); } } } String testAttrName = null; Attr testAttrNode = null; Attr testAttrNodeXPath = null; Attr controlAttrNode = null; Attr controlAttrNodeXPath = null; String controlAttrNodeXPathStr = null; if (testAttrs != null) { for (int j = 0; j < testAttrs.getLength(); j++) { testAttrNode = (Attr) testAttrs.item(j); testAttrName = testAttrNode.getName(); testAttrNodeXPath = (Attr) testAttrsForXPath.getNamedItem(testAttrName); // Skip this attribute if its not in the included attrs if ((!includedEmpty) && (!includedAttrs.contains(testAttrName))) { continue; } String nodeXPath = XMLUtil.generateXPath(testAttrNode, testNodeXPath, _ignoringWhitespace, _includeNodeValueInXPath, false); // Skip this attribute if its in the EList if (_config.isXPathEListEnabled()) { if (_config.applyEListToSiblings()) { nodeXPath = XMLUtil.getNoIndexXPath(nodeXPath); } if (_config.getXPathEList().containsKey(nodeXPath)) { continue; // exclude this if its Xpath is in the EList } } if (controlAttrs != null) { controlAttrNode = (Attr) controlAttrs.getNamedItem(testAttrName); controlAttrNodeXPath = (Attr) controlAttrsForXPath.getNamedItem(testAttrName); } XNode xTestNode = null; XNode xControlNode = null; if (controlAttrNode == null) { log("Added Attribute: Test node " + nodeXPath); if (!_config.isCustomDifference()) { differences.add("New Attribute added: Test Node " + nodeXPath); } else { if (testAttrNode != null) { xTestNode = new XNode(testAttrNodeXPath, XMLUtil.generateXPath(testAttrNode, testNodeXPath, _ignoringWhitespace, _includeNodeValueInXPath, false)); } if (controlAttrNode != null) { xControlNode = new XNode(controlAttrNodeXPath, XMLUtil.generateXPath(controlAttrNode, controlNodeXPath, _ignoringWhitespace, _includeNodeValueInXPath, false)); } Difference diff = new Difference(DifferenceConstants.ATTR_NAME_NOT_FOUND, xControlNode, xTestNode); differences.add(diff); } } else { if (!controlAttrNode.getValue().equals(testAttrNode.getValue())) { controlAttrNodeXPathStr = XMLUtil.generateXPath(controlAttrNode, controlNodeXPath, _ignoringWhitespace, _includeNodeValueInXPath, false); log("Different Attributes: Test document Node " + nodeXPath + " --> Control document Node " + controlAttrNodeXPathStr); if (!_config.isCustomDifference()) { differences.add("Different Attributes: Current Node " + nodeXPath + " --> Golden Node " + controlAttrNodeXPathStr); } else { if (testAttrNode != null) { xTestNode = new XNode(testAttrNode, XMLUtil.generateXPath(testAttrNode, testNodeXPath, _ignoringWhitespace, _includeNodeValueInXPath, false)); } if (controlAttrNode != null) { xControlNode = new XNode(controlAttrNode, controlAttrNodeXPathStr); } Difference diff = new Difference(DifferenceConstants.ATTR_VALUE, xControlNode, xTestNode); differences.add(diff); } } // Remove Attribute from the Control document, whatever that is left // in the end didnt exist in the Test document controlAttrs.removeNamedItem(testAttrName); } } } // If any attributes left unmatched, add them to differences list if (controlAttrs.getLength() > 0) { XNode xTestNode = null; XNode xControlNode = null; for (int i = 0; i < controlAttrs.getLength(); i++) { controlAttrNode = (Attr) controlAttrs.item(i); controlAttrNodeXPath = (Attr) controlAttrsForXPath.getNamedItem(controlAttrNode.getName()); testAttrNodeXPath = (Attr) testAttrsForXPath.getNamedItem(controlAttrNode.getName()); controlAttrNodeXPathStr = XMLUtil.generateXPath(controlAttrNodeXPath, controlNodeXPath, _ignoringWhitespace, _includeNodeValueInXPath, false); // Skip this attribute if its in the EList if (_config.isXPathEListEnabled()) { if (_config.applyEListToSiblings()) { controlAttrNodeXPathStr = XMLUtil.getNoIndexXPath(controlAttrNodeXPathStr); } if (_config.getXPathEList().containsKey(controlAttrNodeXPathStr)) { continue; } } log("Missing Attribute: Test document is missing attribute " + XMLUtil.generateXPath(controlAttrNodeXPath, isIgnoringWhitespace())); if (!_config.isCustomDifference()) { differences.add("Missing Attribute: Test Node " + controlAttrNodeXPathStr); } else { if (testAttrNodeXPath != null) { xTestNode = new XNode(testAttrNodeXPath, XMLUtil.generateXPath(testAttrNodeXPath, testNodeXPath, _ignoringWhitespace, _includeNodeValueInXPath, false)); } if (controlAttrNodeXPath != null) { xControlNode = new XNode(controlAttrNodeXPath, controlAttrNodeXPathStr); } Difference diff = new Difference(DifferenceConstants.ATTR_NAME_NOT_FOUND, xControlNode, xTestNode); differences.add(diff); } } } return differences; } /** * FIXME FIXME FIXME!! * *

Refactor above function to reduce the complexity and put compare attributes here * *

Compares Attribute Nodes */ protected NodeResult compareAttributes(final XNode xControl, final XNode xTest) { Differences differences = new Differences(); NodeResult nodeResult = new NodeResult(xControl, xTest, differences); return nodeResult; } /** * Compares all the Child Nodes for given Control and Test Nodes recursively. * *

*
* Reports differences between the entire subtree till the leaf nodes for * *

control and test Nodes * * @param controlNode the control Node * @param testNode the test Node * * @return the Differences between control Node and test Node * * @see Differences */ protected Differences compareChildNodes(final XNode xControl, final XNode xTest) { if ((xControl == null) || (xTest == null)) { throw new IllegalArgumentException("Test and/or Control Node argument cannot be null"); } log("Comparing CHILD Nodes for Test:" + xTest.getXPath() + "-Control:" + xControl.getXPath() + "-"); Differences differences = new Differences(); OrderedMap nodeTracker = new OrderedMap(OrderedMap.TYPE_UNSYNCHRONIZED_MOV); Node control = xControl.getNode(); Node test = xTest.getNode(); NodeList testChildNodes = null; NodeList controlChildNodes = null; Node testChildNode = null; Node controlChildNode = null; // Check to see if the Control Node or Test Node has been added if (control == null) { if (test != null) { if (!_config.isCustomDifference()) { differences.add("Test Node added at " + xTest.getXPath()); } else { Difference diff = new Difference(DifferenceConstants.ADDED_NODE, null, xTest); differences.add(diff); } return differences; } else { return null; } } else { if (test == null) { if (!_config.isCustomDifference()) { differences.add("Golden Node added at " + xControl.getXPath()); } else { Difference diff = new Difference(DifferenceConstants.ADDED_NODE, xControl, null); differences.add(diff); } return differences; } } // If it came this far, then both control and test are NON NULL if (test.hasChildNodes()) { if (control.hasChildNodes()) { testChildNodes = test.getChildNodes(); controlChildNodes = control.getChildNodes(); NodeResult matchedNodeResult = null; String testNodeXPath = null; XNode xControlChildNode = null; XNode xTestChildNode = null; String testChildXPath = null; String controlChildXPath = null; // For Test Node Child and find a matching Control Node for (int i = 0; i < testChildNodes.getLength(); i++) { testChildNode = testChildNodes.item(i); testChildXPath = XMLUtil.generateXPath(testChildNode, xTest.getXPath(), _ignoringWhitespace, _includeNodeValueInXPath, false); xTestChildNode = new XNode(testChildNode, testChildXPath); xTestChildNode.setDepth(xTest.getDepth() + 1); xTestChildNode.setPosition(i); // If controlChildNode is null, return first child since we are going to get its parent // and find similar Nodes anyway controlChildNode = controlChildNodes.item(i) == null ? controlChildNodes.item(0) : controlChildNodes.item(i); log("******** controlChildNode " + controlChildNode.getNodeName() + " type " + controlChildNode.getNodeType()); controlChildXPath = XMLUtil.generateXPath(controlChildNode, xControl.getXPath(), _ignoringWhitespace, _includeNodeValueInXPath, false); log("compareChildNodes()......controlChildXPath " + controlChildXPath); xControlChildNode = new XNode(controlChildNode, controlChildXPath); if (_config.isXPathEListEnabled()) { testNodeXPath = _config.applyEListToSiblings() ? xTestChildNode.getNoIndexXPath() : xTestChildNode.getXPath(); } if ((XMLUtil.isWhitespaceTextNode(testChildNode)) && (isIgnoringWhitespace())) { // skip // Ignore Whitespace only TEXT nodes log("Ignoring Whitespace Node"); } else if (XMLUtil.isCommentNode(testChildNode) && (_config.isIgnoringComments())) { // skip // Ignore Comment nodes log("Ignoring Comment Node"); } else if ((testNodeXPath != null) && (_config.getXPathEList().containsKey(testNodeXPath))) { // exclude this if its Xpath is in the EList log("Ignoring the Node since its XPath entry in EList file"); } // Now we need to find Similar control nodes else { // Skip the global excluded Elements if ((testChildNode.getNodeType() == Node.ELEMENT_NODE) && (_config.getExcludedElementsSet().contains(testChildNode.getNodeName()))) { // Skip log("compareChildNodes: Ignoring element child node at " + XMLUtil.generateXPath(testChildNode, isIgnoringWhitespace()) + " since its in ignore list"); } else { matchedNodeResult = compareSimilarNodes(xControlChildNode, xTestChildNode, nodeTracker); log("CompareChildNodes: Matched node result " + matchedNodeResult.toString()); // Ignore Control nodes for non matched Test nodes if (matchedNodeResult.getControlNode() != null) { // Add this matched entry in the nodeTracker // This is for the scenerio where multiple Test Nodes potentially // match a given Control Node nodeTracker.add(matchedNodeResult.getControlNode().getNode(), matchedNodeResult); } else { differences.add(matchedNodeResult); } } } } // end for each test child Node // Node Tracker contains Control Nodes as keys and Matching Test nodes // as Objects, these Objects can be List if there are more Test nodes // for a given Control node // Go thru the matched Test nodes to see if there are more Test Nodes // for a given Control Node // Since potentially many Test Nodes can match one Control Node Object[] nodeResults = nodeTracker.elements(); for (int i = 0; i < nodeResults.length; i++) { Object nodeResult = nodeResults[i]; // System.out.println(" i = " + i); NodeResult minDiffNR = null; // if its a List its MOV if (nodeResult instanceof List) { log("CompareChildNodes: ++++++ Adding child differences from LIST NodeResult " + nodeResult.toString()); NodeResult currentNR = null; minDiffNR = (NodeResult) ((List) nodeResult).get(0); for (int j = 1; j < ((List) nodeResult).size(); j++) { currentNR = (NodeResult) ((List) nodeResult).get(j); // Gather all the Nodes with more Differences if ((currentNR.getNumDifferences()) < (minDiffNR.getNumDifferences())) { if (!_config.isCustomDifference()) { // If currentNR is an exact match if (currentNR.getNumDifferences() == 0) { differences.add("Added Node: Test Node " + minDiffNR.getTestNode().getXPath()); } else { differences.add("Added Node/Multiple Matches: Current Node " + minDiffNR.getTestNode().getXPath() + " seems to match with already matched Golden Node at " + minDiffNR.getControlNode().getXPath()); differences.add(minDiffNR); } } else { XNode xTNode = new XNode(minDiffNR.getTestNode().getNode(), minDiffNR.getTestNode().getXPath()); XNode xCNode = new XNode(minDiffNR.getControlNode().getNode(), minDiffNR.getControlNode().getXPath()); Difference diff = null; if (currentNR.getNumDifferences() == 0) { diff = new Difference(DifferenceConstants.ADDED_NODE, xCNode, xTNode); } else { diff = new Difference(DifferenceConstants.MULTIPLE_MATCHES_ADDED_NODE, xCNode, xTNode); } differences.add(diff); } minDiffNR = currentNR; } else { if (!_config.isCustomDifference()) { // If minNR is an exact match if (minDiffNR.getNumDifferences() == 0) { differences.add("Added Node: Test Node " + currentNR.getTestNode().getXPath()); } else { differences.add("Added Node/Multiple matches: Current Node " + currentNR.getTestNode().getXPath() + " seems to match with already matched Golden Node at " + currentNR.getControlNode().getXPath()); differences.add(currentNR); } } else { XNode xTNode = new XNode(currentNR.getTestNode().getNode(), currentNR.getTestNode().getXPath()); XNode xCNode = new XNode(minDiffNR.getControlNode().getNode(), currentNR.getControlNode().getXPath()); Difference diff = null; if (minDiffNR.getNumDifferences() == 0) { diff = new Difference(DifferenceConstants.ADDED_NODE, xCNode, xTNode); } else { diff = new Difference(DifferenceConstants.MULTIPLE_MATCHES_ADDED_NODE, xCNode, xTNode); } differences.add(diff); } } } // The Node with least differences is now added // to the list of differences if ((!_config.isIgnoringOrder()) && (minDiffNR.getControlNode().getPosition() != minDiffNR.getTestNode().getPosition())) { if (!_config.isCustomDifference()) { differences.add("Position Mismatch: Current Node " + minDiffNR.getTestNode().getXPath() + " at position " + minDiffNR.getTestNode().getPosition() + " matches " + (minDiffNR.isExactMatch() ? "" : "closely") + " with Golden Node " + minDiffNR.getControlNode().getXPath() + " at position " + minDiffNR.getControlNode().getPosition()); } else { differences.add(new Difference(DifferenceConstants.POSITION_MISMATCH, minDiffNR.getControlNode(), minDiffNR.getTestNode())); } } differences.add(minDiffNR); } else { NodeResult nr = (NodeResult) nodeResult; log("compareChildNodes: ++++++ Adding child differences from INDIVIDUAL NodeResult " + nodeResult.toString()); if ((!_config.isIgnoringOrder()) && (nr.getControlNode().getPosition() != nr.getTestNode().getPosition())) { if (!_config.isCustomDifference()) { differences.add("Position Mismatch: Current Node " + nr.getTestNode().getXPath() + " at position " + nr.getTestNode().getPosition() + " matches " + (nr.isExactMatch() ? "" : "closely") + " with Golden Node " + nr.getControlNode().getXPath() + " at position " + nr.getControlNode().getPosition()); } else { differences.add(new Difference(DifferenceConstants.POSITION_MISMATCH, nr.getControlNode(), nr.getTestNode())); } } differences.add(nr); } } String controlChildNodeXPathStr = null; // Go thru the Control Nodes to see if there are unmatched Control nodes for (int i = 0; i < controlChildNodes.getLength(); i++) { controlChildNode = controlChildNodes.item(i); controlChildNodeXPathStr = XMLUtil.generateXPath(controlChildNode, xControl.getXPath(), _ignoringWhitespace, _includeNodeValueInXPath, false); if (!nodeTracker.containsElementKey(controlChildNode)) { if (_config.applyEListToSiblings()) { controlChildNodeXPathStr = XMLUtil.getNoIndexXPath(controlChildNodeXPathStr); } if (!_config.getXPathEList().containsKey(controlChildNodeXPathStr)) { reportNodeDifference(new XNode(controlChildNode, XMLUtil.generateXPath(controlChildNode, xControl.getXPath(), _ignoringWhitespace, _includeNodeValueInXPath, false)), xTest, differences, "Missing Node: Current Node "); } } } } // Report all the Nodes under Test node as being NEWLY added else { testChildNodes = test.getChildNodes(); String testChildNodeXPathStr = null; for (int i = 0; i < testChildNodes.getLength(); i++) { testChildNode = testChildNodes.item(i); testChildNodeXPathStr = XMLUtil.generateXPath(testChildNode, xTest.getXPath(), _ignoringWhitespace, _includeNodeValueInXPath, false); reportNodeDifference(xControl, new XNode(testChildNode, testChildNodeXPathStr), differences, "Added Node: Test Node "); } } // end else NO control child Nodes } // end if Test node has Child Nodes // Report all the Control Nodes as being missing else { if (control.hasChildNodes()) { String controlChildNodeXPathStr = null; controlChildNodes = control.getChildNodes(); for (int i = 0; i < controlChildNodes.getLength(); i++) { controlChildNode = controlChildNodes.item(i); controlChildNodeXPathStr = XMLUtil.generateXPath(controlChildNode, xControl.getXPath(), _ignoringWhitespace, _includeNodeValueInXPath, false); reportNodeDifference(new XNode(controlChildNode, controlChildNodeXPathStr), xTest, differences, "Missing Node: Test document is missing node "); } } } return differences; } /** * Reports a Node difference, based on the configuration settings. * * @param node the Node to report the Differences for * @param differences the Differences to which all the differences will be added * @param msg the Message that will be appended before the actual difference String */ private void reportNodeDifference(final XNode xControl, final XNode xTest, final Differences differences, final String msg) { Node control = xControl.getNode(); Node test = xTest.getNode(); if ((control == null) && (test == null)) { return; } XNode node = control == null ? xTest : xControl; if ((XMLUtil.isWhitespaceTextNode(node.getNode())) && (isIgnoringWhitespace())) { // skip } else if (XMLUtil.isCommentNode(node.getNode()) && (_config.isIgnoringComments())) { // skip // Ignore Comment nodes } else { // Skip the excluded Elements if ((node.getNode().getNodeType() == Node.ELEMENT_NODE) && (_config.getExcludedElementsSet().contains(node.getNode().getNodeName()))) { // Skip log("Ignoring element child node at " + node.getXPath() + " since its in ignore list"); } else { if (!_config.isCustomDifference()) { differences.add(msg + node.getXPath()); } else { NodeDetail testNodeDetail = null; NodeDetail controlNodeDetail = null; Difference diff = new Difference(DifferenceConstants.NODE_NOT_FOUND, xControl, xTest); differences.add(diff); } } } } /** * Prints msg to System.out. */ public static void log(final String msg) { if (DEBUG) { System.out.println("Comparator:" + msg); } } /** * Prints msg and Exception to System.out. */ public static void log(final String msg, final Throwable t) { if (DEBUG) { log(msg); t.printStackTrace(System.out); } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy