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      */
Driver()79     public Driver() throws XmlPullParserException {
80         final XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
81         factory.setNamespaceAware(true);
82         pp = factory.newPullParser();
83     }
84 
Driver(XmlPullParser pp)85     public Driver(XmlPullParser pp) throws XmlPullParserException {
86         this.pp = pp;
87     }
88 
89     // -- Attributes interface
90 
getLength()91     public int getLength() { return pp.getAttributeCount(); }
getURI(int index)92     public String getURI(int index) { return pp.getAttributeNamespace(index); }
getLocalName(int index)93     public String getLocalName(int index) { return pp.getAttributeName(index); }
getQName(int 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     }
getType(int index)102     public String getType(int index) { return pp.getAttributeType(index); }
getValue(int index)103     public String getValue(int index) { return pp.getAttributeValue(index); }
104 
getIndex(String uri, String localName)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 
getIndex(String qName)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 
getType(String uri, String localName)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     }
getType(String qName)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     }
getValue(String uri, String localName)153     public String getValue(String uri, String localName) {
154         return pp.getAttributeValue(uri, localName);
155     }
getValue(String qName)156     public String getValue(String qName) {
157         return pp.getAttributeValue(null, qName);
158     }
159 
160     // -- Locator interface
161 
getPublicId()162     public String getPublicId() { return null; }
getSystemId()163     public String getSystemId() { return systemId; }
getLineNumber()164     public int getLineNumber() { return pp.getLineNumber(); }
getColumnNumber()165     public int getColumnNumber() { return pp.getColumnNumber(); }
166 
167     // --- XMLReader interface
168 
getFeature(String name)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 
setFeature(String name, boolean value)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 
getProperty(String name)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 
setProperty(String name, Object value)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 
setEntityResolver(EntityResolver resolver)249     public void setEntityResolver (EntityResolver resolver) {}
250 
getEntityResolver()251     public EntityResolver getEntityResolver () { return null; }
252 
setDTDHandler(DTDHandler handler)253     public void setDTDHandler (DTDHandler handler) {}
254 
getDTDHandler()255     public DTDHandler getDTDHandler () { return null; }
256 
setContentHandler(ContentHandler handler)257     public void setContentHandler (ContentHandler handler)
258     {
259         this.contentHandler = handler;
260     }
261 
getContentHandler()262     public ContentHandler getContentHandler() { return contentHandler; }
263 
setErrorHandler(ErrorHandler handler)264     public void setErrorHandler(ErrorHandler handler) {
265         this.errorHandler = handler;
266     }
267 
getErrorHandler()268     public ErrorHandler getErrorHandler() { return errorHandler; }
269 
parse(InputSource source)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 
parse(String systemId)347     public void parse(String systemId) throws SAXException, IOException {
348         parse(new InputSource(systemId));
349     }
350 
351 
parseSubTree(XmlPullParser pp)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      */
startElement(String namespace, String localName, String qName)465     protected void startElement(String namespace, String localName, String qName) throws SAXException {
466         contentHandler.startElement(namespace, localName, qName, this);
467     }
468 
469 }
470