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 javax.xml.stream.Location; 29 import javax.xml.stream.XMLStreamConstants; 30 import javax.xml.stream.XMLStreamException; 31 32 import com.sun.xml.internal.bind.WhiteSpaceProcessor; 33 import com.sun.xml.internal.fastinfoset.stax.StAXDocumentParser; 34 import com.sun.xml.internal.org.jvnet.fastinfoset.EncodingAlgorithmIndexes; 35 import 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 */ 45 final 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 FastInfosetConnector(StAXDocumentParser fastInfosetStreamReader, XmlVisitor visitor)59 public FastInfosetConnector(StAXDocumentParser fastInfosetStreamReader, 60 XmlVisitor visitor) { 61 super(visitor); 62 fastInfosetStreamReader.setStringInterning(true); 63 this.fastInfosetStreamReader = fastInfosetStreamReader; 64 } 65 bridge()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 getCurrentLocation()131 protected Location getCurrentLocation() { 132 return fastInfosetStreamReader.getLocation(); 133 } 134 getCurrentQName()135 protected String getCurrentQName() { 136 return fastInfosetStreamReader.getNameString(); 137 } 138 handleStartElement()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 handleFragmentedCharacters()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 handleEndElement()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 CharSequenceImpl()204 CharSequenceImpl() { 205 } 206 CharSequenceImpl(final char[] ch, final int start, final int length)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 set()213 public void set() { 214 ch = fastInfosetStreamReader.getTextCharacters(); 215 start = fastInfosetStreamReader.getTextStart(); 216 length = fastInfosetStreamReader.getTextLength(); 217 } 218 219 // CharSequence interface 220 length()221 public final int length() { 222 return length; 223 } 224 charAt(final int index)225 public final char charAt(final int index) { 226 return ch[start + index]; 227 } 228 subSequence(final int start, final int end)229 public final CharSequence subSequence(final int start, final int end) { 230 return new CharSequenceImpl(ch, this.start + start, end - start); 231 } 232 toString()233 public String toString() { 234 return new String(ch, start, length); 235 } 236 } 237 238 final private CharSequenceImpl charArray = new CharSequenceImpl(); 239 processNonIgnorableText()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 processIgnorableText()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 processBufferedText(boolean ignorable)281 private void processBufferedText(boolean ignorable) throws SAXException { 282 if (!ignorable || !WhiteSpaceProcessor.isWhiteSpace(buffer)) { 283 visitor.text(buffer); 284 textReported = true; 285 } 286 } 287 processUnreportedText()288 private void processUnreportedText() throws SAXException { 289 if(!textReported && predictor.expectText()) { 290 visitor.text(""); 291 } 292 textReported = false; 293 } 294 } 295