View Javadoc
1   /*
2    * Copyright (c) 1997, 2012, Oracle and/or its affiliates. All rights reserved.
3    * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4    *
5    * This code is free software; you can redistribute it and/or modify it
6    * under the terms of the GNU General Public License version 2 only, as
7    * published by the Free Software Foundation.  Oracle designates this
8    * particular file as subject to the "Classpath" exception as provided
9    * by Oracle in the LICENSE file that accompanied this code.
10   *
11   * This code is distributed in the hope that it will be useful, but WITHOUT
12   * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13   * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14   * version 2 for more details (a copy is included in the LICENSE file that
15   * accompanied this code).
16   *
17   * You should have received a copy of the GNU General Public License version
18   * 2 along with this work; if not, write to the Free Software Foundation,
19   * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20   *
21   * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22   * or visit www.oracle.com if you need additional information or have any
23   * questions.
24   */
25  
26  package com.sun.istack.internal;
27  
28  import org.xml.sax.ContentHandler;
29  import org.xml.sax.SAXException;
30  import org.xml.sax.Locator;
31  import org.xml.sax.Attributes;
32  import org.xml.sax.helpers.AttributesImpl;
33  
34  import javax.xml.stream.XMLStreamReader;
35  import javax.xml.stream.XMLStreamException;
36  import javax.xml.stream.XMLStreamConstants;
37  import javax.xml.namespace.QName;
38  
39  /**
40   * This is a simple utility class that adapts StAX events from an
41   * {@link XMLStreamReader} to SAX events on a
42   * {@link ContentHandler}, bridging between the two
43   * parser technologies.
44   *
45   * @author Ryan.Shoemaker@Sun.COM
46   * @version 1.0
47   */
48  public class XMLStreamReaderToContentHandler {
49  
50      // StAX event source
51      private final XMLStreamReader staxStreamReader;
52  
53      // SAX event sink
54      private final ContentHandler saxHandler;
55  
56      // if true, when the conversion is completed, leave the cursor to the last
57      // event that was fired (such as end element)
58      private final boolean eagerQuit;
59  
60      /**
61       * If true, not start/endDocument event.
62       */
63      private final boolean fragment;
64  
65      // array of the even length of the form { prefix0, uri0, prefix1, uri1, ... }
66      private final String[] inscopeNamespaces;
67  
68      /**
69       * @see #XMLStreamReaderToContentHandler(XMLStreamReader, ContentHandler, boolean, boolean, String[])
70       */
71      public XMLStreamReaderToContentHandler(XMLStreamReader staxCore, ContentHandler saxCore, boolean eagerQuit, boolean fragment) {
72          this(staxCore, saxCore, eagerQuit, fragment, new String[0]);
73      }
74  
75      /**
76       * Construct a new StAX to SAX adapter that will convert a StAX event
77       * stream into a SAX event stream.
78       *
79       * @param staxCore
80       *                StAX event source
81       * @param saxCore
82       *                SAXevent sink
83       * @param eagerQuit
84       * @param fragment
85       * @param inscopeNamespaces
86       *                array of the even length of the form { prefix0, uri0, prefix1, uri1, ... }
87       */
88      public XMLStreamReaderToContentHandler(XMLStreamReader staxCore, ContentHandler saxCore,
89              boolean eagerQuit, boolean fragment, String[] inscopeNamespaces) {
90          this.staxStreamReader = staxCore;
91          this.saxHandler = saxCore;
92          this.eagerQuit = eagerQuit;
93          this.fragment = fragment;
94          this.inscopeNamespaces = inscopeNamespaces;
95          assert inscopeNamespaces.length%2 == 0;
96      }
97  
98  
99      /*
100      * @see StAXReaderToContentHandler#bridge()
101      */
102     public void bridge() throws XMLStreamException {
103 
104         try {
105             // remembers the nest level of elements to know when we are done.
106             int depth=0;
107 
108             // if the parser is at the start tag, proceed to the first element
109             int event = staxStreamReader.getEventType();
110             if(event == XMLStreamConstants.START_DOCUMENT) {
111                 // nextTag doesn't correctly handle DTDs
112                 while( !staxStreamReader.isStartElement() )
113                     event = staxStreamReader.next();
114             }
115 
116 
117             if( event!=XMLStreamConstants.START_ELEMENT)
118                 throw new IllegalStateException("The current event is not START_ELEMENT\n but " + event);
119 
120             handleStartDocument();
121 
122             for(int i=0; i < inscopeNamespaces.length; i+=2) {
123                 saxHandler.startPrefixMapping(inscopeNamespaces[i], inscopeNamespaces[i+1]);
124             }
125 
126             OUTER:
127             do {
128                 // These are all of the events listed in the javadoc for
129                 // XMLEvent.
130                 // The spec only really describes 11 of them.
131                 switch (event) {
132                     case XMLStreamConstants.START_ELEMENT :
133                         depth++;
134                         handleStartElement();
135                         break;
136                     case XMLStreamConstants.END_ELEMENT :
137                         handleEndElement();
138                         depth--;
139                         if(depth==0 && eagerQuit)
140                             break OUTER;
141                         break;
142                     case XMLStreamConstants.CHARACTERS :
143                         handleCharacters();
144                         break;
145                     case XMLStreamConstants.ENTITY_REFERENCE :
146                         handleEntityReference();
147                         break;
148                     case XMLStreamConstants.PROCESSING_INSTRUCTION :
149                         handlePI();
150                         break;
151                     case XMLStreamConstants.COMMENT :
152                         handleComment();
153                         break;
154                     case XMLStreamConstants.DTD :
155                         handleDTD();
156                         break;
157                     case XMLStreamConstants.ATTRIBUTE :
158                         handleAttribute();
159                         break;
160                     case XMLStreamConstants.NAMESPACE :
161                         handleNamespace();
162                         break;
163                     case XMLStreamConstants.CDATA :
164                         handleCDATA();
165                         break;
166                     case XMLStreamConstants.ENTITY_DECLARATION :
167                         handleEntityDecl();
168                         break;
169                     case XMLStreamConstants.NOTATION_DECLARATION :
170                         handleNotationDecl();
171                         break;
172                     case XMLStreamConstants.SPACE :
173                         handleSpace();
174                         break;
175                     default :
176                         throw new InternalError("processing event: " + event);
177                 }
178 
179                 event=staxStreamReader.next();
180             } while (depth!=0);
181 
182             for(int i=0; i < inscopeNamespaces.length; i+=2) {
183                 saxHandler.endPrefixMapping(inscopeNamespaces[i]);
184             }
185 
186             handleEndDocument();
187         } catch (SAXException e) {
188             throw new XMLStreamException2(e);
189         }
190     }
191 
192     private void handleEndDocument() throws SAXException {
193         if(fragment)
194             return;
195 
196         saxHandler.endDocument();
197     }
198 
199     private void handleStartDocument() throws SAXException {
200         if(fragment)
201             return;
202 
203         saxHandler.setDocumentLocator(new Locator() {
204             public int getColumnNumber() {
205                 return staxStreamReader.getLocation().getColumnNumber();
206             }
207             public int getLineNumber() {
208                 return staxStreamReader.getLocation().getLineNumber();
209             }
210             public String getPublicId() {
211                 return staxStreamReader.getLocation().getPublicId();
212             }
213             public String getSystemId() {
214                 return staxStreamReader.getLocation().getSystemId();
215             }
216         });
217         saxHandler.startDocument();
218     }
219 
220     private void handlePI() throws XMLStreamException {
221         try {
222             saxHandler.processingInstruction(
223                 staxStreamReader.getPITarget(),
224                 staxStreamReader.getPIData());
225         } catch (SAXException e) {
226             throw new XMLStreamException2(e);
227         }
228     }
229 
230     private void handleCharacters() throws XMLStreamException {
231         try {
232             saxHandler.characters(
233                 staxStreamReader.getTextCharacters(),
234                 staxStreamReader.getTextStart(),
235                 staxStreamReader.getTextLength() );
236         } catch (SAXException e) {
237             throw new XMLStreamException2(e);
238         }
239     }
240 
241     private void handleEndElement() throws XMLStreamException {
242         QName qName = staxStreamReader.getName();
243 
244         try {
245             String pfix = qName.getPrefix();
246             String rawname = (pfix == null || pfix.length() == 0)
247                     ? qName.getLocalPart()
248                     : pfix + ':' + qName.getLocalPart();
249             // fire endElement
250             saxHandler.endElement(
251                 qName.getNamespaceURI(),
252                 qName.getLocalPart(),
253                 rawname);
254 
255             // end namespace bindings
256             int nsCount = staxStreamReader.getNamespaceCount();
257             for (int i = nsCount - 1; i >= 0; i--) {
258                 String prefix = staxStreamReader.getNamespacePrefix(i);
259                 if (prefix == null) { // true for default namespace
260                     prefix = "";
261                 }
262                 saxHandler.endPrefixMapping(prefix);
263             }
264         } catch (SAXException e) {
265             throw new XMLStreamException2(e);
266         }
267     }
268 
269     private void handleStartElement() throws XMLStreamException {
270 
271         try {
272             // start namespace bindings
273             int nsCount = staxStreamReader.getNamespaceCount();
274             for (int i = 0; i < nsCount; i++) {
275                 saxHandler.startPrefixMapping(
276                     fixNull(staxStreamReader.getNamespacePrefix(i)),
277                     fixNull(staxStreamReader.getNamespaceURI(i)));
278             }
279 
280             // fire startElement
281             QName qName = staxStreamReader.getName();
282             String prefix = qName.getPrefix();
283             String rawname;
284             if(prefix==null || prefix.length()==0)
285                 rawname = qName.getLocalPart();
286             else
287                 rawname = prefix + ':' + qName.getLocalPart();
288             Attributes attrs = getAttributes();
289             saxHandler.startElement(
290                 qName.getNamespaceURI(),
291                 qName.getLocalPart(),
292                 rawname,
293                 attrs);
294         } catch (SAXException e) {
295             throw new XMLStreamException2(e);
296         }
297     }
298 
299     private static String fixNull(String s) {
300         if(s==null)     return "";
301         else            return s;
302     }
303 
304     /**
305      * Get the attributes associated with the given START_ELEMENT or ATTRIBUTE
306      * StAXevent.
307      *
308      * @return the StAX attributes converted to an org.xml.sax.Attributes
309      */
310     private Attributes getAttributes() {
311         AttributesImpl attrs = new AttributesImpl();
312 
313         int eventType = staxStreamReader.getEventType();
314         if (eventType != XMLStreamConstants.ATTRIBUTE
315             && eventType != XMLStreamConstants.START_ELEMENT) {
316             throw new InternalError(
317                 "getAttributes() attempting to process: " + eventType);
318         }
319 
320         // in SAX, namespace declarations are not part of attributes by default.
321         // (there's a property to control that, but as far as we are concerned
322         // we don't use it.) So don't add xmlns:* to attributes.
323 
324         // gather non-namespace attrs
325         for (int i = 0; i < staxStreamReader.getAttributeCount(); i++) {
326             String uri = staxStreamReader.getAttributeNamespace(i);
327             if(uri==null)   uri="";
328             String localName = staxStreamReader.getAttributeLocalName(i);
329             String prefix = staxStreamReader.getAttributePrefix(i);
330             String qName;
331             if(prefix==null || prefix.length()==0)
332                 qName = localName;
333             else
334                 qName = prefix + ':' + localName;
335             String type = staxStreamReader.getAttributeType(i);
336             String value = staxStreamReader.getAttributeValue(i);
337 
338             attrs.addAttribute(uri, localName, qName, type, value);
339         }
340 
341         return attrs;
342     }
343 
344     private void handleNamespace() {
345         // no-op ???
346         // namespace events don't normally occur outside of a startElement
347         // or endElement
348     }
349 
350     private void handleAttribute() {
351         // no-op ???
352         // attribute events don't normally occur outside of a startElement
353         // or endElement
354     }
355 
356     private void handleDTD() {
357         // no-op ???
358         // it seems like we need to pass this info along, but how?
359     }
360 
361     private void handleComment() {
362         // no-op ???
363     }
364 
365     private void handleEntityReference() {
366         // no-op ???
367     }
368 
369     private void handleSpace() {
370         // no-op ???
371         // this event is listed in the javadoc, but not in the spec.
372     }
373 
374     private void handleNotationDecl() {
375         // no-op ???
376         // this event is listed in the javadoc, but not in the spec.
377     }
378 
379     private void handleEntityDecl() {
380         // no-op ???
381         // this event is listed in the javadoc, but not in the spec.
382     }
383 
384     private void handleCDATA() {
385         // no-op ???
386         // this event is listed in the javadoc, but not in the spec.
387     }
388 }