jlibs.xml.sax.XMLDocument Maven / Gradle / Ivy
/**
* Copyright 2015 Santhosh Kumar Tekuri
*
* The JLibs authors license 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 jlibs.xml.sax;
import jlibs.core.lang.StringUtil;
import jlibs.xml.sax.helpers.MyNamespaceSupport;
import jlibs.xml.xsl.TransformerUtil;
import org.xml.sax.*;
import org.xml.sax.helpers.AttributesImpl;
import org.xml.sax.helpers.NamespaceSupport;
import javax.xml.namespace.QName;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.Result;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.sax.TransformerHandler;
import javax.xml.transform.stream.StreamResult;
import java.io.IOException;
import java.io.StringReader;
import java.util.Enumeration;
import java.util.Stack;
/**
* This class is used to write xml documents
*
* @author Santhosh Kumar T
*/
public class XMLDocument{
private SAXDelegate xml;
public XMLDocument(SAXDelegate xml){
this.xml = xml;
}
public XMLDocument(Result result, boolean omitXMLDeclaration, int indentAmount, String encoding) throws TransformerConfigurationException{
TransformerHandler handler = TransformerUtil.newTransformerHandler(null, omitXMLDeclaration, indentAmount, encoding);
handler.setResult(result);
xml = new SAXDelegate(handler);
}
/*-------------------------------------------------[ Document ]---------------------------------------------------*/
public XMLDocument startDocument() throws SAXException{
nsSupport.reset();
attrs.clear();
elemStack.clear();
elem = null;
depth = 0;
nsSupport.pushContext();
xml.startDocument();
mark();
return this;
}
public XMLDocument endDocument() throws SAXException{
release(0);
xml.endDocument();
return this;
}
/*-------------------------------------------------[ Namespaces ]---------------------------------------------------*/
private MyNamespaceSupport nsSupport = new MyNamespaceSupport();
private boolean needsNewContext = true;
public MyNamespaceSupport getNamespaceSupport(){
return nsSupport;
}
public void suggestPrefix(String prefix, String uri){
nsSupport.suggestPrefix(prefix, uri);
}
public String declarePrefix(String uri){
if(uri==null)
uri = "";
String prefix = nsSupport.findPrefix(uri);
if(prefix==null){
if(needsNewContext){
nsSupport.pushContext();
needsNewContext = false;
}
prefix = nsSupport.declarePrefix(uri);
}
return prefix;
}
public boolean declarePrefix(String prefix, String uri){
if(needsNewContext){
nsSupport.pushContext();
needsNewContext = false;
}
return nsSupport.declarePrefix(prefix, uri);
}
private QName declareQName(String uri, String localPart){
if(uri==null)
uri = "";
return new QName(uri, localPart, declarePrefix(uri));
}
public String toQName(String uri, String localPart){
if(uri==null)
uri = "";
String prefix = declarePrefix(uri);
return prefix.length()==0 ? localPart : prefix+':'+localPart;
}
public String toAttrQName(String uri, String localPart){
return uri.equals("") ? localPart : toQName(uri, localPart);
}
private void startPrefixMappings() throws SAXException{
Enumeration enumer = nsSupport.getDeclaredPrefixes();
while(enumer.hasMoreElements()){
String prefix = (String) enumer.nextElement();
xml.startPrefixMapping(prefix, nsSupport.findURI(prefix));
}
}
private void endPrefixMappings() throws SAXException{
Enumeration enumer = nsSupport.getDeclaredPrefixes();
while(enumer.hasMoreElements())
xml.endPrefixMapping((String)enumer.nextElement());
}
/*-------------------------------------------------[ start-element ]---------------------------------------------------*/
private Stack elemStack = new Stack();
private QName elem;
int depth = 0; // document is at 0; root is at 1
public int getDepth(){
return depth;
}
private int marks = -1;
public int mark() throws SAXException{
finishStartElement();
elemStack.push(null);
return ++marks;
}
public int release() throws SAXException{
if(marks==-1 || elemStack.empty())
throw new SAXException("no mark found to be released");
endElements();
if(elemStack.peek()!=null)
throw new SAXException("expected "+toString(elemStack.peek())+'>');
elemStack.pop();
return --marks;
}
public void release(int mark) throws SAXException{
while(marks>=mark)
release();
}
private String toString(QName qname){
return qname.getPrefix().length()==0 ? qname.getLocalPart() : qname.getPrefix()+':'+qname.getLocalPart();
}
private void finishStartElement() throws SAXException{
if(elem!=null){
if(needsNewContext)
nsSupport.pushContext();
else
needsNewContext = true;
startPrefixMappings();
elemStack.push(elem);
xml.startElement(elem.getNamespaceURI(), elem.getLocalPart(), toString(elem), attrs);
elem = null;
attrs.clear();
}
}
public XMLDocument startElement(String name) throws SAXException{
return startElement("", name);
}
public XMLDocument startElement(String uri, String name) throws SAXException{
if(uri==null)
uri = "";
finishStartElement();
elem = declareQName(uri, name);
depth++;
return this;
}
/*-------------------------------------------------[ Add-Element ]---------------------------------------------------*/
public XMLDocument addElement(String name, String text, boolean cdata) throws SAXException{
return addElement("", name, text, cdata);
}
public XMLDocument addElement(String uri, String name, String text, boolean cdata) throws SAXException{
if(text!=null){
startElement(uri, name);
if(cdata)
addCDATA(text);
else
addText(text);
endElement();
}
return this;
}
public XMLDocument addElement(String name, String text) throws SAXException{
return addElement("", name, text, false);
}
public XMLDocument addElement(String uri, String name, String text) throws SAXException{
return addElement(uri, name, text, false);
}
public XMLDocument addCDATAElement(String name, String text) throws SAXException{
return addElement("", name, text, true);
}
public XMLDocument addCDATAElement(String uri, String name, String text) throws SAXException{
return addElement(uri, name, text, true);
}
/*-------------------------------------------------[ Attributes ]---------------------------------------------------*/
private AttributesImpl attrs = new AttributesImpl();
public XMLDocument addAttribute(String name, String value) throws SAXException{
return addAttribute("", name, value);
}
public XMLDocument addAttribute(String uri, String name, String value) throws SAXException{
if(elem==null)
throw new SAXException("no start element found to associate this attribute");
if(value!=null){
if(uri==null)
uri = "";
attrs.addAttribute(uri, name, toAttrQName(uri, name), "CDATA", value);
}
return this;
}
/*-------------------------------------------------[ Text ]---------------------------------------------------*/
public XMLDocument addText(String text) throws SAXException{
if(!StringUtil.isEmpty(text)){
finishStartElement();
xml.characters(text.toCharArray(), 0, text.length());
}
return this;
}
public XMLDocument addCDATA(String text) throws SAXException{
if(!StringUtil.isEmpty(text)){
finishStartElement();
xml.startCDATA();
xml.characters(text.toCharArray(), 0, text.length());
xml.endCDATA();
}
return this;
}
/*-------------------------------------------------[ end-element ]---------------------------------------------------*/
private QName findEndElement() throws SAXException{
finishStartElement();
if(elemStack.empty() || elemStack.peek()==null)
throw new SAXException("can't find matching start element");
return elemStack.pop();
}
private XMLDocument endElement(QName qname) throws SAXException{
xml.endElement(qname.getNamespaceURI(), qname.getLocalPart(), toString(qname));
depth--;
endPrefixMappings();
nsSupport.popContext();
needsNewContext = true;
return this;
}
public XMLDocument endElement(String uri, String name) throws SAXException{
QName qname = findEndElement();
if(!qname.getNamespaceURI().equals(uri) || !qname.getLocalPart().equals(name))
throw new SAXException("expected "+toString(qname)+'>');
return endElement(qname);
}
public XMLDocument endElement(String name) throws SAXException{
return endElement("", name);
}
public XMLDocument endElement() throws SAXException{
return endElement(findEndElement());
}
/*-------------------------------------------------[ end-elements ]---------------------------------------------------*/
public XMLDocument endElements(String uri, String name) throws SAXException{
QName qname = findEndElement();
while(true){
endElement(qname);
if(qname.getNamespaceURI().equals(uri) && qname.getLocalPart().equals(name))
break;
}
return this;
}
public XMLDocument endElements(String name) throws SAXException{
return endElements("", name);
}
public XMLDocument endElements() throws SAXException{
finishStartElement();
while(!elemStack.empty() && elemStack.peek()!=null)
endElement();
return this;
}
/*-------------------------------------------------[ Errors ]---------------------------------------------------*/
@SuppressWarnings({"ThrowableInstanceNeverThrown"})
public void warning(String msg, Exception ex) throws SAXException{
xml.warning(new SAXParseException(msg, null, ex));
}
@SuppressWarnings({"ThrowableInstanceNeverThrown"})
public void error(String msg, Exception ex) throws SAXException{
xml.error(new SAXParseException(msg, null, ex));
}
@SuppressWarnings({"ThrowableInstanceNeverThrown"})
public void fatalError(String msg, Exception ex) throws SAXException{
SAXParseException saxException = new SAXParseException(msg, null, ex);
xml.fatalError(saxException);
throw saxException;
}
/*-------------------------------------------------[ XML ]---------------------------------------------------*/
public XMLDocument addXML(String xmlString, boolean excludeRoot) throws SAXException{
if(!StringUtil.isWhitespace(xmlString))
addXML(new InputSource(new StringReader(xmlString)), excludeRoot);
return this;
}
public XMLDocument addXML(InputSource is, boolean excludeRoot) throws SAXException{
finishStartElement();
try{
SAXDelegate delegate;
if(excludeRoot){
delegate = new SAXDelegate(xml){
private int depth = 0;
private NamespaceSupport nsSupport = new NamespaceSupport();
@Override public void startDocument(){}
@Override public void endDocument(){}
@Override public void setDocumentLocator(Locator locator){}
@Override
public void characters(char[] ch, int start, int length) throws SAXException{
if(depth!=1)
super.characters(ch, start, length);
}
@Override
public void ignorableWhitespace(char[] ch, int start, int length) throws SAXException{
if(depth!=1)
super.ignorableWhitespace(ch, start, length);
}
@Override
public void startPrefixMapping(String prefix, String uri) throws SAXException{
if(depth==0)
nsSupport.declarePrefix(prefix, uri);
else
super.startPrefixMapping(prefix, uri);
}
@Override
public void startElement(String uri, String localName, String qName, Attributes atts) throws SAXException{
depth++;
if(depth>1){
if(depth==2)
XMLDocument.this.startPrefixMappings();
super.startElement(uri, localName, qName, atts);
}
}
@Override
public void endElement(String uri, String localName, String qName) throws SAXException{
if(depth>1){
super.endElement(uri, localName, qName);
if(depth==2)
XMLDocument.this.endPrefixMappings();
}
depth--;
}
@Override
public void endPrefixMapping(String prefix) throws SAXException{
if(depth>0)
super.endPrefixMapping(prefix);
}
};
}else{
delegate = new SAXDelegate(xml){
@Override public void startDocument(){}
@Override public void endDocument(){}
@Override public void setDocumentLocator(Locator locator){}
};
}
XMLReader reader = SAXUtil.newSAXParser(true, false, false).getXMLReader();
SAXUtil.setHandler(reader, delegate);
reader.parse(is);
}catch(ParserConfigurationException ex){
throw new SAXException(ex);
}catch(IOException ex){
throw new SAXException(ex);
}
return this;
}
/*-------------------------------------------------[ Others ]---------------------------------------------------*/
public XMLDocument addProcessingInstruction(String target, String data) throws SAXException{
xml.processingInstruction(target, data);
return this;
}
public XMLDocument addComment(String text) throws SAXException{
if(!StringUtil.isEmpty(text)){
finishStartElement();
xml.comment(text.toCharArray(), 0, text.length());
}
return this;
}
/*-------------------------------------------------[ SAXProducer ]---------------------------------------------------*/
public XMLDocument add(SAXProducer saxProducer) throws SAXException{
return add(saxProducer, (QName)null);
}
public XMLDocument add(SAXProducer saxProducer, String name) throws SAXException{
return add(saxProducer, "", name);
}
public XMLDocument add(SAXProducer saxProducer, String uri, String name) throws SAXException{
return add(saxProducer, new QName(uri, name));
}
public XMLDocument add(SAXProducer saxProducer, QName qname) throws SAXException{
if(saxProducer!=null){
mark();
saxProducer.serializeTo(qname, this);
endElements();
release();
}
return this;
}
/*-------------------------------------------------[ DTD ]---------------------------------------------------*/
public XMLDocument addPublicDTD(String name, String publicId, String systemID) throws SAXException{
xml.startDTD(name, publicId, systemID);
xml.endDTD();
return this;
}
public XMLDocument addSystemDTD(String name, String systemId) throws SAXException{
xml.startDTD(name, null, systemId);
xml.endDTD();
return this;
}
/*-------------------------------------------------[ Testing ]---------------------------------------------------*/
public static void main(String[] args) throws Exception{
String google = "http://google.com";
String yahoo = "http://yahoo.com";
XMLDocument xml = new XMLDocument(new StreamResult(System.out), false, 4, null);
xml.startDocument();{
xml.addProcessingInstruction("san", "test='1.2'");
xml.declarePrefix("google", google);
xml.declarePrefix("yahoo", yahoo);
xml.declarePrefix("http://msn.com");
xml.startElement(google, "hello");{
xml.addAttribute("name", "value");
xml.addElement("xyz", "helloworld");
xml.addElement(google, "hai", "test");
xml.addXML(new InputSource("xml/xsds/note.xsd"), true);
xml.addComment("this is comment");
xml.addCDATA("this is sample cdata");
}
}
xml.endDocument();
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy