001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common Development
008: * and Distribution License("CDDL") (collectively, the "License"). You
009: * may not use this file except in compliance with the License. You can obtain
010: * a copy of the License at https://glassfish.dev.java.net/public/CDDL+GPL.html
011: * or glassfish/bootstrap/legal/LICENSE.txt. See the License for the specific
012: * language governing permissions and limitations under the License.
013: *
014: * When distributing the software, include this License Header Notice in each
015: * file and include the License file at glassfish/bootstrap/legal/LICENSE.txt.
016: * Sun designates this particular file as subject to the "Classpath" exception
017: * as provided by Sun in the GPL Version 2 section of the License file that
018: * accompanied this code. If applicable, add the following below the License
019: * Header, with the fields enclosed by brackets [] replaced by your own
020: * identifying information: "Portions Copyrighted [year]
021: * [name of copyright owner]"
022: *
023: * Contributor(s):
024: *
025: * If you wish your version of this file to be governed by only the CDDL or
026: * only the GPL Version 2, indicate your decision by adding "[Contributor]
027: * elects to include this software in this distribution under the [CDDL or GPL
028: * Version 2] license." If you don't indicate a single choice of license, a
029: * recipient has the option to distribute your version of this file under
030: * either the CDDL, the GPL Version 2 or to extend the choice of license to
031: * its licensees as provided above. However, if you add GPL Version 2 code
032: * and therefore, elected the GPL Version 2 license, then the option applies
033: * only if the new code is made subject to such option by the copyright
034: * holder.
035: */
036:
037: /* $Id: StAXStreamConnector.java,v 1.9.2.10 2007/05/31 21:59:57 ofung Exp $
038: *
039: * Copyright (c) 2004, Sun Microsystems, Inc.
040: * All rights reserved.
041: *
042: * Redistribution and use in source and binary forms, with or without
043: * modification, are permitted provided that the following conditions are
044: * met:
045: *
046: * * Redistributions of source code must retain the above copyright
047: * notice, this list of conditions and the following disclaimer.
048: *
049: * * Redistributions in binary form must reproduce the above
050: * copyright notice, this list of conditions and the following
051: * disclaimer in the documentation and/or other materials provided
052: * with the distribution.
053: *
054: * * Neither the name of Sun Microsystems, Inc. nor the names of its
055: * contributors may be used to endorse or promote products derived
056: * from this software without specific prior written permission.
057: *
058: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
059: * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
060: * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
061: * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
062: * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
063: * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
064: * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
065: * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
066: * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
067: * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
068: * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
069: */
070: package com.sun.xml.bind.v2.runtime.unmarshaller;
071:
072: import java.lang.reflect.Constructor;
073:
074: import javax.xml.stream.Location;
075: import javax.xml.stream.XMLStreamConstants;
076: import javax.xml.stream.XMLStreamException;
077: import javax.xml.stream.XMLStreamReader;
078:
079: import com.sun.xml.bind.WhiteSpaceProcessor;
080:
081: import org.xml.sax.Attributes;
082: import org.xml.sax.SAXException;
083:
084: /**
085: * Reads XML from StAX {@link XMLStreamReader} and
086: * feeds events to {@link XmlVisitor}.
087: * <p>
088: * TODO:
089: * Finding the optimized FI implementations is a bit hacky and not very
090: * extensible. Can we use the service provider mechnism in general for
091: * concrete implementations of StAXConnector.
092: *
093: * @author Ryan.Shoemaker@Sun.COM
094: * @author Kohsuke Kawaguchi
095: * @version JAXB 2.0
096: */
097: class StAXStreamConnector extends StAXConnector {
098:
099: /**
100: * Creates a {@link StAXConnector} from {@link XMLStreamReader}.
101: *
102: * This method checks if the parser is FI parser and acts accordingly.
103: */
104: public static StAXConnector create(XMLStreamReader reader,
105: XmlVisitor visitor) {
106: // try optimized codepath
107: final Class readerClass = reader.getClass();
108: if (FI_STAX_READER_CLASS != null
109: && FI_STAX_READER_CLASS.isAssignableFrom(readerClass)
110: && FI_CONNECTOR_CTOR != null) {
111: try {
112: return FI_CONNECTOR_CTOR.newInstance(reader, visitor);
113: } catch (Exception t) {
114: }
115: }
116:
117: // Quick hack until SJSXP fixes 6270116
118: boolean isZephyr = readerClass.getName().equals(
119: "com.sun.xml.stream.XMLReaderImpl");
120: if (isZephyr)
121: ; // no need for interning
122: else if (checkImplementaionNameOfSjsxp(reader))
123: ; // no need for interning
124: else if (getBoolProp(reader, "org.codehaus.stax2.internNames")
125: && getBoolProp(reader,
126: "org.codehaus.stax2.internNsUris"))
127: ; // no need for interning.
128: else
129: visitor = new InterningXmlVisitor(visitor);
130:
131: if (STAX_EX_READER_CLASS != null
132: && STAX_EX_READER_CLASS.isAssignableFrom(readerClass)) {
133: try {
134: return STAX_EX_CONNECTOR_CTOR.newInstance(reader,
135: visitor);
136: } catch (Exception t) {
137: }
138: }
139:
140: return new StAXStreamConnector(reader, visitor);
141: }
142:
143: private static boolean checkImplementaionNameOfSjsxp(
144: XMLStreamReader reader) {
145: try {
146: Object name = reader
147: .getProperty("http://java.sun.com/xml/stream/properties/implementation-name");
148: return name != null && name.equals("sjsxp");
149: } catch (Exception e) {
150: // be defensive against broken StAX parsers since javadoc is not clear
151: // about when an error happens
152: return false;
153: }
154: }
155:
156: private static boolean getBoolProp(XMLStreamReader r, String n) {
157: try {
158: Object o = r.getProperty(n);
159: if (o instanceof Boolean)
160: return (Boolean) o;
161: return false;
162: } catch (Exception e) {
163: // be defensive against broken StAX parsers since javadoc is not clear
164: // about when an error happens
165: return false;
166: }
167: }
168:
169: // StAX event source
170: private final XMLStreamReader staxStreamReader;
171:
172: /**
173: * SAX may fire consective characters event, but we don't allow it.
174: * so use this buffer to perform buffering.
175: */
176: protected final StringBuilder buffer = new StringBuilder();
177:
178: /**
179: * Set to true if the text() event is reported, and therefore
180: * the following text() event should be suppressed.
181: */
182: protected boolean textReported = false;
183:
184: protected StAXStreamConnector(XMLStreamReader staxStreamReader,
185: XmlVisitor visitor) {
186: super (visitor);
187: this .staxStreamReader = staxStreamReader;
188: }
189:
190: public void bridge() throws XMLStreamException {
191:
192: try {
193: // remembers the nest level of elements to know when we are done.
194: int depth = 0;
195:
196: // if the parser is at the start tag, proceed to the first element
197: int event = staxStreamReader.getEventType();
198: if (event == XMLStreamConstants.START_DOCUMENT) {
199: // nextTag doesn't correctly handle DTDs
200: while (!staxStreamReader.isStartElement())
201: event = staxStreamReader.next();
202: }
203:
204: if (event != XMLStreamConstants.START_ELEMENT)
205: throw new IllegalStateException(
206: "The current event is not START_ELEMENT\n but "
207: + event);
208:
209: handleStartDocument(staxStreamReader.getNamespaceContext());
210:
211: OUTER: while (true) {
212: // These are all of the events listed in the javadoc for
213: // XMLEvent.
214: // The spec only really describes 11 of them.
215: switch (event) {
216: case XMLStreamConstants.START_ELEMENT:
217: handleStartElement();
218: depth++;
219: break;
220: case XMLStreamConstants.END_ELEMENT:
221: depth--;
222: handleEndElement();
223: if (depth == 0)
224: break OUTER;
225: break;
226: case XMLStreamConstants.CHARACTERS:
227: case XMLStreamConstants.CDATA:
228: case XMLStreamConstants.SPACE:
229: handleCharacters();
230: break;
231: // otherwise simply ignore
232: }
233:
234: event = staxStreamReader.next();
235: }
236:
237: staxStreamReader.next(); // move beyond the end tag.
238:
239: handleEndDocument();
240: } catch (SAXException e) {
241: throw new XMLStreamException(e);
242: }
243: }
244:
245: protected Location getCurrentLocation() {
246: return staxStreamReader.getLocation();
247: }
248:
249: protected String getCurrentQName() {
250: return getQName(staxStreamReader.getPrefix(), staxStreamReader
251: .getLocalName());
252: }
253:
254: private void handleEndElement() throws SAXException {
255: processText(false);
256:
257: // fire endElement
258: tagName.uri = fixNull(staxStreamReader.getNamespaceURI());
259: tagName.local = staxStreamReader.getLocalName();
260: visitor.endElement(tagName);
261:
262: // end namespace bindings
263: int nsCount = staxStreamReader.getNamespaceCount();
264: for (int i = nsCount - 1; i >= 0; i--) {
265: visitor.endPrefixMapping(fixNull(staxStreamReader
266: .getNamespacePrefix(i)));
267: }
268: }
269:
270: private void handleStartElement() throws SAXException {
271: processText(true);
272:
273: // start namespace bindings
274: int nsCount = staxStreamReader.getNamespaceCount();
275: for (int i = 0; i < nsCount; i++) {
276: visitor.startPrefixMapping(fixNull(staxStreamReader
277: .getNamespacePrefix(i)), fixNull(staxStreamReader
278: .getNamespaceURI(i)));
279: }
280:
281: // fire startElement
282: tagName.uri = fixNull(staxStreamReader.getNamespaceURI());
283: tagName.local = staxStreamReader.getLocalName();
284: tagName.atts = attributes;
285:
286: visitor.startElement(tagName);
287: }
288:
289: /**
290: * Proxy of {@link Attributes} that read from {@link XMLStreamReader}.
291: */
292: private final Attributes attributes = new Attributes() {
293: public int getLength() {
294: return staxStreamReader.getAttributeCount();
295: }
296:
297: public String getURI(int index) {
298: String uri = staxStreamReader.getAttributeNamespace(index);
299: if (uri == null)
300: return "";
301: return uri;
302: }
303:
304: public String getLocalName(int index) {
305: return staxStreamReader.getAttributeLocalName(index);
306: }
307:
308: public String getQName(int index) {
309: String prefix = staxStreamReader.getAttributePrefix(index);
310: if (prefix == null || prefix.length() == 0)
311: return getLocalName(index);
312: else
313: return prefix + ':' + getLocalName(index);
314: }
315:
316: public String getType(int index) {
317: return staxStreamReader.getAttributeType(index);
318: }
319:
320: public String getValue(int index) {
321: return staxStreamReader.getAttributeValue(index);
322: }
323:
324: public int getIndex(String uri, String localName) {
325: for (int i = getLength() - 1; i >= 0; i--)
326: if (localName.equals(getLocalName(i))
327: && uri.equals(getURI(i)))
328: return i;
329: return -1;
330: }
331:
332: // this method sholdn't be used that often (if at all)
333: // so it's OK to be slow.
334: public int getIndex(String qName) {
335: for (int i = getLength() - 1; i >= 0; i--) {
336: if (qName.equals(getQName(i)))
337: return i;
338: }
339: return -1;
340: }
341:
342: public String getType(String uri, String localName) {
343: int index = getIndex(uri, localName);
344: if (index < 0)
345: return null;
346: return getType(index);
347: }
348:
349: public String getType(String qName) {
350: int index = getIndex(qName);
351: if (index < 0)
352: return null;
353: return getType(index);
354: }
355:
356: public String getValue(String uri, String localName) {
357: int index = getIndex(uri, localName);
358: if (index < 0)
359: return null;
360: return getValue(index);
361: }
362:
363: public String getValue(String qName) {
364: int index = getIndex(qName);
365: if (index < 0)
366: return null;
367: return getValue(index);
368: }
369: };
370:
371: protected void handleCharacters() throws XMLStreamException,
372: SAXException {
373: if (predictor.expectText())
374: buffer.append(staxStreamReader.getTextCharacters(),
375: staxStreamReader.getTextStart(), staxStreamReader
376: .getTextLength());
377: }
378:
379: private void processText(boolean ignorable) throws SAXException {
380: if (predictor.expectText()
381: && (!ignorable || !WhiteSpaceProcessor
382: .isWhiteSpace(buffer))) {
383: if (textReported) {
384: textReported = false;
385: } else {
386: visitor.text(buffer);
387: }
388: }
389: buffer.setLength(0);
390: }
391:
392: /**
393: * Reference to FI's StAXReader class, if FI can be loaded.
394: */
395: private static final Class FI_STAX_READER_CLASS = initFIStAXReaderClass();
396: private static final Constructor<? extends StAXConnector> FI_CONNECTOR_CTOR = initFastInfosetConnectorClass();
397:
398: private static Class initFIStAXReaderClass() {
399: try {
400: Class fisr = UnmarshallerImpl.class
401: .getClassLoader()
402: .loadClass(
403: "org.jvnet.fastinfoset.stax.FastInfosetStreamReader");
404: Class sdp = UnmarshallerImpl.class
405: .getClassLoader()
406: .loadClass(
407: "com.sun.xml.fastinfoset.stax.StAXDocumentParser");
408: // Check if StAXDocumentParser implements FastInfosetStreamReader
409: if (fisr.isAssignableFrom(sdp))
410: return sdp;
411: else
412: return null;
413: } catch (Throwable e) {
414: return null;
415: }
416: }
417:
418: private static Constructor<? extends StAXConnector> initFastInfosetConnectorClass() {
419: try {
420: if (FI_STAX_READER_CLASS == null)
421: return null;
422:
423: Class c = UnmarshallerImpl.class
424: .getClassLoader()
425: .loadClass(
426: "com.sun.xml.bind.v2.runtime.unmarshaller.FastInfosetConnector");
427: return c.getConstructor(FI_STAX_READER_CLASS,
428: XmlVisitor.class);
429: } catch (Throwable e) {
430: return null;
431: }
432: }
433:
434: //
435: // reference to StAXEx classes
436: //
437: private static final Class STAX_EX_READER_CLASS = initStAXExReader();
438: private static final Constructor<? extends StAXConnector> STAX_EX_CONNECTOR_CTOR = initStAXExConnector();
439:
440: private static Class initStAXExReader() {
441: try {
442: return UnmarshallerImpl.class.getClassLoader().loadClass(
443: "org.jvnet.staxex.XMLStreamReaderEx");
444: } catch (Throwable e) {
445: return null;
446: }
447: }
448:
449: private static Constructor<? extends StAXConnector> initStAXExConnector() {
450: try {
451: Class c = UnmarshallerImpl.class
452: .getClassLoader()
453: .loadClass(
454: "com.sun.xml.bind.v2.runtime.unmarshaller.StAXExConnector");
455: return c.getConstructor(STAX_EX_READER_CLASS,
456: XmlVisitor.class);
457: } catch (Throwable e) {
458: return null;
459: }
460: }
461: }
|