| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| package org.apache.empire.xml; |
| |
| // Java |
| import java.io.File; |
| import java.io.FileOutputStream; |
| import java.io.IOException; |
| import java.io.OutputStream; |
| import java.io.OutputStreamWriter; |
| import java.io.PrintWriter; |
| import java.io.UnsupportedEncodingException; |
| import java.io.Writer; |
| |
| import javax.xml.transform.Transformer; |
| import javax.xml.transform.TransformerFactory; |
| import javax.xml.transform.dom.DOMSource; |
| import javax.xml.transform.stream.StreamResult; |
| |
| import org.slf4j.Logger; |
| import org.slf4j.LoggerFactory; |
| import org.w3c.dom.Attr; |
| import org.w3c.dom.Document; |
| import org.w3c.dom.NamedNodeMap; |
| import org.w3c.dom.Node; |
| import org.w3c.dom.NodeList; |
| |
| |
| /** |
| * This class prints out a XML-DOM-Tree to an output stream. |
| * <P> |
| * |
| * |
| */ |
| public class XMLWriter |
| { |
| // Logger |
| protected static final Logger log = LoggerFactory.getLogger(XMLWriter.class); |
| |
| /** Print writer. */ |
| protected PrintWriter out; |
| /** Canonical output. */ |
| protected boolean canonical; |
| /** xmlWriterRoot for debugToFile function */ |
| private static String xmlWriterRoot = null; |
| |
| /** Default Encoding */ |
| private String charsetEncoding = "utf-8"; |
| |
| /** |
| * Prints out the DOM-Tree on System.out for debugging purposes. |
| * |
| * @param doc The XML-Document to print |
| */ |
| public static void debug(Document doc) |
| { |
| XMLWriter dbg = new XMLWriter(System.out); |
| dbg.print(doc); |
| } |
| |
| /** |
| * Prints out the DOM-Tree to a file for debugging purposes. |
| * The file will be truncated if it exists or created if if does |
| * not exist. |
| * |
| * @param doc The XML-Document to print |
| * @param filename The name of the file to write the XML-Document to |
| */ |
| public static void debugToFile(Document doc, String filename) |
| { |
| |
| String styleSheet = "../" + filename.substring(0, filename.length() - 3) + "xslt"; |
| |
| FileOutputStream fileOutputStream = null; |
| try |
| { |
| File file = new File(xmlWriterRoot, filename); |
| if (file.exists() == true) |
| { |
| file.delete(); |
| } |
| // do wen need this? file.createNewFile(); |
| fileOutputStream = new FileOutputStream(file); |
| // write xml |
| XMLWriter dbg = new XMLWriter(fileOutputStream); |
| dbg.print(doc, styleSheet); |
| } catch (IOException ioe) |
| { |
| log.error("Error: Could not write XML to file: " + filename + " in directory: " + xmlWriterRoot); |
| } finally |
| { |
| try |
| { |
| if (fileOutputStream != null) |
| { |
| fileOutputStream.close(); |
| } |
| } catch (IOException ioe) |
| { |
| log.error("Cannot write Document file", ioe); |
| /* Ignore IOExceptions */ |
| } |
| } |
| } |
| |
| /** |
| * Prints out the DOM-Tree. The file will be truncated if it |
| * exists or created if if does not exist. |
| * |
| * @param doc The XML-Document to print |
| * @param filename The name of the file to write the XML-Document to |
| */ |
| public static void saveAsFile(Document doc, String filename) |
| { |
| try |
| { |
| File file = new File(filename); |
| if (file.exists() == true) |
| { |
| file.delete(); |
| } |
| |
| DOMSource domSource = new DOMSource(doc); |
| StreamResult streamResult = new StreamResult(file); |
| |
| TransformerFactory transformerFactory = TransformerFactory.newInstance(); |
| Transformer trf = transformerFactory.newTransformer(); |
| trf.transform(domSource, streamResult); |
| |
| } catch (Exception ex) |
| { |
| log.error("Error: Could not write XML to file: " + filename); |
| } |
| } |
| |
| public static void setXmlWriterDebugPath(String path) |
| { |
| xmlWriterRoot = path; |
| } |
| |
| /** |
| * Creates a XML Writer object. |
| * |
| * @param writer a writer to the output stream |
| * @param charsetEncoding encoding type (i.e. utf-8) |
| */ |
| public XMLWriter(Writer writer, String charsetEncoding) |
| { |
| this.out = new PrintWriter(writer); |
| this.charsetEncoding = charsetEncoding; |
| this.canonical = false; |
| } |
| |
| /** |
| * Creates a XML Writer object. |
| * |
| * @param outStream the output stream |
| * @param charsetEncoding The name of a supported |
| * {@link java.nio.charset.Charset </code>charset<code>} |
| * |
| * @throws UnsupportedEncodingException If the named encoding is not supported |
| */ |
| public XMLWriter(OutputStream outStream, String charsetEncoding) |
| throws UnsupportedEncodingException |
| { // Set Debug Level |
| this(new OutputStreamWriter(outStream, charsetEncoding), charsetEncoding); |
| } |
| |
| /** |
| * Constructor |
| * |
| * @param outStream the output stream |
| */ |
| public XMLWriter(OutputStream outStream) |
| { |
| try |
| { |
| this.charsetEncoding = "utf-8"; |
| this.out = new PrintWriter(new OutputStreamWriter(outStream, charsetEncoding)); |
| this.canonical = false; |
| } catch (UnsupportedEncodingException e) |
| { |
| log.error("The encoding \"" + this.charsetEncoding + "\" is not supported!", e); |
| } |
| } |
| |
| /** |
| * Prints the specified node recursively |
| * |
| * @param node the current node to print |
| * @param level the nesting level used for indenting the output |
| * @return the node type of this node |
| */ |
| public int print(Node node, int level) |
| { |
| // is there anything to do? |
| if (node == null) |
| { |
| return 0; |
| } |
| |
| int type = node.getNodeType(); |
| switch (type) |
| { |
| // print document |
| case Node.DOCUMENT_NODE: |
| { // Sound not come here |
| print(((Document) node).getDocumentElement(), 0); |
| } |
| break; |
| // print element with attributes |
| case Node.ELEMENT_NODE: |
| { |
| // out |
| out.print('<'); |
| out.print(node.getNodeName()); |
| Attr attrs[] = sortAttributes(node.getAttributes()); |
| for (int i = 0; i < attrs.length; i++) |
| { |
| Attr attr = attrs[i]; |
| out.print(' '); |
| out.print(attr.getNodeName()); |
| out.print("=\""); |
| out.print(normalize(attr.getNodeValue())); |
| out.print('"'); |
| } |
| // children |
| NodeList children = node.getChildNodes(); |
| if (children != null) |
| { |
| // close-tag |
| int len = children.getLength(); |
| if (len > 0 && children.item(0).getNodeType() != Node.TEXT_NODE) |
| out.println('>'); |
| else |
| out.print('>'); |
| // Print all Children |
| int prevType = 0; |
| for (int i = 0; i < len; i++) |
| { |
| if (i > 0 || children.item(i).getNodeType() != Node.TEXT_NODE) |
| { // Indent next Line |
| for (int s = 0; s < level; s++) |
| out.print(" "); |
| } |
| // Print a child |
| prevType = print(children.item(i), level + 1); |
| } |
| // Endtag |
| if (len > 0 && prevType != Node.TEXT_NODE) |
| { // padding |
| for (int s = 1; s < level; s++) |
| out.print(" "); |
| } |
| out.print("</"); |
| out.print(node.getNodeName()); |
| out.println('>'); |
| } |
| else |
| out.println("/>"); |
| break; |
| } |
| |
| // handle entity reference nodes |
| case Node.ENTITY_REFERENCE_NODE: |
| { |
| if (canonical) |
| { |
| NodeList children = node.getChildNodes(); |
| if (children != null) |
| { |
| int len = children.getLength(); |
| for (int i = 0; i < len; i++) |
| { |
| print(children.item(i), level + 1); |
| } |
| } |
| } |
| else |
| { |
| out.print('&'); |
| out.print(node.getNodeName()); |
| out.print(';'); |
| } |
| break; |
| } |
| |
| // print cdata sections |
| case Node.CDATA_SECTION_NODE: |
| { |
| if (canonical == false) |
| { |
| out.print("<![CDATA["); |
| out.print(node.getNodeValue()); |
| out.println("]]>"); |
| } |
| else |
| out.print(normalize(node.getNodeValue())); |
| break; |
| } |
| |
| // print text |
| case Node.TEXT_NODE: |
| { // Text |
| out.print(normalize(node.getNodeValue())); |
| break; |
| } |
| |
| // print processing instruction |
| case Node.PROCESSING_INSTRUCTION_NODE: |
| { |
| out.print("<?"); |
| out.print(node.getNodeName()); |
| String data = node.getNodeValue(); |
| if (data != null && data.length() > 0) |
| { |
| out.print(' '); |
| out.print(data); |
| } |
| out.println("?>"); |
| break; |
| } |
| } |
| |
| out.flush(); |
| return type; |
| |
| } // print(Node) |
| |
| /** |
| * Prints the specified document. |
| * |
| * @param doc the XML-DOM-Document to print |
| */ |
| public void print(Document doc) |
| { |
| print(doc, null); |
| } |
| |
| /** |
| * Prints the specified document. |
| * |
| * @param doc the XML-DOM-Document to print |
| * @param styleSheet the XML-DOM-Document to print |
| */ |
| public void print(Document doc, String styleSheet) |
| { |
| if (!canonical) |
| { |
| out.println("<?xml version=\"1.0\" encoding=\"" + charsetEncoding + "\"?>"); |
| } |
| if (styleSheet != null) |
| { |
| //20040427 Marco: xml stylesheet document specification changed from "<?xml:stylesheet name=\"text/xsl\" href=\"" |
| // to |
| // "<?xml-stylesheet type=\"text/xsl\" href=\"" |
| out.print("<?xml-stylesheet type=\"text/xsl\" href=\""); |
| out.print(styleSheet); |
| out.println("\"?>"); |
| } |
| // Print the Document |
| print(doc.getDocumentElement(), 0); |
| out.flush(); |
| } |
| |
| /** |
| * Sorts attributes by name. |
| * |
| * @param attrs the unsorted list of attributes |
| * @return the 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); |
| |
| } // sortAttributes(NamedNodeMap):Attr[] |
| |
| /** |
| * Converts a string to valid XML-Syntax replacing XML entities. |
| * |
| * @param s the string to normalize |
| */ |
| protected String normalize(String s) |
| { |
| return normalize(s, canonical); |
| } |
| |
| static public String normalize(String s, boolean canonical) |
| { |
| StringBuilder str = new StringBuilder(); |
| |
| int len = (s != null) ? s.length() : 0; |
| for (int i = 0; i < len; i++) |
| { |
| char ch = s.charAt(i); |
| switch (ch) |
| { |
| case '<': |
| { |
| str.append("<"); |
| break; |
| } |
| case '>': |
| { |
| str.append(">"); |
| break; |
| } |
| case '&': |
| { |
| str.append("&"); |
| break; |
| } |
| case '"': |
| { |
| str.append("""); |
| break; |
| } |
| case '\r': |
| case '\n': |
| { |
| if (canonical) |
| { |
| str.append("&#"); |
| str.append(Integer.toString(ch)); |
| str.append(';'); |
| break; |
| } |
| // else, default append char |
| } |
| default: |
| { |
| str.append(ch); |
| } |
| } |
| } |
| |
| return (str.toString()); |
| |
| } // normalize(String):String |
| |
| } |