001// SAXDriver.java: The SAX driver for AElfred. 002// NO WARRANTY! See README, and copyright below. 003// $Id$ 004package com.microstar.xml.driver; 005 006import java.io.IOException; 007import java.util.Enumeration; 008import java.util.Hashtable; 009import java.util.Locale; 010import java.util.Stack; 011import java.util.Vector; 012 013import org.xml.sax.Attributes; 014import org.xml.sax.ContentHandler; 015import org.xml.sax.DTDHandler; 016import org.xml.sax.EntityResolver; 017import org.xml.sax.ErrorHandler; 018import org.xml.sax.InputSource; 019import org.xml.sax.Locator; 020import org.xml.sax.SAXException; 021import org.xml.sax.SAXParseException; 022import org.xml.sax.XMLReader; 023import org.xml.sax.helpers.DefaultHandler; 024 025import com.microstar.xml.XmlHandler; 026import com.microstar.xml.XmlParser; 027 028/** 029 * A SAX driver for Microstar's Ælfred XML parser. 030 * 031 * <p>This driver acts as a front-end for Ælfred, and 032 * translates Ælfred's events into SAX events. It 033 * implements the SAX parser interface, and you can use it without 034 * directly calling Ælfred at all:</p> 035 * 036 * <pre> 037 * org.xml.sax.Parser parser = new com.microstar.xml.SAXDriver(); 038 * </pre> 039 * 040 * <p>When you are using SAX, you do not need to use the 041 * <code>XmlParser</code> or <code>XmlHandler</code> classes at 042 * all: this class is your entry point.</p> 043 * 044 * <p>This driver is based on the 1.0gamma version of SAX, 045 * available from http://www.megginson.com/SAX/</p> 046 * 047 * @author Copyright (c) 1998 by Microstar Software Ltd. 048 * @author written by David Megginson <dmeggins@microstar.com> 049 * @version 1.1 050 * @since Ptolemy II 0.2 051 * @see XmlParser 052 */ 053public class SAXDriver implements XmlHandler, Locator, Attributes, XMLReader //implements XmlHandler, Locator, AttributeList, Parser 054{ 055 // 056 // Variables. 057 // 058 //private HandlerBase base = new HandlerBase(); 059 private DefaultHandler base = new DefaultHandler(); 060 061 private XmlParser parser; 062 063 private boolean seenDTDEvents = false; 064 065 // Encapsulate the default behaviour 066 // from HandlerBase 067 private EntityResolver entityResolver = base; 068 069 private DTDHandler dtdHandler = base; 070 071 private ContentHandler documentHandler = base; 072 073 private ErrorHandler errorHandler = base; 074 075 private String elementName = null; 076 077 private Stack entityStack = new Stack(); 078 079 private Vector attributeNames = new Vector(); 080 081 private Vector attributeValues = new Vector(); 082 083 private Hashtable properties = new Hashtable(); 084 085 private Hashtable features = new Hashtable(); 086 087 // 088 // Constructor. 089 // 090 public SAXDriver() { 091 } 092 093 // 094 // Implementation of org.xml.sax.Parser. 095 // 096 097 /** 098 * Set the locale. 099 * @param locale The Locale 100 * @exception SAXException Always thrown in this base class. 101 */ 102 public void setLocale(Locale locale) throws SAXException { 103 throw new SAXException( 104 "AElfred driver does not yet have locale support."); 105 } 106 107 /** 108 * Set the entity resolver for this parser. 109 * @param resolver The object to receive resolve entity events. 110 */ 111 @Override 112 public void setEntityResolver(EntityResolver resolver) { 113 this.entityResolver = resolver; 114 } 115 116 /** 117 * Set the DTD handler for this parser. 118 * @param handler The object to receive DTD events. 119 */ 120 @Override 121 public void setDTDHandler(DTDHandler handler) { 122 this.dtdHandler = handler; 123 } 124 125 /** 126 * Set the document handler for this parser. 127 * @param handler The object to receive document events. 128 */ 129 public void setDocumentHandler(ContentHandler handler) { 130 this.documentHandler = handler; 131 } 132 133 /** 134 * Set the error handler for this parser. 135 * @param handler The object to receive error events. 136 */ 137 @Override 138 public void setErrorHandler(ErrorHandler handler) { 139 this.errorHandler = handler; 140 } 141 142 /** 143 * Parse a document. 144 * <p>If you want anything useful to happen, you should set 145 * at least one type of handler. 146 * @param source The XML input source. 147 * @see #setEntityResolver 148 * @see #setDTDHandler 149 * @see #setDocumentHandler 150 * @see #setErrorHandler 151 * @exception SAXException The handlers may throw any exception. 152 */ 153 @Override 154 public void parse(InputSource source) throws SAXException { 155 parser = new XmlParser(); 156 parser.setHandler(this); 157 158 try { 159 if (source.getCharacterStream() != null) { 160 parser.parse(source.getSystemId(), source.getPublicId(), 161 source.getCharacterStream()); 162 } else if (source.getByteStream() != null) { 163 parser.parse(source.getSystemId(), source.getPublicId(), 164 source.getByteStream(), source.getEncoding()); 165 } else { 166 parser.parse(source.getSystemId(), source.getPublicId(), 167 source.getEncoding()); 168 } 169 } catch (SAXException e) { 170 throw e; 171 } catch (Exception e) { 172 throw new SAXException(e); 173 } finally { 174 try { 175 closeStreams(source); 176 } catch (Exception e) { 177 throw new SAXException(e); 178 } 179 180 ; 181 } 182 183 try { 184 closeStreams(source); 185 } catch (IOException e) { 186 throw new SAXException(e); 187 } 188 } 189 190 /** 191 * Parse an XML document from a system identifier (URI). 192 */ 193 @Override 194 public void parse(String systemId) throws SAXException { 195 parse(new InputSource(systemId)); 196 } 197 198 /** 199 * Close any streams provided. 200 */ 201 private void closeStreams(InputSource source) throws IOException { 202 if (source.getCharacterStream() != null) { 203 source.getCharacterStream().close(); 204 } 205 206 if (source.getByteStream() != null) { 207 source.getByteStream().close(); 208 } 209 } 210 211 // 212 // Implementation of com.microstar.xml.XmlHandler. 213 // This is where the driver receives AElfred events and translates 214 // them into SAX events. 215 // 216 217 /** 218 * Implement com.microstar.xml.XmlHandler#startDocument. 219 * <p>Translate to the SAX interface. 220 * <p>Users should never invoke this method directly. 221 * @see com.microstar.xml.XmlHandler#startDocument 222 * @exception SAXException May throw any exception. 223 */ 224 @Override 225 public void startDocument() throws SAXException { 226 documentHandler.setDocumentLocator(this); 227 documentHandler.startDocument(); 228 attributeNames.removeAllElements(); 229 attributeValues.removeAllElements(); 230 } 231 232 /** 233 * Implement com.microstar.xml.XmlHandler#endDocument. 234 * <p>Translate to the SAX interface. 235 * <p>Users should never invoke this method directly. 236 * @see com.microstar.xml.XmlHandler#endDocument 237 * @exception SAXException May throw any exception. 238 */ 239 @Override 240 public void endDocument() throws SAXException { 241 documentHandler.endDocument(); 242 } 243 244 /** 245 * Implement com.microstar.xml.XmlHandler.resolveSystemId 246 * <p>Translate to the SAX interface. 247 * <p>Users should never invoke this method directly. 248 * @see com.microstar.xml.XmlHandler#resolveEntity 249 * @exception SAXException May throw any exception. 250 */ 251 @Override 252 public Object resolveEntity(String publicId, String systemId) 253 throws SAXException, IOException { 254 InputSource source = entityResolver.resolveEntity(publicId, systemId); 255 256 if (source == null) { 257 return null; 258 } else if (source.getCharacterStream() != null) { 259 return source.getCharacterStream(); 260 } else if (source.getByteStream() != null) { 261 return source.getByteStream(); 262 } else { 263 return source.getSystemId(); 264 } 265 266 // FIXME: no way to tell AElfred 267 // about a new public id. 268 } 269 270 /** 271 * Implement com.microstar.xml.XmlHandler#startExternalEntity. 272 * <p>Translate to the SAX interface. 273 * <p>Users should never invoke this method directly. 274 * @see com.microstar.xml.XmlHandler#startExternalEntity 275 * @exception SAXException May throw any exception. 276 */ 277 @Override 278 public void startExternalEntity(String systemId) throws SAXException { 279 entityStack.push(systemId); 280 } 281 282 /** 283 * Implement com.microstar.xml.XmlHandler#endExternalEntity. 284 * <p>Translate to the SAX interface. 285 * <p>Users should never invoke this method directly. 286 * @see com.microstar.xml.XmlHandler#endExternalEntity 287 * @exception SAXException May throw any exception. 288 */ 289 @Override 290 public void endExternalEntity(String systemId) throws SAXException { 291 entityStack.pop(); 292 } 293 294 /** 295 * Implement com.microstar.xml.XmlHandler#doctypeDecl. 296 * <p>Translate to the SAX interface. 297 * <p>Users should never invoke this method directly. 298 * @see com.microstar.xml.XmlHandler#doctypeDecl 299 * @exception SAXException May throw any exception. 300 */ 301 @Override 302 public void doctypeDecl(String name, String publicId, String systemId) 303 throws SAXException { 304 // no op 305 } 306 307 /** 308 * Implement com.microstar.xml.XmlHandler#attribute. 309 * <p>Translate to the SAX interface. 310 * <p>Users should never invoke this method directly. 311 * @see com.microstar.xml.XmlHandler#attribute 312 * @exception SAXException May throw any exception. 313 */ 314 @Override 315 public void attribute(String aname, String value, boolean isSpecified) 316 throws SAXException { 317 if (value != null) { 318 attributeNames.addElement(aname); 319 attributeValues.addElement(value); 320 } 321 } 322 323 /** 324 * Implement com.microstar.xml.XmlHandler#startElement. 325 * <p>Translate to the SAX interface. 326 * <p>Users should never invoke this method directly. 327 * @see com.microstar.xml.XmlHandler#startElement 328 * @exception SAXException May throw any exception. 329 */ 330 @Override 331 public void startElement(String elname) throws SAXException { 332 // We should deliver all DTD events 333 // before the first startElement event. 334 if (!seenDTDEvents) { 335 deliverDTDEvents(); 336 seenDTDEvents = true; 337 } 338 339 elementName = elname; 340 341 //documentHandler.startElement(elname, this); 342 documentHandler.startElement("", elname, "", this); 343 elementName = null; 344 attributeNames.removeAllElements(); 345 attributeValues.removeAllElements(); 346 } 347 348 /** 349 * Implement com.microstar.xml.XmlHandler#endElement. 350 * <p>Translate to the SAX interface. 351 * <p>Users should never invoke this method directly. 352 * @see com.microstar.xml.XmlHandler#endElement 353 * @exception SAXException May throw any exception. 354 */ 355 @Override 356 public void endElement(String elname) throws SAXException { 357 //documentHandler.endElement(elname); 358 documentHandler.endElement("", elname, ""); 359 } 360 361 /** 362 * Implement com.microstar.xml.XmlHandler#charData. 363 * <p>Translate to the SAX interface. 364 * <p>Users should never invoke this method directly. 365 * @see com.microstar.xml.XmlHandler#charData 366 * @exception SAXException May throw any exception. 367 */ 368 @Override 369 public void charData(char[] ch, int start, int length) throws SAXException { 370 documentHandler.characters(ch, start, length); 371 } 372 373 /** 374 * Implement com.microstar.xml.XmlHandler#ignorableWhitespace. 375 * <p>Translate to the SAX interface. 376 * <p>Users should never invoke this method directly. 377 * @see com.microstar.xml.XmlHandler#ignorableWhitespace 378 * @exception SAXException May throw any exception. 379 */ 380 @Override 381 public void ignorableWhitespace(char[] ch, int start, int length) 382 throws SAXException { 383 documentHandler.ignorableWhitespace(ch, start, length); 384 } 385 386 /** 387 * Implement com.microstar.xml.XmlHandler#processingInstruction. 388 * <p>Translate to the SAX interface. 389 * <p>Users should never invoke this method directly. 390 * @see com.microstar.xml.XmlHandler#processingInstruction 391 * @exception SAXException May throw any exception. 392 */ 393 @Override 394 public void processingInstruction(String target, String data) 395 throws SAXException { 396 documentHandler.processingInstruction(target, data); 397 } 398 399 /** 400 * Implement com.microstar.xml.XmlHandler#error. 401 * <p>Translate to the SAX interface. 402 * <p>Users should never invoke this method directly. 403 * @see com.microstar.xml.XmlHandler#error 404 * @exception SAXException May throw any exception. 405 */ 406 @Override 407 public void error(String message, String url, int line, int column) 408 throws SAXException { 409 errorHandler.fatalError( 410 new SAXParseException(message, null, url, line, column)); 411 } 412 413 /** 414 * Before the first startElement event, deliver all notation 415 * and unparsed entity declarations. 416 */ 417 private void deliverDTDEvents() throws SAXException { 418 String ename; 419 String nname; 420 String publicId; 421 String systemId; 422 423 Enumeration notationNames = parser.declaredNotations(); 424 Enumeration entityNames = parser.declaredEntities(); 425 426 // First, report all notations. 427 while (notationNames.hasMoreElements()) { 428 nname = (String) notationNames.nextElement(); 429 publicId = parser.getNotationPublicId(nname); 430 systemId = parser.getNotationSystemId(nname); 431 dtdHandler.notationDecl(nname, publicId, systemId); 432 } 433 434 // Next, report all unparsed entities. 435 while (entityNames.hasMoreElements()) { 436 ename = (String) entityNames.nextElement(); 437 438 if (parser.getEntityType(ename) == XmlParser.ENTITY_NDATA) { 439 publicId = parser.getEntityPublicId(ename); 440 systemId = parser.getEntitySystemId(ename); 441 nname = parser.getEntityNotationName(ename); 442 dtdHandler.unparsedEntityDecl(ename, publicId, systemId, nname); 443 } 444 } 445 } 446 447 // 448 // Implementation of org.xml.sax.AttributeList. 449 // 450 @Override 451 public int getLength() { 452 return attributeNames.size(); 453 } 454 455 public String getName(int i) { 456 return (String) attributeNames.elementAt(i); 457 } 458 459 @Override 460 public String getType(int i) { 461 switch (parser.getAttributeType(elementName, getName(i))) { 462 case XmlParser.ATTRIBUTE_UNDECLARED: 463 case XmlParser.ATTRIBUTE_CDATA: 464 return "CDATA"; 465 466 case XmlParser.ATTRIBUTE_ID: 467 return "ID"; 468 469 case XmlParser.ATTRIBUTE_IDREF: 470 return "IDREF"; 471 472 case XmlParser.ATTRIBUTE_IDREFS: 473 return "IDREFS"; 474 475 case XmlParser.ATTRIBUTE_ENTITY: 476 return "ENTITY"; 477 478 case XmlParser.ATTRIBUTE_ENTITIES: 479 return "ENTITIES"; 480 481 case XmlParser.ATTRIBUTE_NMTOKEN: 482 case XmlParser.ATTRIBUTE_ENUMERATED: 483 return "NMTOKEN"; 484 485 case XmlParser.ATTRIBUTE_NMTOKENS: 486 return "NMTOKENS"; 487 488 case XmlParser.ATTRIBUTE_NOTATION: 489 return "NOTATION"; 490 } 491 492 return null; 493 } 494 495 @Override 496 public String getValue(int i) { 497 return (String) attributeValues.elementAt(i); 498 } 499 500 @Override 501 public String getType(String name) { 502 for (int i = 0; i < getLength(); i++) { 503 if (name.equals(getName(i))) { 504 return getType(i); 505 } 506 } 507 508 return null; 509 } 510 511 @Override 512 public String getValue(String name) { 513 for (int i = 0; i < getLength(); i++) { 514 if (name.equals(getName(i))) { 515 return getValue(i); 516 } 517 } 518 519 return null; 520 } 521 522 // 523 // Implementation of org.xml.sax.Locator. 524 // 525 @Override 526 public String getPublicId() { 527 return null; // TODO 528 } 529 530 @Override 531 public String getSystemId() { 532 return (String) entityStack.peek(); 533 } 534 535 @Override 536 public int getLineNumber() { 537 return parser.getLineNumber(); 538 } 539 540 @Override 541 public int getColumnNumber() { 542 return parser.getColumnNumber(); 543 } 544 545 @Override 546 public boolean getFeature(String name) { 547 // Not Yet Implemented 548 if (features.containsKey(name)) { 549 return ((Boolean) features.get(name)).booleanValue(); 550 } 551 552 return false; 553 } 554 555 @Override 556 public void setFeature(String name, boolean value) { 557 // Not Yet Implemented 558 features.put(name, Boolean.valueOf(value)); 559 } 560 561 @Override 562 public Object getProperty(String name) { 563 // Not Yet Implemented 564 if (properties.containsKey(name)) { 565 return properties.get(name); 566 } 567 568 return null; 569 } 570 571 @Override 572 public void setProperty(String name, Object value) { 573 // Not Yet Implemented 574 properties.put(name, value); 575 } 576 577 @Override 578 public EntityResolver getEntityResolver() { 579 // Not Yet Implemented 580 return null; 581 } 582 583 @Override 584 public DTDHandler getDTDHandler() { 585 // Not Yet Implemented 586 return null; 587 } 588 589 @Override 590 public void setContentHandler(ContentHandler handler) { 591 this.documentHandler = handler; 592 } 593 594 @Override 595 public ContentHandler getContentHandler() { 596 return this.documentHandler; 597 } 598 599 @Override 600 public ErrorHandler getErrorHandler() { 601 return this.errorHandler; 602 } 603 604 @Override 605 public String getURI(int index) { 606 // Not Yet Implemented 607 return ""; 608 } 609 610 @Override 611 public String getLocalName(int index) { 612 // Not Yet Implemented 613 return ""; 614 } 615 616 @Override 617 public String getQName(int index) { 618 // Not Yet Implemented 619 return ""; 620 } 621 622 @Override 623 public int getIndex(String uri, String localPart) { 624 // Not Yet Implemented 625 return -1; 626 } 627 628 @Override 629 public int getIndex(String qName) { 630 // Not Yet Implemented 631 return -1; 632 } 633 634 @Override 635 public String getType(String uri, String localName) { 636 return ""; 637 } 638 639 @Override 640 public String getValue(String uri, String localName) { 641 return ""; 642 } 643}