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

org.cyclades.xml.comparitor.XMLComparitor Maven / Gradle / Ivy

/*******************************************************************************
 * Copyright (c) 2012, THE BOARD OF TRUSTEES OF THE LELAND STANFORD JUNIOR UNIVERSITY
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *
 *    Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 *    Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 *    Neither the name of the STANFORD UNIVERSITY nor the names of its contributors
 *    may be used to endorse or promote products derived from this software without
 *    specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
 * OF THE POSSIBILITY OF SUCH DAMAGE.
 *******************************************************************************/
package org.cyclades.xml.comparitor;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import org.xml.sax.InputSource;
import java.io.StringReader;

import org.cyclades.xml.parser.XMLParserException;
import org.w3c.dom.Attr;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.w3c.dom.Node;
import org.w3c.dom.NamedNodeMap;
import java.io.File;
import java.util.Vector;

/**
 * This class will compare two XML Strings to see if they are similar.
 * We need to include special logic, so it is not as straight forward as
 * simply comparing the dom for matching elements. You create this class
 * using the "template" XML you want to compare other XML documents with.
 * As long as the "compared" XML contains an exact subset of the "template" XML
 * then it is a considered match. We need to take into consideration the
 * logic of specifying a node that is not to exist in the compared node.
 * For our purposes we will have a reserved attribute that will flag a
 * leaf node as one we will not want to see in the compared node.
 *
 * RULES FOR ADDING AN OMIT ENTRY:
 *  The "omit" attribute needs to be defined on it's own...meaning one leaf defined
 *  all the way from the root. Here is an example of what it would look like:
 *  
 *      [" + compared.getFirstChild().getNodeValue() + "]");
                    if (nl.item(i).getNodeValue() == null) {
                        // WE HAVE AN EMPTY NODE
                        //NodeList list = ((Element)compared).getElementsByTagName(nl.item(i).getNodeName());
                        Vector list = getMatchingChildNodes(compared, nl.item(i).getNodeName());
                        if (list == null || list.size() < 1) {
                            throw new Exception(eLabel + "Missing node. See node:[" + nl.item(i).getNodeName() + "]");
                        }
                        // Loop through all possible matching empty elements and make sure one does not exist that matches
                        boolean failure = true;
                        for (int j = 0; j < list.size(); j++) {
                            if (((Node)list.elementAt(j)).getFirstChild() == null) {
                                //System.out.println("Matching empty nodes:[" + nl.item(i).getNodeName() + "] [" + ((Node)list.elementAt(j)).getNodeName() + "]");
                                try {
                                    compareAttributes(nl.item(i), (Node)list.elementAt(j));
                                    failure = false;
                                    break;
                                } catch (Exception e) {
                                    // We got an exception in this sibling. Try
                                    // the next one until all possibilities are
                                    // exhausted, then let exception loose.
                                    if (j == list.size() - 1) {
                                        throw new Exception ("Exception checking empty nodes: " + e);
                                    }
                                }
                            }
                        }
                        if (!failure) {
                            continue;
                        }
                        throw new Exception(eLabel + "Nested node encountered in Compared where there is blank in Comparator. See node:[" + nl.item(i).getNodeName() + "]");
                    } else {
                        // WE HAVE A TEXT LEAF NODE
                        // Go through all possible children and test
                        boolean match = false;
                        NodeList children = compared.getChildNodes();
                        for (int j = 0; j < children.getLength(); j++) {
                            match = nl.item(i).getNodeValue().equals(children.item(j).getNodeValue());
                            if (match) {
                                if (mOmit) {
                                    // Showstopper here folks!!
                                    throw new XMLComparitorException("Omit match found. Element:[" + comparitor.getNodeName() + "]");
                                }
                                break;
                            }
                        }
                        // If success on this node, continue to compare the next one...if not throw an XMLParserException
                        if (!match) throw new XMLParserException(eLabel + "XML mismatch. Leaf match not found for: [" + comparitor.getNodeName() + "][" + nl.item(i).getNodeValue() + "]");
                    }
                } else {
                    // WE HAVE CHILDREN NODES
                    Vector list = getMatchingChildNodes(compared, nl.item(i).getNodeName());
                    if ( list.size() < 1 ) {
                        throw new XMLParserException(eLabel + "Node name:[" + nl.item(i).getNodeName() + "] does not exist");
                    } else {
                        //matchElement(nl.item(i), (((Element)compared).getElementsByTagName( nl.item(i).getNodeName())).item(0));
                        // Run this code below to browse through siblings. Above line only looks at first
                        // similar child and then bases the match on that
                        for (int j = 0; j < list.size(); j++) {
                            // We only want to catch XMLParserException here...
                            try {
                                compareAttributes(nl.item(i), (Node)list.elementAt(j));
                                // If a match on a sibling occurs, break, unless there is a
                                // match with the omit flag set, then this is a showstopper.
                                // An XMLComparitorException signifies the condition
                                // that the omit flag is triggered. Throwing an XMLComparitorException
                                // is a showstopper. Anything else lets the comparison continue.
                                try {
                                    matchElement(nl.item(i), (Node)list.elementAt(j));
                                } catch (XMLComparitorException ex) {
                                    throw new XMLComparitorException("" + ex);
                                } catch (Exception ex) {
                                    throw new XMLParserException("" + ex);
                                }
                                break;
                            } catch (XMLParserException exception) {
                                // We got an exception on this sibling, try the next one
                                // We only catch XMLParserException here, any other
                                // exception will be a showstopper! Again, we only throw an
                                // exception when we dont find any matches in any of the siblings (hence
                                // we let the exceptions go through only on the last sibling)
                                //System.out.println(eLabel + "Looking for next sibling node to match");
                                if (j == list.size() - 1) {
                                    // OK, this sibling match does not exist.
                                    //System.out.println(comparitor.getNodeName() + " " + omit);
                                    if (omit) {
                                        // We have gone through all of the compared
                                        // child nodes without a match for the omit node.
                                        // This is a good case, just break and set mOmit to
                                        // false so you can move to next compared node and
                                        // process agin from there
                                        mOmit = false;
                                        break;
                                    }
                                    throw new Exception (eLabel + exception);
                                }
                            }
                        }
                    }
                }
            }
        } catch (XMLComparitorException e) {
            // Showstopper, discontinue comparison on the spot
            throw new XMLComparitorException(eLabel + e);
        } catch (Exception e) {
            // Flag a failure condition, but continue comaprison if children are left on the same level
            /*if (omit) {
              mOmit = false;
              return;
              }*/
            throw new XMLParserException(eLabel + e);
        }
    }

    /**
     * Will compare the attributes of a node, throwing an exception if a mismatch is encountered.
     * The attribute "omit" will be ignored here. As long as compared contains the attributes that
     * are in comparator and their corresponding values, this will not throw an exception.
     *
     * @param comparitor
     * @param compared
     * @throws XMLParserException
     */
    private void compareAttributes(Node comparitor, Node compared) throws XMLParserException {
        final String eLabel = "XMLComparitor.compareAttributes: ";
        try {
            //System.out.println(eLabel + "Comparing attributes to tags:[" + comparitor.getNodeName() + "][" +  compared.getNodeName() + "]");
            NamedNodeMap comparitorMap = comparitor.getAttributes();
            NamedNodeMap comparedMap = compared.getAttributes();
            if (comparitorMap == null && comparedMap == null) {
                // This is ok, both nodes are not Elements
                return;
            }
            for (int i = 0; i < comparitorMap.getLength(); i++) {
                Node comparatorNode = comparitorMap.item(i);
                String key = comparatorNode.getNodeName();
                // If this is an omit special request just skip the
                // key word here. We understand it is a reserve word
                // and we will take care of it above
                if (key.toUpperCase().equals(OMIT_FLAG.toUpperCase())) {
                    continue;
                }
                String value = comparatorNode.getNodeValue();
                //System.out.println(eLabel + key + " " + value);
                Node comparedNode = comparedMap.getNamedItem(key);
                if (comparedNode == null) {
                    throw new Exception("Attribute [" + key + "] not found in compared XML tag [" + comparitor.getNodeName() + "]");
                }
                String valueToCompare = comparedNode.getNodeValue();
                //System.out.println(eLabel + key + " " + valueToCompare);
                if (!value.equals(valueToCompare)) {
                    throw new Exception("Value of attribute [" + key + "] not equal in compared XML tag [" + comparitor.getNodeName() + "]");
                }
            }
        } catch (Exception e) {
            throw new XMLParserException(eLabel + e);
        }
    }

    public static String getAttributeOrError(Node node, String attrName) throws XMLParserException {
        final String aValue = XMLComparitor.getAttribute(node, attrName);
        if( aValue == null || aValue.trim().equals("") ) {
            throw new XMLParserException(attrName+" attribute must exist!");
        }
        return aValue.trim();
    }

    public static String getAttributeOrNull(Node node, String attrName) throws XMLParserException {
        final String aValue = XMLComparitor.getAttribute(node, attrName);
        if( aValue == null || aValue.trim().equals("") ) {
            return null;
        }
        return aValue.trim();
    }

    /**
     * Get the attribute specified for a node
     *
     * @param node
     * @param matchString
     * @return
     * @throws XMLParserException
     */
    public static String getAttribute(Node node, String matchString) throws XMLParserException {
        final String eLabel = "XMLComparitor.getAttribute: ";
        String returnString = null;
        try {
            NamedNodeMap map = node.getAttributes();
            if (map == null) {
                // This is ok, both nodes are not Elements
                return null;
            }
            for (int i = 0; i < map.getLength(); i++) {
                Node comparatorNode = map.item(i);
                String key = comparatorNode.getNodeName();
                // If this is an omit special request just skip the
                // key word here. We understand it is a reserve word
                // and we will take care of it above
                if (key.toUpperCase().equals(matchString.toUpperCase())) {
                    returnString =  comparatorNode.getNodeValue();
                }
            }
        } catch (Exception e) {
            throw new XMLParserException(eLabel + e);
        }
        return returnString;
    }

    /**
     * Get a Vector list of the children of this node matching this name
     *
     * @param node
     * @param name
     * @return
     * @throws XMLParserException
     */
    public static Vector getMatchingChildNodes (Node node, String name) throws XMLParserException {
        final String eLabel = "XMLComparitor.getMatchingChildNodes: ";
        Vector nodeVector = new Vector();
        try {
            NodeList nl = node.getChildNodes();
            for (int i = 0; i < nl.getLength(); i++) {
                if (nl.item(i).getNodeName().equals(name)) {
                    nodeVector.add(nl.item(i));
                }
            }
        } catch (Exception e) {
            throw new XMLParserException(eLabel + e);
        }
        return nodeVector;
    }

    /**
     * Return the highest node of a list, based on its long value
     *
     * @param list              The list to search
     * @param attributeName     The attribute to parse as a long
     * @return
     * @throws Exception
     */
    public static Node getLargestChildNodeLong (NodeList list, String attributeName) throws Exception {
        final String eLabel = "XMLComparitor.getLargestChildNodeLong: ";
        try {
            Node highestNode = null;
            for (int i = 0; i < list.getLength(); i++) {
                if (highestNode == null || Long.parseLong(getAttribute(list.item(i), attributeName)) >
                    Long.parseLong(getAttribute(highestNode, attributeName))) {
                    highestNode = list.item(i);
                }
            }
            return highestNode;
        } catch (Exception e) {
            throw new Exception(eLabel + e);
        }
    }

    /**
     * Return the highest node of a list, based on its float value
     *
     * @param list              The list to search
     * @param attributeName     The attribute to parse as a float
     * @return
     * @throws Exception
     */
    public static Node getLargestChildNodeFloat (NodeList list, String attributeName) throws Exception {
        final String eLabel = "XMLComparitor.getLargestChildNodeFloat: ";
        try {
            Node highestNode = null;
            for (int i = 0; i < list.getLength(); i++) {
                if (highestNode == null || Float.parseFloat(getAttribute(list.item(i), attributeName)) >
                    Float.parseFloat(getAttribute(highestNode, attributeName))) {
                    highestNode = list.item(i);
                }
            }
            return highestNode;
        } catch (Exception e) {
            throw new Exception(eLabel + e);
        }
    }

    /**
     * Get matching child node, allow a create if none exist...
     *
     * @param doc               The document object to traverse
     * @param parentNode        The parent node of the document
     * @param tagValue          The value to search for
     * @param create            If true, create a node if not found
     * @param attributeName     The name of the attribute to use for a value
     * @return
     * @throws Exception
     */
    public static Node getChildNode (Document doc, Node parentNode, String tagValue, boolean create, String attributeName) throws Exception {
        final String eLabel = "XMLComparitor.getChildNode: ";
        try {
            Node child = null;
            NodeList list = parentNode.getChildNodes();
            for (int i = 0; i < list.getLength(); i++) {
                if (tagValue.equals(XMLComparitor.getAttribute(list.item(i), attributeName))) {
                    return list.item(i);
                }
            }
            if (create) {
                child = addNewChildNode(doc, parentNode, tagValue, attributeName);
            }
            return child;
        } catch (Exception e) {
            throw new Exception (eLabel + e);
        }
    }

    /**
     * Add a new child node with the given attribute value for the "tag" attribute
     *
     * @param doc               The document object to traverse
     * @param parentNode        The parent node of the document
     * @param tagValue          The value to search for
     * @param attributeName     The name of the attribute to use for a value
     * @return
     * @throws Exception
     */
    public static Node addNewChildNode (Document doc, Node parentNode, String tagValue, String attributeName) throws Exception {
        final String eLabel = "XMLComparitor.addNewChildNode: ";
        try {
            Node newChildNode = doc.createElement("node");
            NamedNodeMap childAtts = newChildNode.getAttributes();
            Attr tag = doc.createAttribute(attributeName);
            tag.setValue(tagValue);
            childAtts.setNamedItem(tag);
            parentNode.appendChild(newChildNode);
            return newChildNode;
        } catch (Exception e) {
            throw new Exception (eLabel + e);
        }
    }

    /**
     * Main to run as example for implementation. Typically, no files will be
     * accessed. We will use only Strings, but can use files. Also, make sure the
     * files and the strings have no new lines or white space around tags. This
     * breaks the logic of this class because of parser inconsistencies.
     *
     * @param args
     */
    public static void main (String[] args) {
        try {
            if (args.length != 2) {
                System.out.println("useage: cmd comparator compared");
                return;
            }

            File file1 = new File(args[0]);
            File file2 = new File(args[1]);

            XMLComparitor xmlc = new XMLComparitor(parseXML(file1));
            if (xmlc.isMatch(parseXML(file2))) {
                System.out.println("MATCH");
            } else {
                System.out.println("NON_MATCH");
            }
        } catch (Exception e) {
            System.out.println("" + e);
        }
    }

    public Element getDomRootElement () {
        return templateRootElement;
    }

    private Element templateRootElement;
    private boolean mOmit;
    private final static String OMIT_FLAG = "omit";
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy