Coverage Report - net.mtu.eggplant.xml.XMLUtils
 
Classes in this File Line Coverage Branch Coverage Complexity
XMLUtils
12%
9/71
0%
0/12
2.5
XMLUtils$1
50%
1/2
N/A
2.5
XMLUtils$2
20%
1/5
N/A
2.5
 
 1  
 /*
 2  
  * Copyright (c) 2008
 3  
  *      Jon Schewe.  All rights reserved
 4  
  *
 5  
  * Redistribution and use in source and binary forms, with or without
 6  
  * modification, are permitted provided that the following conditions
 7  
  * are met:
 8  
  * 1. Redistributions of source code must retain the above copyright
 9  
  *    notice, this list of conditions and the following disclaimer.
 10  
  * 2. Redistributions in binary form must reproduce the above copyright
 11  
  *    notice, this list of conditions and the following disclaimer in the
 12  
  *    documentation and/or other materials provided with the distribution.
 13  
  *
 14  
  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 15  
  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 16  
  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 17  
  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 18  
  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 19  
  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 20  
  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 21  
  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 22  
  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 23  
  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 24  
  * SUCH DAMAGE.
 25  
  *
 26  
  * I'd appreciate comments/suggestions on the code jpschewe@mtu.net
 27  
  */
 28  
 package net.mtu.eggplant.xml;
 29  
 
 30  
 import java.io.IOException;
 31  
 import java.io.InputStream;
 32  
 import java.io.Reader;
 33  
 import java.io.StringReader;
 34  
 import java.io.StringWriter;
 35  
 import java.io.Writer;
 36  
 import java.text.DateFormat;
 37  
 import java.text.SimpleDateFormat;
 38  
 
 39  
 import javax.xml.parsers.DocumentBuilder;
 40  
 import javax.xml.parsers.DocumentBuilderFactory;
 41  
 import javax.xml.parsers.ParserConfigurationException;
 42  
 import javax.xml.transform.OutputKeys;
 43  
 import javax.xml.transform.Transformer;
 44  
 import javax.xml.transform.TransformerException;
 45  
 import javax.xml.transform.TransformerFactory;
 46  
 import javax.xml.transform.dom.DOMSource;
 47  
 import javax.xml.transform.stream.StreamResult;
 48  
 import javax.xml.validation.Schema;
 49  
 
 50  
 import org.apache.commons.io.IOUtils;
 51  
 import org.custommonkey.xmlunit.Diff;
 52  
 import org.slf4j.Logger;
 53  
 import org.slf4j.LoggerFactory;
 54  
 import org.w3c.dom.Document;
 55  
 import org.w3c.dom.Element;
 56  
 import org.xml.sax.ErrorHandler;
 57  
 import org.xml.sax.InputSource;
 58  
 import org.xml.sax.SAXException;
 59  
 import org.xml.sax.SAXParseException;
 60  
 
 61  
 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
 62  
 
 63  
 /**
 64  
  * Some utilities for working with XML.
 65  
  */
 66  0
 public class XMLUtils {
 67  
 
 68  1
   private static final Logger LOGGER = LoggerFactory.getLogger(XMLUtils.class);
 69  
 
 70  
   /**
 71  
    * Date format for time type.
 72  
    */
 73  1
   public static final ThreadLocal<DateFormat> XML_TIME_FORMAT = new ThreadLocal<DateFormat>() {
 74  
     @Override
 75  
     protected DateFormat initialValue() {
 76  0
       return new SimpleDateFormat("HH:mm:ss");
 77  
     }
 78  
   };
 79  
 
 80  
   public static final DocumentBuilder DOCUMENT_BUILDER;
 81  
 
 82  
   /**
 83  
    * Standard error handler that throws exceptions on error and fatalError and
 84  
    * logs a warning on warning.
 85  
    */
 86  1
   public static final ErrorHandler STANDARD_ERROR_HANDLER = new ErrorHandler() {
 87  
     public void error(final SAXParseException spe) throws SAXParseException {
 88  0
       throw spe;
 89  
     }
 90  
 
 91  
     public void fatalError(final SAXParseException spe) throws SAXParseException {
 92  0
       throw spe;
 93  
     }
 94  
 
 95  
     public void warning(final SAXParseException spe) throws SAXParseException {
 96  0
       LOGGER.warn(spe.getMessage(), spe);
 97  0
     }
 98  
   };
 99  
 
 100  
   // create basic document builder
 101  
   static {
 102  
     try {
 103  1
       final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
 104  1
       factory.setNamespaceAware(true);
 105  1
       DOCUMENT_BUILDER = factory.newDocumentBuilder();
 106  1
       DOCUMENT_BUILDER.setErrorHandler(STANDARD_ERROR_HANDLER);
 107  0
     } catch (final ParserConfigurationException pce) {
 108  0
       throw new RuntimeException(pce.getMessage(), pce);
 109  1
     }
 110  1
   }
 111  
 
 112  
   /**
 113  
    * Parse the document from the given stream. The document is validated with
 114  
    * the specified schema. Does not close the stream after reading. Warnings are
 115  
    * output to the logger for this class.
 116  
    * 
 117  
    * @param stream a stream containing document
 118  
    * @return the document
 119  
    * @throws IOException if there is an error reading the stream
 120  
    * @throws SAXException if there is an error parsing the document or it
 121  
    *           doesn't match the schema
 122  
    * @throws RuntimeException if there is an error configuring the XML parser,
 123  
    *           this shouldn't happen
 124  
    */
 125  
   public static Document parse(final Reader stream,
 126  
                                final Schema schema)
 127  
       throws IOException, SAXException {
 128  
     try {
 129  0
       final DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();
 130  0
       builderFactory.setNamespaceAware(true);
 131  0
       builderFactory.setSchema(schema);
 132  0
       builderFactory.setIgnoringComments(true);
 133  0
       builderFactory.setIgnoringElementContentWhitespace(true);
 134  0
       final DocumentBuilder parser = builderFactory.newDocumentBuilder();
 135  
 
 136  0
       parser.setErrorHandler(STANDARD_ERROR_HANDLER);
 137  
 
 138  
       // parse
 139  0
       final StringWriter writer = new StringWriter();
 140  0
       IOUtils.copy(stream, writer);
 141  0
       final String content = writer.toString();
 142  0
       final Document document = parser.parse(new InputSource(new StringReader(content)));
 143  
 
 144  0
       return document;
 145  0
     } catch (ParserConfigurationException e) {
 146  0
       throw new RuntimeException("Error configuring the XML parser", e);
 147  
     }
 148  
   }
 149  
 
 150  
   /**
 151  
    * Parse xmlDoc an XML document. Just does basic parsing, no validity checks.
 152  
    * Does not close the stream after parsing. Warnings are output to the logger
 153  
    * for this class.
 154  
    * 
 155  
    * @throws IOException if there is an error reading the stream
 156  
    * @throws SAXException if the document is found to be invalid
 157  
    * @throws RuntimeException if there is an error configuring the XML parser,
 158  
    *           this shouldn't happen
 159  
    */
 160  
   public static Document parseXMLDocument(final InputStream xmlDocStream) throws SAXException, IOException {
 161  0
     return parseXMLDocument(new InputSource(xmlDocStream));
 162  
   }
 163  
 
 164  
   /**
 165  
    * Parse xmlDoc an XML document. Just does basic parsing, no validity checks.
 166  
    * Does not close the stream after parsing. Warnings are output to the logger
 167  
    * for this class.
 168  
    * 
 169  
    * @throws IOException if there is an error reading the stream
 170  
    * @throws SAXException if the document is found to be invalid
 171  
    * @throws RuntimeException if there is an error configuring the XML parser,
 172  
    *           this shouldn't happen
 173  
    */
 174  
   public static Document parseXMLDocument(final Reader xmlDocStream) throws SAXException, IOException {
 175  0
     return parseXMLDocument(new InputSource(xmlDocStream));
 176  
   }
 177  
 
 178  
   /**
 179  
    * Parse xmlDoc an XML document. Just does basic parsing, no validity checks.
 180  
    * Does not close the stream after parsing. Warnings are output to the logger
 181  
    * for this class.
 182  
    * 
 183  
    * @throws IOException if there is an error reading the stream
 184  
    * @throws SAXException if the document is found to be invalid
 185  
    * @throws RuntimeException if there is an error configuring the XML parser,
 186  
    *           this shouldn't happen
 187  
    */
 188  
   public static Document parseXMLDocument(final InputSource xmlDocStream) throws SAXException, IOException {
 189  
     try {
 190  0
       final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
 191  0
       factory.setNamespaceAware(true);
 192  0
       factory.setIgnoringComments(true);
 193  0
       factory.setIgnoringElementContentWhitespace(true);
 194  
 
 195  0
       final DocumentBuilder parser = factory.newDocumentBuilder();
 196  0
       parser.setErrorHandler(STANDARD_ERROR_HANDLER);
 197  
 
 198  0
       final Document document = parser.parse(xmlDocStream);
 199  0
       return document;
 200  0
     } catch (final ParserConfigurationException pce) {
 201  0
       throw new RuntimeException("Error configuring the XML parser", pce);
 202  
     }
 203  
   }
 204  
 
 205  
   /**
 206  
    * @see #getDoubleAttributeValue(Element, String)
 207  
    */
 208  
   public static String getStringAttributeValue(final Element element,
 209  
                                                final String attributeName) {
 210  0
     if (null == element) {
 211  0
       return null;
 212  
     }
 213  0
     final String str = element.getAttribute(attributeName);
 214  0
     return str;
 215  
   }
 216  
 
 217  
   /**
 218  
    * @see #getDoubleAttributeValue(Element, String)
 219  
    */
 220  
   @SuppressFBWarnings(value = { "NP_BOOLEAN_RETURN_NULL" }, justification = "Need to return Null so that we can determine when there is no score")
 221  
   public static Boolean getBooleanAttributeValue(final Element element,
 222  
                                                  final String attributeName) {
 223  0
     if (null == element) {
 224  0
       return null;
 225  
     }
 226  0
     final String str = element.getAttribute(attributeName);
 227  0
     if (str.isEmpty()) {
 228  0
       return null;
 229  
     } else {
 230  0
       return Boolean.valueOf(str);
 231  
     }
 232  
   }
 233  
 
 234  
   /**
 235  
    * Get a double value from an attribute.
 236  
    * 
 237  
    * @param element the element to get the attribute from, may be null
 238  
    * @param attributeName the attribute name to get
 239  
    * @return the value, null if element is null or the attribute value is null
 240  
    *         or empty
 241  
    */
 242  
   public static Double getDoubleAttributeValue(final Element element,
 243  
                                                final String attributeName) {
 244  0
     if (null == element) {
 245  0
       return null;
 246  
     }
 247  0
     final String str = element.getAttribute(attributeName);
 248  0
     if (str.isEmpty()) {
 249  0
       return null;
 250  
     } else {
 251  0
       return Double.valueOf(str);
 252  
     }
 253  
   }
 254  
 
 255  
   /**
 256  
    * Write the document to a writer.
 257  
    * 
 258  
    * @param doc the document to write
 259  
    * @param writer where to write the document
 260  
    */
 261  
   public static void writeXML(final Document doc,
 262  
                               final Writer writer) {
 263  0
     writeXML(doc, writer, null);
 264  0
   }
 265  
 
 266  
   /**
 267  
    * Write the document to a writer.
 268  
    * 
 269  
    * @param doc the document to write
 270  
    * @param writer where to write the document
 271  
    * @param encoding if non-null use this as the encoding for the text
 272  
    * @throws RuntimeException if a {@link TransformerException} occurs.
 273  
    */
 274  
   public static void writeXML(final Document doc,
 275  
                               final Writer writer,
 276  
                               final String encoding) {
 277  
     try {
 278  0
       final TransformerFactory transformerFactory = TransformerFactory.newInstance();
 279  0
       final Transformer transformer = transformerFactory.newTransformer();
 280  0
       transformer.setOutputProperty(OutputKeys.INDENT, "yes");
 281  0
       transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2");
 282  0
       if (null != encoding) {
 283  0
         transformer.setOutputProperty(OutputKeys.ENCODING, encoding);
 284  
       }
 285  0
       final DOMSource source = new DOMSource(doc);
 286  0
       final StreamResult result = new StreamResult(writer);
 287  0
       transformer.transform(source, result);
 288  0
     } catch (final TransformerException e) {
 289  0
       throw new RuntimeException("Internal error writing xml", e);
 290  0
     }
 291  0
   }
 292  
 
 293  
   /**
 294  
    * Compare two documents and check if they are the same or not.
 295  
    * 
 296  
    * @param controlDoc
 297  
    * @param testDoc
 298  
    * @return true if the documents have the same elements and attributes,
 299  
    *         reguardless of order
 300  
    */
 301  
   public static boolean compareDocuments(final Document controlDoc,
 302  
                                          final Document testDoc) {
 303  0
     final Diff xmldiff = new Diff(controlDoc, testDoc);
 304  0
     return xmldiff.similar();
 305  
   }
 306  
 
 307  
 }