--- /dev/null
+package com.quantum.util.xml;
+
+import org.w3c.dom.Attr;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+/**
+ * @author BC Holmes
+ */
+public class XMLRenderer {
+
+ protected boolean insertNewLine = false;
+ protected int numberOfTabs;
+
+ protected final static String LINE_SEPARATOR = System
+ .getProperty("line.separator");
+
+ protected final static String INDENT_STRING = "\t";
+
+ /** Returns a sorted list of attributes. */
+ protected Attr[] sortAttributes(NamedNodeMap attrs) {
+
+ int len = (attrs != null) ? attrs.getLength() : 0;
+ Attr array[] = new Attr[len];
+ for (int i = 0; i < len; i++) {
+ array[i] = (Attr) attrs.item(i);
+ }
+ for (int i = 0; i < len - 1; i++) {
+ String name = array[i].getNodeName();
+ int index = i;
+ for (int j = i + 1; j < len; j++) {
+ String curName = array[j].getNodeName();
+ if (curName.compareTo(name) < 0) {
+ name = curName;
+ index = j;
+ }
+ }
+ if (index != i) {
+ Attr temp = array[i];
+ array[i] = array[index];
+ array[index] = temp;
+ }
+ }
+
+ return (array);
+
+ }
+
+ protected XMLRenderer() {
+ this.numberOfTabs = 0;
+ this.insertNewLine = true;
+ }
+
+ protected void newLine(StringBuffer buffer) {
+ if (this.insertNewLine) {
+ buffer.append(LINE_SEPARATOR);
+ for (int i = 0; i < this.numberOfTabs; i++) {
+ buffer.append(INDENT_STRING);
+ }
+ }
+ }
+
+ /** Prints the specified node, recursively. */
+ protected void print(Node node, StringBuffer buffer) {
+ // is there anything to do?
+ if (node != null) {
+ int type = node.getNodeType();
+ switch (type) {
+
+ case Node.DOCUMENT_NODE:
+ printDocumentNode(node, buffer);
+ break;
+
+ // print element with attributes
+ case Node.ELEMENT_NODE:
+ printElementNode(node, buffer);
+ break;
+ // handle entity reference nodes
+ case Node.ENTITY_REFERENCE_NODE:
+ printEntityReferenceNode(node, buffer);
+ break;
+
+ // print cdata sections
+ case Node.CDATA_SECTION_NODE:
+ printCDataSectionNode(node, buffer);
+ break;
+
+ // print text
+ case Node.TEXT_NODE:
+ printTextNode(node, buffer);
+ break;
+
+ // print processing instruction
+ case Node.PROCESSING_INSTRUCTION_NODE:
+ printProcessingInstructionNode(node, buffer);
+ break;
+ }
+ }
+ }
+
+ protected void printProcessingInstructionNode(Node node, StringBuffer buffer) {
+ buffer.append("<?");
+ buffer.append(node.getNodeName());
+ String data = node.getNodeValue();
+ if (data != null && data.length() > 0) {
+ buffer.append(' ');
+ buffer.append(data);
+ }
+ buffer.append("?>");
+ }
+
+ protected void printTextNode(Node node, StringBuffer buffer) {
+ printString(node.getNodeValue(), buffer);
+ this.insertNewLine = false;
+ }
+
+ protected void printCDataSectionNode(Node node, StringBuffer buffer) {
+ buffer.append("<![CDATA[");
+ buffer.append(node.getNodeValue());
+ buffer.append("]]>");
+ }
+
+ protected void printEntityReferenceNode(Node node, StringBuffer buffer) {
+ buffer.append('&');
+ buffer.append(node.getNodeName());
+ buffer.append(';');
+ }
+
+ protected void printElementNode(Node node, StringBuffer buffer) {
+ newLine(buffer);
+ this.numberOfTabs++;
+ buffer.append('<');
+ buffer.append(node.getNodeName());
+ Attr attrs[] = sortAttributes(node.getAttributes());
+ for (int i = 0; i < attrs.length; i++) {
+ Attr attr = attrs[i];
+ buffer.append(' ');
+ buffer.append(attr.getNodeName());
+ buffer.append("=\"");
+ printString(attr.getNodeValue(), buffer);
+ buffer.append('"');
+ }
+
+ if (!node.hasChildNodes()) {
+ buffer.append(" />");
+ this.numberOfTabs--;
+ } else {
+ buffer.append(">");
+ printAllChildNodes(node, buffer);
+ this.numberOfTabs--;
+ newLine(buffer);
+
+ buffer.append("</");
+ buffer.append(node.getNodeName());
+ buffer.append(">");
+ }
+ this.insertNewLine = true;
+ }
+
+ protected void printDocumentNode(Node node, StringBuffer buffer) {
+ buffer.append("<?xml version=\"1.0\" ?>");
+
+ printAllChildNodes(node, buffer);
+ }
+
+ protected void printAllChildNodes(Node node, StringBuffer buffer) {
+ NodeList children = node.getChildNodes();
+ for (int i = 0; i < children.getLength(); i++) {
+ print(children.item(i), buffer);
+ }
+ }
+
+ /** Normalizes the given string. */
+ protected void printString(String s, StringBuffer buffer) {
+
+ int len = (s != null) ? s.length() : 0;
+ for (int i = 0; i < len; i++) {
+ char ch = s.charAt(i);
+ switch (ch) {
+ case '<': {
+ buffer.append("<");
+ break;
+ }
+ case '>': {
+ buffer.append(">");
+ break;
+ }
+ case '&': {
+ buffer.append("&");
+ break;
+ }
+ case '"':
+ buffer.append(""");
+ break;
+ case '\r':
+ case '\n':
+ default: {
+ buffer.append(ch);
+ }
+ }
+ }
+ }
+
+ public static String render(Node node) {
+ XMLRenderer renderer = new XMLRenderer();
+ StringBuffer buffer = new StringBuffer();
+ renderer.print(node, buffer);
+
+ return buffer.toString();
+ }
+
+ /**
+ * <p>
+ * Renders a String in a format that it would appear in a text node. That is
+ * to say, special characters (such as &) are converted into entity
+ * references (&amp;).
+ */
+ public static String render(String string) {
+ XMLRenderer renderer = new XMLRenderer();
+ StringBuffer buffer = new StringBuffer();
+ renderer.printString(string, buffer);
+ return buffer.toString();
+ }
+}
\ No newline at end of file