/* * RelaxerOrg class library * Copyright (C) 2000-2004 ASAMI, Tomoharu (asami@relaxer.org) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ package com.AsamiOffice.xml.tester; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.Reader; import java.io.StringReader; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.net.URL; import java.net.URLClassLoader; import java.util.ArrayList; import java.util.List; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import org.w3c.dom.Attr; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.NamedNodeMap; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.xml.sax.InputSource; import org.xml.sax.SAXException; import com.AsamiOffice.xml.UXMLMaker; import com.AsamiOffice.xml.sax.Sax1DomMaker; import com.AsamiOffice.xml.sax.Sax2DomMaker; /** * XmlTester * * @since Nov. 20, 2002 * @version Feb. 4, 2004 * @author ASAMI, Tomoharu (asami@relaxer.org) */ public class XmlTester { public static final int MESSAGE_NOT_USED = 0; public static final int MESSAGE_NONE = 1; public static final int MESSAGE_INFO = 2; public static final int MESSAGE_DEBUG = 3; // object scope private Class targetClass_; private Method setupMethod_; private Method makeMethod_; private Method makeTextMethod_; private Method makeSax1Method_; private Method makeSax2Method_; private Method verifyMethod_; // test scope private Object targetObject_; private Document targetDoc_; private Document generatedDoc_; private Document generatedTextDoc_; // notification private static int gMessageLevel__ = MESSAGE_INFO; // private static int gMessageLevel__ = MESSAGE_DEBUG; private int messageLevel_ = MESSAGE_NOT_USED; public XmlTester() { targetClass_ = null; } public XmlTester(String relaxerObject) throws ClassNotFoundException, NoSuchMethodException, IOException { _init(relaxerObject); } public XmlTester(String relaxerObject, ClassLoader loader) throws ClassNotFoundException, NoSuchMethodException { _init(relaxerObject, loader); } public XmlTester(String relaxerObject, String[] classpath) throws ClassNotFoundException, NoSuchMethodException, IOException { _init(relaxerObject, classpath); } private void _init(String relaxerObject) throws ClassNotFoundException, NoSuchMethodException, IOException { _init(relaxerObject, new String[] { "." }); } private void _init(String relaxerObject, String[] classpath) throws ClassNotFoundException, NoSuchMethodException, IOException { URL[] urls = new URL[classpath.length]; for (int i = 0; i < classpath.length; i++) { String path = classpath[i]; try { urls[i] = new URL(path); } catch (IOException e) { File file = new File(path).getCanonicalFile(); urls[i] = file.toURL(); } } ClassLoader loader = new URLClassLoader(urls); _init(relaxerObject, loader); } private void _init(String relaxerObject, ClassLoader loader) throws ClassNotFoundException, NoSuchMethodException { targetClass_ = loader.loadClass(relaxerObject); _init(); } private void _init() throws ClassNotFoundException, NoSuchMethodException { setupMethod_ = targetClass_.getMethod("setup", new Class[] { String.class }); makeMethod_ = targetClass_.getMethod("makeDocument", new Class[0]); makeTextMethod_ = targetClass_.getMethod("makeTextDocument", new Class[0]); try { makeSax1Method_ = targetClass_.getMethod( "makeDocument", new Class[] { org.xml.sax.DocumentHandler.class }); } catch (NoSuchMethodException e) { // ignore sax } try { makeSax2Method_ = targetClass_.getMethod( "makeDocument", new Class[] { org.xml.sax.ContentHandler.class }); } catch (NoSuchMethodException e) { // ignore sax } try { verifyMethod_ = targetClass_.getMethod("verify", new Class[0]); } catch (NoSuchMethodException e) { // ignore sax } } public Status execute(String xmlDoc) throws SecurityException, IOException, SAXException, ParserConfigurationException, InstantiationException, IllegalAccessException, InvocationTargetException, NoSuchMethodException { return (execute(xmlDoc, xmlDoc)); } public Status execute(Document source, String compare) throws SecurityException, IOException, SAXException, ParserConfigurationException, InstantiationException, IllegalAccessException, InvocationTargetException, NoSuchMethodException { return (_compareDocument(source, _getDocument(compare))); } public Status execute(String xmlDoc, String compareDoc) throws SecurityException, IOException, SAXException, ParserConfigurationException, InstantiationException, IllegalAccessException, InvocationTargetException, NoSuchMethodException { if (targetClass_ != null) { return (_execute(xmlDoc, compareDoc)); } else { return (_compareDocument(_getDocument(xmlDoc), _getDocument(compareDoc))); } } private Status _execute(String xmlDoc, String compareDoc) throws IOException, SAXException, ParserConfigurationException, SecurityException, InstantiationException, IllegalAccessException, InvocationTargetException, NoSuchMethodException { targetObject_ = _makeObject(xmlDoc); targetDoc_ = _getDocument(compareDoc); _messageDebug("Target doc: ", targetDoc_); generatedDoc_ = _generateDoc(); generatedTextDoc_ = _generateTextDoc(); _messageInfo("Start DOM test..."); Status status = _compareDocument(generatedDoc_, targetDoc_); if (status != null) { return (status); } _messageInfo("Start TEXT test..."); status = _compareDocument(generatedTextDoc_, targetDoc_); if (status != null) { return (status); } if (makeSax1Method_ != null) { _messageInfo("Start SAX test..."); Document saxDoc = _generateSax1Doc(); status = _compareDocument(saxDoc, targetDoc_); if (status != null) { return (status); } } else if (makeSax2Method_ != null) { _messageInfo("Start SAX test..."); Document saxDoc = _generateSax2Doc(); status = _compareDocument(saxDoc, targetDoc_); if (status != null) { return (status); } } if (verifyMethod_ != null) { status = _verify(); if (status != null) { return (status); } } _messageInfo("Done."); return (null); } private Object _makeObject(String xmlDoc) throws InstantiationException, IllegalAccessException, InvocationTargetException { Object object = targetClass_.newInstance(); setupMethod_.invoke(object, new Object[] { xmlDoc }); return (object); } private Document _generateDoc() throws InstantiationException, IllegalAccessException, InvocationTargetException { Document doc = (Document)makeMethod_.invoke(targetObject_, new Object[0]); _messageDebug("doc:", doc); return (doc); } private Document _generateTextDoc() throws InstantiationException, IllegalAccessException, InvocationTargetException, SAXException, ParserConfigurationException { try { String text = (String)makeTextMethod_.invoke(targetObject_, new Object[0]); _messageDebug("text doc:" + text); // Document doc = _getDocument( // new ByteArrayInputStream(text.getBytes("UTF-8")) // ); Document doc = _getDocument(new StringReader(text)); // _messageDebug("???:", doc); return (doc); } catch (IOException e) { throw (new InternalError()); } } private Document _generateSax1Doc() throws InstantiationException, IllegalAccessException, InvocationTargetException { Sax1DomMaker maker = new Sax1DomMaker(); makeSax1Method_.invoke(targetObject_, new Object[] { maker }); Document doc = maker.getDocument(); _messageDebug("sax1 doc:", doc); return (doc); } private Document _generateSax2Doc() throws InstantiationException, IllegalAccessException, InvocationTargetException { Sax2DomMaker maker = new Sax2DomMaker(); makeSax2Method_.invoke(targetObject_, new Object[] { maker }); Document doc = maker.getDocument(); _messageDebug("sax2 doc:" + UXMLMaker.getXMLText(doc)); return (doc); } private Document _getDocument(String uri) throws IOException, SAXException, ParserConfigurationException { if (uri.charAt(0) == '<') { return (_getDocument(new StringReader(uri))); } else { DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); DocumentBuilder builder = factory.newDocumentBuilder(); // builder.setErrorHandler(handler); // builder.setEntityResolver(getEntityResolver()); Document doc = builder.parse(uri); return (doc); } } private Document _getDocument(Reader reader) throws IOException, SAXException, ParserConfigurationException { DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); DocumentBuilder builder = factory.newDocumentBuilder(); // builder.setErrorHandler(handler); // builder.setEntityResolver(getEntityResolver()); Document doc = builder.parse(new InputSource(reader)); return (doc); } private Document _getDocument(InputStream in) throws IOException, SAXException, ParserConfigurationException { DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); DocumentBuilder builder = factory.newDocumentBuilder(); // builder.setErrorHandler(handler); // builder.setEntityResolver(getEntityResolver()); Document doc = builder.parse(in); return (doc); } private Status _compareDocument(Document lhs, Document rhs) { return ( _compareTree(lhs.getDocumentElement(), rhs.getDocumentElement())); } private Status _compareTree(Node lhs, Node rhs) { Status status = _compareNode(lhs, rhs); if (status != null) { return (status); } return (_compareChildren(lhs, rhs)); } private Status _compareNode(Node lhs, Node rhs) { short lhsType = lhs.getNodeType(); short rhsType = rhs.getNodeType(); if (lhsType != rhsType) { return (new Status("Unmatch node type")); } switch (lhsType) { case Node.ELEMENT_NODE : Element lhsElement = (Element)lhs; Element rhsElement = (Element)rhs; String lhsTagName = lhsElement.getTagName(); // Tag vs. ns String rhsTagName = rhsElement.getTagName(); if (!lhsTagName.equals(rhsTagName)) { Status status = new Status("No match element"); status.lhs = lhsElement; status.rhs = rhsElement; status.lhsMessage = _makeElementInfo(lhsElement); status.rhsMessage = _makeElementInfo(rhsElement); return (status); } Status status = _compareAttributes(lhs, rhs); if (status != null) { return (status); } return (_compareChildren(lhsElement, rhsElement)); case Node.ATTRIBUTE_NODE : return (new Status("Illegal attribute node")); case Node.TEXT_NODE : return (new Status("Illegal text node")); case Node.CDATA_SECTION_NODE : return (new Status("Illegal cdata node")); case Node.ENTITY_REFERENCE_NODE : return (new Status("Illegal entity reference node")); case Node.ENTITY_NODE : return (new Status("Illegal entity node")); case Node.PROCESSING_INSTRUCTION_NODE : return (null); case Node.COMMENT_NODE : return (null); case Node.DOCUMENT_NODE : return (null); case Node.DOCUMENT_TYPE_NODE : return (new Status("Illegal document type node")); case Node.DOCUMENT_FRAGMENT_NODE : return (new Status("Illegal fragment node")); case Node.NOTATION_NODE : return (new Status("Illegal notation node")); default : throw (new InternalError()); } } private Status _compareAttributes(Node lhs, Node rhs) { List lhsAttrs = _distillAttrs(lhs); List rhsAttrs = _distillAttrs(rhs); int size = lhsAttrs.size(); if (size != rhsAttrs.size()) { Status status = new Status("The number of attributes is different."); status.lhs = lhs; status.rhs = rhs; status.lhsMessage = _makeAttributeInfo(lhs); status.rhsMessage = _makeAttributeInfo(rhs); return (status); } for (int i = 0; i < size; i++) { Attr attr = (Attr)lhsAttrs.get(i); Status status = _compareAttributes(attr, rhsAttrs); if (status != null) { status.lhs = lhs; status.rhs = rhs; status.lhsMessage = _makeAttributeInfo(lhs); status.rhsMessage = _makeAttributeInfo(rhs); return (status); } } return (null); } private List _distillAttrs(Node element) { List result = new ArrayList(); NamedNodeMap attrs = element.getAttributes(); int size = attrs.getLength(); for (int i = 0; i < size; i++) { Attr attr = (Attr)attrs.item(i); String attrName = attr.getName(); if (false) { if ("xmlns".equals(attrName) || attrName.startsWith("xmlns:")) { continue; } } result.add(attr); } return (result); } private Status _compareAttributes(Attr attr, List attrs) { // String ns = attr.getNamespaceURI(); // String name = attr.getLocalName(); String attrName = attr.getName(); int size = attrs.size(); for (int i = 0; i < size; i++) { Attr targetAttr = (Attr)attrs.get(i); if (attrName.equals(targetAttr.getName())) { Status status = _compareString(attr.getValue(), targetAttr.getValue()); return (status); } } return (new Status("Unmatch attribute")); } private Status _compareChildren(Node lhs, Node rhs) { Object[] lhsChildren = _normalizeChildren(lhs.getChildNodes()); Object[] rhsChildren = _normalizeChildren(rhs.getChildNodes()); if (lhsChildren.length != rhsChildren.length) { Status status = new Status("No match children length"); status.lhs = lhs; status.rhs = rhs; status.lhsMessage = _makeChildrenInfo(lhsChildren); status.rhsMessage = _makeChildrenInfo(rhsChildren); return (status); } for (int i = 0; i < lhsChildren.length; i++) { Object lhsObj = lhsChildren[i]; Object rhsObj = rhsChildren[i]; if (lhsObj instanceof Element) { if (rhsObj instanceof Element) { Status status = _compareTree( (Element)lhsObj, (Element)rhsObj ); if (status != null) { return (status); } } else { return (new Status("Error c")); // XXX } } else if (lhsObj instanceof String) { if (rhsObj instanceof String) { Status status = _compareString( (String)lhsObj, (String)rhsObj ); if (status != null) { return (status); } } else { return (new Status("Error d")); // XXX } } else { return (new Status("Error b")); // XXX } } return (null); } private Status _compareString(String lhs, String rhs) { if (lhs.trim().equals(rhs.trim())) { return (null); } else { Status status = new Status("No match string"); status.lhsMessage = lhs; status.rhsMessage = rhs; return (status); } } private Object[] _normalizeChildren(NodeList children) { List list = new ArrayList(); StringBuffer buffer = null; int size = children.getLength(); for (int i = 0; i < size; i++) { Node child = children.item(i); switch (child.getNodeType()) { case Node.ELEMENT_NODE : if (buffer != null) { _appendString(list, buffer); buffer = null; } list.add(child); break; case Node.ATTRIBUTE_NODE : throw (new InternalError()); case Node.TEXT_NODE : // continue case Node.CDATA_SECTION_NODE : if (buffer == null) { buffer = new StringBuffer(); } buffer.append(child.getNodeValue()); break; case Node.ENTITY_REFERENCE_NODE : throw (new InternalError()); case Node.ENTITY_NODE : throw (new InternalError()); case Node.PROCESSING_INSTRUCTION_NODE : // do nothing break; case Node.COMMENT_NODE : // do nothing break; case Node.DOCUMENT_NODE : throw (new InternalError()); case Node.DOCUMENT_TYPE_NODE : throw (new InternalError()); case Node.DOCUMENT_FRAGMENT_NODE : throw (new InternalError()); case Node.NOTATION_NODE : throw (new InternalError()); default : throw (new InternalError()); } } if (buffer != null) { _appendString(list, buffer); } Object[] result = new Object[list.size()]; return ((Object[])list.toArray(result)); } private void _appendString(List list, StringBuffer buffer) { int size = buffer.length(); if (size == 0) { return; } for (int i = 0; i < size; i++) { switch (buffer.charAt(i)) { case ' ' : case '\n' : case '\r' : case '\t' : break; default : list.add(new String(buffer)); return; } } } private String _makeAttributeInfo(Node node) { StringBuffer buffer = new StringBuffer(); buffer.append(node.getNodeName()); _makeAttributeInfo(node.getAttributes(), buffer); return (new String(buffer)); } private void _makeAttributeInfo(NamedNodeMap attrs, StringBuffer buffer) { int size = attrs.getLength(); buffer.append("["); if (size > 0) { Attr attr = (Attr)attrs.item(0); buffer.append(attr.getName()); buffer.append("="); buffer.append(attr.getValue()); for (int i = 1; i < size; i++) { attr = (Attr)attrs.item(i); buffer.append(","); buffer.append(attr.getName()); buffer.append("="); buffer.append(attr.getValue()); } } buffer.append("]"); } private String _makeElementInfo(Element element) { return (UXMLMaker.getXMLText(element)); } private String _makeChildrenInfo(Object[] children) { StringBuffer buffer = new StringBuffer(); buffer.append("["); for (int i = 0; i < children.length; i++) { Object child = children[i]; buffer.append("["); if (child instanceof Element) { buffer.append(UXMLMaker.getXMLText((Element)child)); } else if (child instanceof String) { buffer.append((String)child); } else { throw (new InternalError()); } buffer.append("]"); } buffer.append("]"); return (new String(buffer)); } private Status _verify() throws SecurityException, InstantiationException, IllegalAccessException, InvocationTargetException, NoSuchMethodException { Object report = verifyMethod_.invoke(targetObject_, new Object[0]); // RVerifyReport report = // (RVerifyReport)verifyMethod_.invoke(targetObject_, new Object[0]); if (_isValidReport(report)) { return (null); } else { return (new Status(report.toString())); } } private boolean _isValidReport(Object report) throws InstantiationException, IllegalAccessException, InvocationTargetException, SecurityException, NoSuchMethodException { Class clazz = report.getClass(); Method isValidMethod = clazz.getMethod("isValid", new Class[0]); return (Boolean.TRUE.equals(isValidMethod.invoke(report, new Object[0]))); } // public static void main(String[] args) throws IOException, SAXException, ParserConfigurationException, ClassNotFoundException, NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException { String relaxerObject = args[0]; String xmlDoc = args[1]; Status status; if (args.length > 2) { String compareDoc = args[2]; status = test(relaxerObject, xmlDoc, compareDoc); } else { status = test(relaxerObject, xmlDoc); } if (status == null) { System.out.println("Success."); } else { System.out.println("Error: " + status.message); if (status.lhsMessage != null) { System.out.println("relaxer = " + status.lhsMessage); } if (status.rhsMessage != null) { System.out.println("original = " + status.rhsMessage); } } } public static Status test(String relaxerObject, String xmlDoc) throws IOException, SAXException, ParserConfigurationException, ClassNotFoundException, NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException { XmlTester tester = new XmlTester(relaxerObject); return (tester.execute(xmlDoc)); } public static Status test( String relaxerObject, String xmlDoc, String compareDoc) throws IOException, SAXException, ParserConfigurationException, ClassNotFoundException, NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException { XmlTester tester = new XmlTester(relaxerObject); return (tester.execute(xmlDoc, compareDoc)); } public static Status test( String relaxerObject, String xmlDoc, ClassLoader loader) throws IOException, SAXException, ParserConfigurationException, ClassNotFoundException, NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException { XmlTester tester = new XmlTester(relaxerObject, loader); return (tester.execute(xmlDoc)); } public static Status test( String relaxerObject, String xmlDoc, String compareDoc, ClassLoader loader) throws IOException, SAXException, ParserConfigurationException, ClassNotFoundException, NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException { XmlTester tester = new XmlTester(relaxerObject, loader); return (tester.execute(xmlDoc, compareDoc)); } public static Status test( String relaxerObject, String xmlDoc, String[] classPath) throws IOException, SAXException, ParserConfigurationException, ClassNotFoundException, NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException { XmlTester tester = new XmlTester(relaxerObject, classPath); return (tester.execute(xmlDoc)); } public static Status test( String relaxerObject, String xmlDoc, String compareDoc, String[] classPath) throws IOException, SAXException, ParserConfigurationException, ClassNotFoundException, NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException { XmlTester tester = new XmlTester(relaxerObject, classPath); return (tester.execute(xmlDoc, compareDoc)); } public static void setGlobalMessageLevel(int level) { gMessageLevel__ = level; } public void setMessageLevel(int level) { messageLevel_ = level; } private void _messageInfo(String message) { switch (messageLevel_) { case MESSAGE_NOT_USED : switch (gMessageLevel__) { case MESSAGE_NOT_USED : throw (new InternalError()); case MESSAGE_NONE : return; case MESSAGE_INFO : break; case MESSAGE_DEBUG : break; default : throw (new InternalError()); } break; case MESSAGE_NONE : return; case MESSAGE_INFO : break; case MESSAGE_DEBUG : break; default : throw (new InternalError()); } System.out.println(message); } private void _messageDebug(String message) { switch (messageLevel_) { case MESSAGE_NOT_USED : switch (gMessageLevel__) { case MESSAGE_NOT_USED : throw (new InternalError()); case MESSAGE_NONE : return; case MESSAGE_INFO : return; case MESSAGE_DEBUG : break; default : throw (new InternalError()); } break; case MESSAGE_NONE : return; case MESSAGE_INFO : return; case MESSAGE_DEBUG : break; default : throw (new InternalError()); } System.out.println(message); } private void _messageDebug(String message, Document doc) { switch (messageLevel_) { case MESSAGE_NOT_USED : switch (gMessageLevel__) { case MESSAGE_NOT_USED : throw (new InternalError()); case MESSAGE_NONE : return; case MESSAGE_INFO : return; case MESSAGE_DEBUG : break; default : throw (new InternalError()); } break; case MESSAGE_NONE : return; case MESSAGE_INFO : return; case MESSAGE_DEBUG : break; default : throw (new InternalError()); } Element root = doc.getDocumentElement(); System.out.println(message + UXMLMaker.getXMLText(root)); } public static class Status { public String message; public Node lhs; public String lhsMessage; public Node rhs; public String rhsMessage; public Status(String message) { this.message = message; } }; }