package net.sourceforge.javautil.common.jaxb.xml;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import javax.xml.bind.JAXBException;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import net.sourceforge.javautil.common.exception.ThrowableManagerRegistry;
import net.sourceforge.javautil.common.io.IInputSource;
import net.sourceforge.javautil.common.jaxb.IJavaXMLElement;
import net.sourceforge.javautil.common.jaxb.xml.IXMLElement.NoopElement;
import org.xml.sax.Attributes;
import org.xml.sax.InputSource;
import org.xml.sax.Locator;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
import org.xml.sax.ext.DefaultHandler2;
/**
* This will parse XML and integrate it with the {@link IXMLDocument} API.
*
* @author elponderador
* @author $Author$
* @version $Id$
*/
public class XMLReader<CTX extends XMLContext> extends DefaultHandler2 {
/**
* @param <CTX> The type of context
* @param <D> The type of handler
* @param input The source of the raw XML
* @param handler The handler instance that will handle XML events
* @param context The context instance to use with the handler
* @return The handler for chaining
*/
public static <CTX extends XMLContext, D extends IXMLDocument<CTX>> CTX parse (IInputSource input, D handler, CTX context) {
try {
SAXParserFactory factory = SAXParserFactory.newInstance();
SAXParser parser = factory.newSAXParser();
XMLReader<CTX> xmlHandler = new XMLReader<CTX>(handler, context);
org.xml.sax.XMLReader reader = parser.getXMLReader();
reader.setContentHandler(xmlHandler);
reader.setDTDHandler(xmlHandler);
reader.setEntityResolver(xmlHandler);
reader.setErrorHandler(xmlHandler);
reader.setProperty("http://xml.org/sax/properties/lexical-handler", xmlHandler);
reader.parse(new InputSource( input.getInputStream() ));
return context;
} catch (ParserConfigurationException e) {
throw ThrowableManagerRegistry.caught(e);
} catch (SAXException e) {
throw ThrowableManagerRegistry.caught(e);
} catch (IOException e) {
throw ThrowableManagerRegistry.caught(e);
}
}
protected final IXMLDocument<CTX> document;
protected final CTX context;
protected NoopElement<CTX> NULL = new NoopElement<CTX>();
protected IXMLElement<CTX> currentElement;
protected boolean cdata = false;
protected IXMLCharacterData<CTX> cdataElement;
protected final List<IXMLElement<CTX>> elements = new ArrayList<IXMLElement<CTX>>();
public XMLReader(IXMLDocument<CTX> document, CTX context) {
this.document = document;
this.context = context;
this.currentElement = document;
}
@Override public void startDTD(String name, String publicId, String systemId) throws SAXException {
document.visitDTD(context, name, publicId, systemId);
}
@Override public void endDTD() throws SAXException {}
@Override public void startDocument() throws SAXException {}
@Override public void comment(char[] ch, int start, int length) throws SAXException {
if (currentElement != NULL) {
try {
currentElement.visitComment(context, ch, start, length);
} catch (JAXBException e) {
throw new SAXException(e);
}
}
}
@Override public void startPrefixMapping(String prefix, String uri) throws SAXException {
if (currentElement != NULL) {
try {
currentElement.visitXmlNSDeclaration(context, prefix, uri);
} catch (JAXBException e) {
throw new SAXException(e);
}
}
}
@Override public void endPrefixMapping(String prefix) throws SAXException {}
@Override public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
try {
if (!"".equals(qName)) {
String[] parts = qName.split(":");
this.currentElement = currentElement.visitElement(context, parts.length == 2 ? parts[0] : null, parts.length == 2 ? parts[1] : parts[0]);
} else if (!"".equals(localName)) {
this.currentElement = currentElement.visitElement(context, null, localName);
}
this.elements.add(0, this.currentElement == null ? this.currentElement = NULL : this.currentElement);
if (this.currentElement != NULL) {
for (int i=0; i<attributes.getLength(); i++) {
String name = !"".equals( attributes.getLocalName(i) ) ? attributes.getLocalName(i) : attributes.getQName(i);
String[] parts = name.split(":");
this.currentElement.visitAttribute(context, parts.length == 2 ? parts[0] : null, parts.length == 2 ? parts[1] : parts[0], attributes.getValue(i));
}
}
} catch (JAXBException e) {
throw new SAXException(e);
}
}
@Override public void startCDATA() throws SAXException {
if (currentElement != NULL) {
this.cdata = true;
try {
this.cdataElement = currentElement.visitCharacterData(context);
} catch (JAXBException e) {
throw new SAXException(e);
}
}
}
@Override public void endCDATA() throws SAXException {
if (this.cdataElement != null) {
try {
this.cdataElement.visitEnd(context);
} catch (JAXBException e) {
throw new SAXException(e);
}
}
this.cdata = false;
}
@Override public void characters(char[] ch, int start, int length) throws SAXException {
if (this.cdata) {
try {
if (this.cdataElement != NULL) this.cdataElement.visitData(context, ch, start, length);
} catch (JAXBException e) {
throw new SAXException(e);
}
} else {
try {
if (this.currentElement != NULL)
this.currentElement.visitContent(context, ch, start, length);
} catch (JAXBException e) {
throw new SAXException(e);
}
}
}
@Override public void ignorableWhitespace(char[] ch, int start, int length) throws SAXException {
if (this.currentElement != NULL) {
try {
this.currentElement.visitWhitespace(context, ch, start, length);
} catch (JAXBException e) {
throw new SAXException(e);
}
}
}
@Override public void endElement(String uri, String localName, String qName) throws SAXException {
if (this.currentElement != NULL) {
try {
this.currentElement.visitEnd(context);
} catch (JAXBException e) {
throw new SAXException(e);
}
}
if (this.elements.size() == 0) {
throw new SAXException("Stack incorrectly managed");
} else {
this.elements.remove(0);
}
if (this.elements.size() > 0) {
this.currentElement = this.elements.get(0);
} else {
this.currentElement = null;
}
}
@Override public void endDocument() throws SAXException {
// try {
// this.document.visitEnd(context);
// } catch (JAXBException e) {
// throw new SAXException(e);
// }
}
//
// ERROR HANDLING
//
@Override public void warning(SAXParseException e) throws SAXException {
}
@Override public void error(SAXParseException e) throws SAXException {
}
@Override public void fatalError(SAXParseException e) throws SAXException {
}
//
// SPECIAL PROCESSING
//
@Override public InputSource resolveEntity(String publicId, String systemId) throws IOException, SAXException { return super.resolveEntity(publicId, systemId); }
@Override public void notationDecl(String name, String publicId, String systemId) throws SAXException { }
@Override public void unparsedEntityDecl(String name, String publicId, String systemId, String notationName) throws SAXException { }
@Override public void setDocumentLocator(Locator locator) { }
@Override public void processingInstruction(String target, String data) throws SAXException {}
@Override public void skippedEntity(String name) throws SAXException {}
@Override public void startEntity(String name) throws SAXException {}
@Override public void endEntity(String name) throws SAXException {}
@Override public void attributeDecl(String eName, String aName, String type, String mode, String value) throws SAXException { }
@Override public void elementDecl(String name, String model) throws SAXException { }
@Override public void externalEntityDecl(String name, String publicId, String systemId) throws SAXException { }
@Override public void internalEntityDecl(String name, String value) throws SAXException { }
@Override public InputSource getExternalSubset(String name, String baseURI) throws SAXException, IOException {
return super.getExternalSubset(name, baseURI);
}
@Override public InputSource resolveEntity(String name, String publicId, String baseURI, String systemId) throws SAXException, IOException {
return super.resolveEntity(name, publicId, baseURI, systemId);
}
}