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