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