View Javadoc
1   /*
2    * reserved comment block
3    * DO NOT REMOVE OR ALTER!
4    */
5   /*
6    * Copyright 1999-2004 The Apache Software Foundation.
7    *
8    * Licensed under the Apache License, Version 2.0 (the "License");
9    * you may not use this file except in compliance with the License.
10   * You may obtain a copy of the License at
11   *
12   *     http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing, software
15   * distributed under the License is distributed on an "AS IS" BASIS,
16   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17   * See the License for the specific language governing permissions and
18   * limitations under the License.
19   */
20  /*
21   * $Id: DOMBuilder.java,v 1.2.4.1 2005/09/15 08:15:39 suresh_emailid Exp $
22   */
23  package com.sun.org.apache.xml.internal.utils;
24  
25  import java.util.Stack;
26  
27  import com.sun.org.apache.xml.internal.res.XMLErrorResources;
28  import com.sun.org.apache.xml.internal.res.XMLMessages;
29  
30  import org.w3c.dom.Document;
31  import org.w3c.dom.DocumentFragment;
32  import org.w3c.dom.Element;
33  import org.w3c.dom.Node;
34  import org.w3c.dom.Text;
35  import org.w3c.dom.CDATASection;
36  
37  import org.xml.sax.Attributes;
38  import org.xml.sax.ContentHandler;
39  import org.xml.sax.Locator;
40  import org.xml.sax.ext.LexicalHandler;
41  /**
42   * This class takes SAX events (in addition to some extra events
43   * that SAX doesn't handle yet) and adds the result to a document
44   * or document fragment.
45   * @xsl.usage general
46   */
47  public class DOMBuilder
48          implements ContentHandler, LexicalHandler
49  {
50  
51    /** Root document          */
52    public Document m_doc;
53  
54    /** Current node           */
55    protected Node m_currentNode = null;
56  
57    /** The root node          */
58    protected Node m_root = null;
59  
60    /** The next sibling node  */
61    protected Node m_nextSibling = null;
62  
63    /** First node of document fragment or null if not a DocumentFragment     */
64    public DocumentFragment m_docFrag = null;
65  
66    /** Vector of element nodes          */
67    protected Stack m_elemStack = new Stack();
68  
69    /**
70     * DOMBuilder instance constructor... it will add the DOM nodes
71     * to the document fragment.
72     *
73     * @param doc Root document
74     * @param node Current node
75     */
76    public DOMBuilder(Document doc, Node node)
77    {
78      m_doc = doc;
79      m_currentNode = m_root = node;
80  
81      if (node instanceof Element)
82        m_elemStack.push(node);
83    }
84  
85    /**
86     * DOMBuilder instance constructor... it will add the DOM nodes
87     * to the document fragment.
88     *
89     * @param doc Root document
90     * @param docFrag Document fragment
91     */
92    public DOMBuilder(Document doc, DocumentFragment docFrag)
93    {
94      m_doc = doc;
95      m_docFrag = docFrag;
96    }
97  
98    /**
99     * DOMBuilder instance constructor... it will add the DOM nodes
100    * to the document.
101    *
102    * @param doc Root document
103    */
104   public DOMBuilder(Document doc)
105   {
106     m_doc = doc;
107   }
108 
109   /**
110    * Get the root document or DocumentFragment of the DOM being created.
111    *
112    * @return The root document or document fragment if not null
113    */
114   public Node getRootDocument()
115   {
116     return (null != m_docFrag) ? (Node) m_docFrag : (Node) m_doc;
117   }
118 
119   /**
120    * Get the root node of the DOM tree.
121    */
122   public Node getRootNode()
123   {
124     return m_root;
125   }
126 
127   /**
128    * Get the node currently being processed.
129    *
130    * @return the current node being processed
131    */
132   public Node getCurrentNode()
133   {
134     return m_currentNode;
135   }
136 
137   /**
138    * Set the next sibling node, which is where the result nodes
139    * should be inserted before.
140    *
141    * @param nextSibling the next sibling node.
142    */
143   public void setNextSibling(Node nextSibling)
144   {
145     m_nextSibling = nextSibling;
146   }
147 
148   /**
149    * Return the next sibling node.
150    *
151    * @return the next sibling node.
152    */
153   public Node getNextSibling()
154   {
155     return m_nextSibling;
156   }
157 
158   /**
159    * Return null since there is no Writer for this class.
160    *
161    * @return null
162    */
163   public java.io.Writer getWriter()
164   {
165     return null;
166   }
167 
168   /**
169    * Append a node to the current container.
170    *
171    * @param newNode New node to append
172    */
173   protected void append(Node newNode) throws org.xml.sax.SAXException
174   {
175 
176     Node currentNode = m_currentNode;
177 
178     if (null != currentNode)
179     {
180       if (currentNode == m_root && m_nextSibling != null)
181         currentNode.insertBefore(newNode, m_nextSibling);
182       else
183         currentNode.appendChild(newNode);
184 
185       // System.out.println(newNode.getNodeName());
186     }
187     else if (null != m_docFrag)
188     {
189       if (m_nextSibling != null)
190         m_docFrag.insertBefore(newNode, m_nextSibling);
191       else
192         m_docFrag.appendChild(newNode);
193     }
194     else
195     {
196       boolean ok = true;
197       short type = newNode.getNodeType();
198 
199       if (type == Node.TEXT_NODE)
200       {
201         String data = newNode.getNodeValue();
202 
203         if ((null != data) && (data.trim().length() > 0))
204         {
205           throw new org.xml.sax.SAXException(
206             XMLMessages.createXMLMessage(
207               XMLErrorResources.ER_CANT_OUTPUT_TEXT_BEFORE_DOC, null));  //"Warning: can't output text before document element!  Ignoring...");
208         }
209 
210         ok = false;
211       }
212       else if (type == Node.ELEMENT_NODE)
213       {
214         if (m_doc.getDocumentElement() != null)
215         {
216           ok = false;
217 
218           throw new org.xml.sax.SAXException(
219             XMLMessages.createXMLMessage(
220               XMLErrorResources.ER_CANT_HAVE_MORE_THAN_ONE_ROOT, null));  //"Can't have more than one root on a DOM!");
221         }
222       }
223 
224       if (ok)
225       {
226         if (m_nextSibling != null)
227           m_doc.insertBefore(newNode, m_nextSibling);
228         else
229           m_doc.appendChild(newNode);
230       }
231     }
232   }
233 
234   /**
235    * Receive an object for locating the origin of SAX document events.
236    *
237    * <p>SAX parsers are strongly encouraged (though not absolutely
238    * required) to supply a locator: if it does so, it must supply
239    * the locator to the application by invoking this method before
240    * invoking any of the other methods in the ContentHandler
241    * interface.</p>
242    *
243    * <p>The locator allows the application to determine the end
244    * position of any document-related event, even if the parser is
245    * not reporting an error.  Typically, the application will
246    * use this information for reporting its own errors (such as
247    * character content that does not match an application's
248    * business rules).  The information returned by the locator
249    * is probably not sufficient for use with a search engine.</p>
250    *
251    * <p>Note that the locator will return correct information only
252    * during the invocation of the events in this interface.  The
253    * application should not attempt to use it at any other time.</p>
254    *
255    * @param locator An object that can return the location of
256    *                any SAX document event.
257    * @see org.xml.sax.Locator
258    */
259   public void setDocumentLocator(Locator locator)
260   {
261 
262     // No action for the moment.
263   }
264 
265   /**
266    * Receive notification of the beginning of a document.
267    *
268    * <p>The SAX parser will invoke this method only once, before any
269    * other methods in this interface or in DTDHandler (except for
270    * setDocumentLocator).</p>
271    */
272   public void startDocument() throws org.xml.sax.SAXException
273   {
274 
275     // No action for the moment.
276   }
277 
278   /**
279    * Receive notification of the end of a document.
280    *
281    * <p>The SAX parser will invoke this method only once, and it will
282    * be the last method invoked during the parse.  The parser shall
283    * not invoke this method until it has either abandoned parsing
284    * (because of an unrecoverable error) or reached the end of
285    * input.</p>
286    */
287   public void endDocument() throws org.xml.sax.SAXException
288   {
289 
290     // No action for the moment.
291   }
292 
293   /**
294    * Receive notification of the beginning of an element.
295    *
296    * <p>The Parser will invoke this method at the beginning of every
297    * element in the XML document; there will be a corresponding
298    * endElement() event for every startElement() event (even when the
299    * element is empty). All of the element's content will be
300    * reported, in order, before the corresponding endElement()
301    * event.</p>
302    *
303    * <p>If the element name has a namespace prefix, the prefix will
304    * still be attached.  Note that the attribute list provided will
305    * contain only attributes with explicit values (specified or
306    * defaulted): #IMPLIED attributes will be omitted.</p>
307    *
308    *
309    * @param ns The namespace of the node
310    * @param localName The local part of the qualified name
311    * @param name The element name.
312    * @param atts The attributes attached to the element, if any.
313    * @see #endElement
314    * @see org.xml.sax.Attributes
315    */
316   public void startElement(
317           String ns, String localName, String name, Attributes atts)
318             throws org.xml.sax.SAXException
319   {
320 
321     Element elem;
322 
323         // Note that the namespace-aware call must be used to correctly
324         // construct a Level 2 DOM, even for non-namespaced nodes.
325     if ((null == ns) || (ns.length() == 0))
326       elem = m_doc.createElementNS(null,name);
327     else
328       elem = m_doc.createElementNS(ns, name);
329 
330     append(elem);
331 
332     try
333     {
334       int nAtts = atts.getLength();
335 
336       if (0 != nAtts)
337       {
338         for (int i = 0; i < nAtts; i++)
339         {
340 
341           //System.out.println("type " + atts.getType(i) + " name " + atts.getLocalName(i) );
342           // First handle a possible ID attribute
343           if (atts.getType(i).equalsIgnoreCase("ID"))
344             setIDAttribute(atts.getValue(i), elem);
345 
346           String attrNS = atts.getURI(i);
347 
348           if("".equals(attrNS))
349             attrNS = null; // DOM represents no-namespace as null
350 
351           // System.out.println("attrNS: "+attrNS+", localName: "+atts.getQName(i)
352           //                   +", qname: "+atts.getQName(i)+", value: "+atts.getValue(i));
353           // Crimson won't let us set an xmlns: attribute on the DOM.
354           String attrQName = atts.getQName(i);
355 
356           // In SAX, xmlns[:] attributes have an empty namespace, while in DOM they
357           // should have the xmlns namespace
358           if (attrQName.startsWith("xmlns:") || attrQName.equals("xmlns")) {
359             attrNS = "http://www.w3.org/2000/xmlns/";
360           }
361 
362           // ALWAYS use the DOM Level 2 call!
363           elem.setAttributeNS(attrNS,attrQName, atts.getValue(i));
364         }
365       }
366 
367       // append(elem);
368 
369       m_elemStack.push(elem);
370 
371       m_currentNode = elem;
372 
373       // append(elem);
374     }
375     catch(java.lang.Exception de)
376     {
377       // de.printStackTrace();
378       throw new org.xml.sax.SAXException(de);
379     }
380 
381   }
382 
383   /**
384 
385 
386 
387    * Receive notification of the end of an element.
388    *
389    * <p>The SAX parser will invoke this method at the end of every
390    * element in the XML document; there will be a corresponding
391    * startElement() event for every endElement() event (even when the
392    * element is empty).</p>
393    *
394    * <p>If the element name has a namespace prefix, the prefix will
395    * still be attached to the name.</p>
396    *
397    *
398    * @param ns the namespace of the element
399    * @param localName The local part of the qualified name of the element
400    * @param name The element name
401    */
402   public void endElement(String ns, String localName, String name)
403           throws org.xml.sax.SAXException
404   {
405     m_elemStack.pop();
406     m_currentNode = m_elemStack.isEmpty() ? null : (Node)m_elemStack.peek();
407   }
408 
409   /**
410    * Set an ID string to node association in the ID table.
411    *
412    * @param id The ID string.
413    * @param elem The associated ID.
414    */
415   public void setIDAttribute(String id, Element elem)
416   {
417 
418     // Do nothing. This method is meant to be overiden.
419   }
420 
421   /**
422    * Receive notification of character data.
423    *
424    * <p>The Parser will call this method to report each chunk of
425    * character data.  SAX parsers may return all contiguous character
426    * data in a single chunk, or they may split it into several
427    * chunks; however, all of the characters in any single event
428    * must come from the same external entity, so that the Locator
429    * provides useful information.</p>
430    *
431    * <p>The application must not attempt to read from the array
432    * outside of the specified range.</p>
433    *
434    * <p>Note that some parsers will report whitespace using the
435    * ignorableWhitespace() method rather than this one (validating
436    * parsers must do so).</p>
437    *
438    * @param ch The characters from the XML document.
439    * @param start The start position in the array.
440    * @param length The number of characters to read from the array.
441    * @see #ignorableWhitespace
442    * @see org.xml.sax.Locator
443    */
444   public void characters(char ch[], int start, int length) throws org.xml.sax.SAXException
445   {
446     if(isOutsideDocElem()
447        && com.sun.org.apache.xml.internal.utils.XMLCharacterRecognizer.isWhiteSpace(ch, start, length))
448       return;  // avoid DOM006 Hierarchy request error
449 
450     if (m_inCData)
451     {
452       cdata(ch, start, length);
453 
454       return;
455     }
456 
457     String s = new String(ch, start, length);
458     Node childNode;
459     childNode =  m_currentNode != null ? m_currentNode.getLastChild(): null;
460     if( childNode != null && childNode.getNodeType() == Node.TEXT_NODE ){
461        ((Text)childNode).appendData(s);
462     }
463     else{
464        Text text = m_doc.createTextNode(s);
465        append(text);
466     }
467   }
468 
469   /**
470    * If available, when the disable-output-escaping attribute is used,
471    * output raw text without escaping.  A PI will be inserted in front
472    * of the node with the name "lotusxsl-next-is-raw" and a value of
473    * "formatter-to-dom".
474    *
475    * @param ch Array containing the characters
476    * @param start Index to start of characters in the array
477    * @param length Number of characters in the array
478    */
479   public void charactersRaw(char ch[], int start, int length)
480           throws org.xml.sax.SAXException
481   {
482     if(isOutsideDocElem()
483        && com.sun.org.apache.xml.internal.utils.XMLCharacterRecognizer.isWhiteSpace(ch, start, length))
484       return;  // avoid DOM006 Hierarchy request error
485 
486 
487     String s = new String(ch, start, length);
488 
489     append(m_doc.createProcessingInstruction("xslt-next-is-raw",
490                                              "formatter-to-dom"));
491     append(m_doc.createTextNode(s));
492   }
493 
494   /**
495    * Report the beginning of an entity.
496    *
497    * The start and end of the document entity are not reported.
498    * The start and end of the external DTD subset are reported
499    * using the pseudo-name "[dtd]".  All other events must be
500    * properly nested within start/end entity events.
501    *
502    * @param name The name of the entity.  If it is a parameter
503    *        entity, the name will begin with '%'.
504    * @see #endEntity
505    * @see org.xml.sax.ext.DeclHandler#internalEntityDecl
506    * @see org.xml.sax.ext.DeclHandler#externalEntityDecl
507    */
508   public void startEntity(String name) throws org.xml.sax.SAXException
509   {
510 
511     // Almost certainly the wrong behavior...
512     // entityReference(name);
513   }
514 
515   /**
516    * Report the end of an entity.
517    *
518    * @param name The name of the entity that is ending.
519    * @see #startEntity
520    */
521   public void endEntity(String name) throws org.xml.sax.SAXException{}
522 
523   /**
524    * Receive notivication of a entityReference.
525    *
526    * @param name name of the entity reference
527    */
528   public void entityReference(String name) throws org.xml.sax.SAXException
529   {
530     append(m_doc.createEntityReference(name));
531   }
532 
533   /**
534    * Receive notification of ignorable whitespace in element content.
535    *
536    * <p>Validating Parsers must use this method to report each chunk
537    * of ignorable whitespace (see the W3C XML 1.0 recommendation,
538    * section 2.10): non-validating parsers may also use this method
539    * if they are capable of parsing and using content models.</p>
540    *
541    * <p>SAX parsers may return all contiguous whitespace in a single
542    * chunk, or they may split it into several chunks; however, all of
543    * the characters in any single event must come from the same
544    * external entity, so that the Locator provides useful
545    * information.</p>
546    *
547    * <p>The application must not attempt to read from the array
548    * outside of the specified range.</p>
549    *
550    * @param ch The characters from the XML document.
551    * @param start The start position in the array.
552    * @param length The number of characters to read from the array.
553    * @see #characters
554    */
555   public void ignorableWhitespace(char ch[], int start, int length)
556           throws org.xml.sax.SAXException
557   {
558     if(isOutsideDocElem())
559       return;  // avoid DOM006 Hierarchy request error
560 
561     String s = new String(ch, start, length);
562 
563     append(m_doc.createTextNode(s));
564   }
565 
566   /**
567    * Tell if the current node is outside the document element.
568    *
569    * @return true if the current node is outside the document element.
570    */
571    private boolean isOutsideDocElem()
572    {
573       return (null == m_docFrag) && m_elemStack.size() == 0 && (null == m_currentNode || m_currentNode.getNodeType() == Node.DOCUMENT_NODE);
574    }
575 
576   /**
577    * Receive notification of a processing instruction.
578    *
579    * <p>The Parser will invoke this method once for each processing
580    * instruction found: note that processing instructions may occur
581    * before or after the main document element.</p>
582    *
583    * <p>A SAX parser should never report an XML declaration (XML 1.0,
584    * section 2.8) or a text declaration (XML 1.0, section 4.3.1)
585    * using this method.</p>
586    *
587    * @param target The processing instruction target.
588    * @param data The processing instruction data, or null if
589    *        none was supplied.
590    */
591   public void processingInstruction(String target, String data)
592           throws org.xml.sax.SAXException
593   {
594     append(m_doc.createProcessingInstruction(target, data));
595   }
596 
597   /**
598    * Report an XML comment anywhere in the document.
599    *
600    * This callback will be used for comments inside or outside the
601    * document element, including comments in the external DTD
602    * subset (if read).
603    *
604    * @param ch An array holding the characters in the comment.
605    * @param start The starting position in the array.
606    * @param length The number of characters to use from the array.
607    */
608   public void comment(char ch[], int start, int length) throws org.xml.sax.SAXException
609   {
610     append(m_doc.createComment(new String(ch, start, length)));
611   }
612 
613   /** Flag indicating that we are processing a CData section          */
614   protected boolean m_inCData = false;
615 
616   /**
617    * Report the start of a CDATA section.
618    *
619    * @see #endCDATA
620    */
621   public void startCDATA() throws org.xml.sax.SAXException
622   {
623     m_inCData = true;
624     append(m_doc.createCDATASection(""));
625   }
626 
627   /**
628    * Report the end of a CDATA section.
629    *
630    * @see #startCDATA
631    */
632   public void endCDATA() throws org.xml.sax.SAXException
633   {
634     m_inCData = false;
635   }
636 
637   /**
638    * Receive notification of cdata.
639    *
640    * <p>The Parser will call this method to report each chunk of
641    * character data.  SAX parsers may return all contiguous character
642    * data in a single chunk, or they may split it into several
643    * chunks; however, all of the characters in any single event
644    * must come from the same external entity, so that the Locator
645    * provides useful information.</p>
646    *
647    * <p>The application must not attempt to read from the array
648    * outside of the specified range.</p>
649    *
650    * <p>Note that some parsers will report whitespace using the
651    * ignorableWhitespace() method rather than this one (validating
652    * parsers must do so).</p>
653    *
654    * @param ch The characters from the XML document.
655    * @param start The start position in the array.
656    * @param length The number of characters to read from the array.
657    * @see #ignorableWhitespace
658    * @see org.xml.sax.Locator
659    */
660   public void cdata(char ch[], int start, int length) throws org.xml.sax.SAXException
661   {
662     if(isOutsideDocElem()
663        && com.sun.org.apache.xml.internal.utils.XMLCharacterRecognizer.isWhiteSpace(ch, start, length))
664       return;  // avoid DOM006 Hierarchy request error
665 
666     String s = new String(ch, start, length);
667 
668     CDATASection section  =(CDATASection) m_currentNode.getLastChild();
669     section.appendData(s);
670   }
671 
672   /**
673    * Report the start of DTD declarations, if any.
674    *
675    * Any declarations are assumed to be in the internal subset
676    * unless otherwise indicated.
677    *
678    * @param name The document type name.
679    * @param publicId The declared public identifier for the
680    *        external DTD subset, or null if none was declared.
681    * @param systemId The declared system identifier for the
682    *        external DTD subset, or null if none was declared.
683    * @see #endDTD
684    * @see #startEntity
685    */
686   public void startDTD(String name, String publicId, String systemId)
687           throws org.xml.sax.SAXException
688   {
689 
690     // Do nothing for now.
691   }
692 
693   /**
694    * Report the end of DTD declarations.
695    *
696    * @see #startDTD
697    */
698   public void endDTD() throws org.xml.sax.SAXException
699   {
700 
701     // Do nothing for now.
702   }
703 
704   /**
705    * Begin the scope of a prefix-URI Namespace mapping.
706    *
707    * <p>The information from this event is not necessary for
708    * normal Namespace processing: the SAX XML reader will
709    * automatically replace prefixes for element and attribute
710    * names when the http://xml.org/sax/features/namespaces
711    * feature is true (the default).</p>
712    *
713    * <p>There are cases, however, when applications need to
714    * use prefixes in character data or in attribute values,
715    * where they cannot safely be expanded automatically; the
716    * start/endPrefixMapping event supplies the information
717    * to the application to expand prefixes in those contexts
718    * itself, if necessary.</p>
719    *
720    * <p>Note that start/endPrefixMapping events are not
721    * guaranteed to be properly nested relative to each-other:
722    * all startPrefixMapping events will occur before the
723    * corresponding startElement event, and all endPrefixMapping
724    * events will occur after the corresponding endElement event,
725    * but their order is not guaranteed.</p>
726    *
727    * @param prefix The Namespace prefix being declared.
728    * @param uri The Namespace URI the prefix is mapped to.
729    * @see #endPrefixMapping
730    * @see #startElement
731    */
732   public void startPrefixMapping(String prefix, String uri)
733           throws org.xml.sax.SAXException
734   {
735 
736     /*
737     // Not sure if this is needed or wanted
738     // Also, it fails in the stree.
739     if((null != m_currentNode)
740        && (m_currentNode.getNodeType() == Node.ELEMENT_NODE))
741     {
742       String qname;
743       if(((null != prefix) && (prefix.length() == 0))
744          || (null == prefix))
745         qname = "xmlns";
746       else
747         qname = "xmlns:"+prefix;
748 
749       Element elem = (Element)m_currentNode;
750       String val = elem.getAttribute(qname); // Obsolete, should be DOM2...?
751       if(val == null)
752       {
753         elem.setAttributeNS("http://www.w3.org/XML/1998/namespace",
754                             qname, uri);
755       }
756     }
757     */
758   }
759 
760   /**
761    * End the scope of a prefix-URI mapping.
762    *
763    * <p>See startPrefixMapping for details.  This event will
764    * always occur after the corresponding endElement event,
765    * but the order of endPrefixMapping events is not otherwise
766    * guaranteed.</p>
767    *
768    * @param prefix The prefix that was being mapping.
769    * @see #startPrefixMapping
770    * @see #endElement
771    */
772   public void endPrefixMapping(String prefix) throws org.xml.sax.SAXException{}
773 
774   /**
775    * Receive notification of a skipped entity.
776    *
777    * <p>The Parser will invoke this method once for each entity
778    * skipped.  Non-validating processors may skip entities if they
779    * have not seen the declarations (because, for example, the
780    * entity was declared in an external DTD subset).  All processors
781    * may skip external entities, depending on the values of the
782    * http://xml.org/sax/features/external-general-entities and the
783    * http://xml.org/sax/features/external-parameter-entities
784    * properties.</p>
785    *
786    * @param name The name of the skipped entity.  If it is a
787    *        parameter entity, the name will begin with '%'.
788    */
789   public void skippedEntity(String name) throws org.xml.sax.SAXException{}
790 }