View Javadoc
1   /* $Id$
2    *
3    * Copyright (c) 2004, Sun Microsystems, Inc.
4    * All rights reserved.
5    *
6    * Redistribution and use in source and binary forms, with or without
7    * modification, are permitted provided that the following conditions are
8    * met:
9    *
10   *     * Redistributions of source code must retain the above copyright
11   *      notice, this list of conditions and the following disclaimer.
12   *
13   *     * Redistributions in binary form must reproduce the above
14   *      copyright notice, this list of conditions and the following
15   *       disclaimer in the documentation and/or other materials provided
16   *       with the distribution.
17   *
18   *     * Neither the name of Sun Microsystems, Inc. nor the names of its
19   *       contributors may be used to endorse or promote products derived
20   *       from this software without specific prior written permission.
21   *
22   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
23   * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
24   * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
25   * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
26   * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
27   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
28   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
29   * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
30   * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
31   * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
32   * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33   */
34  
35  package org.mule.module.xml.stax;
36  
37  import javanet.staxutils.DummyLocator;
38  import javanet.staxutils.StAXReaderToContentHandler;
39  import javanet.staxutils.helpers.XMLFilterImplEx;
40  
41  import javax.xml.namespace.QName;
42  import javax.xml.stream.Location;
43  import javax.xml.stream.XMLStreamConstants;
44  import javax.xml.stream.XMLStreamException;
45  import javax.xml.stream.XMLStreamReader;
46  
47  import org.xml.sax.Attributes;
48  import org.xml.sax.Locator;
49  import org.xml.sax.SAXException;
50  import org.xml.sax.helpers.AttributesImpl;
51  
52  /**
53   * This is a simple utility class that adapts StAX events from an
54   * {@link javax.xml.stream.XMLStreamReader} to SAX events on a
55   * {@link org.xml.sax.ContentHandler}, bridging between the two parser technologies.
56   * 
57   * @author Ryan.Shoemaker@Sun.COM
58   * @version 1.0
59   */
60  public class XMLStreamReaderToContentHandler implements StAXReaderToContentHandler
61  {
62  
63      // StAX event source
64      private final XMLStreamReader staxStreamReader;
65  
66      // SAX event sinks
67      private XMLFilterImplEx filter;
68  
69      /**
70       * Construct a new StAX to SAX adapter that will convert a StAX event stream into
71       * a SAX event stream.
72       * 
73       * @param staxCore StAX event source
74       * @param filter SAX event sink
75       */
76      public XMLStreamReaderToContentHandler(XMLStreamReader staxCore, XMLFilterImplEx filter)
77      {
78          staxStreamReader = staxCore;
79  
80          this.filter = filter;
81      }
82  
83      public void bridge() throws XMLStreamException
84      {
85          try
86          {
87              // remembers the nest level of elements to know when we are done.
88              int depth = 0;
89              boolean isDocument = false;
90  
91              handleStartDocument();
92  
93              // if the parser is at the start document, procees any comments or PIs
94              int event = staxStreamReader.getEventType();
95              if (event == XMLStreamConstants.START_DOCUMENT)
96              {
97                  isDocument = true;
98                  event = staxStreamReader.next();
99                  while (event != XMLStreamConstants.START_ELEMENT)
100                 {
101                     switch (event)
102                     {
103                         case XMLStreamConstants.COMMENT :
104                             handleComment();
105                             break;
106                         case XMLStreamConstants.PROCESSING_INSTRUCTION :
107                             handlePI();
108                             break;
109                     }
110                     event = staxStreamReader.next();
111                 }
112             }
113 
114             if (event != XMLStreamConstants.START_ELEMENT)
115                 throw new IllegalStateException("The current event is not START_ELEMENT\n but" + event);
116 
117             do
118             {
119                 // These are all of the events listed in the javadoc for
120                 // XMLEvent.
121                 // The spec only really describes 11 of them.
122                 switch (event)
123                 {
124                     case XMLStreamConstants.START_ELEMENT :
125                         depth++;
126                         handleStartElement();
127                         break;
128                     case XMLStreamConstants.END_ELEMENT :
129                         handleEndElement();
130                         depth--;
131                         break;
132                     case XMLStreamConstants.CHARACTERS :
133                         handleCharacters();
134                         break;
135                     case XMLStreamConstants.ENTITY_REFERENCE :
136                         handleEntityReference();
137                         break;
138                     case XMLStreamConstants.PROCESSING_INSTRUCTION :
139                         handlePI();
140                         break;
141                     case XMLStreamConstants.COMMENT :
142                         handleComment();
143                         break;
144                     case XMLStreamConstants.DTD :
145                         handleDTD();
146                         break;
147                     case XMLStreamConstants.ATTRIBUTE :
148                         handleAttribute();
149                         break;
150                     case XMLStreamConstants.NAMESPACE :
151                         handleNamespace();
152                         break;
153                     case XMLStreamConstants.CDATA :
154                         handleCDATA();
155                         break;
156                     case XMLStreamConstants.ENTITY_DECLARATION :
157                         handleEntityDecl();
158                         break;
159                     case XMLStreamConstants.NOTATION_DECLARATION :
160                         handleNotationDecl();
161                         break;
162                     case XMLStreamConstants.SPACE :
163                         handleSpace();
164                         break;
165                     default :
166                         throw new InternalError("processing event: " + event);
167                 }
168 
169                 event = staxStreamReader.next();
170             }
171             while (depth != 0);
172 
173             // procees any remaining comments or PIs
174             if (isDocument)
175             {
176                 while (event != XMLStreamConstants.END_DOCUMENT)
177                 {
178                     switch (event)
179                     {
180                         case XMLStreamConstants.COMMENT :
181                             handleComment();
182                             break;
183                         case XMLStreamConstants.PROCESSING_INSTRUCTION :
184                             handlePI();
185                             break;
186                     }
187                     event = staxStreamReader.next();
188                 }
189             }
190 
191             handleEndDocument();
192         }
193         catch (SAXException e)
194         {
195             throw new XMLStreamException(e);
196         }
197     }
198 
199     protected void handleEndDocument() throws SAXException
200     {
201         filter.endDocument();
202     }
203 
204     protected void handleStartDocument() throws SAXException
205     {
206         final Location location = staxStreamReader.getLocation();
207         if (location != null)
208         {
209             filter.setDocumentLocator(new Locator()
210             {
211                 public int getColumnNumber()
212                 {
213                     return location.getColumnNumber();
214                 }
215 
216                 public int getLineNumber()
217                 {
218                     return location.getLineNumber();
219                 }
220 
221                 public String getPublicId()
222                 {
223                     return location.getPublicId();
224                 }
225 
226                 public String getSystemId()
227                 {
228                     return location.getSystemId();
229                 }
230             });
231         }
232         else
233         {
234             filter.setDocumentLocator(new DummyLocator());
235         }
236         filter.startDocument();
237     }
238 
239     protected void handlePI() throws XMLStreamException
240     {
241         try
242         {
243             filter.processingInstruction(staxStreamReader.getPITarget(), staxStreamReader.getPIData());
244         }
245         catch (SAXException e)
246         {
247             throw new XMLStreamException(e);
248         }
249     }
250 
251     protected void handleCharacters() throws XMLStreamException
252     {
253         char[] chars = staxStreamReader.getText().toCharArray();
254 
255         try
256         {
257             filter.characters(chars, 0, chars.length);
258         }
259         catch (SAXException e)
260         {
261             throw new XMLStreamException(e);
262         }
263     }
264 
265     protected void handleEndElement() throws XMLStreamException
266     {
267         QName qName = staxStreamReader.getName();
268 
269         try
270         {
271             // fire endElement
272             String prefix = qName.getPrefix();
273             String rawname;
274             if (prefix == null || prefix.length() == 0)
275                 rawname = qName.getLocalPart();
276             else
277                 rawname = prefix + ':' + qName.getLocalPart();
278 
279             filter.endElement(qName.getNamespaceURI(), qName.getLocalPart(), rawname);
280 
281             // end namespace bindings
282             int nsCount = staxStreamReader.getNamespaceCount();
283             for (int i = nsCount - 1; i >= 0; i--)
284             {
285                 String nsprefix = staxStreamReader.getNamespacePrefix(i);
286                 if (nsprefix == null)
287                 { // true for default namespace
288                     nsprefix = "";
289                 }
290                 filter.endPrefixMapping(nsprefix);
291             }
292         }
293         catch (SAXException e)
294         {
295             throw new XMLStreamException(e);
296         }
297     }
298 
299     protected void handleStartElement() throws XMLStreamException
300     {
301 
302         try
303         {
304             // start namespace bindings
305             int nsCount = staxStreamReader.getNamespaceCount();
306             for (int i = 0; i < nsCount; i++)
307             {
308                 String uri = staxStreamReader.getNamespaceURI(i);
309                 if (uri == null)
310                 {
311                     uri = "";
312                 }
313                 String prefix = staxStreamReader.getNamespacePrefix(i);
314                 if (prefix == null)
315                 { // true for default namespace
316                     prefix = "";
317                 }
318                 filter.startPrefixMapping(prefix, uri);
319             }
320 
321             // fire startElement
322             QName qName = staxStreamReader.getName();
323             String prefix = qName.getPrefix();
324             String rawname;
325             if (prefix == null || prefix.length() == 0)
326                 rawname = qName.getLocalPart();
327             else
328                 rawname = prefix + ':' + qName.getLocalPart();
329             Attributes attrs = getAttributes();
330             filter.startElement(qName.getNamespaceURI(), qName.getLocalPart(), rawname, attrs);
331         }
332         catch (SAXException e)
333         {
334             throw new XMLStreamException(e);
335         }
336     }
337 
338     /**
339      * Get the attributes associated with the given START_ELEMENT or ATTRIBUTE
340      * StAXevent.
341      * 
342      * @return the StAX attributes converted to an org.xml.sax.Attributes
343      */
344     protected Attributes getAttributes()
345     {
346         AttributesImpl attrs = new AttributesImpl();
347 
348         int eventType = staxStreamReader.getEventType();
349         if (eventType != XMLStreamConstants.ATTRIBUTE && eventType != XMLStreamConstants.START_ELEMENT)
350         {
351             throw new InternalError("getAttributes() attempting to process: " + eventType);
352         }
353 
354         // Add namspace declarations if required
355         if (filter.getNamespacePrefixes())
356         {
357             for (int i = 0; i < staxStreamReader.getNamespaceCount(); i++)
358             {
359                 String uri = staxStreamReader.getNamespaceURI(i);
360                 if (uri == null) uri = "";
361 
362                 String prefix = staxStreamReader.getNamespacePrefix(i);
363                 if (prefix == null) prefix = "";
364 
365                 String qName = "xmlns";
366                 if (prefix.length() == 0)
367                 {
368                     prefix = qName;
369                 }
370                 else
371                 {
372                     qName = qName + ':' + prefix;
373                 }
374                 attrs.addAttribute("http://www.w3.org/2000/xmlns/", prefix, qName, "CDATA", uri);
375             }
376         }
377 
378         // gather non-namespace attrs
379         for (int i = 0; i < staxStreamReader.getAttributeCount(); i++)
380         {
381             String uri = staxStreamReader.getAttributeNamespace(i);
382             if (uri == null) uri = "";
383             String localName = staxStreamReader.getAttributeLocalName(i);
384             String prefix = staxStreamReader.getAttributePrefix(i);
385             String qName;
386             if (prefix == null || prefix.length() == 0)
387                 qName = localName;
388             else
389                 qName = prefix + ':' + localName;
390             String type = staxStreamReader.getAttributeType(i);
391             String value = staxStreamReader.getAttributeValue(i);
392 
393             attrs.addAttribute(uri, localName, qName, type, value);
394         }
395 
396         return attrs;
397     }
398 
399     protected void handleNamespace()
400     {
401         // no-op ???
402         // namespace events don't normally occur outside of a startElement
403         // or endElement
404     }
405 
406     protected void handleAttribute()
407     {
408         // no-op ???
409         // attribute events don't normally occur outside of a startElement
410         // or endElement
411     }
412 
413     protected void handleDTD()
414     {
415         // no-op ???
416         // it seems like we need to pass this info along, but how?
417     }
418 
419     protected void handleComment() throws XMLStreamException
420     {
421         char[] chars = staxStreamReader.getText().toCharArray();
422         
423         try
424         {
425             filter.comment(chars, 0, chars.length);
426         }
427         catch (SAXException e)
428         {
429             throw new XMLStreamException(e);
430         }
431     }
432 
433     protected void handleEntityReference()
434     {
435         // no-op ???
436     }
437 
438     protected void handleSpace()
439     {
440         // no-op ???
441         // this event is listed in the javadoc, but not in the spec.
442     }
443 
444     protected void handleNotationDecl()
445     {
446         // no-op ???
447         // this event is listed in the javadoc, but not in the spec.
448     }
449 
450     protected void handleEntityDecl()
451     {
452         // no-op ???
453         // this event is listed in the javadoc, but not in the spec.
454     }
455 
456     protected void handleCDATA() throws XMLStreamException
457     {
458         char[] chars = staxStreamReader.getText().toCharArray();
459 
460         try
461         {
462             filter.startCDATA();
463             filter.characters(chars, 0, chars.length);
464             filter.endCDATA();
465         }
466         catch(SAXException e)
467         {
468             throw new XMLStreamException(e);
469         }
470     }
471 }