View Javadoc
1   /*
2    * Redistribution and use of this software and associated documentation ("Software"), with or
3    * without modification, are permitted provided that the following conditions are met:
4    *
5    * 1. Redistributions of source code must retain copyright statements and notices. Redistributions
6    * must also contain a copy of this document.
7    *
8    * 2. Redistributions in binary form must reproduce the above copyright notice, this list of
9    * conditions and the following disclaimer in the documentation and/or other materials provided with
10   * the distribution.
11   *
12   * 3. The name "Exolab" must not be used to endorse or promote products derived from this Software
13   * without prior written permission of Intalio, Inc. For written permission, please contact
14   * info@exolab.org.
15   *
16   * 4. Products derived from this Software may not be called "Exolab" nor may "Exolab" appear in
17   * their names without prior written permission of Intalio, Inc. Exolab is a registered trademark of
18   * Intalio, Inc.
19   *
20   * 5. Due credit should be given to the Exolab Project (http://www.exolab.org/).
21   *
22   * THIS SOFTWARE IS PROVIDED BY INTALIO, INC. AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESSED OR
23   * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
24   * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTALIO, INC. OR ITS
25   * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26   * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27   * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
28   * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
29   * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30   *
31   * Copyright 2001-2004 (C) Intalio, Inc. All Rights Reserved.
32   *
33   * $Id$ Date Author Changes 04/06/2001 Arnaud Blandin Created 09/09/2004 Keith Visco Modified for
34   * SAX 2 support
35   */
36  package org.exolab.castor.xml.util;
37  
38  import java.util.HashSet;
39  import java.util.Set;
40  
41  import org.exolab.castor.types.AnyNode;
42  import org.exolab.castor.xml.Namespaces;
43  import org.exolab.castor.xml.NamespacesStack;
44  import org.xml.sax.ContentHandler;
45  import org.xml.sax.SAXException;
46  import org.xml.sax.helpers.AttributesImpl;
47  
48  /**
49   * A class for converting an AnyNode to SAX events.
50   *
51   * @author <a href="mailto:kvisco@intalio.com">Keith Visco</a>
52   * @author <a href="mailto:blandin@intalio.com">Arnaud Blandin</a>
53   *
54   * @version $Revision$ $Date: 2006-04-25 15:08:23 -0600 (Tue, 25 Apr 2006) $
55   */
56  public class AnyNode2SAX2 {
57  
58    /** The AnyNode we are firing events for. */
59    private AnyNode _node;
60    /** The Content Handler. */
61    private ContentHandler _handler;
62    /** The stack to store the elements. */
63    private Set<AnyNode> _elements;
64    /** The namespace stack. */
65    private NamespacesStack namespacesStack;
66  
67    /**
68     * No-arg constructor.
69     */
70    public AnyNode2SAX2() {
71      this(null, null);
72    }
73  
74    /**
75     * Creates a AnyNode2SAX for the given node.
76     * 
77     * @param node the AnyNode to create AnyNode2SAX2 for.
78     */
79    public AnyNode2SAX2(final AnyNode node) {
80      this(node, null);
81    }
82  
83    /**
84     * Creates a AnyNode2SAX2 for the given node and the namespace context.
85     * 
86     * @param node the AnyNode to create AnyNode2SAX for.
87     * @param namespacesStack a namespace context
88     */
89    public AnyNode2SAX2(final AnyNode node, final NamespacesStack namespacesStack) {
90      _elements = new HashSet<AnyNode>();
91      _node = node;
92      this.namespacesStack = namespacesStack != null ? namespacesStack : new NamespacesStack();
93    }
94  
95    /**
96     * Set the ContentHandler to send events to.
97     *
98     * @param handler the document handler to set
99     */
100   public void setContentHandler(final ContentHandler handler) {
101     if (handler == null) {
102       throw new IllegalArgumentException("AnyNode2SAX2#setContentHandler 'null' value for handler");
103     }
104     _handler = handler;
105   }
106 
107   public static void fireEvents(final AnyNode node, final ContentHandler handler)
108       throws SAXException {
109     fireEvents(node, handler, null);
110   }
111 
112   public static void fireEvents(final AnyNode node, final ContentHandler handler,
113       final NamespacesStack namespacesStack) throws SAXException {
114     AnyNode2SAX2 eventProducer = new AnyNode2SAX2(node, namespacesStack);
115     eventProducer.setContentHandler(handler);
116 
117     // inject a namespace declaration for the AnyNode element
118     if (node != null && namespacesStack != null && node.getNamespacePrefix() != null
119         && namespacesStack.getNamespaceURI(node.getNamespacePrefix()) == null) {
120       handler.startPrefixMapping(node.getNamespacePrefix(), node.getNamespaceURI());
121     }
122 
123     eventProducer.start();
124   }
125 
126   public void start() throws org.xml.sax.SAXException {
127     if (_node == null || _handler == null) {
128       return;
129     }
130     processAnyNode(_node, _handler);
131   }
132 
133   private void processAnyNode(final AnyNode node, final ContentHandler handler)
134       throws SAXException {
135     if (_node == null || _handler == null) {
136       throw new IllegalArgumentException();
137     }
138 
139     // -- so we don't potentially get into an endlessloop
140     if (!_elements.add(node)) {
141       return;
142     }
143 
144     if (node.getNodeType() == AnyNode.ELEMENT) {
145       // -- node local name
146       String name = node.getLocalName();
147 
148       // -- retrieve the namespaces declaration and handle them
149       AnyNode tempNode = node.getFirstNamespace();
150       String prefix = null;
151       while (tempNode != null) {
152         prefix = tempNode.getNamespacePrefix();
153         if (prefix == null) {
154           prefix = "";
155         }
156         String value = tempNode.getNamespaceURI();
157         if (value == null) {
158           value = "";
159         }
160         handler.startPrefixMapping(prefix, value);
161         if (value != null && value.length() > 0) {
162           namespacesStack.addNamespace(prefix, value);
163         }
164         tempNode = tempNode.getNextSibling();
165       } // namespaceNode
166 
167       // -- retrieve the attributes and handle them
168       AttributesImpl atts = new AttributesImpl();
169       tempNode = node.getFirstAttribute();
170       String xmlName = null;
171       String value = null;
172       String attUri = null;
173       String attPrefix = null;
174       while (tempNode != null) {
175         xmlName = tempNode.getLocalName();
176         String localName = xmlName;
177         // --retrieve a prefix?
178         attUri = tempNode.getNamespaceURI();
179         if (attUri != null) {
180           attPrefix = namespacesStack.getNamespacePrefix(attUri);
181         } else {
182           attUri = "";
183         }
184 
185         if (attPrefix != null && attPrefix.length() > 0) {
186           xmlName = attPrefix + ':' + xmlName;
187         }
188 
189         value = tempNode.getStringValue();
190         atts.addAttribute(attUri, localName, xmlName, "CDATA", value);
191         tempNode = tempNode.getNextSibling();
192       } // attributes
193 
194       // -- namespace management
195       // _context = _context.createNamespaces();
196       String nsPrefix = node.getNamespacePrefix();
197       String nsURI = node.getNamespaceURI();
198 
199       String qName = null;
200       // maybe the namespace is already bound to a prefix in the
201       // namespace context
202       if (nsURI != null && nsURI.length() > 0) {
203         String tempPrefix = namespacesStack.getNamespacePrefix(nsURI);
204         if (tempPrefix != null) {
205           nsPrefix = tempPrefix;
206         } else {
207           namespacesStack.addNamespace(nsPrefix, nsURI);
208         }
209       } else {
210         nsURI = "";
211       }
212 
213       if (nsPrefix != null) {
214         int len = nsPrefix.length();
215         if (len > 0) {
216           qName = nsPrefix + ':' + name;
217         } else {
218           qName = name;
219         }
220       } else {
221         qName = name;
222       }
223 
224       try {
225         // _context.declareAsAttributes(atts,true);
226         handler.startElement(nsURI, name, qName, atts);
227       } catch (SAXException sx) {
228         throw new SAXException(sx);
229       }
230 
231       // -- handle child&daughter elements
232       tempNode = node.getFirstChild();
233       while (tempNode != null) {
234         namespacesStack.addNewNamespaceScope();
235         processAnyNode(tempNode, handler);
236         tempNode = tempNode.getNextSibling();
237       }
238 
239       // -- finish element
240       try {
241         handler.endElement(nsURI, name, qName);
242         namespacesStack.removeNamespaceScope();
243 
244         // -- retrieve the namespaces declaration and handle them
245         tempNode = node.getFirstNamespace();
246         while (tempNode != null) {
247           prefix = tempNode.getNamespacePrefix();
248           if (prefix == null) {
249             prefix = "";
250           }
251           handler.endPrefixMapping(prefix);
252           tempNode = tempNode.getNextSibling();
253         } // namespaceNode
254       } catch (org.xml.sax.SAXException sx) {
255         throw new SAXException(sx);
256       }
257     } else {
258       // ELEMENTS
259       if (node.getNodeType() == AnyNode.TEXT) {
260         String value = node.getStringValue();
261         if ((value != null) && (value.length() > 0)) {
262           char[] chars = value.toCharArray();
263           try {
264             handler.characters(chars, 0, chars.length);
265           } catch (org.xml.sax.SAXException sx) {
266             throw new SAXException(sx);
267           }
268         }
269       }
270     }
271   }
272 
273 }