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
26package com.sun.xml.internal.bind.v2.runtime.unmarshaller;
27
28import javax.xml.stream.Location;
29import javax.xml.stream.XMLStreamConstants;
30import javax.xml.stream.XMLStreamException;
31
32import com.sun.xml.internal.bind.WhiteSpaceProcessor;
33import com.sun.xml.internal.fastinfoset.stax.StAXDocumentParser;
34import com.sun.xml.internal.org.jvnet.fastinfoset.EncodingAlgorithmIndexes;
35import org.xml.sax.SAXException;
36
37/**
38 * Reads from FastInfoset StAX parser and feeds into JAXB Unmarshaller.
39 * <p>
40 * This class will peek at future events to ascertain if characters need to be
41 * buffered or not.
42 *
43 * @author Paul Sandoz.
44 */
45final class FastInfosetConnector extends StAXConnector {
46
47    // event source
48    private final StAXDocumentParser fastInfosetStreamReader;
49
50    // Flag set to true if text has been reported
51    private boolean textReported;
52
53    // Buffer for octets
54    private final Base64Data base64Data = new Base64Data();
55
56    // Buffer for characters
57    private final StringBuilder buffer = new StringBuilder();
58
59    public FastInfosetConnector(StAXDocumentParser fastInfosetStreamReader,
60            XmlVisitor visitor) {
61        super(visitor);
62        fastInfosetStreamReader.setStringInterning(true);
63        this.fastInfosetStreamReader = fastInfosetStreamReader;
64    }
65
66    public void bridge() throws XMLStreamException {
67        try {
68            // remembers the nest level of elements to know when we are done.
69            int depth=0;
70
71            // if the parser is at the start tag, proceed to the first element
72            int event = fastInfosetStreamReader.getEventType();
73            if(event == XMLStreamConstants.START_DOCUMENT) {
74                // nextTag doesn't correctly handle DTDs
75                while( !fastInfosetStreamReader.isStartElement() )
76                    event = fastInfosetStreamReader.next();
77            }
78
79
80            if( event!=XMLStreamConstants.START_ELEMENT)
81                throw new IllegalStateException("The current event is not START_ELEMENT\n but " + event);
82
83            // TODO: we don't have to rely on this hack --- we can just emulate
84            // start/end prefix mappings. But for now, I'll rely on this hack.
85            handleStartDocument(fastInfosetStreamReader.getNamespaceContext());
86
87            OUTER:
88            while(true) {
89                // These are all of the events listed in the javadoc for
90                // XMLEvent.
91                // The spec only really describes 11 of them.
92                switch (event) {
93                    case XMLStreamConstants.START_ELEMENT :
94                        handleStartElement();
95                        depth++;
96                        break;
97                    case XMLStreamConstants.END_ELEMENT :
98                        depth--;
99                        handleEndElement();
100                        if(depth==0)    break OUTER;
101                        break;
102                    case XMLStreamConstants.CHARACTERS :
103                    case XMLStreamConstants.CDATA :
104                    case XMLStreamConstants.SPACE :
105                        if (predictor.expectText()) {
106                            // Peek at the next event to see if there are
107                            // fragmented characters
108                            event = fastInfosetStreamReader.peekNext();
109                            if (event == XMLStreamConstants.END_ELEMENT)
110                                processNonIgnorableText();
111                            else if (event == XMLStreamConstants.START_ELEMENT)
112                                processIgnorableText();
113                            else
114                                handleFragmentedCharacters();
115                        }
116                        break;
117                    // otherwise simply ignore
118                }
119
120                event=fastInfosetStreamReader.next();
121            }
122
123            fastInfosetStreamReader.next();    // move beyond the end tag.
124
125            handleEndDocument();
126        } catch (SAXException e) {
127            throw new XMLStreamException(e);
128        }
129    }
130
131    protected Location getCurrentLocation() {
132        return fastInfosetStreamReader.getLocation();
133    }
134
135    protected String getCurrentQName() {
136        return fastInfosetStreamReader.getNameString();
137    }
138
139    private void handleStartElement() throws SAXException {
140        processUnreportedText();
141
142        for (int i = 0; i < fastInfosetStreamReader.accessNamespaceCount(); i++) {
143            visitor.startPrefixMapping(fastInfosetStreamReader.getNamespacePrefix(i),
144                    fastInfosetStreamReader.getNamespaceURI(i));
145        }
146
147        tagName.uri = fastInfosetStreamReader.accessNamespaceURI();
148        tagName.local = fastInfosetStreamReader.accessLocalName();
149        tagName.atts = fastInfosetStreamReader.getAttributesHolder();
150
151        visitor.startElement(tagName);
152    }
153
154    private void handleFragmentedCharacters() throws XMLStreamException, SAXException {
155        buffer.setLength(0);
156
157        // Append characters of first character event
158        buffer.append(fastInfosetStreamReader.getTextCharacters(),
159                fastInfosetStreamReader.getTextStart(),
160                fastInfosetStreamReader.getTextLength());
161
162        // Consume all character
163        while(true) {
164            switch(fastInfosetStreamReader.peekNext()) {
165                case XMLStreamConstants.START_ELEMENT :
166                    processBufferedText(true);
167                    return;
168                case XMLStreamConstants.END_ELEMENT :
169                    processBufferedText(false);
170                    return;
171                case XMLStreamConstants.CHARACTERS :
172                case XMLStreamConstants.CDATA :
173                case XMLStreamConstants.SPACE :
174                    // Append characters of second and subsequent character events
175                    fastInfosetStreamReader.next();
176                    buffer.append(fastInfosetStreamReader.getTextCharacters(),
177                            fastInfosetStreamReader.getTextStart(),
178                            fastInfosetStreamReader.getTextLength());
179                    break;
180                default:
181                    fastInfosetStreamReader.next();
182            }
183        }
184    }
185
186    private void handleEndElement() throws SAXException {
187        processUnreportedText();
188
189        tagName.uri = fastInfosetStreamReader.accessNamespaceURI();
190        tagName.local = fastInfosetStreamReader.accessLocalName();
191
192        visitor.endElement(tagName);
193
194        for (int i = fastInfosetStreamReader.accessNamespaceCount() - 1; i >= 0; i--) {
195            visitor.endPrefixMapping(fastInfosetStreamReader.getNamespacePrefix(i));
196        }
197    }
198
199    final private class CharSequenceImpl implements CharSequence {
200        char[] ch;
201        int start;
202        int length;
203
204        CharSequenceImpl() {
205        }
206
207        CharSequenceImpl(final char[] ch, final int start, final int length) {
208            this.ch = ch;
209            this.start = start;
210            this.length = length;
211        }
212
213        public void set() {
214            ch = fastInfosetStreamReader.getTextCharacters();
215            start = fastInfosetStreamReader.getTextStart();
216            length = fastInfosetStreamReader.getTextLength();
217        }
218
219        // CharSequence interface
220
221        public final int length() {
222            return length;
223        }
224
225        public final char charAt(final int index) {
226            return ch[start + index];
227        }
228
229        public final CharSequence subSequence(final int start, final int end) {
230            return new CharSequenceImpl(ch, this.start + start, end - start);
231        }
232
233        public String toString() {
234            return new String(ch, start, length);
235        }
236    }
237
238    final private CharSequenceImpl charArray = new CharSequenceImpl();
239
240    private void processNonIgnorableText() throws SAXException {
241        textReported = true;
242        boolean isTextAlgorithmAplied =
243                (fastInfosetStreamReader.getTextAlgorithmBytes() != null);
244
245        if (isTextAlgorithmAplied &&
246                fastInfosetStreamReader.getTextAlgorithmIndex() == EncodingAlgorithmIndexes.BASE64) {
247            base64Data.set(fastInfosetStreamReader.getTextAlgorithmBytesClone(),null);
248            visitor.text(base64Data);
249        } else {
250            if (isTextAlgorithmAplied) {
251                fastInfosetStreamReader.getText();
252            }
253
254            charArray.set();
255            visitor.text(charArray);
256        }
257    }
258
259    private void processIgnorableText() throws SAXException {
260        boolean isTextAlgorithmAplied =
261                (fastInfosetStreamReader.getTextAlgorithmBytes() != null);
262
263        if (isTextAlgorithmAplied &&
264                fastInfosetStreamReader.getTextAlgorithmIndex() == EncodingAlgorithmIndexes.BASE64) {
265            base64Data.set(fastInfosetStreamReader.getTextAlgorithmBytesClone(),null);
266            visitor.text(base64Data);
267            textReported = true;
268        } else {
269            if (isTextAlgorithmAplied) {
270                fastInfosetStreamReader.getText();
271            }
272
273            charArray.set();
274            if (!WhiteSpaceProcessor.isWhiteSpace(charArray)) {
275                visitor.text(charArray);
276                textReported = true;
277            }
278        }
279    }
280
281    private void processBufferedText(boolean ignorable) throws SAXException {
282        if (!ignorable || !WhiteSpaceProcessor.isWhiteSpace(buffer)) {
283            visitor.text(buffer);
284            textReported = true;
285        }
286    }
287
288    private void processUnreportedText() throws SAXException {
289        if(!textReported && predictor.expectText()) {
290            visitor.text("");
291        }
292        textReported = false;
293    }
294}
295