/* -*- c-basic-offset: 4; indent-tabs-mode: nil; -*- //------100-columns-wide------>|*/
// for license please see accompanying LICENSE.txt file (available also at http://www.xmlpull.org/)
package org.xmlpull.v1.sax2;
import java.io.InputStream;
import java.io.IOException;
import java.io.Reader;
// not J2ME classes -- remove if you want to run in MIDP devices
import java.net.URL;
import java.net.MalformedURLException;
// not J2ME classes
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import org.xml.sax.Attributes;
import org.xml.sax.DTDHandler;
import org.xml.sax.ContentHandler;
import org.xml.sax.EntityResolver;
import org.xml.sax.ErrorHandler;
import org.xml.sax.InputSource;
import org.xml.sax.Locator;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
import org.xml.sax.SAXNotRecognizedException;
import org.xml.sax.SAXNotSupportedException;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.DefaultHandler;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlPullParserFactory;
/**
* SAX2 Driver that pulls events from XmlPullParser
* and comverts them into SAX2 callbacks.
*
* @author Aleksander Slominski
*/
public class Driver implements Locator, XMLReader, Attributes
{
protected static final String DECLARATION_HANDLER_PROPERTY =
"http://xml.org/sax/properties/declaration-handler";
protected static final String LEXICAL_HANDLER_PROPERTY =
"http://xml.org/sax/properties/lexical-handler";
protected static final String NAMESPACES_FEATURE =
"http://xml.org/sax/features/namespaces";
protected static final String NAMESPACE_PREFIXES_FEATURE =
"http://xml.org/sax/features/namespace-prefixes";
protected static final String VALIDATION_FEATURE =
"http://xml.org/sax/features/validation";
protected static final String APACHE_SCHEMA_VALIDATION_FEATURE =
"http://apache.org/xml/features/validation/schema";
protected static final String APACHE_DYNAMIC_VALIDATION_FEATURE =
"http://apache.org/xml/features/validation/dynamic";
protected ContentHandler contentHandler = new DefaultHandler();
protected ErrorHandler errorHandler = new DefaultHandler();;
protected String systemId;
protected XmlPullParser pp;
//private final static boolean DEBUG = false;
/**
*/
public Driver() throws XmlPullParserException {
final XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
factory.setNamespaceAware(true);
pp = factory.newPullParser();
}
public Driver(XmlPullParser pp) throws XmlPullParserException {
this.pp = pp;
}
// -- Attributes interface
public int getLength() { return pp.getAttributeCount(); }
public String getURI(int index) { return pp.getAttributeNamespace(index); }
public String getLocalName(int index) { return pp.getAttributeName(index); }
public String getQName(int index) {
final String prefix = pp.getAttributePrefix(index);
if(prefix != null) {
return prefix+':'+pp.getAttributeName(index);
} else {
return pp.getAttributeName(index);
}
}
public String getType(int index) { return pp.getAttributeType(index); }
public String getValue(int index) { return pp.getAttributeValue(index); }
public int getIndex(String uri, String localName) {
for (int i = 0; i < pp.getAttributeCount(); i++)
{
if(pp.getAttributeNamespace(i).equals(uri)
&& pp.getAttributeName(i).equals(localName))
{
return i;
}
}
return -1;
}
public int getIndex(String qName) {
for (int i = 0; i < pp.getAttributeCount(); i++)
{
if(pp.getAttributeName(i).equals(qName))
{
return i;
}
}
return -1;
}
public String getType(String uri, String localName) {
for (int i = 0; i < pp.getAttributeCount(); i++)
{
if(pp.getAttributeNamespace(i).equals(uri)
&& pp.getAttributeName(i).equals(localName))
{
return pp.getAttributeType(i);
}
}
return null;
}
public String getType(String qName) {
for (int i = 0; i < pp.getAttributeCount(); i++)
{
if(pp.getAttributeName(i).equals(qName))
{
return pp.getAttributeType(i);
}
}
return null;
}
public String getValue(String uri, String localName) {
return pp.getAttributeValue(uri, localName);
}
public String getValue(String qName) {
return pp.getAttributeValue(null, qName);
}
// -- Locator interface
public String getPublicId() { return null; }
public String getSystemId() { return systemId; }
public int getLineNumber() { return pp.getLineNumber(); }
public int getColumnNumber() { return pp.getColumnNumber(); }
// --- XMLReader interface
public boolean getFeature(String name)
throws SAXNotRecognizedException, SAXNotSupportedException
{
if(NAMESPACES_FEATURE.equals(name)) {
return pp.getFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES);
} else if(NAMESPACE_PREFIXES_FEATURE.equals(name)) {
return pp.getFeature(XmlPullParser.FEATURE_REPORT_NAMESPACE_ATTRIBUTES);
} else if(VALIDATION_FEATURE.equals(name)) {
return pp.getFeature(XmlPullParser.FEATURE_VALIDATION);
// } else if(APACHE_SCHEMA_VALIDATION_FEATURE.equals(name)) {
// return false; //TODO
// } else if(APACHE_DYNAMIC_VALIDATION_FEATURE.equals(name)) {
// return false; //TODO
} else {
return pp.getFeature(name);
//throw new SAXNotRecognizedException("unrecognized feature "+name);
}
}
public void setFeature (String name, boolean value)
throws SAXNotRecognizedException, SAXNotSupportedException
{
try {
if(NAMESPACES_FEATURE.equals(name)) {
pp.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, value);
} else if(NAMESPACE_PREFIXES_FEATURE.equals(name)) {
if(pp.getFeature(XmlPullParser.FEATURE_REPORT_NAMESPACE_ATTRIBUTES) != value) {
pp.setFeature(XmlPullParser.FEATURE_REPORT_NAMESPACE_ATTRIBUTES, value);
}
} else if(VALIDATION_FEATURE.equals(name)) {
pp.setFeature(XmlPullParser.FEATURE_VALIDATION, value);
// } else if(APACHE_SCHEMA_VALIDATION_FEATURE.equals(name)) {
// // can ignore as validation must be false ...
// // if(true == value) {
// // throw new SAXNotSupportedException("schema validation is not supported");
// // }
// } else if(APACHE_DYNAMIC_VALIDATION_FEATURE.equals(name)) {
// if(true == value) {
// throw new SAXNotSupportedException("dynamic validation is not supported");
// }
} else {
pp.setFeature(name, value);
//throw new SAXNotRecognizedException("unrecognized feature "+name);
}
} catch(XmlPullParserException ex) {
// throw new SAXNotSupportedException("problem with setting feature "+name+": "+ex);
}
}
public Object getProperty (String name)
throws SAXNotRecognizedException, SAXNotSupportedException
{
if(DECLARATION_HANDLER_PROPERTY.equals(name)) {
return null;
} else if(LEXICAL_HANDLER_PROPERTY.equals(name)) {
return null;
} else {
return pp.getProperty(name);
//throw new SAXNotRecognizedException("not recognized get property "+name);
}
}
public void setProperty (String name, Object value)
throws SAXNotRecognizedException, SAXNotSupportedException
{
//
if(DECLARATION_HANDLER_PROPERTY.equals(name)) {
throw new SAXNotSupportedException("not supported setting property "+name);//+" to "+value);
} else if(LEXICAL_HANDLER_PROPERTY.equals(name)) {
throw new SAXNotSupportedException("not supported setting property "+name);//+" to "+value);
} else {
try {
pp.setProperty(name, value);
} catch(XmlPullParserException ex) {
throw new SAXNotSupportedException("not supported set property "+name+": "+ ex);
}
//throw new SAXNotRecognizedException("not recognized set property "+name);
}
}
public void setEntityResolver (EntityResolver resolver) {}
public EntityResolver getEntityResolver () { return null; }
public void setDTDHandler (DTDHandler handler) {}
public DTDHandler getDTDHandler () { return null; }
public void setContentHandler (ContentHandler handler)
{
this.contentHandler = handler;
}
public ContentHandler getContentHandler() { return contentHandler; }
public void setErrorHandler(ErrorHandler handler) {
this.errorHandler = handler;
}
public ErrorHandler getErrorHandler() { return errorHandler; }
public void parse(InputSource source) throws SAXException, IOException
{
systemId = source.getSystemId();
contentHandler.setDocumentLocator(this);
final Reader reader = source.getCharacterStream();
try {
if (reader == null) {
InputStream stream = source.getByteStream();
final String encoding = source.getEncoding();
if (stream == null) {
systemId = source.getSystemId();
if(systemId == null) {
SAXParseException saxException = new SAXParseException(
"null source systemId" , this);
errorHandler.fatalError(saxException);
return;
}
// NOTE: replace with Connection to run in J2ME environment
try {
final URL url = new URL(systemId);
stream = url.openStream();
} catch (MalformedURLException nue) {
try {
stream = new FileInputStream(systemId);
} catch (FileNotFoundException fnfe) {
final SAXParseException saxException = new SAXParseException(
"could not open file with systemId "+systemId, this, fnfe);
errorHandler.fatalError(saxException);
return;
}
}
}
pp.setInput(stream, encoding);
} else {
pp.setInput(reader);
}
} catch (XmlPullParserException ex) {
final SAXParseException saxException = new SAXParseException(
"parsing initialization error: "+ex, this, ex);
//if(DEBUG) ex.printStackTrace();
errorHandler.fatalError(saxException);
return;
}
// start parsing - move to first start tag
try {
contentHandler.startDocument();
// get first event
pp.next();
// it should be start tag...
if(pp.getEventType() != XmlPullParser.START_TAG) {
final SAXParseException saxException = new SAXParseException(
"expected start tag not"+pp.getPositionDescription(), this);
//throw saxException;
errorHandler.fatalError(saxException);
return;
}
} catch (XmlPullParserException ex) {
final SAXParseException saxException = new SAXParseException(
"parsing initialization error: "+ex, this, ex);
//ex.printStackTrace();
errorHandler.fatalError(saxException);
return;
}
// now real parsing can start!
parseSubTree(pp);
// and finished ...
contentHandler.endDocument();
}
public void parse(String systemId) throws SAXException, IOException {
parse(new InputSource(systemId));
}
public void parseSubTree(XmlPullParser pp) throws SAXException, IOException {
this.pp = pp;
final boolean namespaceAware = pp.getFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES);
try {
if(pp.getEventType() != XmlPullParser.START_TAG) {
throw new SAXException(
"start tag must be read before skiping subtree"+pp.getPositionDescription());
}
final int[] holderForStartAndLength = new int[2];
final StringBuilder rawName = new StringBuilder(16);
String prefix = null;
String name = null;
int level = pp.getDepth() - 1;
int type = XmlPullParser.START_TAG;
LOOP:
do {
switch(type) {
case XmlPullParser.START_TAG:
if(namespaceAware) {
final int depth = pp.getDepth() - 1;
final int countPrev =
(level > depth) ? pp.getNamespaceCount(depth) : 0;
//int countPrev = pp.getNamespaceCount(pp.getDepth() - 1);
final int count = pp.getNamespaceCount(depth + 1);
for (int i = countPrev; i < count; i++)
{
contentHandler.startPrefixMapping(
pp.getNamespacePrefix(i),
pp.getNamespaceUri(i)
);
}
name = pp.getName();
prefix = pp.getPrefix();
if(prefix != null) {
rawName.setLength(0);
rawName.append(prefix);
rawName.append(':');
rawName.append(name);
}
startElement(pp.getNamespace(),
name,
// TODO Fixed this. Was "not equals".
prefix == null ? name : rawName.toString());
} else {
startElement(pp.getNamespace(),
pp.getName(),
pp.getName());
}
//++level;
break;
case XmlPullParser.TEXT:
final char[] chars = pp.getTextCharacters(holderForStartAndLength);
contentHandler.characters(chars,
holderForStartAndLength[0], //start
holderForStartAndLength[1] //len
);
break;
case XmlPullParser.END_TAG:
//--level;
if(namespaceAware) {
name = pp.getName();
prefix = pp.getPrefix();
if(prefix != null) {
rawName.setLength(0);
rawName.append(prefix);
rawName.append(':');
rawName.append(name);
}
contentHandler.endElement(pp.getNamespace(),
name,
prefix != null ? name : rawName.toString()
);
// when entering show prefixes for all levels!!!!
final int depth = pp.getDepth();
final int countPrev =
(level > depth) ? pp.getNamespaceCount(pp.getDepth()) : 0;
int count = pp.getNamespaceCount(pp.getDepth() - 1);
// undeclare them in reverse order
for (int i = count - 1; i >= countPrev; i--)
{
contentHandler.endPrefixMapping(
pp.getNamespacePrefix(i)
);
}
} else {
contentHandler.endElement(pp.getNamespace(),
pp.getName(),
pp.getName()
);
}
break;
case XmlPullParser.END_DOCUMENT:
break LOOP;
}
type = pp.next();
} while(pp.getDepth() > level);
} catch (XmlPullParserException ex) {
final SAXParseException saxException = new SAXParseException("parsing error: "+ex, this, ex);
ex.printStackTrace();
errorHandler.fatalError(saxException);
}
}
/**
* Calls {@link ContentHandler#startElement(String, String, String, Attributes) startElement}
* on the ContentHandler
with this
driver object as the
* {@link Attributes} implementation. In default implementation
* {@link Attributes} object is valid only during this method call and may not
* be stored. Sub-classes can overwrite this method to cache attributes.
*/
protected void startElement(String namespace, String localName, String qName) throws SAXException {
contentHandler.startElement(namespace, localName, qName, this);
}
}