KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > commons > digester > NodeCreateRule


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

17
18
19 package org.apache.commons.digester;
20
21
22 import javax.xml.parsers.DocumentBuilder JavaDoc;
23 import javax.xml.parsers.DocumentBuilderFactory JavaDoc;
24 import javax.xml.parsers.ParserConfigurationException JavaDoc;
25
26 import org.w3c.dom.Attr JavaDoc;
27 import org.w3c.dom.DOMException JavaDoc;
28 import org.w3c.dom.Document JavaDoc;
29 import org.w3c.dom.Element JavaDoc;
30 import org.w3c.dom.Node JavaDoc;
31 import org.xml.sax.Attributes JavaDoc;
32 import org.xml.sax.ContentHandler JavaDoc;
33 import org.xml.sax.SAXException JavaDoc;
34 import org.xml.sax.helpers.DefaultHandler JavaDoc;
35
36
37 /**
38  * A rule implementation that creates a DOM
39  * {@link org.w3c.dom.Node Node} containing the XML at the element that matched
40  * the rule. Two concrete types of nodes can be created by this rule:
41  * <ul>
42  * <li>the default is to create an {@link org.w3c.dom.Element Element} node.
43  * The created element will correspond to the element that matched the rule,
44  * containing all XML content underneath that element.</li>
45  * <li>alternatively, this rule can create nodes of type
46  * {@link org.w3c.dom.DocumentFragment DocumentFragment}, which will contain
47  * only the XML content under the element the rule was trigged on.</li>
48  * </ul>
49  * The created node will be normalized, meaning it will not contain text nodes
50  * that only contain white space characters.
51  *
52
53  *
54  * <p>The created <code>Node</code> will be pushed on Digester's object stack
55  * when done. To use it in the context of another DOM
56  * {@link org.w3c.dom.Document Document}, it must be imported first, using the
57  * Document method
58  * {@link org.w3c.dom.Document#importNode(org.w3c.dom.Node, boolean) importNode()}.
59  * </p>
60  *
61  * <p><strong>Important Note:</strong> This is implemented by replacing the SAX
62  * {@link org.xml.sax.ContentHandler ContentHandler} in the parser used by
63  * Digester, and resetting it when the matched element is closed. As a side
64  * effect, rules that would match XML nodes under the element that matches
65  * a <code>NodeCreateRule</code> will never be triggered by Digester, which
66  * usually is the behavior one would expect.</p>
67  *
68  * <p><strong>Note</strong> that the current implementation does not set the namespace prefixes
69  * in the exported nodes. The (usually more important) namespace URIs are set,
70  * of course.</p>
71  *
72  * @since Digester 1.4
73  */

74
75 public class NodeCreateRule extends Rule {
76
77
78     // ---------------------------------------------------------- Inner Classes
79

80
81     /**
82      * The SAX content handler that does all the actual work of assembling the
83      * DOM node tree from the SAX events.
84      */

85     private class NodeBuilder
86         extends DefaultHandler JavaDoc {
87
88
89         // ------------------------------------------------------- Constructors
90

91
92         /**
93          * Constructor.
94          *
95          * <p>Stores the content handler currently used by Digester so it can
96          * be reset when done, and initializes the DOM objects needed to
97          * build the node.</p>
98          *
99          * @param doc the document to use to create nodes
100          * @param root the root node
101          * @throws ParserConfigurationException if the DocumentBuilderFactory
102          * could not be instantiated
103          * @throws SAXException if the XMLReader could not be instantiated by
104          * Digester (should not happen)
105          */

106         public NodeBuilder(Document JavaDoc doc, Node JavaDoc root)
107             throws ParserConfigurationException JavaDoc, SAXException JavaDoc {
108
109             this.doc = doc;
110             this.root = root;
111             this.top = root;
112             
113             oldContentHandler = digester.getCustomContentHandler();
114
115         }
116
117
118         // ------------------------------------------------- Instance Variables
119

120
121         /**
122          * The content handler used by Digester before it was set to this
123          * content handler.
124          */

125         protected ContentHandler JavaDoc oldContentHandler = null;
126
127
128         /**
129          * Depth of the current node, relative to the element where the content
130          * handler was put into action.
131          */

132         protected int depth = 0;
133
134
135         /**
136          * A DOM Document used to create the various Node instances.
137          */

138         protected Document JavaDoc doc = null;
139
140
141         /**
142          * The DOM node that will be pushed on Digester's stack.
143          */

144         protected Node JavaDoc root = null;
145
146
147         /**
148          * The current top DOM mode.
149          */

150         protected Node JavaDoc top = null;
151
152
153         // --------------------------------------------- ContentHandler Methods
154

155
156         /**
157          * Appends a {@link org.w3c.dom.Text Text} node to the current node.
158          *
159          * @param ch the characters from the XML document
160          * @param start the start position in the array
161          * @param length the number of characters to read from the array
162          * @throws SAXException if the DOM implementation throws an exception
163          */

164         public void characters(char[] ch, int start, int length)
165             throws SAXException JavaDoc {
166
167             try {
168                 String JavaDoc str = new String JavaDoc(ch, start, length);
169                 if (str.trim().length() > 0) {
170                     top.appendChild(doc.createTextNode(str));
171                 }
172             } catch (DOMException JavaDoc e) {
173                 throw new SAXException JavaDoc(e.getMessage());
174             }
175
176         }
177
178
179         /**
180          * Checks whether control needs to be returned to Digester.
181          *
182          * @param namespaceURI the namespace URI
183          * @param localName the local name
184          * @param qName the qualified (prefixed) name
185          * @throws SAXException if the DOM implementation throws an exception
186          */

187         public void endElement(String JavaDoc namespaceURI, String JavaDoc localName,
188                                String JavaDoc qName)
189             throws SAXException JavaDoc {
190             
191             try {
192                 if (depth == 0) {
193                     getDigester().setCustomContentHandler(oldContentHandler);
194                     getDigester().push(root);
195                     getDigester().endElement(namespaceURI, localName, qName);
196                 }
197     
198                 top = top.getParentNode();
199                 depth--;
200             } catch (DOMException JavaDoc e) {
201                 throw new SAXException JavaDoc(e.getMessage());
202             }
203
204         }
205
206
207         /**
208          * Adds a new
209          * {@link org.w3c.dom.ProcessingInstruction ProcessingInstruction} to
210          * the current node.
211          *
212          * @param target the processing instruction target
213          * @param data the processing instruction data, or null if none was
214          * supplied
215          * @throws SAXException if the DOM implementation throws an exception
216          */

217         public void processingInstruction(String JavaDoc target, String JavaDoc data)
218             throws SAXException JavaDoc {
219             
220             try {
221                 top.appendChild(doc.createProcessingInstruction(target, data));
222             } catch (DOMException JavaDoc e) {
223                 throw new SAXException JavaDoc(e.getMessage());
224             }
225
226         }
227
228
229         /**
230          * Adds a new child {@link org.w3c.dom.Element Element} to the current
231          * node.
232          *
233          * @param namespaceURI the namespace URI
234          * @param localName the local name
235          * @param qName the qualified (prefixed) name
236          * @param atts the list of attributes
237          * @throws SAXException if the DOM implementation throws an exception
238          */

239         public void startElement(String JavaDoc namespaceURI, String JavaDoc localName,
240                                  String JavaDoc qName, Attributes JavaDoc atts)
241             throws SAXException JavaDoc {
242
243             try {
244                 Node JavaDoc previousTop = top;
245                 if ((localName == null) || (localName.length() == 0)) {
246                     top = doc.createElement(qName);
247                 } else {
248                     top = doc.createElementNS(namespaceURI, localName);
249                 }
250                 for (int i = 0; i < atts.getLength(); i++) {
251                     Attr JavaDoc attr = null;
252                     if ((atts.getLocalName(i) == null) ||
253                         (atts.getLocalName(i).length() == 0)) {
254                         attr = doc.createAttribute(atts.getQName(i));
255                         attr.setNodeValue(atts.getValue(i));
256                         ((Element JavaDoc)top).setAttributeNode(attr);
257                     } else {
258                         attr = doc.createAttributeNS(atts.getURI(i),
259                                                      atts.getLocalName(i));
260                         attr.setNodeValue(atts.getValue(i));
261                         ((Element JavaDoc)top).setAttributeNodeNS(attr);
262                     }
263                 }
264                 previousTop.appendChild(top);
265                 depth++;
266             } catch (DOMException JavaDoc e) {
267                 throw new SAXException JavaDoc(e.getMessage());
268             }
269
270         }
271
272     }
273
274
275     // ----------------------------------------------------------- Constructors
276

277
278     /**
279      * Default constructor. Creates an instance of this rule that will create a
280      * DOM {@link org.w3c.dom.Element Element}.
281      */

282     public NodeCreateRule() throws ParserConfigurationException JavaDoc {
283
284         this(Node.ELEMENT_NODE);
285
286     }
287
288
289     /**
290      * Constructor. Creates an instance of this rule that will create a DOM
291      * {@link org.w3c.dom.Element Element}, but lets you specify the JAXP
292      * <code>DocumentBuilder</code> that should be used when constructing the
293      * node tree.
294      *
295      * @param documentBuilder the JAXP <code>DocumentBuilder</code> to use
296      */

297     public NodeCreateRule(DocumentBuilder JavaDoc documentBuilder) {
298
299         this(Node.ELEMENT_NODE, documentBuilder);
300
301     }
302
303
304     /**
305      * Constructor. Creates an instance of this rule that will create either a
306      * DOM {@link org.w3c.dom.Element Element} or a DOM
307      * {@link org.w3c.dom.DocumentFragment DocumentFragment}, depending on the
308      * value of the <code>nodeType</code> parameter.
309      *
310      * @param nodeType the type of node to create, which can be either
311      * {@link org.w3c.dom.Node#ELEMENT_NODE Node.ELEMENT_NODE} or
312      * {@link org.w3c.dom.Node#DOCUMENT_FRAGMENT_NODE Node.DOCUMENT_FRAGMENT_NODE}
313      * @throws IllegalArgumentException if the node type is not supported
314      */

315     public NodeCreateRule(int nodeType) throws ParserConfigurationException JavaDoc {
316
317         this(nodeType,
318              DocumentBuilderFactory.newInstance().newDocumentBuilder());
319
320     }
321
322
323     /**
324      * Constructor. Creates an instance of this rule that will create either a
325      * DOM {@link org.w3c.dom.Element Element} or a DOM
326      * {@link org.w3c.dom.DocumentFragment DocumentFragment}, depending on the
327      * value of the <code>nodeType</code> parameter. This constructor lets you
328      * specify the JAXP <code>DocumentBuilder</code> that should be used when
329      * constructing the node tree.
330      *
331      * @param nodeType the type of node to create, which can be either
332      * {@link org.w3c.dom.Node#ELEMENT_NODE Node.ELEMENT_NODE} or
333      * {@link org.w3c.dom.Node#DOCUMENT_FRAGMENT_NODE Node.DOCUMENT_FRAGMENT_NODE}
334      * @param documentBuilder the JAXP <code>DocumentBuilder</code> to use
335      * @throws IllegalArgumentException if the node type is not supported
336      */

337     public NodeCreateRule(int nodeType, DocumentBuilder JavaDoc documentBuilder) {
338
339         if (!((nodeType == Node.DOCUMENT_FRAGMENT_NODE) ||
340               (nodeType == Node.ELEMENT_NODE))) {
341             throw new IllegalArgumentException JavaDoc(
342                 "Can only create nodes of type DocumentFragment and Element");
343         }
344         this.nodeType = nodeType;
345         this.documentBuilder = documentBuilder;
346
347     }
348
349
350     // ----------------------------------------------------- Instance Variables
351

352
353     /**
354      * The JAXP <code>DocumentBuilder</code> to use.
355      */

356     private DocumentBuilder JavaDoc documentBuilder = null;
357
358
359     /**
360      * The type of the node that should be created. Must be one of the
361      * constants defined in {@link org.w3c.dom.Node Node}, but currently only
362      * {@link org.w3c.dom.Node#ELEMENT_NODE Node.ELEMENT_NODE} and
363      * {@link org.w3c.dom.Node#DOCUMENT_FRAGMENT_NODE Node.DOCUMENT_FRAGMENT_NODE}
364      * are allowed values.
365      */

366     private int nodeType = Node.ELEMENT_NODE;
367
368
369     // ----------------------------------------------------------- Rule Methods
370

371
372     /**
373      * When this method fires, the digester is told to forward all SAX
374      * ContentHandler events to the builder object, resulting in a DOM being
375      * built instead of normal digester rule-handling occurring. When the
376      * end of the current xml element is encountered, the original content
377      * handler is restored (expected to be NULL, allowing normal Digester
378      * operations to continue).
379      *
380      * @param namespaceURI the namespace URI of the matching element, or an
381      * empty string if the parser is not namespace aware or the element has
382      * no namespace
383      * @param name the local name if the parser is namespace aware, or just
384      * the element name otherwise
385      * @param attributes The attribute list of this element
386      * @throws Exception indicates a JAXP configuration problem
387      */

388     public void begin(String JavaDoc namespaceURI, String JavaDoc name, Attributes JavaDoc attributes)
389         throws Exception JavaDoc {
390
391         Document JavaDoc doc = documentBuilder.newDocument();
392         NodeBuilder builder = null;
393         if (nodeType == Node.ELEMENT_NODE) {
394             Element JavaDoc element = null;
395             if (getDigester().getNamespaceAware()) {
396                 element =
397                     doc.createElementNS(namespaceURI, name);
398                 for (int i = 0; i < attributes.getLength(); i++) {
399                     element.setAttributeNS(attributes.getURI(i),
400                                            attributes.getLocalName(i),
401                                            attributes.getValue(i));
402                 }
403             } else {
404                 element = doc.createElement(name);
405                 for (int i = 0; i < attributes.getLength(); i++) {
406                     element.setAttribute(attributes.getQName(i),
407                                          attributes.getValue(i));
408                 }
409             }
410             builder = new NodeBuilder(doc, element);
411         } else {
412             builder = new NodeBuilder(doc, doc.createDocumentFragment());
413         }
414         // the NodeBuilder constructor has already saved the original
415
// value of the digester's custom content handler (expected to
416
// be null, but we save it just in case). So now we just
417
// need to tell the digester to forward events to the builder.
418
getDigester().setCustomContentHandler(builder);
419     }
420
421
422     /**
423      * Pop the Node off the top of the stack.
424      */

425     public void end() throws Exception JavaDoc {
426
427         Object JavaDoc top = digester.pop();
428
429     }
430
431
432 }
433
Popular Tags