Coverage Report - net.sf.jmatchparser.template.engine.formatter.XMLFormatter
 
Classes in this File Line Coverage Branch Coverage Complexity
XMLFormatter
84%
60/71
68%
26/38
7
XMLFormatter$1
25%
1/4
0%
0/2
7
 
 1  
 /*
 2  
  * Copyright (c) 2006 - 2011 Michael Schierl
 3  
  * All rights reserved.
 4  
  * 
 5  
  * This program is free software: you can redistribute it and/or modify
 6  
  * it under the terms of the GNU General Public License as published by
 7  
  * the Free Software Foundation, either version 2 of the License, or
 8  
  * (at your option) any later version.
 9  
  * 
 10  
  * This program is distributed in the hope that it will be useful,
 11  
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
 12  
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 13  
  * GNU General Public License for more details.
 14  
  * 
 15  
  * You should have received a copy of the GNU General Public License
 16  
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 17  
  */
 18  
 package net.sf.jmatchparser.template.engine.formatter;
 19  
 
 20  
 import java.io.IOException;
 21  
 import java.io.PrintStream;
 22  
 import java.io.StringReader;
 23  
 import java.util.Arrays;
 24  
 
 25  
 import javax.xml.parsers.DocumentBuilder;
 26  
 import javax.xml.parsers.DocumentBuilderFactory;
 27  
 import javax.xml.parsers.ParserConfigurationException;
 28  
 
 29  
 import net.sf.jmatchparser.template.Formatter;
 30  
 
 31  
 import org.w3c.dom.Document;
 32  
 import org.w3c.dom.Element;
 33  
 import org.w3c.dom.Node;
 34  
 import org.w3c.dom.NodeList;
 35  
 import org.xml.sax.EntityResolver;
 36  
 import org.xml.sax.InputSource;
 37  
 import org.xml.sax.SAXException;
 38  
 
 39  203
 public class XMLFormatter implements Formatter {
 40  
 
 41  
         @Override
 42  
         public String getName() {
 43  203
                 return "xml";
 44  
         }
 45  
 
 46  
         @Override
 47  
         public String format(String content, PrintStream debugStream) throws IOException {
 48  
                 try {
 49  4
                         DocumentBuilder dbf = DocumentBuilderFactory.newInstance().newDocumentBuilder();
 50  4
                         dbf.setEntityResolver(new EntityResolver() {
 51  
                                 @Override
 52  
                                 public InputSource resolveEntity(String publicId, String systemId) throws SAXException, IOException {
 53  0
                                         if (systemId.startsWith("http://www.w3.org/"))
 54  0
                                                 return new InputSource(new StringReader(""));
 55  0
                                         return null;
 56  
                                 }
 57  
                         });
 58  4
                         Document doc = dbf.parse(new InputSource(new StringReader(content)));
 59  4
                         StringBuilder result = new StringBuilder();
 60  4
                         write(doc.getDocumentElement(), result);
 61  4
                         return result.toString();
 62  0
                 } catch (ParserConfigurationException ex) {
 63  0
                         throw new IOException(ex);
 64  0
                 } catch (SAXException ex) {
 65  0
                         throw new IOException("SAXException while parsing content:\n" + content, ex);
 66  
                 }
 67  
         }
 68  
 
 69  
         private void write(Element element, StringBuilder result) {
 70  
                 // append start tag
 71  22
                 result.append("<" + quote(element.getTagName(), false));
 72  22
                 if (element.getNamespaceURI() != null)
 73  0
                         result.append(" xmlns=\"" + quote(element.getNamespaceURI(), true));
 74  22
                 String[] attrs = new String[element.getAttributes().getLength()];
 75  26
                 for (int i = 0; i < attrs.length; i++) {
 76  4
                         attrs[i] = element.getAttributes().item(i).getNodeName();
 77  4
                         String ns = element.getAttributes().item(i).getNamespaceURI();
 78  4
                         if (ns != null)
 79  0
                                 attrs[i] += "\0" + ns;
 80  
                 }
 81  22
                 Arrays.sort(attrs);
 82  26
                 for (int i = 0; i < attrs.length; i++) {
 83  4
                         String localName = attrs[i];
 84  4
                         String ns = null;
 85  4
                         if (localName.contains("\0")) {
 86  0
                                 ns = localName.substring(localName.indexOf('\0') + 1);
 87  0
                                 localName = localName.substring(0, localName.indexOf('\0'));
 88  
                         }
 89  4
                         if (ns == null) {
 90  4
                                 result.append(" " + quote(localName, false));
 91  
                         } else {
 92  0
                                 result.append(" xmlns:attr" + i + "=\"" + quote(ns, true) + "\" attr" + i + ":" + quote(localName, true));
 93  
                         }
 94  4
                         result.append("=\"" + quote(element.getAttributeNodeNS(ns, localName).getValue(), true) + "\"");
 95  
                 }
 96  22
                 result.append(">\n");
 97  
 
 98  
                 // append content
 99  22
                 NodeList children = element.getChildNodes();
 100  55
                 for (int i = 0; i < children.getLength(); i++) {
 101  33
                         Node node = children.item(i);
 102  33
                         switch (node.getNodeType()) {
 103  
 
 104  
                         case Node.ELEMENT_NODE:
 105  18
                                 write((Element) node, result);
 106  18
                                 break;
 107  
 
 108  
                         case Node.TEXT_NODE:
 109  10
                                 result.append(quoteMultiline(node, "-"));
 110  10
                                 break;
 111  
 
 112  
                         case Node.CDATA_SECTION_NODE:
 113  1
                                 result.append(quoteMultiline(node, "["));
 114  1
                                 break;
 115  
 
 116  
                         case Node.COMMENT_NODE:
 117  2
                                 result.append(quoteMultiline(node, "!"));
 118  2
                                 break;
 119  
 
 120  
                         case Node.PROCESSING_INSTRUCTION_NODE:
 121  2
                                 result.append(quoteMultiline(node, "?" + node.getNodeName() + " "));
 122  2
                                 break;
 123  
 
 124  
                         case Node.ATTRIBUTE_NODE:
 125  0
                                 throw new RuntimeException("Attribute nodes should never be children");
 126  
 
 127  
                         case Node.ENTITY_REFERENCE_NODE:
 128  
                         case Node.ENTITY_NODE:
 129  
                         case Node.DOCUMENT_NODE:
 130  
                         case Node.DOCUMENT_TYPE_NODE:
 131  
                         case Node.DOCUMENT_FRAGMENT_NODE:
 132  
                         case Node.NOTATION_NODE:
 133  0
                                 throw new RuntimeException("Unparsable node found: " + node.getNodeType());
 134  
                         }
 135  
                 }
 136  
 
 137  
                 // append end tag
 138  22
                 result.append("</" + quote(element.getTagName(), false) + ">\n");
 139  22
         }
 140  
 
 141  
         private static String quoteMultiline(Node node, String marker) {
 142  15
                 String value = "\n+" + node.getNodeValue().replaceAll("\n", "\n+");
 143  15
                 int last = value.lastIndexOf("\n+");
 144  15
                 value = value.substring(0, last + 1) + marker + value.substring(last + 2);
 145  15
                 String str = quote(value.substring(1), false) + "\n";
 146  15
                 return str;
 147  
         }
 148  
 
 149  
         private static String quote(String name, boolean quot) {
 150  67
                 if (name.matches("^[^<>&\"]+$"))
 151  64
                         return name;
 152  3
                 StringBuilder result = new StringBuilder();
 153  58
                 for (int i = 0; i < name.length(); i++) {
 154  55
                         switch (name.charAt(i)) {
 155  
                         case '&':
 156  1
                                 result.append("&amp;");
 157  1
                                 break;
 158  
                         case '"':
 159  2
                                 result.append(quot ? "&quot;" : "\"");
 160  2
                                 break;
 161  
                         case '<':
 162  2
                                 result.append("&lt;");
 163  2
                                 break;
 164  
                         case '>':
 165  2
                                 result.append("&gt;");
 166  2
                                 break;
 167  
                         default:
 168  48
                                 result.append(name.charAt(i));
 169  
                                 break;
 170  
                         }
 171  
                 }
 172  3
                 return result.toString();
 173  
         }
 174  
 
 175  
 }