The Android Open Source Project | adc854b | 2009-03-03 19:28:47 -0800 | [diff] [blame] | 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 comverts 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]; |
Elliott Hughes | 8216dc1 | 2011-02-22 17:22:47 -0800 | [diff] [blame] | 361 | final StringBuilder rawName = new StringBuilder(16); |
The Android Open Source Project | adc854b | 2009-03-03 19:28:47 -0800 | [diff] [blame] | 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 | } |