KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > jdom > input > SAXHandler


1 /*--
2
3  $Id: SAXHandler.java,v 1.71 2004/12/11 02:18:55 jhunter Exp $
4
5  Copyright (C) 2000-2004 Jason Hunter & Brett McLaughlin.
6  All rights reserved.
7
8  Redistribution and use in source and binary forms, with or without
9  modification, are permitted provided that the following conditions
10  are met:
11
12  1. Redistributions of source code must retain the above copyright
13     notice, this list of conditions, and the following disclaimer.
14
15  2. Redistributions in binary form must reproduce the above copyright
16     notice, this list of conditions, and the disclaimer that follows
17     these conditions in the documentation and/or other materials
18     provided with the distribution.
19
20  3. The name "JDOM" must not be used to endorse or promote products
21     derived from this software without prior written permission. For
22     written permission, please contact <request_AT_jdom_DOT_org>.
23
24  4. Products derived from this software may not be called "JDOM", nor
25     may "JDOM" appear in their name, without prior written permission
26     from the JDOM Project Management <request_AT_jdom_DOT_org>.
27
28  In addition, we request (but do not require) that you include in the
29  end-user documentation provided with the redistribution and/or in the
30  software itself an acknowledgement equivalent to the following:
31      "This product includes software developed by the
32       JDOM Project (http://www.jdom.org/)."
33  Alternatively, the acknowledgment may be graphical using the logos
34  available at http://www.jdom.org/images/logos.
35
36  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
37  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
38  OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
39  DISCLAIMED. IN NO EVENT SHALL THE JDOM AUTHORS OR THE PROJECT
40  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
41  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
42  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
43  USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
44  ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
45  OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
46  OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
47  SUCH DAMAGE.
48
49  This software consists of voluntary contributions made by many
50  individuals on behalf of the JDOM Project and was originally
51  created by Jason Hunter <jhunter_AT_jdom_DOT_org> and
52  Brett McLaughlin <brett_AT_jdom_DOT_org>. For more information
53  on the JDOM Project, please see <http://www.jdom.org/>.
54
55  */

56
57 package org.jdom.input;
58
59 import java.util.*;
60
61 import org.jdom.*;
62 import org.xml.sax.*;
63 import org.xml.sax.ext.*;
64 import org.xml.sax.helpers.*;
65
66 /**
67  * A support class for {@link SAXBuilder}.
68  *
69  * @version $Revision: 1.71 $, $Date: 2004/12/11 02:18:55 $
70  * @author Brett McLaughlin
71  * @author Jason Hunter
72  * @author Philip Nelson
73  * @author Bradley S. Huffman
74  * @author phil@triloggroup.com
75  */

76 public class SAXHandler extends DefaultHandler implements LexicalHandler,
77                                                           DeclHandler,
78                                                           DTDHandler {
79
80     private static final String JavaDoc CVS_ID =
81       "@(#) $RCSfile: SAXHandler.java,v $ $Revision: 1.71 $ $Date: 2004/12/11 02:18:55 $ $Name: $";
82
83     /** Hash table to map SAX attribute type names to JDOM attribute types. */
84     private static final Map attrNameToTypeMap = new HashMap(13);
85
86     /** <code>Document</code> object being built */
87     private Document document;
88
89     /** <code>Element</code> object being built */
90     private Element currentElement;
91
92     /** Indicator of where in the document we are */
93     private boolean atRoot;
94
95     /** Indicator of whether we are in the DocType. Note that the DTD consists
96      * of both the internal subset (inside the <!DOCTYPE> tag) and the
97       * external subset (in a separate .dtd file). */

98     private boolean inDTD = false;
99
100     /** Indicator of whether we are in the internal subset */
101     private boolean inInternalSubset = false;
102
103     /** Indicator of whether we previously were in a CDATA */
104     private boolean previousCDATA = false;
105
106     /** Indicator of whether we are in a CDATA */
107     private boolean inCDATA = false;
108
109     /** Indicator of whether we should expand entities */
110     private boolean expand = true;
111
112     /** Indicator of whether we are actively suppressing (non-expanding) a
113         current entity */

114     private boolean suppress = false;
115
116     /** How many nested entities we're currently within */
117     private int entityDepth = 0; // XXX may not be necessary anymore?
118

119     /** Temporary holder for namespaces that have been declared with
120       * startPrefixMapping, but are not yet available on the element */

121     private List declaredNamespaces;
122
123     /** Temporary holder for the internal subset */
124     private StringBuffer JavaDoc internalSubset = new StringBuffer JavaDoc();
125
126     /** Temporary holder for Text and CDATA */
127     private TextBuffer textBuffer = new TextBuffer();
128
129     /** The external entities defined in this document */
130     private Map externalEntities;
131
132     /** The JDOMFactory used for JDOM object creation */
133     private JDOMFactory factory;
134
135     /** Whether to ignore ignorable whitespace */
136     private boolean ignoringWhite = false;
137
138     /** Whether to ignore text containing all whitespace */
139     private boolean ignoringBoundaryWhite = false;
140
141     /** The SAX Locator object provided by the parser */
142     private Locator locator;
143
144     /**
145      * Class initializer: Populate a table to translate SAX attribute
146      * type names into JDOM attribute type value (integer).
147      * <p>
148      * <b>Note that all the mappings defined below are compliant with
149      * the SAX 2.0 specification exception for "ENUMERATION" with is
150      * specific to Crimson 1.1.X and Xerces 2.0.0-betaX which report
151      * attributes of enumerated types with a type "ENUMERATION"
152      * instead of the expected "NMTOKEN".
153      * </p>
154      * <p>
155      * Note also that Xerces 1.4.X is not SAX 2.0 compliant either
156      * but handling its case requires
157      * {@link #getAttributeType specific code}.
158      * </p>
159      */

160     static {
161         attrNameToTypeMap.put("CDATA",
162                               new Integer JavaDoc(Attribute.CDATA_TYPE));
163         attrNameToTypeMap.put("ID",
164                               new Integer JavaDoc(Attribute.ID_TYPE));
165         attrNameToTypeMap.put("IDREF",
166                               new Integer JavaDoc(Attribute.IDREF_TYPE));
167         attrNameToTypeMap.put("IDREFS",
168                               new Integer JavaDoc(Attribute.IDREFS_TYPE));
169         attrNameToTypeMap.put("ENTITY",
170                               new Integer JavaDoc(Attribute.ENTITY_TYPE));
171         attrNameToTypeMap.put("ENTITIES",
172                               new Integer JavaDoc(Attribute.ENTITIES_TYPE));
173         attrNameToTypeMap.put("NMTOKEN",
174                               new Integer JavaDoc(Attribute.NMTOKEN_TYPE));
175         attrNameToTypeMap.put("NMTOKENS",
176                               new Integer JavaDoc(Attribute.NMTOKENS_TYPE));
177         attrNameToTypeMap.put("NOTATION",
178                               new Integer JavaDoc(Attribute.NOTATION_TYPE));
179         attrNameToTypeMap.put("ENUMERATION",
180                               new Integer JavaDoc(Attribute.ENUMERATED_TYPE));
181     }
182
183     /**
184      * This will create a new <code>SAXHandler</code> that listens to SAX
185      * events and creates a JDOM Document. The objects will be constructed
186      * using the default factory.
187      */

188     public SAXHandler() {
189         this(null);
190     }
191
192     /**
193      * This will create a new <code>SAXHandler</code> that listens to SAX
194      * events and creates a JDOM Document. The objects will be constructed
195      * using the provided factory.
196      *
197      * @param factory <code>JDOMFactory</code> to be used for constructing
198      * objects
199      */

200     public SAXHandler(JDOMFactory factory) {
201         if (factory != null) {
202             this.factory = factory;
203         } else {
204             this.factory = new DefaultJDOMFactory();
205         }
206
207         atRoot = true;
208         declaredNamespaces = new ArrayList();
209         externalEntities = new HashMap();
210
211         document = this.factory.document(null);
212     }
213
214     /**
215      * Pushes an element onto the tree under construction. Allows subclasses
216      * to put content under a dummy root element which is useful for building
217      * content that would otherwise be a non-well formed document.
218      *
219      * @param element root element under which content will be built
220      */

221     protected void pushElement(Element element) {
222         if (atRoot) {
223             document.setRootElement(element); // XXX should we use a factory call?
224
atRoot = false;
225         }
226         else {
227             factory.addContent(currentElement, element);
228         }
229         currentElement = element;
230     }
231
232     /**
233      * Returns the document. Should be called after parsing is complete.
234      *
235      * @return <code>Document</code> - Document that was built
236      */

237     public Document getDocument() {
238         return document;
239     }
240
241     /**
242      * Returns the factory used for constructing objects.
243      *
244      * @return <code>JDOMFactory</code> - the factory used for
245      * constructing objects.
246      *
247      * @see #SAXHandler(org.jdom.JDOMFactory)
248      */

249     public JDOMFactory getFactory() {
250         return factory;
251     }
252
253     /**
254      * This sets whether or not to expand entities during the build.
255      * A true means to expand entities as normal content. A false means to
256      * leave entities unexpanded as <code>EntityRef</code> objects. The
257      * default is true.
258      *
259      * @param expand <code>boolean</code> indicating whether entity expansion
260      * should occur.
261      */

262     public void setExpandEntities(boolean expand) {
263         this.expand = expand;
264     }
265
266     /**
267      * Returns whether or not entities will be expanded during the
268      * build.
269      *
270      * @return <code>boolean</code> - whether entity expansion
271      * will occur during build.
272      *
273      * @see #setExpandEntities
274      */

275     public boolean getExpandEntities() {
276         return expand;
277     }
278
279     /**
280      * Specifies whether or not the parser should elminate whitespace in
281      * element content (sometimes known as "ignorable whitespace") when
282      * building the document. Only whitespace which is contained within
283      * element content that has an element only content model will be
284      * eliminated (see XML Rec 3.2.1). For this setting to take effect
285      * requires that validation be turned on. The default value of this
286      * setting is <code>false</code>.
287      *
288      * @param ignoringWhite Whether to ignore ignorable whitespace
289      */

290     public void setIgnoringElementContentWhitespace(boolean ignoringWhite) {
291         this.ignoringWhite = ignoringWhite;
292     }
293
294     /**
295      * Specifies whether or not the parser should elminate text() nodes
296      * containing only whitespace when building the document. See
297      * {@link SAXBuilder#setIgnoringBoundaryWhitespace(boolean)}.
298      *
299      * @param ignoringBoundaryWhite Whether to ignore only whitespace content
300      */

301     public void setIgnoringBoundaryWhitespace(boolean ignoringBoundaryWhite) {
302         this.ignoringBoundaryWhite = ignoringBoundaryWhite;
303     }
304
305     /**
306      * Returns whether or not the parser will elminate element content
307      * containing only whitespace.
308      *
309      * @return <code>boolean</code> - whether only whitespace content will
310      * be ignored during build.
311      *
312      * @see #setIgnoringBoundaryWhitespace
313      */

314     public boolean getIgnoringBoundaryWhitespace() {
315         return ignoringBoundaryWhite;
316     }
317
318     /**
319      * Returns whether or not the parser will elminate whitespace in
320      * element content (sometimes known as "ignorable whitespace") when
321      * building the document.
322      *
323      * @return <code>boolean</code> - whether ignorable whitespace will
324      * be ignored during build.
325      *
326      * @see #setIgnoringElementContentWhitespace
327      */

328     public boolean getIgnoringElementContentWhitespace() {
329         return ignoringWhite;
330     }
331
332     public void startDocument() {
333         if (locator != null) {
334             document.setBaseURI(locator.getSystemId());
335         }
336     }
337
338     /**
339      * This is called when the parser encounters an external entity
340      * declaration.
341      *
342      * @param name entity name
343      * @param publicID public id
344      * @param systemID system id
345      * @throws SAXException when things go wrong
346      */

347     public void externalEntityDecl(String JavaDoc name,
348                                    String JavaDoc publicID, String JavaDoc systemID)
349                                    throws SAXException {
350         // Store the public and system ids for the name
351
externalEntities.put(name, new String JavaDoc[]{publicID, systemID});
352
353         if (!inInternalSubset) return;
354
355         internalSubset.append(" <!ENTITY ")
356               .append(name);
357         appendExternalId(publicID, systemID);
358         internalSubset.append(">\n");
359     }
360
361     /**
362      * This handles an attribute declaration in the internal subset.
363      *
364      * @param eName <code>String</code> element name of attribute
365      * @param aName <code>String</code> attribute name
366      * @param type <code>String</code> attribute type
367      * @param valueDefault <code>String</code> default value of attribute
368      * @param value <code>String</code> value of attribute
369      * @throws SAXException
370      */

371     public void attributeDecl(String JavaDoc eName, String JavaDoc aName, String JavaDoc type,
372                               String JavaDoc valueDefault, String JavaDoc value)
373         throws SAXException {
374
375         if (!inInternalSubset) return;
376
377         internalSubset.append(" <!ATTLIST ")
378               .append(eName)
379               .append(' ')
380               .append(aName)
381               .append(' ')
382               .append(type)
383               .append(' ');
384         if (valueDefault != null) {
385               internalSubset.append(valueDefault);
386         } else {
387             internalSubset.append('\"')
388                   .append(value)
389                   .append('\"');
390         }
391         if ((valueDefault != null) && (valueDefault.equals("#FIXED"))) {
392             internalSubset.append(" \"")
393                   .append(value)
394                   .append('\"');
395         }
396         internalSubset.append(">\n");
397     }
398
399     /**
400      * Handle an element declaration in a DTD.
401      *
402      * @param name <code>String</code> name of element
403      * @param model <code>String</code> model of the element in DTD syntax
404      * @throws SAXException
405      */

406     public void elementDecl(String JavaDoc name, String JavaDoc model) throws SAXException {
407         // Skip elements that come from the external subset
408
if (!inInternalSubset) return;
409
410         internalSubset.append(" <!ELEMENT ")
411               .append(name)
412               .append(' ')
413               .append(model)
414               .append(">\n");
415     }
416
417     /**
418      * Handle an internal entity declaration in a DTD.
419      *
420      * @param name <code>String</code> name of entity
421      * @param value <code>String</code> value of the entity
422      * @throws SAXException
423      */

424     public void internalEntityDecl(String JavaDoc name, String JavaDoc value)
425         throws SAXException {
426
427         // Skip entities that come from the external subset
428
if (!inInternalSubset) return;
429
430         internalSubset.append(" <!ENTITY ");
431         if (name.startsWith("%")) {
432            internalSubset.append("% ").append(name.substring(1));
433         } else {
434            internalSubset.append(name);
435         }
436         internalSubset.append(" \"")
437               .append(value)
438               .append("\">\n");
439     }
440
441     /**
442      * This will indicate that a processing instruction has been encountered.
443      * (The XML declaration is not a processing instruction and will not
444      * be reported.)
445      *
446      * @param target <code>String</code> target of PI
447      * @param data <code>String</code> containing all data sent to the PI.
448      * This typically looks like one or more attribute value
449      * pairs.
450      * @throws SAXException when things go wrong
451      */

452     public void processingInstruction(String JavaDoc target, String JavaDoc data)
453         throws SAXException {
454
455         if (suppress) return;
456
457         flushCharacters();
458
459         if (atRoot) {
460             factory.addContent(document, factory.processingInstruction(target, data));
461         } else {
462             factory.addContent(getCurrentElement(),
463                 factory.processingInstruction(target, data));
464         }
465     }
466
467     /**
468      * This indicates that an unresolvable entity reference has been
469      * encountered, normally because the external DTD subset has not been
470      * read.
471      *
472      * @param name <code>String</code> name of entity
473      * @throws SAXException when things go wrong
474      */

475     public void skippedEntity(String JavaDoc name)
476         throws SAXException {
477
478         // We don't handle parameter entity references.
479
if (name.startsWith("%")) return;
480
481         flushCharacters();
482
483         factory.addContent(getCurrentElement(), factory.entityRef(name));
484     }
485
486     /**
487      * This will add the prefix mapping to the JDOM
488      * <code>Document</code> object.
489      *
490      * @param prefix <code>String</code> namespace prefix.
491      * @param uri <code>String</code> namespace URI.
492      */

493     public void startPrefixMapping(String JavaDoc prefix, String JavaDoc uri)
494         throws SAXException {
495
496         if (suppress) return;
497
498         Namespace ns = Namespace.getNamespace(prefix, uri);
499         declaredNamespaces.add(ns);
500     }
501
502     /**
503      * This reports the occurrence of an actual element. It will include
504      * the element's attributes, with the exception of XML vocabulary
505      * specific attributes, such as
506      * <code>xmlns:[namespace prefix]</code> and
507      * <code>xsi:schemaLocation</code>.
508      *
509      * @param namespaceURI <code>String</code> namespace URI this element
510      * is associated with, or an empty
511      * <code>String</code>
512      * @param localName <code>String</code> name of element (with no
513      * namespace prefix, if one is present)
514      * @param qName <code>String</code> XML 1.0 version of element name:
515      * [namespace prefix]:[localName]
516      * @param atts <code>Attributes</code> list for this element
517      * @throws SAXException when things go wrong
518      */

519     public void startElement(String JavaDoc namespaceURI, String JavaDoc localName,
520                              String JavaDoc qName, Attributes atts)
521                              throws SAXException {
522         if (suppress) return;
523
524         Element element = null;
525
526         if ((namespaceURI != null) && (!namespaceURI.equals(""))) {
527             String JavaDoc prefix = "";
528
529             // Determine any prefix on the Element
530
if (!qName.equals(localName)) {
531                 int split = qName.indexOf(":");
532                 prefix = qName.substring(0, split);
533             }
534             Namespace elementNamespace =
535                 Namespace.getNamespace(prefix, namespaceURI);
536             element = factory.element(localName, elementNamespace);
537         } else {
538             element = factory.element(localName);
539         }
540
541         // Take leftover declared namespaces and add them to this element's
542
// map of namespaces
543
if (declaredNamespaces.size() > 0) {
544             transferNamespaces(element);
545         }
546
547         // Handle attributes
548
for (int i=0, len=atts.getLength(); i<len; i++) {
549             Attribute attribute = null;
550
551             String JavaDoc attLocalName = atts.getLocalName(i);
552             String JavaDoc attQName = atts.getQName(i);
553             int attType = getAttributeType(atts.getType(i));
554
555             // Bypass any xmlns attributes which might appear, as we got
556
// them already in startPrefixMapping().
557
// This is sometimes necessary when SAXHandler is used with
558
// another source than SAXBuilder, as with JDOMResult.
559
if (attQName.startsWith("xmlns:") || attQName.equals("xmlns")) {
560                 continue;
561             }
562
563             if (!attQName.equals(attLocalName)) {
564                 String JavaDoc attPrefix = attQName.substring(0, attQName.indexOf(":"));
565                 Namespace attNs = Namespace.getNamespace(attPrefix,
566                                                          atts.getURI(i));
567
568                 attribute = factory.attribute(attLocalName, atts.getValue(i),
569                                               attType, attNs);
570             } else {
571                 attribute = factory.attribute(attLocalName, atts.getValue(i),
572                                               attType);
573             }
574             factory.setAttribute(element, attribute);
575         }
576
577         flushCharacters();
578
579         if (atRoot) {
580             document.setRootElement(element); // XXX should we use a factory call?
581
atRoot = false;
582         } else {
583             factory.addContent(getCurrentElement(), element);
584         }
585         currentElement = element;
586     }
587
588     /**
589      * This will take the supplied <code>{@link Element}</code> and
590      * transfer its namespaces to the global namespace storage.
591      *
592      * @param element <code>Element</code> to read namespaces from.
593      */

594     private void transferNamespaces(Element element) {
595         Iterator i = declaredNamespaces.iterator();
596         while (i.hasNext()) {
597             Namespace ns = (Namespace)i.next();
598             if (ns != element.getNamespace()) {
599                 element.addNamespaceDeclaration(ns);
600             }
601         }
602         declaredNamespaces.clear();
603     }
604
605     /**
606      * This will report character data (within an element).
607      *
608      * @param ch <code>char[]</code> character array with character data
609      * @param start <code>int</code> index in array where data starts.
610      * @param length <code>int</code> length of data.
611      * @throws SAXException
612      */

613     public void characters(char[] ch, int start, int length)
614                     throws SAXException {
615
616         if (suppress || (length == 0))
617             return;
618
619         if (previousCDATA != inCDATA) {
620             flushCharacters();
621         }
622
623         textBuffer.append(ch, start, length);
624     }
625
626     /**
627      * Capture ignorable whitespace as text. If
628      * setIgnoringElementContentWhitespace(true) has been called then this
629      * method does nothing.
630      *
631      * @param ch <code>[]</code> - char array of ignorable whitespace
632      * @param start <code>int</code> - starting position within array
633      * @param length <code>int</code> - length of whitespace after start
634      * @throws SAXException when things go wrong
635      */

636     public void ignorableWhitespace(char[] ch, int start, int length)
637                                                      throws SAXException {
638         if (!ignoringWhite) {
639             characters(ch, start, length);
640         }
641     }
642
643     /**
644      * This will flush any characters from SAX character calls we've
645      * been buffering.
646      *
647      * @throws SAXException when things go wrong
648      */

649     protected void flushCharacters() throws SAXException {
650         if (ignoringBoundaryWhite) {
651             if (!textBuffer.isAllWhitespace()) {
652                 flushCharacters(textBuffer.toString());
653             }
654         }
655         else {
656             flushCharacters(textBuffer.toString());
657         }
658         textBuffer.clear();
659     }
660
661     /**
662      * Flush the given string into the document. This is a protected method
663      * so subclassers can control text handling without knowledge of the
664      * internals of this class.
665      *
666      * @param data string to flush
667      */

668     protected void flushCharacters(String JavaDoc data) throws SAXException {
669         if (data.length() == 0) {
670             previousCDATA = inCDATA;
671             return;
672         }
673
674 /**
675  * This is commented out because of some problems with
676  * the inline DTDs that Xerces seems to have.
677 if (!inDTD) {
678   if (inEntity) {
679     getCurrentElement().setContent(factory.text(data));
680   } else {
681     getCurrentElement().addContent(factory.text(data));
682 }
683 */

684
685         if (previousCDATA) {
686             factory.addContent(getCurrentElement(), factory.cdata(data));
687         }
688         else {
689             factory.addContent(getCurrentElement(), factory.text(data));
690         }
691
692         previousCDATA = inCDATA;
693     }
694
695     /**
696      * Indicates the end of an element
697      * (<code>&lt;/[element name]&gt;</code>) is reached. Note that
698      * the parser does not distinguish between empty
699      * elements and non-empty elements, so this will occur uniformly.
700      *
701      * @param namespaceURI <code>String</code> URI of namespace this
702      * element is associated with
703      * @param localName <code>String</code> name of element without prefix
704      * @param qName <code>String</code> name of element in XML 1.0 form
705      * @throws SAXException when things go wrong
706      */

707     public void endElement(String JavaDoc namespaceURI, String JavaDoc localName,
708                            String JavaDoc qName) throws SAXException {
709
710         if (suppress) return;
711
712         flushCharacters();
713
714         if (!atRoot) {
715             Parent p = currentElement.getParent();
716             if (p instanceof Document) {
717                atRoot = true;
718             }
719             else {
720                 currentElement = (Element) p;
721             }
722         }
723         else {
724             throw new SAXException(
725                 "Ill-formed XML document (missing opening tag for " +
726                 localName + ")");
727         }
728     }
729
730     /**
731      * This will signify that a DTD is being parsed, and can be
732      * used to ensure that comments and other lexical structures
733      * in the DTD are not added to the JDOM <code>Document</code>
734      * object.
735      *
736      * @param name <code>String</code> name of element listed in DTD
737      * @param publicID <code>String</code> public ID of DTD
738      * @param systemID <code>String</code> system ID of DTD
739      */

740     public void startDTD(String JavaDoc name, String JavaDoc publicID, String JavaDoc systemID)
741         throws SAXException {
742
743         flushCharacters(); // Is this needed here?
744

745         factory.addContent(document, factory.docType(name, publicID, systemID));
746         inDTD = true;
747         inInternalSubset = true;
748     }
749
750     /**
751      * This signifies that the reading of the DTD is complete.
752      *
753      * @throws SAXException
754      */

755     public void endDTD() throws SAXException {
756
757         document.getDocType().setInternalSubset(internalSubset.toString());
758         inDTD = false;
759         inInternalSubset = false;
760     }
761
762     public void startEntity(String JavaDoc name) throws SAXException {
763         entityDepth++;
764
765         if (expand || entityDepth > 1) {
766             // Short cut out if we're expanding or if we're nested
767
return;
768         }
769
770         // A "[dtd]" entity indicates the beginning of the external subset
771
if (name.equals("[dtd]")) {
772             inInternalSubset = false;
773             return;
774         }
775
776         // Ignore DTD references, and translate the standard 5
777
if ((!inDTD) &&
778             (!name.equals("amp")) &&
779             (!name.equals("lt")) &&
780             (!name.equals("gt")) &&
781             (!name.equals("apos")) &&
782             (!name.equals("quot"))) {
783
784             if (!expand) {
785                 String JavaDoc pub = null;
786                 String JavaDoc sys = null;
787                 String JavaDoc[] ids = (String JavaDoc[]) externalEntities.get(name);
788                 if (ids != null) {
789                   pub = ids[0]; // may be null, that's OK
790
sys = ids[1]; // may be null, that's OK
791
}
792                 /**
793                  * if no current element, this entity belongs to an attribute
794                  * in these cases, it is an error on the part of the parser
795                  * to call startEntity but this will help in some cases.
796                  * See org/xml/sax/ext/LexicalHandler.html#startEntity(java.lang.String)
797                  * for more information
798                  */

799                 if (!atRoot) {
800                     flushCharacters();
801                     EntityRef entity = factory.entityRef(name, pub, sys);
802
803                     // no way to tell if the entity was from an attribute or element so just assume element
804
factory.addContent(getCurrentElement(), entity);
805                 }
806                 suppress = true;
807             }
808         }
809     }
810
811     public void endEntity(String JavaDoc name) throws SAXException {
812         entityDepth--;
813         if (entityDepth == 0) {
814             // No way are we suppressing if not in an entity,
815
// regardless of the "expand" value
816
suppress = false;
817         }
818         if (name.equals("[dtd]")) {
819             inInternalSubset = true;
820         }
821     }
822
823     /**
824      * Report a CDATA section
825      *
826      * @throws SAXException
827      */

828     public void startCDATA() throws SAXException {
829         if (suppress) return;
830
831         inCDATA = true;
832     }
833
834     /**
835      * Report a CDATA section
836      */

837     public void endCDATA() throws SAXException {
838         if (suppress) return;
839
840         previousCDATA = true;
841         inCDATA = false;
842     }
843
844     /**
845      * This reports that a comments is parsed. If not in the
846      * DTD, this comment is added to the current JDOM
847      * <code>Element</code>, or the <code>Document</code> itself
848      * if at that level.
849      *
850      * @param ch <code>ch[]</code> array of comment characters.
851      * @param start <code>int</code> index to start reading from.
852      * @param length <code>int</code> length of data.
853      * @throws SAXException
854      */

855     public void comment(char[] ch, int start, int length)
856         throws SAXException {
857
858         if (suppress) return;
859
860         flushCharacters();
861
862         String JavaDoc commentText = new String JavaDoc(ch, start, length);
863         if (inDTD && inInternalSubset && (expand == false)) {
864             internalSubset.append(" <!--")
865                   .append(commentText)
866                   .append("-->\n");
867             return;
868         }
869         if ((!inDTD) && (!commentText.equals(""))) {
870             if (atRoot) {
871                 factory.addContent(document, factory.comment(commentText));
872             } else {
873                 factory.addContent(getCurrentElement(), factory.comment(commentText));
874             }
875         }
876     }
877
878     /**
879      * Handle the declaration of a Notation in a DTD
880      *
881      * @param name name of the notation
882      * @param publicID the public ID of the notation
883      * @param systemID the system ID of the notation
884      */

885     public void notationDecl(String JavaDoc name, String JavaDoc publicID, String JavaDoc systemID)
886         throws SAXException {
887
888         if (!inInternalSubset) return;
889
890         internalSubset.append(" <!NOTATION ")
891               .append(name);
892         appendExternalId(publicID, systemID);
893         internalSubset.append(">\n");
894     }
895
896     /**
897      * Handler for unparsed entity declarations in the DTD
898      *
899      * @param name <code>String</code> of the unparsed entity decl
900      * @param publicID <code>String</code> of the unparsed entity decl
901      * @param systemID <code>String</code> of the unparsed entity decl
902      * @param notationName <code>String</code> of the unparsed entity decl
903      */

904     public void unparsedEntityDecl(String JavaDoc name, String JavaDoc publicID,
905                                    String JavaDoc systemID, String JavaDoc notationName)
906         throws SAXException {
907
908         if (!inInternalSubset) return;
909
910         internalSubset.append(" <!ENTITY ")
911               .append(name);
912         appendExternalId(publicID, systemID);
913         internalSubset.append(" NDATA ")
914               .append(notationName);
915         internalSubset.append(">\n");
916     }
917
918     /**
919      * Appends an external ID to the internal subset buffer. Either publicID
920      * or systemID may be null, but not both.
921      *
922      * @param publicID the public ID
923      * @param systemID the system ID
924      */

925     private void appendExternalId(String JavaDoc publicID, String JavaDoc systemID) {
926         if (publicID != null) {
927             internalSubset.append(" PUBLIC \"")
928                   .append(publicID)
929                   .append('\"');
930         }
931         if (systemID != null) {
932             if (publicID == null) {
933                 internalSubset.append(" SYSTEM ");
934             }
935             else {
936                 internalSubset.append(' ');
937             }
938             internalSubset.append('\"')
939                   .append(systemID)
940                   .append('\"');
941         }
942     }
943
944     /**
945      * Returns the being-parsed element.
946      *
947      * @return <code>Element</code> - element being built.
948      * @throws SAXException
949      */

950     public Element getCurrentElement() throws SAXException {
951         if (currentElement == null) {
952             throw new SAXException(
953                 "Ill-formed XML document (multiple root elements detected)");
954         }
955         return currentElement;
956     }
957
958     /**
959      * Returns the the JDOM Attribute type value from the SAX 2.0
960      * attribute type string provided by the parser.
961      *
962      * @param typeName <code>String</code> the SAX 2.0 attribute
963      * type string.
964      *
965      * @return <code>int</code> the JDOM attribute type.
966      *
967      * @see Attribute#setAttributeType
968      * @see Attributes#getType
969      */

970     private static int getAttributeType(String JavaDoc typeName) {
971         Integer JavaDoc type = (Integer JavaDoc)(attrNameToTypeMap.get(typeName));
972         if (type == null) {
973             if (typeName != null && typeName.length() > 0 &&
974                 typeName.charAt(0) == '(') {
975                 // Xerces 1.4.X reports attributes of enumerated type with
976
// a type string equals to the enumeration definition, i.e.
977
// starting with a parenthesis.
978
return Attribute.ENUMERATED_TYPE;
979             }
980             else {
981                 return Attribute.UNDECLARED_TYPE;
982             }
983         } else {
984             return type.intValue();
985         }
986     }
987
988     /**
989      * Receives an object for locating the origin of SAX document
990      * events. This method is invoked by the SAX parser.
991      * <p>
992      * {@link org.jdom.JDOMFactory} implementations can use the
993      * {@link #getDocumentLocator} method to get access to the
994      * {@link Locator} during parse.
995      * </p>
996      *
997      * @param locator <code>Locator</code> an object that can return
998      * the location of any SAX document event.
999      */

1000    public void setDocumentLocator(Locator locator) {
1001        this.locator = locator;
1002    }
1003
1004    /**
1005     * Provides access to the {@link Locator} object provided by the
1006     * SAX parser.
1007     *
1008     * @return <code>Locator</code> an object that can return
1009     * the location of any SAX document event.
1010     */

1011    public Locator getDocumentLocator() {
1012        return locator;
1013    }
1014}
1015
Popular Tags