1 /*
2  * Copyright (c) 1997, 2011, 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.xml.internal.bind.v2.runtime.unmarshaller;
27 
28 import java.util.Iterator;
29 
30 import javax.xml.namespace.QName;
31 import javax.xml.stream.Location;
32 import javax.xml.stream.XMLEventReader;
33 import javax.xml.stream.XMLStreamConstants;
34 import javax.xml.stream.XMLStreamException;
35 import javax.xml.stream.events.Attribute;
36 import javax.xml.stream.events.Characters;
37 import javax.xml.stream.events.EndElement;
38 import javax.xml.stream.events.Namespace;
39 import javax.xml.stream.events.StartElement;
40 import javax.xml.stream.events.XMLEvent;
41 
42 import org.xml.sax.Attributes;
43 import org.xml.sax.SAXException;
44 import org.xml.sax.helpers.AttributesImpl;
45 
46 /**
47  * This is a simple utility class that adapts StAX events from an
48  * {@link XMLEventReader} to unmarshaller events on a
49  * {@link XmlVisitor}, bridging between the two
50  * parser technologies.
51  *
52  * @author Ryan.Shoemaker@Sun.COM
53  * @version 1.0
54  */
55 final class StAXEventConnector extends StAXConnector {
56 
57     // StAX event source
58     private final XMLEventReader staxEventReader;
59 
60     /** Current event. */
61     private XMLEvent event;
62 
63     /**
64      * Shared and reused {@link Attributes}.
65      */
66     private final AttributesImpl attrs = new AttributesImpl();
67 
68     /**
69      * SAX may fire consective characters event, but we don't allow it.
70      * so use this buffer to perform buffering.
71      */
72     private final StringBuilder buffer = new StringBuilder();
73 
74     private boolean seenText;
75 
76     /**
77      * Construct a new StAX to SAX adapter that will convert a StAX event
78      * stream into a SAX event stream.
79      *
80      * @param staxCore
81      *                StAX event source
82      * @param visitor
83      *                sink
84      */
StAXEventConnector(XMLEventReader staxCore, XmlVisitor visitor)85     public StAXEventConnector(XMLEventReader staxCore, XmlVisitor visitor) {
86         super(visitor);
87         staxEventReader = staxCore;
88     }
89 
bridge()90     public void bridge() throws XMLStreamException {
91 
92         try {
93             // remembers the nest level of elements to know when we are done.
94             int depth=0;
95 
96             event = staxEventReader.peek();
97 
98             if( !event.isStartDocument() && !event.isStartElement() )
99                 throw new IllegalStateException();
100 
101             // if the parser is on START_DOCUMENT, skip ahead to the first element
102             do {
103                 event = staxEventReader.nextEvent();
104             } while( !event.isStartElement() );
105 
106             handleStartDocument(event.asStartElement().getNamespaceContext());
107 
108             OUTER:
109             while(true) {
110                 // These are all of the events listed in the javadoc for
111                 // XMLEvent.
112                 // The spec only really describes 11 of them.
113                 switch (event.getEventType()) {
114                     case XMLStreamConstants.START_ELEMENT :
115                         handleStartElement(event.asStartElement());
116                         depth++;
117                         break;
118                     case XMLStreamConstants.END_ELEMENT :
119                         depth--;
120                         handleEndElement(event.asEndElement());
121                         if(depth==0)    break OUTER;
122                         break;
123                     case XMLStreamConstants.CHARACTERS :
124                     case XMLStreamConstants.CDATA :
125                     case XMLStreamConstants.SPACE :
126                         handleCharacters(event.asCharacters());
127                         break;
128                 }
129 
130 
131                 event=staxEventReader.nextEvent();
132             }
133 
134             handleEndDocument();
135             event = null; // avoid keeping a stale reference
136         } catch (SAXException e) {
137             throw new XMLStreamException(e);
138         }
139     }
140 
getCurrentLocation()141     protected Location getCurrentLocation() {
142         return event.getLocation();
143     }
144 
getCurrentQName()145     protected String getCurrentQName() {
146         QName qName;
147         if(event.isEndElement())
148             qName = event.asEndElement().getName();
149         else
150             qName = event.asStartElement().getName();
151         return getQName(qName.getPrefix(), qName.getLocalPart());
152     }
153 
154 
handleCharacters(Characters event)155     private void handleCharacters(Characters event) throws SAXException, XMLStreamException {
156         if(!predictor.expectText())
157             return;     // text isn't expected. simply skip
158 
159         seenText = true;
160 
161         // check the next event
162         XMLEvent next;
163         while(true) {
164             next = staxEventReader.peek();
165             if(!isIgnorable(next))
166                 break;
167             staxEventReader.nextEvent();
168         }
169 
170         if(isTag(next)) {
171             // this is by far the common case --- you have <foo>abc</foo> or <foo>abc<bar/>...</foo>
172             visitor.text(event.getData());
173             return;
174         }
175 
176         // otherwise we have things like "abc<!-- test -->def".
177         // concatenate all text
178         buffer.append(event.getData());
179 
180         while(true) {
181             while(true) {
182                 next = staxEventReader.peek();
183                 if(!isIgnorable(next))
184                     break;
185                 staxEventReader.nextEvent();
186             }
187 
188             if(isTag(next)) {
189                 // found all adjacent text
190                 visitor.text(buffer);
191                 buffer.setLength(0);
192                 return;
193             }
194 
195             buffer.append(next.asCharacters().getData());
196             staxEventReader.nextEvent();    // consume
197         }
198     }
199 
isTag(XMLEvent event)200     private boolean isTag(XMLEvent event) {
201         int eventType = event.getEventType();
202         return eventType==XMLEvent.START_ELEMENT || eventType==XMLEvent.END_ELEMENT;
203     }
204 
isIgnorable(XMLEvent event)205     private boolean isIgnorable(XMLEvent event) {
206         int eventType = event.getEventType();
207         return eventType==XMLEvent.COMMENT || eventType==XMLEvent.PROCESSING_INSTRUCTION;
208     }
209 
handleEndElement(EndElement event)210     private void handleEndElement(EndElement event) throws SAXException {
211         if(!seenText && predictor.expectText()) {
212             visitor.text("");
213         }
214 
215         // fire endElement
216         QName qName = event.getName();
217         tagName.uri = fixNull(qName.getNamespaceURI());
218         tagName.local = qName.getLocalPart();
219         visitor.endElement(tagName);
220 
221         // end namespace bindings
222         for( Iterator<Namespace> i = event.getNamespaces(); i.hasNext();) {
223             String prefix = fixNull(i.next().getPrefix());  // be defensive
224             visitor.endPrefixMapping(prefix);
225         }
226 
227         seenText = false;
228     }
229 
handleStartElement(StartElement event)230     private void handleStartElement(StartElement event) throws SAXException {
231         // start namespace bindings
232         for (Iterator i = event.getNamespaces(); i.hasNext();) {
233             Namespace ns = (Namespace)i.next();
234             visitor.startPrefixMapping(
235                 fixNull(ns.getPrefix()),
236                 fixNull(ns.getNamespaceURI()));
237         }
238 
239         // fire startElement
240         QName qName = event.getName();
241         tagName.uri = fixNull(qName.getNamespaceURI());
242         String localName = qName.getLocalPart();
243         tagName.uri = fixNull(qName.getNamespaceURI());
244         tagName.local = localName;
245         tagName.atts = getAttributes(event);
246         visitor.startElement(tagName);
247 
248         seenText = false;
249     }
250 
251 
252 
253     /**
254      * Get the attributes associated with the given START_ELEMENT StAXevent.
255      *
256      * @return the StAX attributes converted to an org.xml.sax.Attributes
257      */
getAttributes(StartElement event)258     private Attributes getAttributes(StartElement event) {
259         attrs.clear();
260 
261         // in SAX, namespace declarations are not part of attributes by default.
262         // (there's a property to control that, but as far as we are concerned
263         // we don't use it.) So don't add xmlns:* to attributes.
264 
265         // gather non-namespace attrs
266         for (Iterator i = event.getAttributes(); i.hasNext();) {
267             Attribute staxAttr = (Attribute)i.next();
268 
269             QName name = staxAttr.getName();
270             String uri = fixNull(name.getNamespaceURI());
271             String localName = name.getLocalPart();
272             String prefix = name.getPrefix();
273             String qName;
274             if (prefix == null || prefix.length() == 0)
275                 qName = localName;
276             else
277                 qName = prefix + ':' + localName;
278             String type = staxAttr.getDTDType();
279             String value = staxAttr.getValue();
280 
281             attrs.addAttribute(uri, localName, qName, type, value);
282         }
283 
284         return attrs;
285     }
286 }
287