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 
  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 
  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 }