1 /*
   2  * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
   3  * @LastModified: Oct 2017
   4  */
   5 /*
   6  * Licensed to the Apache Software Foundation (ASF) under one or more
   7  * contributor license agreements.  See the NOTICE file distributed with
   8  * this work for additional information regarding copyright ownership.
   9  * The ASF licenses this file to You under the Apache License, Version 2.0
  10  * (the "License"); you may not use this file except in compliance with
  11  * the License.  You may obtain a copy of the License at
  12  *
  13  *      http://www.apache.org/licenses/LICENSE-2.0
  14  *
  15  * Unless required by applicable law or agreed to in writing, software
  16  * distributed under the License is distributed on an "AS IS" BASIS,
  17  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  18  * See the License for the specific language governing permissions and
  19  * limitations under the License.
  20  */
  21 
  22 package com.sun.org.apache.xerces.internal.impl.xs.traversers;
  23 
  24 import com.sun.org.apache.xerces.internal.impl.xs.opti.SchemaDOMParser;
  25 import com.sun.org.apache.xerces.internal.util.JAXPNamespaceContextWrapper;
  26 import com.sun.org.apache.xerces.internal.util.StAXLocationWrapper;
  27 import com.sun.org.apache.xerces.internal.util.SymbolTable;
  28 import com.sun.org.apache.xerces.internal.util.XMLAttributesImpl;
  29 import com.sun.org.apache.xerces.internal.util.XMLStringBuffer;
  30 import com.sun.org.apache.xerces.internal.util.XMLSymbols;
  31 import com.sun.org.apache.xerces.internal.xni.NamespaceContext;
  32 import com.sun.org.apache.xerces.internal.xni.QName;
  33 import com.sun.org.apache.xerces.internal.xni.XMLString;
  34 import com.sun.org.apache.xerces.internal.xni.XNIException;
  35 import java.util.ArrayList;
  36 import java.util.Iterator;
  37 import java.util.List;
  38 import javax.xml.stream.XMLEventReader;
  39 import javax.xml.stream.XMLStreamConstants;
  40 import javax.xml.stream.XMLStreamException;
  41 import javax.xml.stream.XMLStreamReader;
  42 import javax.xml.stream.events.Attribute;
  43 import javax.xml.stream.events.EndElement;
  44 import javax.xml.stream.events.Namespace;
  45 import javax.xml.stream.events.ProcessingInstruction;
  46 import javax.xml.stream.events.StartElement;
  47 import javax.xml.stream.events.XMLEvent;
  48 import org.w3c.dom.Document;
  49 
  50 /**
  51  * <p>StAXSchemaParser reads StAX events, converts them into XNI events
  52  * and passes them directly to the SchemaDOMParser.</p>
  53  *
  54  * @xerces.internal
  55  *
  56  */
  57 final class StAXSchemaParser {
  58 
  59     /** Chunk size (1024). */
  60     private static final int CHUNK_SIZE = (1 << 10);
  61 
  62     /** Chunk mask (CHUNK_SIZE - 1). */
  63     private static final int CHUNK_MASK = CHUNK_SIZE - 1;
  64 
  65     /** Array for holding character data. **/
  66     private final char [] fCharBuffer = new char[CHUNK_SIZE];
  67 
  68     /** Symbol table **/
  69     private SymbolTable fSymbolTable;
  70 
  71     /** SchemaDOMParser, events will be delegated to SchemaDOMParser to pass */
  72     private SchemaDOMParser fSchemaDOMParser;
  73 
  74     /** XML Locator wrapper for SAX. **/
  75     private final StAXLocationWrapper fLocationWrapper = new StAXLocationWrapper();
  76 
  77     /** The namespace context of this document: stores namespaces in scope */
  78     private final JAXPNamespaceContextWrapper fNamespaceContext = new JAXPNamespaceContextWrapper(fSymbolTable);
  79 
  80     /** Fields for start element, end element and characters. */
  81     private final QName fElementQName = new QName();
  82     private final QName fAttributeQName = new QName();
  83     private final XMLAttributesImpl fAttributes = new XMLAttributesImpl();
  84     private final XMLString fTempString = new XMLString();
  85     private final List<String> fDeclaredPrefixes = new ArrayList<>();
  86     private final XMLStringBuffer fStringBuffer = new XMLStringBuffer();
  87     private int fDepth;
  88 
  89     public StAXSchemaParser() {
  90         fNamespaceContext.setDeclaredPrefixes(fDeclaredPrefixes);
  91     }
  92 
  93     public void reset(SchemaDOMParser schemaDOMParser, SymbolTable symbolTable) {
  94         fSchemaDOMParser = schemaDOMParser;
  95         fSymbolTable = symbolTable;
  96         fNamespaceContext.setSymbolTable(fSymbolTable);
  97         fNamespaceContext.reset();
  98     }
  99 
 100     public Document getDocument() {
 101         return fSchemaDOMParser.getDocument();
 102     }
 103 
 104     public void parse(XMLEventReader input) throws XMLStreamException, XNIException {
 105         XMLEvent currentEvent = input.peek();
 106         if (currentEvent != null) {
 107             int eventType = currentEvent.getEventType();
 108             if (eventType != XMLStreamConstants.START_DOCUMENT &&
 109                 eventType != XMLStreamConstants.START_ELEMENT) {
 110                 throw new XMLStreamException();
 111             }
 112             fLocationWrapper.setLocation(currentEvent.getLocation());
 113             fSchemaDOMParser.startDocument(fLocationWrapper, null, fNamespaceContext, null);
 114             loop: while (input.hasNext()) {
 115                 currentEvent = input.nextEvent();
 116                 eventType = currentEvent.getEventType();
 117                 switch (eventType) {
 118                 case XMLStreamConstants.START_ELEMENT:
 119                     ++fDepth;
 120                     StartElement start = currentEvent.asStartElement();
 121                     fillQName(fElementQName, start.getName());
 122                     fLocationWrapper.setLocation(start.getLocation());
 123                     fNamespaceContext.setNamespaceContext(start.getNamespaceContext());
 124                     fillXMLAttributes(start);
 125                     fillDeclaredPrefixes(start);
 126                     addNamespaceDeclarations();
 127                     fNamespaceContext.pushContext();
 128                     fSchemaDOMParser.startElement(fElementQName, fAttributes, null);
 129                     break;
 130                 case XMLStreamConstants.END_ELEMENT:
 131                     EndElement end = currentEvent.asEndElement();
 132                     fillQName(fElementQName, end.getName());
 133                     fillDeclaredPrefixes(end);
 134                     fLocationWrapper.setLocation(end.getLocation());
 135                     fSchemaDOMParser.endElement(fElementQName, null);
 136                     fNamespaceContext.popContext();
 137                     --fDepth;
 138                     if (fDepth <= 0) {
 139                         break loop;
 140                     }
 141                     break;
 142                 case XMLStreamConstants.CHARACTERS:
 143                     sendCharactersToSchemaParser(currentEvent.asCharacters().getData(), false);
 144                     break;
 145                 case XMLStreamConstants.SPACE:
 146                     sendCharactersToSchemaParser(currentEvent.asCharacters().getData(), true);
 147                     break;
 148                 case XMLStreamConstants.CDATA:
 149                     fSchemaDOMParser.startCDATA(null);
 150                     sendCharactersToSchemaParser(currentEvent.asCharacters().getData(), false);
 151                     fSchemaDOMParser.endCDATA(null);
 152                     break;
 153                 case XMLStreamConstants.PROCESSING_INSTRUCTION:
 154                     ProcessingInstruction pi = (ProcessingInstruction)currentEvent;
 155                     fillProcessingInstruction(pi.getData());
 156                     fSchemaDOMParser.processingInstruction(pi.getTarget(), fTempString, null);
 157                     break;
 158                 case XMLStreamConstants.DTD:
 159                     /* There shouldn't be a DTD in the schema */
 160                     break;
 161                 case XMLStreamConstants.ENTITY_REFERENCE:
 162                     /* Not needed for schemas */
 163                     break;
 164                 case XMLStreamConstants.COMMENT:
 165                     /* No point in sending comments */
 166                     break;
 167                 case XMLStreamConstants.START_DOCUMENT:
 168                     fDepth++;
 169                     /* We automatically call startDocument before the loop */
 170                     break;
 171                 case XMLStreamConstants.END_DOCUMENT:
 172                     /* We automatically call endDocument after the loop */
 173                     break;
 174                 }
 175             }
 176             fLocationWrapper.setLocation(null);
 177             fNamespaceContext.setNamespaceContext(null);
 178             fSchemaDOMParser.endDocument(null);
 179         }
 180     }
 181 
 182     public void parse(XMLStreamReader input) throws XMLStreamException, XNIException {
 183         if (input.hasNext()) {
 184             int eventType = input.getEventType();
 185             if (eventType != XMLStreamConstants.START_DOCUMENT &&
 186                 eventType != XMLStreamConstants.START_ELEMENT) {
 187                 throw new XMLStreamException();
 188             }
 189             fLocationWrapper.setLocation(input.getLocation());
 190             fSchemaDOMParser.startDocument(fLocationWrapper, null, fNamespaceContext, null);
 191             boolean first = true;
 192             loop: while (input.hasNext()) {
 193                 if (!first) {
 194                     eventType = input.next();
 195                 }
 196                 else {
 197                     first = false;
 198                 }
 199                 switch (eventType) {
 200                 case XMLStreamConstants.START_ELEMENT:
 201                     ++fDepth;
 202                     fLocationWrapper.setLocation(input.getLocation());
 203                     fNamespaceContext.setNamespaceContext(input.getNamespaceContext());
 204                     fillQName(fElementQName, input.getNamespaceURI(),
 205                         input.getLocalName(), input.getPrefix());
 206                     fillXMLAttributes(input);
 207                     fillDeclaredPrefixes(input);
 208                     addNamespaceDeclarations();
 209                     fNamespaceContext.pushContext();
 210                     fSchemaDOMParser.startElement(fElementQName, fAttributes, null);
 211                     break;
 212                 case XMLStreamConstants.END_ELEMENT:
 213                     fLocationWrapper.setLocation(input.getLocation());
 214                     fNamespaceContext.setNamespaceContext(input.getNamespaceContext());
 215                     fillQName(fElementQName, input.getNamespaceURI(),
 216                         input.getLocalName(), input.getPrefix());
 217                     fillDeclaredPrefixes(input);
 218                     fSchemaDOMParser.endElement(fElementQName, null);
 219                     fNamespaceContext.popContext();
 220                     --fDepth;
 221                     if (fDepth <= 0) {
 222                         break loop;
 223                     }
 224                     break;
 225                 case XMLStreamConstants.CHARACTERS:
 226                     fTempString.setValues(input.getTextCharacters(),
 227                         input.getTextStart(), input.getTextLength());
 228                     fSchemaDOMParser.characters(fTempString, null);
 229                     break;
 230                 case XMLStreamConstants.SPACE:
 231                     fTempString.setValues(input.getTextCharacters(),
 232                         input.getTextStart(), input.getTextLength());
 233                     fSchemaDOMParser.ignorableWhitespace(fTempString, null);
 234                     break;
 235                 case XMLStreamConstants.CDATA:
 236                     fSchemaDOMParser.startCDATA(null);
 237                     fTempString.setValues(input.getTextCharacters(),
 238                         input.getTextStart(), input.getTextLength());
 239                     fSchemaDOMParser.characters(fTempString, null);
 240                     fSchemaDOMParser.endCDATA(null);
 241                     break;
 242                 case XMLStreamConstants.PROCESSING_INSTRUCTION:
 243                     fillProcessingInstruction(input.getPIData());
 244                     fSchemaDOMParser.processingInstruction(input.getPITarget(), fTempString, null);
 245                     break;
 246                 case XMLStreamConstants.DTD:
 247                     /* There shouldn't be a DTD in the schema */
 248                     break;
 249                 case XMLStreamConstants.ENTITY_REFERENCE:
 250                     /* Not needed for schemas */
 251                     break;
 252                 case XMLStreamConstants.COMMENT:
 253                     /* No point in sending comments */
 254                     break;
 255                 case XMLStreamConstants.START_DOCUMENT:
 256                     ++fDepth;
 257                     /* We automatically call startDocument before the loop */
 258                     break;
 259                 case XMLStreamConstants.END_DOCUMENT:
 260                     /* We automatically call endDocument after the loop */
 261                     break;
 262                 }
 263             }
 264             fLocationWrapper.setLocation(null);
 265             fNamespaceContext.setNamespaceContext(null);
 266             fSchemaDOMParser.endDocument(null);
 267         }
 268     }
 269 
 270     /** Send characters to the validator in CHUNK_SIZE character chunks. */
 271     private void sendCharactersToSchemaParser(String str, boolean whitespace) {
 272         if (str != null) {
 273             final int length = str.length();
 274             final int remainder = length & CHUNK_MASK;
 275             if (remainder > 0) {
 276                 str.getChars(0, remainder, fCharBuffer, 0);
 277                 fTempString.setValues(fCharBuffer, 0, remainder);
 278                 if (whitespace) {
 279                     fSchemaDOMParser.ignorableWhitespace(fTempString, null);
 280                 }
 281                 else {
 282                     fSchemaDOMParser.characters(fTempString, null);
 283                 }
 284             }
 285             int i = remainder;
 286             while (i < length) {
 287                 str.getChars(i, i += CHUNK_SIZE, fCharBuffer, 0);
 288                 fTempString.setValues(fCharBuffer, 0, CHUNK_SIZE);
 289                 if (whitespace) {
 290                     fSchemaDOMParser.ignorableWhitespace(fTempString, null);
 291                 }
 292                 else {
 293                     fSchemaDOMParser.characters(fTempString, null);
 294                 }
 295             }
 296         }
 297     }
 298 
 299     // processing instructions must be sent all in one chunk
 300     private void fillProcessingInstruction(String data) {
 301         final int dataLength = data.length();
 302         char [] charBuffer = fCharBuffer;
 303         if (charBuffer.length < dataLength) {
 304             // toCharArray() creates a newly allocated array, so it's okay
 305             // to keep a reference to it.
 306             charBuffer = data.toCharArray();
 307         }
 308         else {
 309             data.getChars(0, dataLength, charBuffer, 0);
 310         }
 311         fTempString.setValues(charBuffer, 0, dataLength);
 312     }
 313 
 314     private void fillXMLAttributes(StartElement event) {
 315         fAttributes.removeAllAttributes();
 316         final Iterator<Attribute> attrs = event.getAttributes();
 317         while (attrs.hasNext()) {
 318             Attribute attr = attrs.next();
 319             fillQName(fAttributeQName, attr.getName());
 320             String type = attr.getDTDType();
 321             int idx = fAttributes.getLength();
 322             fAttributes.addAttributeNS(fAttributeQName,
 323                     (type != null) ? type : XMLSymbols.fCDATASymbol, attr.getValue());
 324             fAttributes.setSpecified(idx, attr.isSpecified());
 325         }
 326     }
 327 
 328     private void fillXMLAttributes(XMLStreamReader input) {
 329         fAttributes.removeAllAttributes();
 330         final int len = input.getAttributeCount();
 331         for (int i = 0; i < len; ++i) {
 332             fillQName(fAttributeQName, input.getAttributeNamespace(i),
 333                 input.getAttributeLocalName(i), input.getAttributePrefix(i));
 334             String type = input.getAttributeType(i);
 335             fAttributes.addAttributeNS(fAttributeQName,
 336                     (type != null) ? type : XMLSymbols.fCDATASymbol, input.getAttributeValue(i));
 337             fAttributes.setSpecified(i, input.isAttributeSpecified(i));
 338         }
 339     }
 340 
 341     private void addNamespaceDeclarations() {
 342         String prefix = null;
 343         String localpart = null;
 344         String rawname = null;
 345         String nsPrefix = null;
 346         String nsURI = null;
 347 
 348         final Iterator<String> iter = fDeclaredPrefixes.iterator();
 349         while (iter.hasNext()) {
 350             nsPrefix = iter.next();
 351             nsURI = fNamespaceContext.getURI(nsPrefix);
 352             if (nsPrefix.length() > 0) {
 353                 prefix = XMLSymbols.PREFIX_XMLNS;
 354                 localpart = nsPrefix;
 355                 fStringBuffer.clear();
 356                 fStringBuffer.append(prefix);
 357                 fStringBuffer.append(':');
 358                 fStringBuffer.append(localpart);
 359                 rawname = fSymbolTable.addSymbol(fStringBuffer.ch, fStringBuffer.offset, fStringBuffer.length);
 360             }
 361             else {
 362                 prefix = XMLSymbols.EMPTY_STRING;
 363                 localpart = XMLSymbols.PREFIX_XMLNS;
 364                 rawname = XMLSymbols.PREFIX_XMLNS;
 365             }
 366             fAttributeQName.setValues(prefix, localpart, rawname, NamespaceContext.XMLNS_URI);
 367             fAttributes.addAttribute(fAttributeQName, XMLSymbols.fCDATASymbol,
 368                     (nsURI != null) ? nsURI : XMLSymbols.EMPTY_STRING);
 369         }
 370     }
 371 
 372     /** Fills in the list of declared prefixes. */
 373     private void fillDeclaredPrefixes(StartElement event) {
 374         fillDeclaredPrefixes(event.getNamespaces());
 375     }
 376 
 377     /** Fills in the list of declared prefixes. */
 378     private void fillDeclaredPrefixes(EndElement event) {
 379         fillDeclaredPrefixes(event.getNamespaces());
 380     }
 381 
 382     /** Fills in the list of declared prefixes. */
 383     private void fillDeclaredPrefixes(Iterator<Namespace> namespaces) {
 384         fDeclaredPrefixes.clear();
 385         while (namespaces.hasNext()) {
 386             Namespace ns = namespaces.next();
 387             String prefix = ns.getPrefix();
 388             fDeclaredPrefixes.add(prefix != null ? prefix : "");
 389         }
 390     }
 391 
 392     /** Fills in the list of declared prefixes. */
 393     private void fillDeclaredPrefixes(XMLStreamReader reader) {
 394         fDeclaredPrefixes.clear();
 395         final int len = reader.getNamespaceCount();
 396         for (int i = 0; i < len; ++i) {
 397             String prefix = reader.getNamespacePrefix(i);
 398             fDeclaredPrefixes.add(prefix != null ? prefix : "");
 399         }
 400     }
 401 
 402     /** Fills in a QName object. */
 403     private void fillQName(QName toFill, javax.xml.namespace.QName toCopy) {
 404         fillQName(toFill, toCopy.getNamespaceURI(), toCopy.getLocalPart(), toCopy.getPrefix());
 405     }
 406 
 407     /** Fills in a QName object. */
 408     final void fillQName(QName toFill, String uri, String localpart, String prefix) {
 409         uri = (uri != null && uri.length() > 0) ? fSymbolTable.addSymbol(uri) : null;
 410         localpart = (localpart != null) ? fSymbolTable.addSymbol(localpart) : XMLSymbols.EMPTY_STRING;
 411         prefix = (prefix != null && prefix.length() > 0) ? fSymbolTable.addSymbol(prefix) : XMLSymbols.EMPTY_STRING;
 412         String raw = localpart;
 413         if (prefix != XMLSymbols.EMPTY_STRING) {
 414             fStringBuffer.clear();
 415             fStringBuffer.append(prefix);
 416             fStringBuffer.append(':');
 417             fStringBuffer.append(localpart);
 418             raw = fSymbolTable.addSymbol(fStringBuffer.ch, fStringBuffer.offset, fStringBuffer.length);
 419         }
 420         toFill.setValues(prefix, localpart, raw, uri);
 421     }
 422 
 423 } // StAXSchemaParser