Coverage Report - net.mtu.eggplant.xml.XMLUtils
 
Classes in this File Line Coverage Branch Coverage Complexity
XMLUtils
12%
9/70
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.Writer;
 35  
 import java.text.DateFormat;
 36  
 import java.text.SimpleDateFormat;
 37  
 
 38  
 import javax.xml.parsers.DocumentBuilder;
 39  
 import javax.xml.parsers.DocumentBuilderFactory;
 40  
 import javax.xml.parsers.ParserConfigurationException;
 41  
 import javax.xml.transform.OutputKeys;
 42  
 import javax.xml.transform.Transformer;
 43  
 import javax.xml.transform.TransformerException;
 44  
 import javax.xml.transform.TransformerFactory;
 45  
 import javax.xml.transform.dom.DOMSource;
 46  
 import javax.xml.transform.stream.StreamResult;
 47  
 import javax.xml.validation.Schema;
 48  
 
 49  
 import org.custommonkey.xmlunit.Diff;
 50  
 import org.slf4j.Logger;
 51  
 import org.slf4j.LoggerFactory;
 52  
 import org.w3c.dom.Document;
 53  
 import org.w3c.dom.Element;
 54  
 import org.xml.sax.ErrorHandler;
 55  
 import org.xml.sax.InputSource;
 56  
 import org.xml.sax.SAXException;
 57  
 import org.xml.sax.SAXParseException;
 58  
 
 59  
 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
 60  
 import net.mtu.eggplant.io.IOUtils;
 61  
 
 62  
 /**
 63  
  * Some utilities for working with XML.
 64  
  */
 65  0
 public class XMLUtils {
 66  
 
 67  1
   private static final Logger LOGGER = LoggerFactory.getLogger(XMLUtils.class);
 68  
 
 69  
   /**
 70  
    * Date format for time type.
 71  
    */
 72  1
   public static final ThreadLocal<DateFormat> XML_TIME_FORMAT = new ThreadLocal<DateFormat>() {
 73  
     @Override
 74  
     protected DateFormat initialValue() {
 75  0
       return new SimpleDateFormat("HH:mm:ss");
 76  
     }
 77  
   };
 78  
 
 79  
   public static final DocumentBuilder DOCUMENT_BUILDER;
 80  
 
 81  
   /**
 82  
    * Standard error handler that throws exceptions on error and fatalError and
 83  
    * logs a warning on warning.
 84  
    */
 85  1
   public static final ErrorHandler STANDARD_ERROR_HANDLER = new ErrorHandler() {
 86  
     public void error(final SAXParseException spe) throws SAXParseException {
 87  0
       throw spe;
 88  
     }
 89  
 
 90  
     public void fatalError(final SAXParseException spe) throws SAXParseException {
 91  0
       throw spe;
 92  
     }
 93  
 
 94  
     public void warning(final SAXParseException spe) throws SAXParseException {
 95  0
       LOGGER.warn(spe.getMessage(), spe);
 96  0
     }
 97  
   };
 98  
 
 99  
   // create basic document builder
 100  
   static {
 101  
     try {
 102  1
       final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
 103  1
       factory.setNamespaceAware(true);
 104  1
       DOCUMENT_BUILDER = factory.newDocumentBuilder();
 105  1
       DOCUMENT_BUILDER.setErrorHandler(STANDARD_ERROR_HANDLER);
 106  0
     } catch (final ParserConfigurationException pce) {
 107  0
       throw new RuntimeException(pce.getMessage(), pce);
 108  1
     }
 109  1
   }
 110  
 
 111  
   /**
 112  
    * Parse the document from the given stream. The document is validated with
 113  
    * the specified schema. Does not close the stream after reading. Warnings are
 114  
    * output to the logger for this class.
 115  
    * 
 116  
    * @param stream a stream containing document
 117  
    * @return the document
 118  
    * @throws IOException if there is an error reading the stream
 119  
    * @throws SAXException if there is an error parsing the document or it
 120  
    *           doesn't match the schema
 121  
    * @throws RuntimeException if there is an error configuring the XML parser,
 122  
    *           this shouldn't happen
 123  
    */
 124  
   public static Document parse(final Reader stream,
 125  
                                final Schema schema)
 126  
       throws IOException, SAXException {
 127  
     try {
 128  0
       final DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();
 129  0
       builderFactory.setNamespaceAware(true);
 130  0
       builderFactory.setSchema(schema);
 131  0
       builderFactory.setIgnoringComments(true);
 132  0
       builderFactory.setIgnoringElementContentWhitespace(true);
 133  0
       final DocumentBuilder parser = builderFactory.newDocumentBuilder();
 134  
 
 135  0
       parser.setErrorHandler(STANDARD_ERROR_HANDLER);
 136  
 
 137  
       // parse
 138  0
       final String content = IOUtils.readIntoString(stream);
 139  0
       final Document document = parser.parse(new InputSource(new StringReader(content)));
 140  
 
 141  0
       return document;
 142  0
     } catch (ParserConfigurationException e) {
 143  0
       throw new RuntimeException("Error configuring the XML parser", e);
 144  
     }
 145  
   }
 146  
 
 147  
   /**
 148  
    * Parse xmlDoc an XML document. Just does basic parsing, no validity checks.
 149  
    * Does not close the stream after parsing. Warnings are output to the logger
 150  
    * for this class.
 151  
    * 
 152  
    * @throws IOException if there is an error reading the stream
 153  
    * @throws SAXException if the document is found to be invalid
 154  
    * @throws RuntimeException if there is an error configuring the XML parser,
 155  
    *           this shouldn't happen
 156  
    */
 157  
   public static Document parseXMLDocument(final InputStream xmlDocStream) throws SAXException, IOException {
 158  0
     return parseXMLDocument(new InputSource(xmlDocStream));
 159  
   }
 160  
 
 161  
   /**
 162  
    * Parse xmlDoc an XML document. Just does basic parsing, no validity checks.
 163  
    * Does not close the stream after parsing. Warnings are output to the logger
 164  
    * for this class.
 165  
    * 
 166  
    * @throws IOException if there is an error reading the stream
 167  
    * @throws SAXException if the document is found to be invalid
 168  
    * @throws RuntimeException if there is an error configuring the XML parser,
 169  
    *           this shouldn't happen
 170  
    */
 171  
   public static Document parseXMLDocument(final Reader xmlDocStream) throws SAXException, IOException {
 172  0
     final String content = IOUtils.readIntoString(xmlDocStream);
 173  0
     return parseXMLDocument(new InputSource(new StringReader(content)));
 174  
   }
 175  
 
 176  
   /**
 177  
    * Parse xmlDoc an XML document. Just does basic parsing, no validity checks.
 178  
    * Does not close the stream after parsing. Warnings are output to the logger
 179  
    * for this class.
 180  
    * 
 181  
    * @throws IOException if there is an error reading the stream
 182  
    * @throws SAXException if the document is found to be invalid
 183  
    * @throws RuntimeException if there is an error configuring the XML parser,
 184  
    *           this shouldn't happen
 185  
    */
 186  
   public static Document parseXMLDocument(final InputSource xmlDocStream) throws SAXException, IOException {
 187  
     try {
 188  0
       final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
 189  0
       factory.setNamespaceAware(true);
 190  0
       factory.setIgnoringComments(true);
 191  0
       factory.setIgnoringElementContentWhitespace(true);
 192  
 
 193  0
       final DocumentBuilder parser = factory.newDocumentBuilder();
 194  0
       parser.setErrorHandler(STANDARD_ERROR_HANDLER);
 195  
 
 196  0
       final Document document = parser.parse(xmlDocStream);
 197  0
       return document;
 198  0
     } catch (final ParserConfigurationException pce) {
 199  0
       throw new RuntimeException("Error configuring the XML parser", pce);
 200  
     }
 201  
   }
 202  
 
 203  
   /**
 204  
    * @see #getDoubleAttributeValue(Element, String)
 205  
    */
 206  
   public static String getStringAttributeValue(final Element element,
 207  
                                                final String attributeName) {
 208  0
     if (null == element) {
 209  0
       return null;
 210  
     }
 211  0
     final String str = element.getAttribute(attributeName);
 212  0
     return str;
 213  
   }
 214  
 
 215  
   /**
 216  
    * @see #getDoubleAttributeValue(Element, String)
 217  
    */
 218  
   @SuppressFBWarnings(value = { "NP_BOOLEAN_RETURN_NULL" }, justification = "Need to return Null so that we can determine when there is no score")
 219  
   public static Boolean getBooleanAttributeValue(final Element element,
 220  
                                                  final String attributeName) {
 221  0
     if (null == element) {
 222  0
       return null;
 223  
     }
 224  0
     final String str = element.getAttribute(attributeName);
 225  0
     if (str.isEmpty()) {
 226  0
       return null;
 227  
     } else {
 228  0
       return Boolean.valueOf(str);
 229  
     }
 230  
   }
 231  
 
 232  
   /**
 233  
    * Get a double value from an attribute.
 234  
    * 
 235  
    * @param element the element to get the attribute from, may be null
 236  
    * @param attributeName the attribute name to get
 237  
    * @return the value, null if element is null or the attribute value is null
 238  
    *         or empty
 239  
    */
 240  
   public static Double getDoubleAttributeValue(final Element element,
 241  
                                                final String attributeName) {
 242  0
     if (null == element) {
 243  0
       return null;
 244  
     }
 245  0
     final String str = element.getAttribute(attributeName);
 246  0
     if (str.isEmpty()) {
 247  0
       return null;
 248  
     } else {
 249  0
       return Double.valueOf(str);
 250  
     }
 251  
   }
 252  
 
 253  
   /**
 254  
    * Write the document to a writer.
 255  
    * 
 256  
    * @param doc the document to write
 257  
    * @param writer where to write the document
 258  
    */
 259  
   public static void writeXML(final Document doc,
 260  
                               final Writer writer) {
 261  0
     writeXML(doc, writer, null);
 262  0
   }
 263  
 
 264  
   /**
 265  
    * Write the document to a writer.
 266  
    * 
 267  
    * @param doc the document to write
 268  
    * @param writer where to write the document
 269  
    * @param encoding if non-null use this as the encoding for the text
 270  
    * @throws RuntimeException if a {@link TransformerException} occurs.
 271  
    */
 272  
   public static void writeXML(final Document doc,
 273  
                               final Writer writer,
 274  
                               final String encoding) {
 275  
     try {
 276  0
       final TransformerFactory transformerFactory = TransformerFactory.newInstance();
 277  0
       final Transformer transformer = transformerFactory.newTransformer();
 278  0
       transformer.setOutputProperty(OutputKeys.INDENT, "yes");
 279  0
       transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2");
 280  0
       if (null != encoding) {
 281  0
         transformer.setOutputProperty(OutputKeys.ENCODING, encoding);
 282  
       }
 283  0
       final DOMSource source = new DOMSource(doc);
 284  0
       final StreamResult result = new StreamResult(writer);
 285  0
       transformer.transform(source, result);
 286  0
     } catch (final TransformerException e) {
 287  0
       throw new RuntimeException("Internal error writing xml", e);
 288  0
     }
 289  0
   }
 290  
 
 291  
   /**
 292  
    * Compare two documents and check if they are the same or not.
 293  
    * 
 294  
    * @param controlDoc
 295  
    * @param testDoc
 296  
    * @return true if the documents have the same elements and attributes,
 297  
    *         reguardless of order
 298  
    */
 299  
   public static boolean compareDocuments(final Document controlDoc,
 300  
                                          final Document testDoc) {
 301  0
     final Diff xmldiff = new Diff(controlDoc, testDoc);
 302  0
     return xmldiff.similar();
 303  
   }
 304  
 
 305  
 }