PageRenderTime 27ms CodeModel.GetById 27ms RepoModel.GetById 1ms app.codeStats 0ms

/src/com/topologi/diffx/xml/XMLIndenter.java

https://code.google.com/p/wo-diffx/
Java | 240 lines | 177 code | 15 blank | 48 comment | 6 complexity | 574a2b1c8b6e89786f6de08b4bb9d4fa MD5 | raw file
Possible License(s): AGPL-3.0
  1. /*
  2. * This file is part of the DiffX library.
  3. *
  4. * For licensing information please see the file license.txt included in the release.
  5. * A copy of this licence can also be found at
  6. * http://www.opensource.org/licenses/artistic-license-2.0.php
  7. */
  8. package com.topologi.diffx.xml;
  9. import java.io.IOException;
  10. import java.io.PrintWriter;
  11. import java.io.Reader;
  12. import java.io.StringReader;
  13. import java.io.StringWriter;
  14. import java.io.Writer;
  15. import java.util.Stack;
  16. import javax.xml.parsers.ParserConfigurationException;
  17. import javax.xml.parsers.SAXParserFactory;
  18. import org.xml.sax.Attributes;
  19. import org.xml.sax.ContentHandler;
  20. import org.xml.sax.InputSource;
  21. import org.xml.sax.SAXException;
  22. import org.xml.sax.XMLReader;
  23. import org.xml.sax.helpers.DefaultHandler;
  24. /**
  25. * A class to indent automatically some XML data.
  26. *
  27. * <p>Note: This implementation is not namespace aware, and will not handle entities other than
  28. * &amp;amp;, &amp;lt;, &amp;gt; or &amp;quot;.
  29. *
  30. * @author Christophe Lauret - Allette Systems (Australia)
  31. * @version 26 February 2005
  32. */
  33. public final class XMLIndenter extends DefaultHandler implements ContentHandler {
  34. /**
  35. * The writer where the XML goes.
  36. */
  37. private final PrintWriter writer;
  38. // state attributes ---------------------------------------------------------------------------
  39. /**
  40. * The indentation level.
  41. */
  42. private transient int indentLevel = 0;
  43. /**
  44. * The stack of states
  45. */
  46. private transient Stack<Integer> states = new Stack<Integer>();
  47. /**
  48. * Element has neither text, nor children.
  49. */
  50. private static final Integer EMPTY = new Integer(0);
  51. /**
  52. * Element has text.
  53. */
  54. private static final Integer HAS_TEXT = new Integer(1);
  55. /**
  56. * Element has children.
  57. */
  58. private static final Integer HAS_CHILDREN = new Integer(2);
  59. /* ----------------------------------------- constructor --------------------------------------- */
  60. /**
  61. * Creates a new XML Indenter.
  62. *
  63. * @param w The writer to use.
  64. */
  65. private XMLIndenter(Writer w) {
  66. if (w instanceof PrintWriter) {
  67. this.writer = (PrintWriter) w;
  68. } else {
  69. this.writer = new PrintWriter(w);
  70. }
  71. }
  72. /* -------------------------------------- handler's methods ------------------------------------ */
  73. /**
  74. * {@inheritDoc}
  75. */
  76. @Override
  77. public void startElement(String uri, String localName, String qName, Attributes atts) {
  78. // update the state of previous element
  79. if (!this.states.empty()) {
  80. if (this.states.pop().equals(EMPTY)) {
  81. this.writer.println('>');
  82. }
  83. this.states.push(HAS_CHILDREN);
  84. }
  85. // always indent
  86. for (int i = 0; i < this.indentLevel; i++) {
  87. this.writer.print(" ");
  88. }
  89. // print XML data
  90. this.writer.print('<' + qName);
  91. for (int i = 0; i < atts.getLength(); i++) {
  92. this.writer.print(' '+atts.getQName(i)+"=\""+atts.getValue(i)+'"');
  93. }
  94. // update attributes
  95. this.indentLevel++;
  96. this.states.push(EMPTY);
  97. }
  98. /**
  99. * {@inheritDoc}
  100. */
  101. @Override
  102. public void endElement(String uri, String localName, String qName) {
  103. this.indentLevel--;
  104. Object state = this.states.pop();
  105. if (EMPTY.equals(state)) {
  106. this.writer.println("/>");
  107. } else if (HAS_TEXT.equals(state)) {
  108. this.writer.println("</" + qName + '>');
  109. } else if (HAS_CHILDREN.equals(state)) {
  110. for (int i = 0; i < this.indentLevel; i++) {
  111. this.writer.print(" ");
  112. }
  113. this.writer.println("</" + qName + '>');
  114. }
  115. }
  116. /**
  117. * Prints the characters.
  118. *
  119. * {@inheritDoc}
  120. */
  121. @Override
  122. public void characters(char[] ch, int position, int offset) {
  123. if (this.states.peek().equals(EMPTY)) {
  124. this.states.pop();
  125. this.writer.print('>');
  126. this.states.push(HAS_TEXT);
  127. }
  128. this.writer.print(new String(ch, position, offset));
  129. }
  130. /**
  131. * Does nothing.
  132. *
  133. * {@inheritDoc}
  134. */
  135. @Override
  136. public void ignorableWhitespace(char[] ch, int position, int offset) {
  137. // do nothing.
  138. }
  139. /* ---------------------------------------- static methods ------------------------------------- */
  140. /**
  141. * Indents the given XML String.
  142. *
  143. * @param xml The XML string to indent
  144. *
  145. * @return The indented XML String.
  146. *
  147. * @throws IOException If an IOException occurs.
  148. * @throws SAXException If the XML is not well-formed.
  149. * @throws ParserConfigurationException If the parser could not be configured
  150. */
  151. public static String indent(String xml)
  152. throws SAXException, IOException, ParserConfigurationException {
  153. Writer writer = new StringWriter();
  154. Reader reader = new StringReader(xml);
  155. indent(reader, writer);
  156. return writer.toString();
  157. }
  158. /**
  159. * Indents the given XML String.
  160. *
  161. * @param r A reader on XML data
  162. * @param w A writer for the indented XML
  163. *
  164. * @throws IOException If an IOException occurs.
  165. * @throws SAXException If the XML is not well-formed.
  166. * @throws ParserConfigurationException If the parser could not be configured
  167. */
  168. public static void indent(Reader r, Writer w)
  169. throws SAXException, IOException, ParserConfigurationException {
  170. // create the indenter
  171. XMLIndenter indenter = new XMLIndenter(w);
  172. // initialise the SAX framework
  173. SAXParserFactory factory = SAXParserFactory.newInstance();
  174. factory.setNamespaceAware(false);
  175. factory.setValidating(false);
  176. InputSource source = new InputSource(r);
  177. // parse the XML
  178. XMLReader xmlreader = factory.newSAXParser().getXMLReader();
  179. xmlreader.setContentHandler(indenter);
  180. xmlreader.parse(source);
  181. }
  182. /**
  183. * Indents the given XML String.
  184. *
  185. * @param xml The XML string to indent
  186. *
  187. * @return The indented XML String or <code>null</code> if an error occurred.
  188. */
  189. public static String indentSilent(String xml) {
  190. try {
  191. return indent(xml);
  192. } catch (Exception ex) {
  193. return null;
  194. }
  195. }
  196. /**
  197. * Indents the given XML String.
  198. *
  199. * <p>This method does not throw any exception out of convenience, instead it returns a
  200. * <code>boolean</code> value to indicate whether the XML indenting was performed succesfully.
  201. *
  202. * @param r A reader on XML data
  203. * @param w A writer for the indented XML
  204. *
  205. * @return <code>true</code> if the operation was successful, <code>false</code> if an error
  206. * occurred.
  207. */
  208. public static boolean indentSilent(Reader r, Writer w) {
  209. try {
  210. indent(r, w);
  211. return true;
  212. } catch (Exception ex) {
  213. return false;
  214. }
  215. }
  216. }