Home | History | Annotate | Download | only in sax2
      1 /* -*-             c-basic-offset: 4; indent-tabs-mode: nil; -*-  //------100-columns-wide------>|*/
      2 // for license please see accompanying LICENSE.txt file (available also at http://www.xmlpull.org/)
      3 
      4 package org.xmlpull.v1.sax2;
      5 
      6 import java.io.InputStream;
      7 import java.io.IOException;
      8 import java.io.Reader;
      9 
     10 // not J2ME classes -- remove if you want to run in MIDP devices
     11 import java.net.URL;
     12 import java.net.MalformedURLException;
     13 
     14 
     15 // not J2ME classes
     16 import java.io.FileInputStream;
     17 import java.io.FileNotFoundException;
     18 
     19 import org.xml.sax.Attributes;
     20 import org.xml.sax.DTDHandler;
     21 import org.xml.sax.ContentHandler;
     22 import org.xml.sax.EntityResolver;
     23 import org.xml.sax.ErrorHandler;
     24 import org.xml.sax.InputSource;
     25 import org.xml.sax.Locator;
     26 import org.xml.sax.SAXException;
     27 import org.xml.sax.SAXParseException;
     28 import org.xml.sax.SAXNotRecognizedException;
     29 import org.xml.sax.SAXNotSupportedException;
     30 import org.xml.sax.XMLReader;
     31 import org.xml.sax.helpers.DefaultHandler;
     32 
     33 import org.xmlpull.v1.XmlPullParser;
     34 import org.xmlpull.v1.XmlPullParserException;
     35 import org.xmlpull.v1.XmlPullParserFactory;
     36 
     37 /**
     38  * SAX2 Driver that pulls events from XmlPullParser
     39  * and converts them into SAX2 callbacks.
     40  *
     41  * @author <a href="http://www.extreme.indiana.edu/~aslom/">Aleksander Slominski</a>
     42  */
     43 
     44 public class Driver implements Locator, XMLReader, Attributes
     45 {
     46 
     47     protected static final String DECLARATION_HANDLER_PROPERTY =
     48         "http://xml.org/sax/properties/declaration-handler";
     49 
     50     protected static final String LEXICAL_HANDLER_PROPERTY =
     51         "http://xml.org/sax/properties/lexical-handler";
     52 
     53     protected static final String NAMESPACES_FEATURE =
     54         "http://xml.org/sax/features/namespaces";
     55 
     56     protected static final String NAMESPACE_PREFIXES_FEATURE =
     57         "http://xml.org/sax/features/namespace-prefixes";
     58 
     59     protected static final String VALIDATION_FEATURE =
     60         "http://xml.org/sax/features/validation";
     61 
     62     protected static final String APACHE_SCHEMA_VALIDATION_FEATURE =
     63         "http://apache.org/xml/features/validation/schema";
     64 
     65     protected static final String APACHE_DYNAMIC_VALIDATION_FEATURE =
     66         "http://apache.org/xml/features/validation/dynamic";
     67 
     68     protected ContentHandler contentHandler = new DefaultHandler();
     69     protected ErrorHandler errorHandler = new DefaultHandler();;
     70 
     71     protected String systemId;
     72 
     73     protected XmlPullParser pp;
     74 
     75     //private final static boolean DEBUG = false;
     76 
     77     /**
     78      */
     79     public Driver() throws XmlPullParserException {
     80         final XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
     81         factory.setNamespaceAware(true);
     82         pp = factory.newPullParser();
     83     }
     84 
     85     public Driver(XmlPullParser pp) throws XmlPullParserException {
     86         this.pp = pp;
     87     }
     88 
     89     // -- Attributes interface
     90 
     91     public int getLength() { return pp.getAttributeCount(); }
     92     public String getURI(int index) { return pp.getAttributeNamespace(index); }
     93     public String getLocalName(int index) { return pp.getAttributeName(index); }
     94     public String getQName(int index) {
     95         final String prefix = pp.getAttributePrefix(index);
     96         if(prefix != null) {
     97             return prefix+':'+pp.getAttributeName(index);
     98         } else {
     99             return pp.getAttributeName(index);
    100         }
    101     }
    102     public String getType(int index) { return pp.getAttributeType(index); }
    103     public String getValue(int index) { return pp.getAttributeValue(index); }
    104 
    105     public int getIndex(String uri, String localName) {
    106         for (int i = 0; i < pp.getAttributeCount(); i++)
    107         {
    108             if(pp.getAttributeNamespace(i).equals(uri)
    109                && pp.getAttributeName(i).equals(localName))
    110             {
    111                 return i;
    112             }
    113 
    114         }
    115         return -1;
    116     }
    117 
    118     public int getIndex(String qName) {
    119         for (int i = 0; i < pp.getAttributeCount(); i++)
    120         {
    121             if(pp.getAttributeName(i).equals(qName))
    122             {
    123                 return i;
    124             }
    125 
    126         }
    127         return -1;
    128     }
    129 
    130     public String getType(String uri, String localName) {
    131         for (int i = 0; i < pp.getAttributeCount(); i++)
    132         {
    133             if(pp.getAttributeNamespace(i).equals(uri)
    134                && pp.getAttributeName(i).equals(localName))
    135             {
    136                 return pp.getAttributeType(i);
    137             }
    138 
    139         }
    140         return null;
    141     }
    142     public String getType(String qName) {
    143         for (int i = 0; i < pp.getAttributeCount(); i++)
    144         {
    145             if(pp.getAttributeName(i).equals(qName))
    146             {
    147                 return pp.getAttributeType(i);
    148             }
    149 
    150         }
    151         return null;
    152     }
    153     public String getValue(String uri, String localName) {
    154         return pp.getAttributeValue(uri, localName);
    155     }
    156     public String getValue(String qName) {
    157         return pp.getAttributeValue(null, qName);
    158     }
    159 
    160     // -- Locator interface
    161 
    162     public String getPublicId() { return null; }
    163     public String getSystemId() { return systemId; }
    164     public int getLineNumber() { return pp.getLineNumber(); }
    165     public int getColumnNumber() { return pp.getColumnNumber(); }
    166 
    167     // --- XMLReader interface
    168 
    169     public boolean getFeature(String name)
    170         throws SAXNotRecognizedException, SAXNotSupportedException
    171     {
    172         if(NAMESPACES_FEATURE.equals(name)) {
    173             return pp.getFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES);
    174         } else if(NAMESPACE_PREFIXES_FEATURE.equals(name)) {
    175             return pp.getFeature(XmlPullParser.FEATURE_REPORT_NAMESPACE_ATTRIBUTES);
    176         } else if(VALIDATION_FEATURE.equals(name)) {
    177             return pp.getFeature(XmlPullParser.FEATURE_VALIDATION);
    178             //        } else if(APACHE_SCHEMA_VALIDATION_FEATURE.equals(name)) {
    179             //            return false;  //TODO
    180             //        } else if(APACHE_DYNAMIC_VALIDATION_FEATURE.equals(name)) {
    181             //            return false; //TODO
    182         } else {
    183             return pp.getFeature(name);
    184             //throw new SAXNotRecognizedException("unrecognized feature "+name);
    185         }
    186     }
    187 
    188     public void setFeature (String name, boolean value)
    189         throws SAXNotRecognizedException, SAXNotSupportedException
    190     {
    191         try {
    192             if(NAMESPACES_FEATURE.equals(name)) {
    193                 pp.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, value);
    194             } else if(NAMESPACE_PREFIXES_FEATURE.equals(name)) {
    195                 if(pp.getFeature(XmlPullParser.FEATURE_REPORT_NAMESPACE_ATTRIBUTES) != value) {
    196                     pp.setFeature(XmlPullParser.FEATURE_REPORT_NAMESPACE_ATTRIBUTES, value);
    197                 }
    198             } else if(VALIDATION_FEATURE.equals(name)) {
    199                 pp.setFeature(XmlPullParser.FEATURE_VALIDATION, value);
    200                 //          } else if(APACHE_SCHEMA_VALIDATION_FEATURE.equals(name)) {
    201                 //              // can ignore as validation must be false ...
    202                 //              //              if(true == value) {
    203                 //              //                  throw new SAXNotSupportedException("schema validation is not supported");
    204                 //              //              }
    205                 //          } else if(APACHE_DYNAMIC_VALIDATION_FEATURE.equals(name)) {
    206                 //              if(true == value) {
    207                 //                  throw new SAXNotSupportedException("dynamic validation is not supported");
    208                 //              }
    209             } else {
    210                 pp.setFeature(name, value);
    211                 //throw new SAXNotRecognizedException("unrecognized feature "+name);
    212             }
    213         } catch(XmlPullParserException ex) {
    214            // throw new SAXNotSupportedException("problem with setting feature "+name+": "+ex);
    215         }
    216     }
    217 
    218     public Object getProperty (String name)
    219         throws SAXNotRecognizedException, SAXNotSupportedException
    220     {
    221         if(DECLARATION_HANDLER_PROPERTY.equals(name)) {
    222             return null;
    223         } else if(LEXICAL_HANDLER_PROPERTY.equals(name)) {
    224             return null;
    225         } else {
    226             return pp.getProperty(name);
    227             //throw new SAXNotRecognizedException("not recognized get property "+name);
    228         }
    229     }
    230 
    231     public void setProperty (String name, Object value)
    232         throws SAXNotRecognizedException, SAXNotSupportedException
    233     {
    234         //
    235         if(DECLARATION_HANDLER_PROPERTY.equals(name)) {
    236             throw new SAXNotSupportedException("not supported setting property "+name);//+" to "+value);
    237         } else if(LEXICAL_HANDLER_PROPERTY.equals(name)) {
    238             throw new SAXNotSupportedException("not supported setting property "+name);//+" to "+value);
    239         } else {
    240             try {
    241                 pp.setProperty(name, value);
    242             } catch(XmlPullParserException ex) {
    243                 throw new SAXNotSupportedException("not supported set property "+name+": "+ ex);
    244             }
    245             //throw new SAXNotRecognizedException("not recognized set property "+name);
    246         }
    247     }
    248 
    249     public void setEntityResolver (EntityResolver resolver) {}
    250 
    251     public EntityResolver getEntityResolver () { return null; }
    252 
    253     public void setDTDHandler (DTDHandler handler) {}
    254 
    255     public DTDHandler getDTDHandler () { return null; }
    256 
    257     public void setContentHandler (ContentHandler handler)
    258     {
    259         this.contentHandler = handler;
    260     }
    261 
    262     public ContentHandler getContentHandler() { return contentHandler; }
    263 
    264     public void setErrorHandler(ErrorHandler handler) {
    265         this.errorHandler = handler;
    266     }
    267 
    268     public ErrorHandler getErrorHandler() { return errorHandler; }
    269 
    270     public void parse(InputSource source) throws SAXException, IOException
    271     {
    272 
    273         systemId = source.getSystemId();
    274         contentHandler.setDocumentLocator(this);
    275 
    276         final Reader reader = source.getCharacterStream();
    277         try {
    278             if (reader == null) {
    279                 InputStream stream = source.getByteStream();
    280                 final String encoding = source.getEncoding();
    281 
    282                 if (stream == null) {
    283                     systemId = source.getSystemId();
    284                     if(systemId == null) {
    285                         SAXParseException saxException = new SAXParseException(
    286                             "null source systemId" , this);
    287                         errorHandler.fatalError(saxException);
    288                         return;
    289                     }
    290                     // NOTE: replace with Connection to run in J2ME environment
    291                     try {
    292                         final URL url = new URL(systemId);
    293                         stream = url.openStream();
    294                     } catch (MalformedURLException nue) {
    295                         try {
    296                             stream = new FileInputStream(systemId);
    297                         } catch (FileNotFoundException fnfe) {
    298                             final SAXParseException saxException = new SAXParseException(
    299                                 "could not open file with systemId "+systemId, this, fnfe);
    300                             errorHandler.fatalError(saxException);
    301                             return;
    302                         }
    303                     }
    304                 }
    305                 pp.setInput(stream, encoding);
    306             } else {
    307                 pp.setInput(reader);
    308             }
    309         } catch (XmlPullParserException ex)  {
    310             final SAXParseException saxException = new SAXParseException(
    311                 "parsing initialization error: "+ex, this, ex);
    312             //if(DEBUG) ex.printStackTrace();
    313             errorHandler.fatalError(saxException);
    314             return;
    315         }
    316 
    317         // start parsing - move to first start tag
    318         try {
    319             contentHandler.startDocument();
    320             // get first event
    321             pp.next();
    322             // it should be start tag...
    323             if(pp.getEventType() != XmlPullParser.START_TAG) {
    324                 final SAXParseException saxException = new SAXParseException(
    325                     "expected start tag not"+pp.getPositionDescription(), this);
    326                 //throw saxException;
    327                 errorHandler.fatalError(saxException);
    328                 return;
    329             }
    330         } catch (XmlPullParserException ex)  {
    331             final SAXParseException saxException = new SAXParseException(
    332                 "parsing initialization error: "+ex, this, ex);
    333             //ex.printStackTrace();
    334             errorHandler.fatalError(saxException);
    335             return;
    336         }
    337 
    338         // now real parsing can start!
    339 
    340         parseSubTree(pp);
    341 
    342         // and finished ...
    343 
    344         contentHandler.endDocument();
    345     }
    346 
    347     public void parse(String systemId) throws SAXException, IOException {
    348         parse(new InputSource(systemId));
    349     }
    350 
    351 
    352     public void parseSubTree(XmlPullParser pp) throws SAXException, IOException {
    353         this.pp = pp;
    354         final boolean namespaceAware = pp.getFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES);
    355         try {
    356             if(pp.getEventType() != XmlPullParser.START_TAG) {
    357                 throw new SAXException(
    358                     "start tag must be read before skiping subtree"+pp.getPositionDescription());
    359             }
    360             final int[] holderForStartAndLength = new int[2];
    361             final StringBuilder rawName = new StringBuilder(16);
    362             String prefix = null;
    363             String name = null;
    364             int level = pp.getDepth() - 1;
    365             int type = XmlPullParser.START_TAG;
    366 
    367             LOOP:
    368             do {
    369                 switch(type) {
    370                     case XmlPullParser.START_TAG:
    371                         if(namespaceAware) {
    372                             final int depth = pp.getDepth() - 1;
    373                             final int countPrev =
    374                                 (level > depth) ? pp.getNamespaceCount(depth) : 0;
    375                             //int countPrev = pp.getNamespaceCount(pp.getDepth() - 1);
    376                             final int count = pp.getNamespaceCount(depth + 1);
    377                             for (int i = countPrev; i < count; i++)
    378                             {
    379                                 contentHandler.startPrefixMapping(
    380                                     pp.getNamespacePrefix(i),
    381                                     pp.getNamespaceUri(i)
    382                                 );
    383                             }
    384                             name = pp.getName();
    385                             prefix = pp.getPrefix();
    386                             if(prefix != null) {
    387                                 rawName.setLength(0);
    388                                 rawName.append(prefix);
    389                                 rawName.append(':');
    390                                 rawName.append(name);
    391                             }
    392                             startElement(pp.getNamespace(),
    393                                          name,
    394                                          // TODO Fixed this. Was "not equals".
    395                                          prefix == null ? name : rawName.toString());
    396                         } else {
    397                             startElement(pp.getNamespace(),
    398                                          pp.getName(),
    399                                          pp.getName());
    400                         }
    401                         //++level;
    402 
    403                         break;
    404                     case XmlPullParser.TEXT:
    405                         final char[] chars = pp.getTextCharacters(holderForStartAndLength);
    406                         contentHandler.characters(chars,
    407                                                   holderForStartAndLength[0], //start
    408                                                   holderForStartAndLength[1] //len
    409                                                  );
    410                         break;
    411                     case XmlPullParser.END_TAG:
    412                         //--level;
    413                         if(namespaceAware) {
    414                             name = pp.getName();
    415                             prefix = pp.getPrefix();
    416                             if(prefix != null) {
    417                                 rawName.setLength(0);
    418                                 rawName.append(prefix);
    419                                 rawName.append(':');
    420                                 rawName.append(name);
    421                             }
    422                             contentHandler.endElement(pp.getNamespace(),
    423                                                       name,
    424                                                       prefix != null ? name : rawName.toString()
    425                                                      );
    426                             // when entering show prefixes for all levels!!!!
    427                             final int depth = pp.getDepth();
    428                             final int countPrev =
    429                                 (level > depth) ? pp.getNamespaceCount(pp.getDepth()) : 0;
    430                             int count = pp.getNamespaceCount(pp.getDepth() - 1);
    431                             // undeclare them in reverse order
    432                             for (int i = count - 1; i >= countPrev; i--)
    433                             {
    434                                 contentHandler.endPrefixMapping(
    435                                     pp.getNamespacePrefix(i)
    436                                 );
    437                             }
    438                         } else {
    439                             contentHandler.endElement(pp.getNamespace(),
    440                                                       pp.getName(),
    441                                                       pp.getName()
    442                                                      );
    443 
    444                         }
    445                         break;
    446                     case XmlPullParser.END_DOCUMENT:
    447                         break LOOP;
    448                 }
    449                 type = pp.next();
    450             } while(pp.getDepth() > level);
    451         } catch (XmlPullParserException ex)  {
    452             final SAXParseException saxException = new SAXParseException("parsing error: "+ex, this, ex);
    453             ex.printStackTrace();
    454             errorHandler.fatalError(saxException);
    455         }
    456     }
    457 
    458     /**
    459      * Calls {@link ContentHandler#startElement(String, String, String, Attributes) startElement}
    460      * on the <code>ContentHandler</code> with <code>this</code> driver object as the
    461      * {@link Attributes} implementation. In default implementation
    462      * {@link Attributes} object is valid only during this method call and may not
    463      * be stored. Sub-classes can overwrite this method to cache attributes.
    464      */
    465     protected void startElement(String namespace, String localName, String qName) throws SAXException {
    466         contentHandler.startElement(namespace, localName, qName, this);
    467     }
    468 
    469 }
    470