View Javadoc

1   /*
2    * Copyright 2001-2004 (C) MetaStuff, Ltd. All Rights Reserved.
3    *
4    * This software is open source.
5    * See the bottom of this file for the licence.
6    *
7    * $Id: SAXContentHandler.java,v 1.59 2004/08/06 09:51:50 maartenc Exp $
8    */
9   
10  package org.dom4j.io;
11  
12  import java.lang.reflect.Method;
13  import java.util.ArrayList;
14  import java.util.HashMap;
15  import java.util.List;
16  import java.util.Map;
17  
18  import org.dom4j.Branch;
19  import org.dom4j.Document;
20  import org.dom4j.DocumentFactory;
21  import org.dom4j.DocumentType;
22  import org.dom4j.Element;
23  import org.dom4j.ElementHandler;
24  import org.dom4j.Namespace;
25  import org.dom4j.QName;
26  import org.dom4j.dtd.AttributeDecl;
27  import org.dom4j.dtd.ElementDecl;
28  import org.dom4j.dtd.ExternalEntityDecl;
29  import org.dom4j.dtd.InternalEntityDecl;
30  import org.dom4j.tree.AbstractElement;
31  import org.dom4j.tree.NamespaceStack;
32  import org.xml.sax.Attributes;
33  import org.xml.sax.DTDHandler;
34  import org.xml.sax.EntityResolver;
35  import org.xml.sax.InputSource;
36  import org.xml.sax.Locator;
37  import org.xml.sax.SAXException;
38  import org.xml.sax.SAXParseException;
39  import org.xml.sax.ext.DeclHandler;
40  import org.xml.sax.ext.LexicalHandler;
41  import org.xml.sax.helpers.DefaultHandler;
42  
43  /*** <p><code>SAXContentHandler</code> builds a dom4j tree via SAX events.</p>
44    *
45    * @author <a href="mailto:jstrachan@apache.org">James Strachan</a>
46    * @version $Revision: 1.59 $
47    */
48  public class SAXContentHandler extends DefaultHandler implements LexicalHandler, DeclHandler, DTDHandler {
49  
50      /*** The factory used to create new <code>Document</code> instances */
51      private DocumentFactory documentFactory;
52  
53      /*** The document that is being built */
54      private Document document;
55  
56      /*** stack of <code>Element</code> objects */
57      private ElementStack elementStack;
58  
59      /*** stack of <code>Namespace</code> and <code>QName</code> objects */
60      private NamespaceStack namespaceStack;
61  
62      /*** the <code>ElementHandler</code> called as the elements are complete */
63      private ElementHandler elementHandler;
64      
65      /*** the Locator */
66      private Locator locator;
67  
68      /*** The name of the current entity */
69      private String entity;
70  
71      /*** Flag used to indicate that we are inside a DTD section */
72      private boolean insideDTDSection;
73  
74      /*** Flag used to indicate that we are inside a CDATA section */
75      private boolean insideCDATASection;
76      
77      /*** buffer to hold contents of cdata section across multiple characters events */
78      private StringBuffer cdataText;
79  
80      /*** namespaces that are available for use */
81      private Map availableNamespaceMap = new HashMap();
82  
83      /*** declared namespaces that are not yet available for use */
84      private List declaredNamespaceList = new ArrayList();
85  
86      /*** internal DTD declarations */
87      private List internalDTDDeclarations;
88  
89      /*** external DTD declarations */
90      private List externalDTDDeclarations;
91  
92      /*** The number of namespaces that are declared in the current scope */
93      private int declaredNamespaceIndex;
94  
95      /*** The entity resolver */
96      private EntityResolver entityResolver;
97  
98      private InputSource inputSource;
99  
100     /*** The current element we are on */
101     private Element currentElement;
102 
103     /*** Should internal DTD declarations be expanded into a List in the DTD */
104     private boolean includeInternalDTDDeclarations = false;
105 
106     /*** Should external DTD declarations be expanded into a List in the DTD */
107     private boolean includeExternalDTDDeclarations = false;
108 
109     /*** The number of levels deep we are inside a startEntity / endEntity call */
110     private int entityLevel;
111 
112     /*** Are we in an internal DTD subset? */
113     private boolean internalDTDsubset = false;
114 
115     /*** Whether adjacent text nodes should be merged */
116     private boolean mergeAdjacentText = false;
117 
118     /*** Have we added text to the buffer */
119     private boolean textInTextBuffer = false;
120 
121     /*** Should we ignore comments */
122     private boolean ignoreComments = false;
123     
124     /*** Buffer used to concatenate text together */
125     private StringBuffer textBuffer;
126 
127     /*** Holds value of property stripWhitespaceText. */
128     private boolean stripWhitespaceText = false;
129 
130 
131     public SAXContentHandler() {
132         this(DocumentFactory.getInstance());
133     }
134 
135     public SAXContentHandler(DocumentFactory documentFactory) {
136         this(documentFactory, null);
137     }
138 
139     public SAXContentHandler(DocumentFactory documentFactory, ElementHandler elementHandler) {
140         this(documentFactory, elementHandler, null);
141         this.elementStack = createElementStack();
142     }
143 
144     public SAXContentHandler(DocumentFactory documentFactory, ElementHandler elementHandler, ElementStack elementStack) {
145         this.documentFactory = documentFactory;
146         this.elementHandler = elementHandler;
147         this.elementStack = elementStack;
148         this.namespaceStack = new NamespaceStack(documentFactory);
149     }
150 
151     /*** @return the document that has been or is being built
152       */
153     public Document getDocument() {
154         if ( document == null ) {
155             document = createDocument();
156         }
157         return document;
158     }
159 
160     // ContentHandler interface
161     //-------------------------------------------------------------------------
162 
163     public void setDocumentLocator(Locator locator) {
164         this.locator = locator;
165     }
166     
167     public void processingInstruction(String target, String data) throws SAXException {
168         if ( mergeAdjacentText && textInTextBuffer ) {
169             completeCurrentTextNode();
170         }
171         if ( currentElement != null ) {
172             currentElement.addProcessingInstruction(target, data);
173         }
174         else {
175             getDocument().addProcessingInstruction(target, data);
176         }
177     }
178 
179     public void startPrefixMapping(String prefix, String uri) throws SAXException {
180         namespaceStack.push( prefix, uri );
181     }
182 
183     public void endPrefixMapping(String prefix) throws SAXException {
184         namespaceStack.pop( prefix );
185         declaredNamespaceIndex = namespaceStack.size();
186     }
187 
188     public void startDocument() throws SAXException {
189         //document = createDocument();
190         document = null;
191         currentElement = null;
192 
193         elementStack.clear();
194 
195         if ( (elementHandler != null) &&
196              (elementHandler instanceof DispatchHandler) ) {
197             elementStack.setDispatchHandler((DispatchHandler)elementHandler);
198         }
199 
200         namespaceStack.clear();
201         declaredNamespaceIndex = 0;
202 
203         if ( mergeAdjacentText && textBuffer == null ) {
204             textBuffer = new StringBuffer();
205         }
206         textInTextBuffer = false;
207     }
208 
209     public void endDocument() throws SAXException {
210         namespaceStack.clear();
211         elementStack.clear();
212         currentElement = null;
213         textBuffer = null;
214     }
215 
216     public void startElement(String namespaceURI, String localName, String qualifiedName, Attributes attributes) throws SAXException {
217         if ( mergeAdjacentText && textInTextBuffer ) {
218             completeCurrentTextNode();
219         }
220 
221         QName qName = namespaceStack.getQName(
222             namespaceURI, localName, qualifiedName
223         );
224 
225         Branch branch = currentElement;
226         if ( branch == null ) {
227             branch = getDocument();
228         }
229         Element element = branch.addElement(qName);
230 
231         // add all declared namespaces
232         addDeclaredNamespaces(element);
233 
234         // now lets add all attribute values
235         addAttributes( element, attributes );
236 
237         elementStack.pushElement(element);
238         currentElement = element;
239 
240         entity = null;      // fixes bug527062
241 
242         if ( elementHandler != null ) {
243             elementHandler.onStart(elementStack);
244         }
245     }
246 
247     public void endElement(String namespaceURI, String localName, String qName) throws SAXException {
248         if ( mergeAdjacentText && textInTextBuffer ) {
249             completeCurrentTextNode();
250         }
251 
252         if ( elementHandler != null && currentElement != null ) {
253             elementHandler.onEnd(elementStack);
254         }
255         elementStack.popElement();
256         currentElement = elementStack.peekElement();
257     }
258 
259     public void characters(char[] ch, int start, int end) throws SAXException {
260         if ( end == 0 ) {
261             return;
262         }
263         
264         if ( currentElement != null ) {
265             if (entity != null) {
266                 if ( mergeAdjacentText && textInTextBuffer ) {
267                     completeCurrentTextNode();
268                 }
269                 currentElement.addEntity(entity, new String(ch, start, end));
270                 entity = null;
271             }
272             else if (insideCDATASection) {
273                 if ( mergeAdjacentText && textInTextBuffer ) {
274                     completeCurrentTextNode();
275                 }
276                 cdataText.append(new String(ch, start, end));
277             }
278             else {
279                 if ( mergeAdjacentText ) {
280                     textBuffer.append(ch, start, end);
281                     textInTextBuffer = true;
282                 }
283                 else {
284                     currentElement.addText(new String(ch, start, end));
285                 }
286             }
287         }
288     }
289 
290     // ErrorHandler interface
291     //-------------------------------------------------------------------------
292 
293     /*** This method is called when a warning occurs during the parsing
294       * of the document.
295       * This method does nothing.
296       */
297     public void warning(SAXParseException exception) throws SAXException {
298         // ignore warnings by default
299     }
300 
301     /*** This method is called when an error is detected during parsing
302       * such as a validation error.
303       * This method rethrows the exception
304       */
305     public void error(SAXParseException exception) throws SAXException {
306         throw exception;
307     }
308 
309     /*** This method is called when a fatal error occurs during parsing.
310       * This method rethrows the exception
311       */
312     public void fatalError(SAXParseException exception) throws SAXException {
313         throw exception;
314     }
315 
316     // LexicalHandler interface
317     //-------------------------------------------------------------------------
318 
319     public void startDTD(String name, String publicId, String systemId) throws SAXException {
320         getDocument().addDocType(name, publicId, systemId);
321         insideDTDSection = true;
322         internalDTDsubset = true;
323     }
324 
325     public void endDTD() throws SAXException {
326         insideDTDSection = false;
327 
328         DocumentType docType = getDocument().getDocType();
329         if ( docType != null ) {
330             if ( internalDTDDeclarations != null ) {
331                 docType.setInternalDeclarations( internalDTDDeclarations );
332             }
333             if ( externalDTDDeclarations != null ) {
334                 docType.setExternalDeclarations( externalDTDDeclarations );
335             }
336         }
337 
338         internalDTDDeclarations = null;
339         externalDTDDeclarations = null;
340     }
341 
342     public void startEntity(String name) throws SAXException {
343         ++entityLevel;
344 
345         // Ignore DTD references
346         entity = null;
347         if (! insideDTDSection ) {
348             if ( ! isIgnorableEntity(name) ) {
349                 entity = name;
350             }
351         }
352 
353         // internal DTD subsets can only appear outside of a
354         // startEntity/endEntity block
355         // see the startDTD method in
356         // http://dom4j.org/javadoc/org/xml/sax/ext/LexicalHandler.html
357         // or here:-
358         // http://dom4j.org/javadoc/org/xml/sax/ext/LexicalHandler.html#startDTD(java.lang.String, java.lang.String, java.lang.String)
359         internalDTDsubset = false;
360     }
361 
362     public void endEntity(String name) throws SAXException {
363         --entityLevel;
364         entity = null;
365         if ( entityLevel == 0 ) {
366             internalDTDsubset = true;
367         }
368 
369     }
370 
371     public void startCDATA() throws SAXException {
372         insideCDATASection = true;
373         cdataText = new StringBuffer();
374     }
375 
376     public void endCDATA() throws SAXException {
377         insideCDATASection = false;
378         currentElement.addCDATA(cdataText.toString());
379     }
380 
381     public void comment(char[] ch, int start, int end) throws SAXException {
382         if (!ignoreComments) {
383             if ( mergeAdjacentText && textInTextBuffer ) {
384                 completeCurrentTextNode();
385             }
386             String text = new String(ch, start, end);
387             if (!insideDTDSection && text.length() > 0) {
388                 if ( currentElement != null ) {
389                     currentElement.addComment(text);
390                 }
391                 else {
392                     getDocument().addComment(text);
393                 }
394             }
395         }
396     }
397 
398     // DeclHandler interface
399     //-------------------------------------------------------------------------
400 
401     /***
402      * Report an element type declaration.
403      *
404      * <p>The content model will consist of the string "EMPTY", the
405      * string "ANY", or a parenthesised group, optionally followed
406      * by an occurrence indicator.  The model will be normalized so
407      * that all parameter entities are fully resolved and all whitespace
408      * is removed,and will include the enclosing parentheses.  Other
409      * normalization (such as removing redundant parentheses or
410      * simplifying occurrence indicators) is at the discretion of the
411      * parser.</p>
412      *
413      * @param name The element type name.
414      * @param model The content model as a normalized string.
415      * @exception SAXException The application may raise an exception.
416      */
417     public void elementDecl(String name, String model) throws SAXException {
418         if ( internalDTDsubset ) {
419             if ( includeInternalDTDDeclarations ) {
420                 addDTDDeclaration( new ElementDecl( name, model ) );
421             }
422         }
423         else {
424             if ( includeExternalDTDDeclarations ) {
425                 addExternalDTDDeclaration( new ElementDecl( name, model ) );
426             }
427         }
428     }
429 
430     /***
431      * Report an attribute type declaration.
432      *
433      * <p>Only the effective (first) declaration for an attribute will
434      * be reported.  The type will be one of the strings "CDATA",
435      * "ID", "IDREF", "IDREFS", "NMTOKEN", "NMTOKENS", "ENTITY",
436      * "ENTITIES", a parenthesized token group with
437      * the separator "|" and all whitespace removed, or the word
438      * "NOTATION" followed by a space followed by a parenthesized
439      * token group with all whitespace removed.</p>
440      *
441      * <p>Any parameter entities in the attribute value will be
442      * expanded, but general entities will not.</p>
443      *
444      * @param eName The name of the associated element.
445      * @param aName The name of the attribute.
446      * @param type A string representing the attribute type.
447      * @param valueDefault A string representing the attribute default
448      *       ("#IMPLIED", "#REQUIRED", or "#FIXED") or null if
449      *       none of these applies.
450      * @param value A string representing the attribute's default value,
451      *       or null if there is none.
452      * @exception SAXException The application may raise an exception.
453      */
454     public void attributeDecl(String eName,String aName,String type,String valueDefault,String value) throws SAXException {
455         if ( internalDTDsubset ) {
456             if ( includeInternalDTDDeclarations ) {
457                 addDTDDeclaration( new AttributeDecl( eName, aName, type, valueDefault, value) );
458             }
459         }
460         else {
461             if ( includeExternalDTDDeclarations ) {
462                 addExternalDTDDeclaration( new AttributeDecl( eName, aName, type, valueDefault, value) );
463             }
464         }
465     }
466 
467     /***
468      * Report an internal entity declaration.
469      *
470      * <p>Only the effective (first) declaration for each entity
471      * will be reported.  All parameter entities in the value
472      * will be expanded, but general entities will not.</p>
473      *
474      * @param name The name of the entity.  If it is a parameter
475      *       entity, the name will begin with '%'.
476      * @param value The replacement text of the entity.
477      * @exception SAXException The application may raise an exception.
478      * @see #externalEntityDecl
479      * @see org.xml.sax.DTDHandler#unparsedEntityDecl
480      */
481     public void internalEntityDecl(String name, String value) throws SAXException {
482         if ( internalDTDsubset ) {
483             if ( includeInternalDTDDeclarations ) {
484                 addDTDDeclaration( new InternalEntityDecl( name, value ) );
485             }
486         }
487         else {
488             if ( includeExternalDTDDeclarations ) {
489                 addExternalDTDDeclaration( new InternalEntityDecl( name, value ) );
490             }
491         }
492     }
493 
494     /***
495      * Report a parsed external entity declaration.
496      *
497      * <p>Only the effective (first) declaration for each entity
498      * will be reported.</p>
499      *
500      * @param name The name of the entity.  If it is a parameter
501      *       entity, the name will begin with '%'.
502      * @param publicId The declared public identifier of the entity, or
503      *       null if none was declared.
504      * @param systemId The declared system identifier of the entity.
505      * @exception SAXException The application may raise an exception.
506      * @see #internalEntityDecl
507      * @see org.xml.sax.DTDHandler#unparsedEntityDecl
508      */
509     public void externalEntityDecl(String name, String publicId, String systemId) throws SAXException {
510         if ( internalDTDsubset ) {
511             if ( includeInternalDTDDeclarations ) {
512                 addDTDDeclaration( new ExternalEntityDecl( name, publicId, systemId ) );
513             }
514         }
515         else {
516             if ( includeExternalDTDDeclarations ) {
517                 addExternalDTDDeclaration( new ExternalEntityDecl( name, publicId, systemId ) );
518             }
519         }
520     }
521 
522 
523     // DTDHandler interface
524     //-------------------------------------------------------------------------
525 
526     /***
527      * Receive notification of a notation declaration event.
528      *
529      * <p>It is up to the application to record the notation for later
530      * reference, if necessary.</p>
531      *
532      * <p>At least one of publicId and systemId must be non-null.
533      * If a system identifier is present, and it is a URL, the SAX
534      * parser must resolve it fully before passing it to the
535      * application through this event.</p>
536      *
537      * <p>There is no guarantee that the notation declaration will be
538      * reported before any unparsed entities that use it.</p>
539      *
540      * @param name The notation name.
541      * @param publicId The notation's public identifier, or null if
542      *       none was given.
543      * @param systemId The notation's system identifier, or null if
544      *       none was given.
545      * @exception org.xml.sax.SAXException Any SAX exception, possibly
546      *           wrapping another exception.
547      * @see #unparsedEntityDecl
548      * @see org.xml.sax.AttributeList
549      */
550     public void notationDecl(String name,String publicId,String systemId) throws SAXException {
551         // #### not supported yet!
552     }
553 
554     /***
555      * Receive notification of an unparsed entity declaration event.
556      *
557      * <p>Note that the notation name corresponds to a notation
558      * reported by the {@link #notationDecl notationDecl} event.
559      * It is up to the application to record the entity for later
560      * reference, if necessary.</p>
561      *
562      * <p>If the system identifier is a URL, the parser must resolve it
563      * fully before passing it to the application.</p>
564      *
565      * @exception org.xml.sax.SAXException Any SAX exception, possibly
566      *           wrapping another exception.
567      * @param name The unparsed entity's name.
568      * @param publicId The entity's public identifier, or null if none
569      *       was given.
570      * @param systemId The entity's system identifier.
571      * @param notationName The name of the associated notation.
572      * @see #notationDecl
573      * @see org.xml.sax.AttributeList
574      */
575     public void unparsedEntityDecl(String name,String publicId,String systemId,String notationName) throws SAXException {
576         // #### not supported yet!
577     }
578 
579 
580     // Properties
581     //-------------------------------------------------------------------------
582     public ElementStack getElementStack() {
583         return elementStack;
584     }
585 
586     public void setElementStack(ElementStack elementStack) {
587         this.elementStack = elementStack;
588     }
589 
590     public EntityResolver getEntityResolver() {
591         return entityResolver;
592     }
593 
594     public void setEntityResolver(EntityResolver entityResolver) {
595         this.entityResolver = entityResolver;
596     }
597 
598     public InputSource getInputSource() {
599         return inputSource;
600     }
601 
602     public void setInputSource(InputSource inputSource) {
603         this.inputSource = inputSource;
604     }
605 
606     /*** @return whether internal DTD declarations should be expanded into the DocumentType
607       * object or not.
608       */
609     public boolean isIncludeInternalDTDDeclarations() {
610         return includeInternalDTDDeclarations;
611     }
612 
613     /*** Sets whether internal DTD declarations should be expanded into the DocumentType
614       * object or not.
615       *
616       * @param includeInternalDTDDeclarations whether or not DTD declarations should be expanded
617       * and included into the DocumentType object.
618       */
619     public void setIncludeInternalDTDDeclarations(boolean includeInternalDTDDeclarations) {
620         this.includeInternalDTDDeclarations = includeInternalDTDDeclarations;
621     }
622 
623     /*** @return whether external DTD declarations should be expanded into the DocumentType
624       * object or not.
625       */
626     public boolean isIncludeExternalDTDDeclarations() {
627         return includeExternalDTDDeclarations;
628     }
629 
630     /*** Sets whether DTD external declarations should be expanded into the DocumentType
631       * object or not.
632       *
633       * @param includeExternalDTDDeclarations whether or not DTD declarations should be expanded
634       * and included into the DocumentType object.
635       */
636     public void setIncludeExternalDTDDeclarations(boolean includeExternalDTDDeclarations) {
637         this.includeExternalDTDDeclarations = includeExternalDTDDeclarations;
638     }
639 
640     /*** Returns whether adjacent text nodes should be merged together.
641       * @return Value of property mergeAdjacentText.
642       */
643     public boolean isMergeAdjacentText() {
644         return mergeAdjacentText;
645     }
646 
647     /*** Sets whether or not adjacent text nodes should be merged
648       * together when parsing.
649       * @param mergeAdjacentText New value of property mergeAdjacentText.
650       */
651     public void setMergeAdjacentText(boolean mergeAdjacentText) {
652         this.mergeAdjacentText = mergeAdjacentText;
653     }
654 
655 
656     /*** Sets whether whitespace between element start and end tags should be ignored
657       *
658       * @return Value of property stripWhitespaceText.
659       */
660     public boolean isStripWhitespaceText() {
661         return stripWhitespaceText;
662     }
663 
664     /*** Sets whether whitespace between element start and end tags should be ignored.
665       *
666       * @param stripWhitespaceText New value of property stripWhitespaceText.
667       */
668     public void setStripWhitespaceText(boolean stripWhitespaceText) {
669         this.stripWhitespaceText = stripWhitespaceText;
670     }
671 
672     /***
673      * Returns whether we should ignore comments or not.
674      * @return boolean
675      */
676     public boolean isIgnoreComments() {
677         return ignoreComments;
678     }
679 
680     /***
681      * Sets whether we should ignore comments or not.
682      * @param ignoreComments whether we should ignore comments or not.
683      */
684     public void setIgnoreComments(boolean ignoreComments) {
685         this.ignoreComments = ignoreComments;
686     }
687 
688 
689     // Implementation methods
690     //-------------------------------------------------------------------------
691 
692     /*** If the current text buffer contains any text then create a new
693       * text node with it and add it to the current element
694       */
695     protected void completeCurrentTextNode() {
696         if ( stripWhitespaceText ) {
697             boolean whitespace = true;
698             for ( int i = 0, size = textBuffer.length(); i < size; i++ ) {
699                 if ( ! Character.isWhitespace( textBuffer.charAt(i) ) ) {
700                     whitespace = false;
701                     break;
702                 }
703             }
704             if ( ! whitespace ) {
705                 currentElement.addText( textBuffer.toString() );
706             }
707         }
708         else {
709             currentElement.addText( textBuffer.toString() );
710         }
711         textBuffer.setLength(0);
712         textInTextBuffer = false;
713     }
714 
715     /*** @return the current document
716       */
717     protected Document createDocument() {
718         String encoding = getEncoding();
719         Document document = documentFactory.createDocument(encoding);
720 
721         // set the EntityResolver
722         document.setEntityResolver(entityResolver);
723         if (inputSource != null) {
724             document.setName(inputSource.getSystemId());
725         }
726 
727         return document;
728     }
729     
730     private String getEncoding() {
731         if (locator == null) {
732             return null;
733         }
734         
735         // use reflection to avoid dependency on Locator2 
736         // or other locator implemenations.
737         try {
738             Method m = locator.getClass().getMethod("getEncoding", new Class[]{});
739             if (m != null) {
740                 return (String) m.invoke(locator, null);
741             }
742         } catch (Exception e) {
743             // do nothing
744         }
745         
746         // couldn't determine encoding, returning null...
747         return null;
748     }
749 
750     /*** a Strategy Method to determine if a given entity name is ignorable
751       */
752     protected boolean isIgnorableEntity(String name) {
753         return "amp".equals( name )
754             || "apos".equals( name )
755             || "gt".equals( name )
756             || "lt".equals( name )
757             || "quot".equals( name );
758     }
759 
760 
761     /*** Add all namespaces declared before the startElement() SAX event
762       * to the current element so that they are available to child elements
763       * and attributes
764       */
765     protected void addDeclaredNamespaces(Element element) {
766         Namespace elementNamespace = element.getNamespace();
767         for ( int size = namespaceStack.size(); declaredNamespaceIndex < size; declaredNamespaceIndex++ ) {
768             Namespace namespace = namespaceStack.getNamespace(declaredNamespaceIndex);
769 //            if ( namespace != elementNamespace ) {
770                 element.add( namespace );
771 //            }
772         }
773     }
774 
775     /*** Add all the attributes to the given elements
776       */
777     protected void addAttributes( Element element, Attributes attributes ) {
778         // XXXX: as an optimisation, we could deduce this value from the current
779         // SAX parser settings, the SAX namespaces-prefixes feature
780 
781         boolean noNamespaceAttributes = false;
782         if ( element instanceof AbstractElement ) {
783             // optimised method
784             AbstractElement baseElement = (AbstractElement) element;
785             baseElement.setAttributes( attributes, namespaceStack, noNamespaceAttributes );
786         }
787         else {
788             int size = attributes.getLength();
789             for ( int i = 0; i < size; i++ ) {
790                 String attributeQualifiedName = attributes.getQName(i);
791                 if ( noNamespaceAttributes || ! attributeQualifiedName.startsWith( "xmlns" ) ) {
792                     String attributeURI = attributes.getURI(i);
793                     String attributeLocalName = attributes.getLocalName(i);
794                     String attributeValue = attributes.getValue(i);
795 
796                     QName attributeQName = namespaceStack.getAttributeQName(
797                         attributeURI, attributeLocalName, attributeQualifiedName
798                     );
799                     element.addAttribute(attributeQName, attributeValue);
800                 }
801             }
802         }
803     }
804 
805 
806     /*** Adds an internal DTD declaration to the list of declarations */
807     protected void addDTDDeclaration(Object declaration) {
808         if ( internalDTDDeclarations == null ) {
809             internalDTDDeclarations = new ArrayList();
810         }
811         internalDTDDeclarations.add( declaration );
812     }
813 
814     /*** Adds an external DTD declaration to the list of declarations */
815     protected void addExternalDTDDeclaration(Object declaration) {
816         if ( externalDTDDeclarations == null ) {
817             externalDTDDeclarations = new ArrayList();
818         }
819         externalDTDDeclarations.add( declaration );
820     }
821 
822     protected ElementStack createElementStack() {
823         return new ElementStack();
824     }
825 }
826 
827 
828 
829 
830 /*
831  * Redistribution and use of this software and associated documentation
832  * ("Software"), with or without modification, are permitted provided
833  * that the following conditions are met:
834  *
835  * 1. Redistributions of source code must retain copyright
836  *    statements and notices.  Redistributions must also contain a
837  *    copy of this document.
838  *
839  * 2. Redistributions in binary form must reproduce the
840  *    above copyright notice, this list of conditions and the
841  *    following disclaimer in the documentation and/or other
842  *    materials provided with the distribution.
843  *
844  * 3. The name "DOM4J" must not be used to endorse or promote
845  *    products derived from this Software without prior written
846  *    permission of MetaStuff, Ltd.  For written permission,
847  *    please contact dom4j-info@metastuff.com.
848  *
849  * 4. Products derived from this Software may not be called "DOM4J"
850  *    nor may "DOM4J" appear in their names without prior written
851  *    permission of MetaStuff, Ltd. DOM4J is a registered
852  *    trademark of MetaStuff, Ltd.
853  *
854  * 5. Due credit should be given to the DOM4J Project - 
855  *    http://www.dom4j.org
856  *
857  * THIS SOFTWARE IS PROVIDED BY METASTUFF, LTD. AND CONTRIBUTORS
858  * ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
859  * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
860  * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
861  * METASTUFF, LTD. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
862  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
863  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
864  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
865  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
866  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
867  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
868  * OF THE POSSIBILITY OF SUCH DAMAGE.
869  *
870  * Copyright 2001-2004 (C) MetaStuff, Ltd. All Rights Reserved.
871  *
872  * $Id: SAXContentHandler.java,v 1.59 2004/08/06 09:51:50 maartenc Exp $
873  */