/** * * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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 test.sdo21.tests.util; import java.io.IOException; import java.io.InputStream; import java.net.URL; import java.util.ArrayList; import java.util.List; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.FactoryConfigurationError; import javax.xml.parsers.ParserConfigurationException; import org.w3c.dom.Document; import org.w3c.dom.DocumentType; import org.w3c.dom.NamedNodeMap; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.xml.sax.SAXException; /** * Provides a series of methods for comparing output from the XMLHelper with an * expected XML file. */ public class XMLEqualityChecker { private static final String XSI_NAMESPACE_URI = "http://www.w3.org/2001/XMLSchema-instance"; /** * Places the nodes in nodeList into a indexed list. * * @param nodeList * @param nodes */ private static void getAllNodes(NodeList nodeList, List nodes) { int length = nodeList.getLength(); if (length == 0) return; for (int i = 0; i < length; i++) { Node node = nodeList.item(i); nodes.add(node); getAllNodes(node.getChildNodes(), nodes); } // for } /** * Compares two NamedNodeMap instances and throws an exception if they are not equivalent. * * @param mapA * @param mapB */ private static void compareNamedNodeMap(NamedNodeMap mapA, NamedNodeMap mapB) throws XMLDifferenceException { if (mapA == null) { if (mapB == null) { return; } throw new XMLDifferenceException( "mapA was null, mapB wasn't" ); } if (mapA.getLength() != mapB.getLength()) { throw new XMLDifferenceException( "map length", mapA.getLength(), mapB.getLength() ); } for (int i = 0; i < mapA.getLength(); i++) { Node trialNode = mapA.item(i); if (trialNode == null) { throw new XMLDifferenceException("mapB did not contain value at index " + i); } if (trialNode.getNodeName().startsWith("xmlns:")) { // ignore namespace declarations since these will be checked on each element // using the namespace continue; } Node checkNode = mapB.getNamedItem(trialNode.getNodeName()); if (checkNode == null) { throw new XMLDifferenceException("mapB did not contain value named '" + trialNode.getNodeName() + "'"); } compareNode(trialNode, checkNode); } } /** * Compares two Node instances and throws an exception if they are not equivalent. * * @param nodeA * @param nodeB */ private static void compareNode(Node nodeA, Node nodeB) throws XMLDifferenceException { if (nodeA == null) { if (nodeB == null) { return; } throw new XMLDifferenceException( "nodeA was null, nodeB wasn't" ); } // following is intended to provide same function as 1.5 isEqualNode() if (nodeA.getNodeType() != nodeB.getNodeType()) { throw new XMLDifferenceException( "node type", nodeA.getNodeType(), nodeB.getNodeType() ); } if (!equalString(nodeA.getLocalName(), nodeB.getLocalName())) { throw new XMLDifferenceException( "local name", nodeA.getLocalName(), nodeB.getLocalName() ); } if (!equalString(nodeA.getNamespaceURI(), nodeB.getNamespaceURI())) { throw new XMLDifferenceException( "namespace URI", nodeA.getNamespaceURI(), nodeB.getNamespaceURI() ); } // special handling for xsi:type if (XSI_NAMESPACE_URI.equals(nodeA.getNamespaceURI()) && nodeA.getLocalName().equals("type")) { // because the gold xml file might use a different namespace prefix // than the sdo implementation under test, we need to ignore the // prefix used in any xsi:type attribute String nodeValueA = nodeA.getNodeValue(); String nodeValueB = nodeB.getNodeValue(); int posA = nodeValueA.indexOf(':'); int posB = nodeValueB.indexOf(':'); if (posA != -1 && posB != -1 && nodeValueA.substring(posA+1).equals(nodeValueB.substring(posB+1))) { // the type name does match } else { if (!equalString(nodeA.getNodeValue(), nodeB.getNodeValue())) { throw new XMLDifferenceException ("node value", nodeA.getNodeValue(), nodeB.getNodeValue() ); } } } else { if (!equalString(nodeA.getNodeValue(), nodeB.getNodeValue())) { throw new XMLDifferenceException( "value of node '" + nodeA.getLocalName() + "' in namespace '" + nodeA.getNamespaceURI() + "'", nodeA.getNodeValue(), nodeB.getNodeValue() ); } compareNamedNodeMap(nodeA.getAttributes(), nodeB.getAttributes()); compareNodeList( nodeA.getChildNodes(), nodeB.getChildNodes()); if (nodeA.getNodeType() == Node.DOCUMENT_TYPE_NODE) { DocumentType documentTypeA = (DocumentType)nodeA; DocumentType documentTypeB = (DocumentType)nodeB; if (!equalString(documentTypeA.getPublicId(), documentTypeB.getPublicId())) { throw new XMLDifferenceException( "public ID", documentTypeA.getPublicId(), documentTypeB.getPublicId() ); } if (!equalString(documentTypeA.getSystemId(), documentTypeB.getSystemId())) { throw new XMLDifferenceException( "system ID", documentTypeA.getSystemId(), documentTypeB.getSystemId() ); } if (!equalString(documentTypeA.getInternalSubset(), documentTypeB.getInternalSubset())) { throw new XMLDifferenceException( "internal subset", documentTypeA.getInternalSubset(), documentTypeB.getInternalSubset() ); } compareNamedNodeMap(documentTypeA.getEntities(), documentTypeB.getEntities()); compareNamedNodeMap(documentTypeA.getNotations(), documentTypeB.getNotations()); } } } /** * Compares two NodeList instances and throws an exception if they are not equivalent. * * @param nodeListA * @param nodeListB */ private static void compareNodeList(NodeList nodeListA, NodeList nodeListB) throws XMLDifferenceException { if (nodeListA == null) { if (nodeListB == null) { return; } throw new XMLDifferenceException( "nodeListA was null, nodeListB wasn't" ); } compareNodes(nodeListA, nodeListB); } /** * Returns true of the two Strings are equivalent, false otherwise. * * @param stringA * @param stringB */ private static boolean equalString(String stringA, String stringB) { if (stringA == null) { if (stringB == null) { return true; } return false; } return stringA.equals(stringB); } /** * Returns true if the two NodeLists are equivalent, false otherwise. * * @param sourceNodeList * @param targetNodeList */ private static void compareNodes(NodeList sourceNodeList, NodeList targetNodeList) throws XMLDifferenceException { ArrayList sourceNodes = new ArrayList(); ArrayList targetNodes = new ArrayList(); getAllNodes(sourceNodeList, sourceNodes); getAllNodes(targetNodeList, targetNodes); int sourceLength = sourceNodes.size(); int targetLength = targetNodes.size(); if (sourceLength != targetLength) { throw new XMLDifferenceException( "node count", sourceLength, targetLength ); } for (int i = 0; i < sourceLength; i++) { Node sourceNode = (Node)sourceNodes.get(i); Node targetNode = (Node)targetNodes.get(i); compareNode(sourceNode, targetNode); } // for } /** * Throws exception if the two XML files are not equivalent. * Accepts as input two URLs which identify the XML files, and calls * equalXMLFiles(InputStream, InputStream). * * @param source * @param target */ public static void compareXmlFiles(URL source, URL target) throws XMLDifferenceException, IOException { compareXmlFiles(source.openStream(), target.openStream()); } /** * Throws exception if the two XML files are not equivalent. * Accepts as input an InputStream and a URL which identify the XML files, * and calls equalXMLFiles(InputStream, InputStream). * * @param sourceStream * @param target */ public static void compareXmlFiles(InputStream sourceStream, URL target) throws XMLDifferenceException, IOException { compareXmlFiles(sourceStream, target.openStream()); } /** * Throws exception if the two XML files are not equivalent. * Accepts as input a URL and an InputStream which identify the XML files, * and calls equalXMLFiles(InputStream, InputStream). * * @param source * @param targetStream */ public static void compareXmlFiles(URL source, InputStream targetStream) throws XMLDifferenceException, IOException { compareXmlFiles(source.openStream(), targetStream); } /** * Throws exception if the two XML files are not equivalent. * Accepts as input two InputStreams which identify the XML files. * * @param sourceStream * @param targetStream */ public static void compareXmlFiles(InputStream sourceStream, InputStream targetStream) throws XMLDifferenceException, IOException { DocumentBuilder builder; Document sourceDocument; Document targetDocument; try { DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); factory.setNamespaceAware(true); builder = factory.newDocumentBuilder(); sourceDocument = builder.parse(sourceStream); targetDocument = builder.parse(targetStream); } catch (IOException ie) { throw ie; } catch (Exception e) { throw new XMLDifferenceException( "Failed to parse files", e ); } sourceDocument.normalize(); targetDocument.normalize(); /* * remove comment when migrated to Java 1.5 if * (!sourceDocument.getXmlVersion().equals(targetDocument.getXmlVersion())) * return false; String sourceXmlEncoding = * sourceDocument.getXmlEncoding(); String targetXmlEncoding = * targetDocument.getXmlEncoding(); if (sourceXmlEncoding != null && * targetXmlEncoding != null && * sourceXmlEncoding.equalsIgnoreCase(targetXmlEncoding)) { // continue } * else { return false; } */ NodeList sourceNodes = sourceDocument.getChildNodes(); NodeList targetNodes = targetDocument.getChildNodes(); compareNodes(sourceNodes, targetNodes); } }