/*
* Copyright 2014 Skynav, Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY SKYNAV, INC. AND ITS CONTRIBUTORS “AS IS” AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL SKYNAV, INC. OR ITS CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.skynav.ttv.util;
import java.io.IOException;
import java.io.Writer;
import java.util.Comparator;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import javax.xml.namespace.QName;
import javax.xml.transform.ErrorListener;
import javax.xml.transform.Result;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.URIResolver;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.w3c.dom.Attr;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.w3c.dom.Text;
import com.skynav.xml.helpers.XML;
public class TextTransformer extends Transformer {
private String encoding;
private boolean indent;
private Map<String,String> prefixes;
private Set<QName> startTagIndentExclusions;
private Set<QName> endTagIndentExclusions;
private int otherNamespaceSequence;
public TextTransformer(String encoding, boolean indent, Map<String, String> prefixes, Set<QName> startTagIndentExclusions, Set<QName> endTagIndentExclusions) {
this.encoding = encoding;
this.indent = indent;
this.prefixes = prefixes;
this.startTagIndentExclusions = startTagIndentExclusions;
this.endTagIndentExclusions = endTagIndentExclusions;
}
public ErrorListener getErrorListener() {
return null;
}
public void setErrorListener(ErrorListener listener) {
}
public Properties getOutputProperties() {
return null;
}
public void setOutputProperties(Properties properties) {
}
public String getOutputProperty(String name) {
return null;
}
public void setOutputProperty(String name, String value) {
}
public Object getParameter(String name) {
return null;
}
public void setParameter(String name, Object value) {
}
public void clearParameters() {
}
public URIResolver getURIResolver() {
return null;
}
public void setURIResolver(URIResolver resolver) {
}
public void transform(Source source, Result result) throws TransformerException {
if (!(source instanceof DOMSource) || !(result instanceof StreamResult)) {
throw new IllegalArgumentException();
}
Node node = ((DOMSource) source).getNode();
if (!(node instanceof Document)) {
throw new IllegalArgumentException();
}
try {
serialize(((StreamResult) result).getWriter(), (Document) node);
} catch (IOException e) {
throw new TransformerException(e);
}
}
private void serialize(Writer w, Document d) throws IOException {
w.write("<?xml version=\"1.0\"");
if (encoding != null) {
w.write(" encoding=\"");
w.write(encoding.toLowerCase());
w.write('"');
}
w.write("?>");
if (indent)
w.write('\n');
Element e = d.getDocumentElement();
if (e != null)
serialize(w, e);
}
private void serialize(Writer w, Element e) throws IOException {
String ns = e.getNamespaceURI();
String ln = e.getLocalName();
if (ln == null)
ln = e.getNodeName();
QName qn = new QName(ns, ln);
w.write('<');
serialize(w, qn);
NamedNodeMap attrs = e.getAttributes();
Set<Attr> attrsOrdered = new java.util.TreeSet<Attr>(new Comparator<Attr>() {
public int compare(Attr a1, Attr a2) {
String n1 = a1.getNodeName();
String n2 = a2.getNodeName();
if (n1.equals(n2))
return 0;
else if (n1.equals("xml:id"))
return -1;
else if (n2.equals("xml:id"))
return 1;
else {
String p1 = a1.getPrefix();
String p2 = a2.getPrefix();
if (p1 == null) {
if (p2 == null) {
return n1.compareTo(n2);
} else
return -1;
} else if (p2 == null) {
return 1;
} else {
return n1.compareTo(n2);
}
}
}
});
for (int i = 0, n = attrs.getLength(); i < n; ++i) {
Node node = attrs.item(i);
if (node instanceof Attr)
attrsOrdered.add((Attr) node);
}
for (Attr a : attrsOrdered) {
w.write(' ');
serialize(w, a);
}
NodeList children = e.getChildNodes();
int numChildren = children.getLength();
if (numChildren == 0) {
w.write("/>");
} else {
w.write('>');
if (indent) {
if ((startTagIndentExclusions == null) || !startTagIndentExclusions.contains(qn))
w.write('\n');
}
for (int i = 0, n = numChildren; i < n; ++i) {
Node node = children.item(i);
if (node instanceof Element) {
serialize(w, (Element) node);
} else if (node instanceof Text) {
serialize(w, (Text) node);
}
}
w.write("</");
serialize(w, qn);
w.write('>');
}
if (indent) {
if ((endTagIndentExclusions == null) || !endTagIndentExclusions.contains(qn))
w.write('\n');
}
}
private void serialize(Writer w, Attr a) throws IOException {
String ns = a.getNamespaceURI();
String ln = a.getLocalName();
if (ln == null)
ln = a.getName();
QName qn = new QName(ns, ln);
serialize(w, qn);
w.write('=');
w.write('"');
serialize(w, a.getValue());
w.write('"');
}
private void serialize(Writer w, Text t) throws IOException {
serialize(w, t.getWholeText());
}
private void serialize(Writer w, String s) throws IOException {
for (int i = 0, n = s.length(); i < n; ++i) {
char c = s.charAt(i);
if (c == '<')
w.write("<");
else if (c == '>')
w.write(">");
else if (c == '"')
w.write(""");
else if (c == '&')
w.write("&");
else
w.write(c);
}
}
private void serialize(Writer w, QName qn) throws IOException {
String ns = qn.getNamespaceURI();
String ln = qn.getLocalPart();
if ((ns != null) && (ns.length() > 0)) {
String prefix = prefixes.get(ns);
if (prefix == null) {
prefix = "ns" + ++otherNamespaceSequence;
prefixes.put(ns, prefix);
}
if (prefix.length() > 0) {
if (!ln.equals(XML.xmlnsPrefix)) {
w.write(prefix);
w.write(':');
}
}
}
w.write(ln);
}
}