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