KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > sun > org > apache > xml > internal > utils > DOMBuilder


1 /*
2  * Copyright 1999-2004 The Apache Software Foundation.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */

16 /*
17  * $Id: DOMBuilder.java,v 1.19 2004/02/25 13:07:51 aruny Exp $
18  */

19 package com.sun.org.apache.xml.internal.utils;
20
21 import java.util.Stack JavaDoc;
22
23 import com.sun.org.apache.xml.internal.res.XMLErrorResources;
24 import com.sun.org.apache.xml.internal.res.XMLMessages;
25
26 import org.w3c.dom.Document JavaDoc;
27 import org.w3c.dom.DocumentFragment JavaDoc;
28 import org.w3c.dom.Element JavaDoc;
29 import org.w3c.dom.Node JavaDoc;
30 import org.w3c.dom.Text JavaDoc;
31 import org.w3c.dom.CDATASection JavaDoc;
32
33 import org.xml.sax.Attributes JavaDoc;
34 import org.xml.sax.ContentHandler JavaDoc;
35 import org.xml.sax.Locator JavaDoc;
36 import org.xml.sax.ext.LexicalHandler JavaDoc;
37 /**
38  * This class takes SAX events (in addition to some extra events
39  * that SAX doesn't handle yet) and adds the result to a document
40  * or document fragment.
41  * @xsl.usage general
42  */

43 public class DOMBuilder
44         implements ContentHandler JavaDoc, LexicalHandler JavaDoc
45 {
46
47   /** Root document */
48   public Document JavaDoc m_doc;
49
50   /** Current node */
51   protected Node JavaDoc m_currentNode = null;
52
53   /** First node of document fragment or null if not a DocumentFragment */
54   public DocumentFragment JavaDoc m_docFrag = null;
55
56   /** Vector of element nodes */
57   protected Stack JavaDoc m_elemStack = new Stack JavaDoc();
58
59   /**
60    * DOMBuilder instance constructor... it will add the DOM nodes
61    * to the document fragment.
62    *
63    * @param doc Root document
64    * @param node Current node
65    */

66   public DOMBuilder(Document JavaDoc doc, Node JavaDoc node)
67   {
68     m_doc = doc;
69     m_currentNode = node;
70   }
71
72   /**
73    * DOMBuilder instance constructor... it will add the DOM nodes
74    * to the document fragment.
75    *
76    * @param doc Root document
77    * @param docFrag Document fragment
78    */

79   public DOMBuilder(Document JavaDoc doc, DocumentFragment JavaDoc docFrag)
80   {
81     m_doc = doc;
82     m_docFrag = docFrag;
83   }
84
85   /**
86    * DOMBuilder instance constructor... it will add the DOM nodes
87    * to the document.
88    *
89    * @param doc Root document
90    */

91   public DOMBuilder(Document JavaDoc doc)
92   {
93     m_doc = doc;
94   }
95
96   /**
97    * Get the root node of the DOM being created. This
98    * is either a Document or a DocumentFragment.
99    *
100    * @return The root document or document fragment if not null
101    */

102   public Node JavaDoc getRootNode()
103   {
104     return (null != m_docFrag) ? (Node JavaDoc) m_docFrag : (Node JavaDoc) m_doc;
105   }
106
107   /**
108    * Get the node currently being processed.
109    *
110    * @return the current node being processed
111    */

112   public Node JavaDoc getCurrentNode()
113   {
114     return m_currentNode;
115   }
116
117   /**
118    * Return null since there is no Writer for this class.
119    *
120    * @return null
121    */

122   public java.io.Writer JavaDoc getWriter()
123   {
124     return null;
125   }
126
127   /**
128    * Append a node to the current container.
129    *
130    * @param newNode New node to append
131    */

132   protected void append(Node JavaDoc newNode) throws org.xml.sax.SAXException JavaDoc
133   {
134
135     Node JavaDoc currentNode = m_currentNode;
136
137     if (null != currentNode)
138     {
139       currentNode.appendChild(newNode);
140
141       // System.out.println(newNode.getNodeName());
142
}
143     else if (null != m_docFrag)
144     {
145       m_docFrag.appendChild(newNode);
146     }
147     else
148     {
149       boolean ok = true;
150       short type = newNode.getNodeType();
151
152       if (type == Node.TEXT_NODE)
153       {
154         String JavaDoc data = newNode.getNodeValue();
155
156         if ((null != data) && (data.trim().length() > 0))
157         {
158           throw new org.xml.sax.SAXException JavaDoc(
159             XMLMessages.createXMLMessage(
160               XMLErrorResources.ER_CANT_OUTPUT_TEXT_BEFORE_DOC, null)); //"Warning: can't output text before document element! Ignoring...");
161
}
162
163         ok = false;
164       }
165       else if (type == Node.ELEMENT_NODE)
166       {
167         if (m_doc.getDocumentElement() != null)
168         {
169           throw new org.xml.sax.SAXException JavaDoc(
170             XMLMessages.createXMLMessage(
171               XMLErrorResources.ER_CANT_HAVE_MORE_THAN_ONE_ROOT, null)); //"Can't have more than one root on a DOM!");
172
}
173       }
174
175       if (ok)
176         m_doc.appendChild(newNode);
177     }
178   }
179
180   /**
181    * Receive an object for locating the origin of SAX document events.
182    *
183    * <p>SAX parsers are strongly encouraged (though not absolutely
184    * required) to supply a locator: if it does so, it must supply
185    * the locator to the application by invoking this method before
186    * invoking any of the other methods in the ContentHandler
187    * interface.</p>
188    *
189    * <p>The locator allows the application to determine the end
190    * position of any document-related event, even if the parser is
191    * not reporting an error. Typically, the application will
192    * use this information for reporting its own errors (such as
193    * character content that does not match an application's
194    * business rules). The information returned by the locator
195    * is probably not sufficient for use with a search engine.</p>
196    *
197    * <p>Note that the locator will return correct information only
198    * during the invocation of the events in this interface. The
199    * application should not attempt to use it at any other time.</p>
200    *
201    * @param locator An object that can return the location of
202    * any SAX document event.
203    * @see org.xml.sax.Locator
204    */

205   public void setDocumentLocator(Locator JavaDoc locator)
206   {
207
208     // No action for the moment.
209
}
210
211   /**
212    * Receive notification of the beginning of a document.
213    *
214    * <p>The SAX parser will invoke this method only once, before any
215    * other methods in this interface or in DTDHandler (except for
216    * setDocumentLocator).</p>
217    */

218   public void startDocument() throws org.xml.sax.SAXException JavaDoc
219   {
220
221     // No action for the moment.
222
}
223
224   /**
225    * Receive notification of the end of a document.
226    *
227    * <p>The SAX parser will invoke this method only once, and it will
228    * be the last method invoked during the parse. The parser shall
229    * not invoke this method until it has either abandoned parsing
230    * (because of an unrecoverable error) or reached the end of
231    * input.</p>
232    */

233   public void endDocument() throws org.xml.sax.SAXException JavaDoc
234   {
235
236     // No action for the moment.
237
}
238
239   /**
240    * Receive notification of the beginning of an element.
241    *
242    * <p>The Parser will invoke this method at the beginning of every
243    * element in the XML document; there will be a corresponding
244    * endElement() event for every startElement() event (even when the
245    * element is empty). All of the element's content will be
246    * reported, in order, before the corresponding endElement()
247    * event.</p>
248    *
249    * <p>If the element name has a namespace prefix, the prefix will
250    * still be attached. Note that the attribute list provided will
251    * contain only attributes with explicit values (specified or
252    * defaulted): #IMPLIED attributes will be omitted.</p>
253    *
254    *
255    * @param ns The namespace of the node
256    * @param localName The local part of the qualified name
257    * @param name The element name.
258    * @param atts The attributes attached to the element, if any.
259    * @see #endElement
260    * @see org.xml.sax.Attributes
261    */

262   public void startElement(
263           String JavaDoc ns, String JavaDoc localName, String JavaDoc name, Attributes JavaDoc atts)
264             throws org.xml.sax.SAXException JavaDoc
265   {
266
267     Element JavaDoc elem;
268
269     // Note that the namespace-aware call must be used to correctly
270
// construct a Level 2 DOM, even for non-namespaced nodes.
271
if ((null == ns) || (ns.length() == 0))
272       elem = m_doc.createElementNS(null,name);
273     else
274       elem = m_doc.createElementNS(ns, name);
275
276     append(elem);
277
278     try
279     {
280       int nAtts = atts.getLength();
281
282       if (0 != nAtts)
283       {
284         for (int i = 0; i < nAtts; i++)
285         {
286
287           //System.out.println("type " + atts.getType(i) + " name " + atts.getLocalName(i) );
288
// First handle a possible ID attribute
289
if (atts.getType(i).equalsIgnoreCase("ID"))
290             setIDAttribute(atts.getValue(i), elem);
291
292           String JavaDoc attrNS = atts.getURI(i);
293
294           if("".equals(attrNS))
295             attrNS = null; // DOM represents no-namespace as null
296

297           // System.out.println("attrNS: "+attrNS+", localName: "+atts.getQName(i)
298
// +", qname: "+atts.getQName(i)+", value: "+atts.getValue(i));
299
// Crimson won't let us set an xmlns: attribute on the DOM.
300
String JavaDoc attrQName = atts.getQName(i);
301
302           // In SAX, xmlns: attributes have an empty namespace, while in DOM they
303
// should have the xmlns namespace
304
if (attrQName.startsWith("xmlns:") || attrQName.equals("xmlns")) {
305             attrNS = "http://www.w3.org/2000/xmlns/";
306           }
307
308           // ALWAYS use the DOM Level 2 call!
309
elem.setAttributeNS(attrNS,attrQName, atts.getValue(i));
310         }
311       }
312
313       // append(elem);
314

315       m_elemStack.push(elem);
316
317       m_currentNode = elem;
318
319       // append(elem);
320
}
321     catch(java.lang.Exception JavaDoc de)
322     {
323       // de.printStackTrace();
324
throw new org.xml.sax.SAXException JavaDoc(de);
325     }
326
327   }
328
329   /**
330
331
332
333    * Receive notification of the end of an element.
334    *
335    * <p>The SAX parser will invoke this method at the end of every
336    * element in the XML document; there will be a corresponding
337    * startElement() event for every endElement() event (even when the
338    * element is empty).</p>
339    *
340    * <p>If the element name has a namespace prefix, the prefix will
341    * still be attached to the name.</p>
342    *
343    *
344    * @param ns the namespace of the element
345    * @param localName The local part of the qualified name of the element
346    * @param name The element name
347    */

348   public void endElement(String JavaDoc ns, String JavaDoc localName, String JavaDoc name)
349           throws org.xml.sax.SAXException JavaDoc
350   {
351     m_elemStack.pop();
352     m_currentNode = m_elemStack.isEmpty() ? null : (Node JavaDoc)m_elemStack.peek();
353   }
354
355   /**
356    * Set an ID string to node association in the ID table.
357    *
358    * @param id The ID string.
359    * @param elem The associated ID.
360    */

361   public void setIDAttribute(String JavaDoc id, Element JavaDoc elem)
362   {
363
364     // Do nothing. This method is meant to be overiden.
365
}
366
367   /**
368    * Receive notification of character data.
369    *
370    * <p>The Parser will call this method to report each chunk of
371    * character data. SAX parsers may return all contiguous character
372    * data in a single chunk, or they may split it into several
373    * chunks; however, all of the characters in any single event
374    * must come from the same external entity, so that the Locator
375    * provides useful information.</p>
376    *
377    * <p>The application must not attempt to read from the array
378    * outside of the specified range.</p>
379    *
380    * <p>Note that some parsers will report whitespace using the
381    * ignorableWhitespace() method rather than this one (validating
382    * parsers must do so).</p>
383    *
384    * @param ch The characters from the XML document.
385    * @param start The start position in the array.
386    * @param length The number of characters to read from the array.
387    * @see #ignorableWhitespace
388    * @see org.xml.sax.Locator
389    */

390   public void characters(char ch[], int start, int length) throws org.xml.sax.SAXException JavaDoc
391   {
392     if(isOutsideDocElem()
393        && com.sun.org.apache.xml.internal.utils.XMLCharacterRecognizer.isWhiteSpace(ch, start, length))
394       return; // avoid DOM006 Hierarchy request error
395

396     if (m_inCData)
397     {
398       cdata(ch, start, length);
399
400       return;
401     }
402
403     String JavaDoc s = new String JavaDoc(ch, start, length);
404     Node JavaDoc childNode;
405     childNode = m_currentNode != null ? m_currentNode.getLastChild(): null;
406     if( childNode != null && childNode.getNodeType() == Node.TEXT_NODE ){
407        ((Text JavaDoc)childNode).appendData(s);
408     }
409     else{
410        Text JavaDoc text = m_doc.createTextNode(s);
411        append(text);
412     }
413   }
414
415   /**
416    * If available, when the disable-output-escaping attribute is used,
417    * output raw text without escaping. A PI will be inserted in front
418    * of the node with the name "lotusxsl-next-is-raw" and a value of
419    * "formatter-to-dom".
420    *
421    * @param ch Array containing the characters
422    * @param start Index to start of characters in the array
423    * @param length Number of characters in the array
424    */

425   public void charactersRaw(char ch[], int start, int length)
426           throws org.xml.sax.SAXException JavaDoc
427   {
428     if(isOutsideDocElem()
429        && com.sun.org.apache.xml.internal.utils.XMLCharacterRecognizer.isWhiteSpace(ch, start, length))
430       return; // avoid DOM006 Hierarchy request error
431

432
433     String JavaDoc s = new String JavaDoc(ch, start, length);
434
435     append(m_doc.createProcessingInstruction("xslt-next-is-raw",
436                                              "formatter-to-dom"));
437     append(m_doc.createTextNode(s));
438   }
439
440   /**
441    * Report the beginning of an entity.
442    *
443    * The start and end of the document entity are not reported.
444    * The start and end of the external DTD subset are reported
445    * using the pseudo-name "[dtd]". All other events must be
446    * properly nested within start/end entity events.
447    *
448    * @param name The name of the entity. If it is a parameter
449    * entity, the name will begin with '%'.
450    * @see #endEntity
451    * @see org.xml.sax.ext.DeclHandler#internalEntityDecl
452    * @see org.xml.sax.ext.DeclHandler#externalEntityDecl
453    */

454   public void startEntity(String JavaDoc name) throws org.xml.sax.SAXException JavaDoc
455   {
456
457     // Almost certainly the wrong behavior...
458
// entityReference(name);
459
}
460
461   /**
462    * Report the end of an entity.
463    *
464    * @param name The name of the entity that is ending.
465    * @see #startEntity
466    */

467   public void endEntity(String JavaDoc name) throws org.xml.sax.SAXException JavaDoc{}
468
469   /**
470    * Receive notivication of a entityReference.
471    *
472    * @param name name of the entity reference
473    */

474   public void entityReference(String JavaDoc name) throws org.xml.sax.SAXException JavaDoc
475   {
476     append(m_doc.createEntityReference(name));
477   }
478
479   /**
480    * Receive notification of ignorable whitespace in element content.
481    *
482    * <p>Validating Parsers must use this method to report each chunk
483    * of ignorable whitespace (see the W3C XML 1.0 recommendation,
484    * section 2.10): non-validating parsers may also use this method
485    * if they are capable of parsing and using content models.</p>
486    *
487    * <p>SAX parsers may return all contiguous whitespace in a single
488    * chunk, or they may split it into several chunks; however, all of
489    * the characters in any single event must come from the same
490    * external entity, so that the Locator provides useful
491    * information.</p>
492    *
493    * <p>The application must not attempt to read from the array
494    * outside of the specified range.</p>
495    *
496    * @param ch The characters from the XML document.
497    * @param start The start position in the array.
498    * @param length The number of characters to read from the array.
499    * @see #characters
500    */

501   public void ignorableWhitespace(char ch[], int start, int length)
502           throws org.xml.sax.SAXException JavaDoc
503   {
504     if(isOutsideDocElem())
505       return; // avoid DOM006 Hierarchy request error
506

507     String JavaDoc s = new String JavaDoc(ch, start, length);
508
509     append(m_doc.createTextNode(s));
510   }
511
512   /**
513    * Tell if the current node is outside the document element.
514    *
515    * @return true if the current node is outside the document element.
516    */

517    private boolean isOutsideDocElem()
518    {
519       return (null == m_docFrag) && m_elemStack.size() == 0 && (null == m_currentNode || m_currentNode.getNodeType() == Node.DOCUMENT_NODE);
520    }
521
522   /**
523    * Receive notification of a processing instruction.
524    *
525    * <p>The Parser will invoke this method once for each processing
526    * instruction found: note that processing instructions may occur
527    * before or after the main document element.</p>
528    *
529    * <p>A SAX parser should never report an XML declaration (XML 1.0,
530    * section 2.8) or a text declaration (XML 1.0, section 4.3.1)
531    * using this method.</p>
532    *
533    * @param target The processing instruction target.
534    * @param data The processing instruction data, or null if
535    * none was supplied.
536    */

537   public void processingInstruction(String JavaDoc target, String JavaDoc data)
538           throws org.xml.sax.SAXException JavaDoc
539   {
540     append(m_doc.createProcessingInstruction(target, data));
541   }
542
543   /**
544    * Report an XML comment anywhere in the document.
545    *
546    * This callback will be used for comments inside or outside the
547    * document element, including comments in the external DTD
548    * subset (if read).
549    *
550    * @param ch An array holding the characters in the comment.
551    * @param start The starting position in the array.
552    * @param length The number of characters to use from the array.
553    */

554   public void comment(char ch[], int start, int length) throws org.xml.sax.SAXException JavaDoc
555   {
556     append(m_doc.createComment(new String JavaDoc(ch, start, length)));
557   }
558
559   /** Flag indicating that we are processing a CData section */
560   protected boolean m_inCData = false;
561
562   /**
563    * Report the start of a CDATA section.
564    *
565    * @see #endCDATA
566    */

567   public void startCDATA() throws org.xml.sax.SAXException JavaDoc
568   {
569     m_inCData = true;
570     append(m_doc.createCDATASection(""));
571   }
572
573   /**
574    * Report the end of a CDATA section.
575    *
576    * @see #startCDATA
577    */

578   public void endCDATA() throws org.xml.sax.SAXException JavaDoc
579   {
580     m_inCData = false;
581   }
582
583   /**
584    * Receive notification of cdata.
585    *
586    * <p>The Parser will call this method to report each chunk of
587    * character data. SAX parsers may return all contiguous character
588    * data in a single chunk, or they may split it into several
589    * chunks; however, all of the characters in any single event
590    * must come from the same external entity, so that the Locator
591    * provides useful information.</p>
592    *
593    * <p>The application must not attempt to read from the array
594    * outside of the specified range.</p>
595    *
596    * <p>Note that some parsers will report whitespace using the
597    * ignorableWhitespace() method rather than this one (validating
598    * parsers must do so).</p>
599    *
600    * @param ch The characters from the XML document.
601    * @param start The start position in the array.
602    * @param length The number of characters to read from the array.
603    * @see #ignorableWhitespace
604    * @see org.xml.sax.Locator
605    */

606   public void cdata(char ch[], int start, int length) throws org.xml.sax.SAXException JavaDoc
607   {
608     if(isOutsideDocElem()
609        && com.sun.org.apache.xml.internal.utils.XMLCharacterRecognizer.isWhiteSpace(ch, start, length))
610       return; // avoid DOM006 Hierarchy request error
611

612     String JavaDoc s = new String JavaDoc(ch, start, length);
613
614     CDATASection JavaDoc section =(CDATASection JavaDoc) m_currentNode.getLastChild();
615     section.appendData(s);
616   }
617
618   /**
619    * Report the start of DTD declarations, if any.
620    *
621    * Any declarations are assumed to be in the internal subset
622    * unless otherwise indicated.
623    *
624    * @param name The document type name.
625    * @param publicId The declared public identifier for the
626    * external DTD subset, or null if none was declared.
627    * @param systemId The declared system identifier for the
628    * external DTD subset, or null if none was declared.
629    * @see #endDTD
630    * @see #startEntity
631    */

632   public void startDTD(String JavaDoc name, String JavaDoc publicId, String JavaDoc systemId)
633           throws org.xml.sax.SAXException JavaDoc
634   {
635
636     // Do nothing for now.
637
}
638
639   /**
640    * Report the end of DTD declarations.
641    *
642    * @see #startDTD
643    */

644   public void endDTD() throws org.xml.sax.SAXException JavaDoc
645   {
646
647     // Do nothing for now.
648
}
649
650   /**
651    * Begin the scope of a prefix-URI Namespace mapping.
652    *
653    * <p>The information from this event is not necessary for
654    * normal Namespace processing: the SAX XML reader will
655    * automatically replace prefixes for element and attribute
656    * names when the http://xml.org/sax/features/namespaces
657    * feature is true (the default).</p>
658    *
659    * <p>There are cases, however, when applications need to
660    * use prefixes in character data or in attribute values,
661    * where they cannot safely be expanded automatically; the
662    * start/endPrefixMapping event supplies the information
663    * to the application to expand prefixes in those contexts
664    * itself, if necessary.</p>
665    *
666    * <p>Note that start/endPrefixMapping events are not
667    * guaranteed to be properly nested relative to each-other:
668    * all startPrefixMapping events will occur before the
669    * corresponding startElement event, and all endPrefixMapping
670    * events will occur after the corresponding endElement event,
671    * but their order is not guaranteed.</p>
672    *
673    * @param prefix The Namespace prefix being declared.
674    * @param uri The Namespace URI the prefix is mapped to.
675    * @see #endPrefixMapping
676    * @see #startElement
677    */

678   public void startPrefixMapping(String JavaDoc prefix, String JavaDoc uri)
679           throws org.xml.sax.SAXException JavaDoc
680   {
681
682     /*
683     // Not sure if this is needed or wanted
684     // Also, it fails in the stree.
685     if((null != m_currentNode)
686        && (m_currentNode.getNodeType() == Node.ELEMENT_NODE))
687     {
688       String qname;
689       if(((null != prefix) && (prefix.length() == 0))
690          || (null == prefix))
691         qname = "xmlns";
692       else
693         qname = "xmlns:"+prefix;
694
695       Element elem = (Element)m_currentNode;
696       String val = elem.getAttribute(qname); // Obsolete, should be DOM2...?
697       if(val == null)
698       {
699         elem.setAttributeNS("http://www.w3.org/XML/1998/namespace",
700                             qname, uri);
701       }
702     }
703     */

704   }
705
706   /**
707    * End the scope of a prefix-URI mapping.
708    *
709    * <p>See startPrefixMapping for details. This event will
710    * always occur after the corresponding endElement event,
711    * but the order of endPrefixMapping events is not otherwise
712    * guaranteed.</p>
713    *
714    * @param prefix The prefix that was being mapping.
715    * @see #startPrefixMapping
716    * @see #endElement
717    */

718   public void endPrefixMapping(String JavaDoc prefix) throws org.xml.sax.SAXException JavaDoc{}
719
720   /**
721    * Receive notification of a skipped entity.
722    *
723    * <p>The Parser will invoke this method once for each entity
724    * skipped. Non-validating processors may skip entities if they
725    * have not seen the declarations (because, for example, the
726    * entity was declared in an external DTD subset). All processors
727    * may skip external entities, depending on the values of the
728    * http://xml.org/sax/features/external-general-entities and the
729    * http://xml.org/sax/features/external-parameter-entities
730    * properties.</p>
731    *
732    * @param name The name of the skipped entity. If it is a
733    * parameter entity, the name will begin with '%'.
734    */

735   public void skippedEntity(String JavaDoc name) throws org.xml.sax.SAXException JavaDoc{}
736 }
737
Popular Tags