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: package com.sun.xml.bind.v2.runtime.unmarshaller;
038:
039: import javax.xml.stream.Location;
040: import javax.xml.stream.XMLStreamConstants;
041: import javax.xml.stream.XMLStreamException;
042:
043: import com.sun.xml.bind.WhiteSpaceProcessor;
044: import com.sun.xml.fastinfoset.stax.StAXDocumentParser;
045: import org.jvnet.fastinfoset.EncodingAlgorithmIndexes;
046: import org.xml.sax.SAXException;
047:
048: /**
049: * Reads from FastInfoset StAX parser and feeds into JAXB Unmarshaller.
050: * <p>
051: * This class will peek at future events to ascertain if characters need to be
052: * buffered or not.
053: *
054: * @author Paul Sandoz.
055: */
056: final class FastInfosetConnector extends StAXConnector {
057:
058: // event source
059: private final StAXDocumentParser fastInfosetStreamReader;
060:
061: // Flag set to true if text has been reported
062: private boolean textReported;
063:
064: // Buffer for octets
065: private final Base64Data base64Data = new Base64Data();
066:
067: // Buffer for characters
068: private final StringBuilder buffer = new StringBuilder();
069:
070: public FastInfosetConnector(
071: StAXDocumentParser fastInfosetStreamReader,
072: XmlVisitor visitor) {
073: super (visitor);
074: fastInfosetStreamReader.setStringInterning(true);
075: this .fastInfosetStreamReader = fastInfosetStreamReader;
076: }
077:
078: public void bridge() throws XMLStreamException {
079: try {
080: // remembers the nest level of elements to know when we are done.
081: int depth = 0;
082:
083: // if the parser is at the start tag, proceed to the first element
084: int event = fastInfosetStreamReader.getEventType();
085: if (event == XMLStreamConstants.START_DOCUMENT) {
086: // nextTag doesn't correctly handle DTDs
087: while (!fastInfosetStreamReader.isStartElement())
088: event = fastInfosetStreamReader.next();
089: }
090:
091: if (event != XMLStreamConstants.START_ELEMENT)
092: throw new IllegalStateException(
093: "The current event is not START_ELEMENT\n but "
094: + event);
095:
096: // TODO: we don't have to rely on this hack --- we can just emulate
097: // start/end prefix mappings. But for now, I'll rely on this hack.
098: handleStartDocument(fastInfosetStreamReader
099: .getNamespaceContext());
100:
101: OUTER: while (true) {
102: // These are all of the events listed in the javadoc for
103: // XMLEvent.
104: // The spec only really describes 11 of them.
105: switch (event) {
106: case XMLStreamConstants.START_ELEMENT:
107: handleStartElement();
108: depth++;
109: break;
110: case XMLStreamConstants.END_ELEMENT:
111: depth--;
112: handleEndElement();
113: if (depth == 0)
114: break OUTER;
115: break;
116: case XMLStreamConstants.CHARACTERS:
117: case XMLStreamConstants.CDATA:
118: case XMLStreamConstants.SPACE:
119: if (predictor.expectText()) {
120: // Peek at the next event to see if there are
121: // fragmented characters
122: event = fastInfosetStreamReader.peekNext();
123: if (event == XMLStreamConstants.END_ELEMENT)
124: processNonIgnorableText();
125: else if (event == XMLStreamConstants.START_ELEMENT)
126: processIgnorableText();
127: else
128: handleFragmentedCharacters();
129: }
130: break;
131: // otherwise simply ignore
132: }
133:
134: event = fastInfosetStreamReader.next();
135: }
136:
137: fastInfosetStreamReader.next(); // move beyond the end tag.
138:
139: handleEndDocument();
140: } catch (SAXException e) {
141: throw new XMLStreamException(e);
142: }
143: }
144:
145: protected Location getCurrentLocation() {
146: return fastInfosetStreamReader.getLocation();
147: }
148:
149: protected String getCurrentQName() {
150: return fastInfosetStreamReader.getNameString();
151: }
152:
153: private void handleStartElement() throws SAXException {
154: processUnreportedText();
155:
156: for (int i = 0; i < fastInfosetStreamReader
157: .accessNamespaceCount(); i++) {
158: visitor.startPrefixMapping(fastInfosetStreamReader
159: .getNamespacePrefix(i), fastInfosetStreamReader
160: .getNamespaceURI(i));
161: }
162:
163: tagName.uri = fastInfosetStreamReader.accessNamespaceURI();
164: tagName.local = fastInfosetStreamReader.accessLocalName();
165: tagName.atts = fastInfosetStreamReader.getAttributesHolder();
166:
167: visitor.startElement(tagName);
168: }
169:
170: private void handleFragmentedCharacters()
171: throws XMLStreamException, SAXException {
172: buffer.setLength(0);
173:
174: // Append characters of first character event
175: buffer.append(fastInfosetStreamReader.accessTextCharacters(),
176: fastInfosetStreamReader.accessTextStart(),
177: fastInfosetStreamReader.accessTextLength());
178:
179: // Consume all character
180: while (true) {
181: switch (fastInfosetStreamReader.peekNext()) {
182: case XMLStreamConstants.START_ELEMENT:
183: processBufferedText(true);
184: return;
185: case XMLStreamConstants.END_ELEMENT:
186: processBufferedText(false);
187: return;
188: case XMLStreamConstants.CHARACTERS:
189: case XMLStreamConstants.CDATA:
190: case XMLStreamConstants.SPACE:
191: // Append characters of second and subsequent character events
192: fastInfosetStreamReader.next();
193: buffer.append(fastInfosetStreamReader
194: .accessTextCharacters(),
195: fastInfosetStreamReader.accessTextStart(),
196: fastInfosetStreamReader.accessTextLength());
197: break;
198: default:
199: fastInfosetStreamReader.next();
200: }
201: }
202: }
203:
204: private void handleEndElement() throws SAXException {
205: processUnreportedText();
206:
207: tagName.uri = fastInfosetStreamReader.accessNamespaceURI();
208: tagName.local = fastInfosetStreamReader.accessLocalName();
209:
210: visitor.endElement(tagName);
211:
212: for (int i = fastInfosetStreamReader.accessNamespaceCount() - 1; i >= 0; i--) {
213: visitor.endPrefixMapping(fastInfosetStreamReader
214: .getNamespacePrefix(i));
215: }
216: }
217:
218: final private class CharSequenceImpl implements CharSequence {
219: char[] ch;
220: int start;
221: int length;
222:
223: CharSequenceImpl() {
224: }
225:
226: CharSequenceImpl(final char[] ch, final int start,
227: final int length) {
228: this .ch = ch;
229: this .start = start;
230: this .length = length;
231: }
232:
233: public void set() {
234: ch = fastInfosetStreamReader.accessTextCharacters();
235: start = fastInfosetStreamReader.accessTextStart();
236: length = fastInfosetStreamReader.accessTextLength();
237: }
238:
239: // CharSequence interface
240:
241: public final int length() {
242: return length;
243: }
244:
245: public final char charAt(final int index) {
246: return ch[start + index];
247: }
248:
249: public final CharSequence subSequence(final int start,
250: final int end) {
251: return new CharSequenceImpl(ch, this .start + start, end
252: - start);
253: }
254:
255: public String toString() {
256: return new String(ch, start, length);
257: }
258: }
259:
260: final private CharSequenceImpl charArray = new CharSequenceImpl();
261:
262: private void processNonIgnorableText() throws SAXException {
263: textReported = true;
264: boolean isTextAlgorithmAplied = (fastInfosetStreamReader
265: .getTextAlgorithmBytes() != null);
266:
267: if (isTextAlgorithmAplied
268: && fastInfosetStreamReader.getTextAlgorithmIndex() == EncodingAlgorithmIndexes.BASE64) {
269: base64Data.set(fastInfosetStreamReader
270: .getTextAlgorithmBytesClone(), null);
271: visitor.text(base64Data);
272: } else {
273: if (isTextAlgorithmAplied) {
274: fastInfosetStreamReader.getText();
275: }
276:
277: charArray.set();
278: visitor.text(charArray);
279: }
280: }
281:
282: private void processIgnorableText() throws SAXException {
283: boolean isTextAlgorithmAplied = (fastInfosetStreamReader
284: .getTextAlgorithmBytes() != null);
285:
286: if (isTextAlgorithmAplied
287: && fastInfosetStreamReader.getTextAlgorithmIndex() == EncodingAlgorithmIndexes.BASE64) {
288: base64Data.set(fastInfosetStreamReader
289: .getTextAlgorithmBytesClone(), null);
290: visitor.text(base64Data);
291: textReported = true;
292: } else {
293: if (isTextAlgorithmAplied) {
294: fastInfosetStreamReader.getText();
295: }
296:
297: charArray.set();
298: if (!WhiteSpaceProcessor.isWhiteSpace(charArray)) {
299: visitor.text(charArray);
300: textReported = true;
301: }
302: }
303: }
304:
305: private void processBufferedText(boolean ignorable)
306: throws SAXException {
307: if (!ignorable || !WhiteSpaceProcessor.isWhiteSpace(buffer)) {
308: visitor.text(buffer);
309: textReported = true;
310: }
311: }
312:
313: private void processUnreportedText() throws SAXException {
314: if (!textReported && predictor.expectText()) {
315: visitor.text("");
316: }
317: textReported = false;
318: }
319: }
|