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 &AElig;lfred, and
032 * translates &AElig;lfred's events into SAX events.  It
033 * implements the SAX parser interface, and you can use it without
034 * directly calling &AElig;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 &lt;dmeggins@microstar.com&gt;
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}