package org.activityinfo.server.endpoint.kml.xml; /* * #%L * ActivityInfo Server * %% * Copyright (C) 2009 - 2013 UNICEF * %% * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this program. If not, see * <http://www.gnu.org/licenses/gpl-3.0.html>. * #L% */ import org.xml.sax.SAXException; import org.xml.sax.helpers.AttributesImpl; import javax.xml.transform.OutputKeys; import javax.xml.transform.Transformer; import javax.xml.transform.TransformerConfigurationException; import javax.xml.transform.sax.SAXTransformerFactory; import javax.xml.transform.sax.TransformerHandler; import javax.xml.transform.stream.StreamResult; import java.util.Map.Entry; import java.util.Stack; public class XmlBuilder { private TransformerHandler hd; private static class State { private XmlElement element; private String qname; private String defaultNamespace; public State(XmlElement element, String qname, String defaultNamespace) { super(); this.element = element; this.qname = qname; this.defaultNamespace = defaultNamespace; } public void setElement(XmlElement element) { this.element = element; } public XmlElement getElement() { return element; } public void setQname(String qname) { this.qname = qname; } public String getQname() { return qname; } public void setDefaultNamespace(String defaultNamespace) { this.defaultNamespace = defaultNamespace; } public String getDefaultNamespace() { return defaultNamespace; } } private XmlElement pendingTag = null; private Stack<State> openTags = new Stack<State>(); public XmlBuilder(StreamResult result) throws TransformerConfigurationException, SAXException { SAXTransformerFactory tf = (SAXTransformerFactory) SAXTransformerFactory.newInstance(); hd = tf.newTransformerHandler(); Transformer serializer = hd.getTransformer(); serializer.setOutputProperty(OutputKeys.ENCODING, "UTF-8"); serializer.setOutputProperty(OutputKeys.INDENT, "yes"); hd.setResult(result); } public void startDocument() throws SAXException { hd.startDocument(); } public void endDocument() throws SAXException { hd.endDocument(); } protected String getPrefix(XmlElement tag) { return getPrefix(tag, tag.getNamespace()); } protected String getPrefix(XmlElement tag, String namespace) { String prefix = tag.getNamespacePrefixes().get(namespace); if (prefix != null) { return prefix; } for (int i = openTags.size() - 1; i >= 0; i--) { prefix = openTags.get(i).getElement().getNamespacePrefixes().get(namespace); if (prefix != null) { return prefix; } } return null; } protected void writeTag(XmlElement tag, boolean closed) throws SAXException { if (tag != null) { AttributesImpl atts = new AttributesImpl(); String prefix = getPrefix(tag); String qname = tag.getName(); if (prefix != null) { qname = prefix + ":" + qname; } String defaultNamespace = ""; if (openTags.size() != 0) { defaultNamespace = openTags.peek().getDefaultNamespace(); } if (prefix == null && !defaultNamespace.equals(tag.getNamespace())) { // atts.addAttribute("", "", "xmlns", "string", // tag.getNamespace()); defaultNamespace = tag.getNamespace(); } for (Entry<String, String> entry : tag.getNamespacePrefixes().entrySet()) { hd.startPrefixMapping(entry.getValue(), entry.getKey()); } for (XmlAttribute attr : tag.getAttributes()) { String attrQName = attr.getName(); String attrNs = attr.getNamespace(); // if(attr.getNamespace() == null) { // attrNs = tag.getNamespace(); // } if (attrNs != null && !defaultNamespace.equals(attrNs)) { String attrPrefix = getPrefix(tag, attrNs); if (attrPrefix == null) { throw new SAXException(); } attrQName = attrPrefix + ":" + attr.getName(); } atts.addAttribute(attrNs, attr.getName(), attrQName, "string", attr.getValue()); } // for(Entry<String, String> entry : // tag.getNamespacePrefixes().entrySet()) { // atts.addAttribute("", "", "xmlns:" + entry.getValue(), "string", // entry.getKey()); // } hd.startElement(tag.getNamespace(), tag.getName(), qname, atts); if (closed) { if (tag.getInnerText() != null) { char[] text = pendingTag.getInnerText().toCharArray(); hd.characters(text, 0, text.length); } hd.endElement(tag.getNamespace(), tag.getName(), qname); } else { openTags.push(new State(tag, qname, defaultNamespace)); } } } protected void writePendingTag() throws SAXException { if (pendingTag != null) { writeTag(pendingTag, pendingTag.isClosed()); pendingTag = null; } } public XmlBuilder text(String text) throws SAXException { writePendingTag(); char[] chars = text.toCharArray(); hd.characters(chars, 0, chars.length); return this; } public XmlBuilder cdata(String text) throws SAXException { writePendingTag(); hd.startCDATA(); char[] chars = text.toCharArray(); hd.characters(chars, 0, chars.length); hd.endCDATA(); return this; } public XmlBuilder close() throws SAXException { writePendingTag(); State openTag = openTags.pop(); hd.endElement(openTag.getElement().getNamespace(), openTag.getElement().getName(), openTag.getQname()); for (Entry<String, String> entry : openTag.getElement().getNamespacePrefixes().entrySet()) { hd.endPrefixMapping(entry.getValue()); } return this; } public <XmlElementT extends XmlElement> XmlElementT start(XmlElementT tag) throws SAXException { writePendingTag(); pendingTag = tag; return tag; } public <T extends XmlElement> T e(T element) throws SAXException { writePendingTag(); start(element).close(); return element; } public XmlBuilder e(XmlElement tag, SimpleXmlElement... child) throws SAXException { start(tag); for (SimpleXmlElement c : child) { e(c); } close(); return this; } public XmlBuilder e(SimpleXmlElement tag) throws SAXException { e(new XmlElement(tag.getNamespace(), tag.getName())).text(tag.getText().toString()); return this; } public XmlBuilder closeVerify(String tagName) throws SAXException { // if(!openTags.peek().equals(tagName)) { // throw new MismatchedTagError(openTags.peek()); // } return close(); } }