org.apache.xerces.impl.xs.traversers.StAXSchemaParser Maven / Gradle / Ivy
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.xerces.impl.xs.traversers;
import java.util.ArrayList;
import java.util.Iterator;
import javax.xml.stream.XMLEventReader;
import javax.xml.stream.XMLStreamConstants;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;
import javax.xml.stream.events.Attribute;
import javax.xml.stream.events.EndElement;
import javax.xml.stream.events.Namespace;
import javax.xml.stream.events.ProcessingInstruction;
import javax.xml.stream.events.StartElement;
import javax.xml.stream.events.XMLEvent;
import org.apache.xerces.impl.xs.opti.SchemaDOMParser;
import org.apache.xerces.util.JAXPNamespaceContextWrapper;
import org.apache.xerces.util.StAXLocationWrapper;
import org.apache.xerces.util.SymbolTable;
import org.apache.xerces.util.XMLAttributesImpl;
import org.apache.xerces.util.XMLStringBuffer;
import org.apache.xerces.util.XMLSymbols;
import org.apache.xerces.xni.NamespaceContext;
import org.apache.xerces.xni.QName;
import org.apache.xerces.xni.XMLString;
import org.apache.xerces.xni.XNIException;
import org.w3c.dom.Document;
/**
* StAXSchemaParser reads StAX events, converts them into XNI events
* and passes them directly to the SchemaDOMParser.
*
* @xerces.internal
*
* @version $Id: StAXSchemaParser.java 719878 2008-11-22 17:34:30Z mrglavas $
*/
final class StAXSchemaParser {
/** Chunk size (1024). */
private static final int CHUNK_SIZE = (1 << 10);
/** Chunk mask (CHUNK_SIZE - 1). */
private static final int CHUNK_MASK = CHUNK_SIZE - 1;
/** Array for holding character data. **/
private final char [] fCharBuffer = new char[CHUNK_SIZE];
/** Symbol table **/
private SymbolTable fSymbolTable;
/** SchemaDOMParser, events will be delegated to SchemaDOMParser to pass */
private SchemaDOMParser fSchemaDOMParser;
/** XML Locator wrapper for SAX. **/
private final StAXLocationWrapper fLocationWrapper = new StAXLocationWrapper();
/** The namespace context of this document: stores namespaces in scope */
private final JAXPNamespaceContextWrapper fNamespaceContext = new JAXPNamespaceContextWrapper(fSymbolTable);
/** Fields for start element, end element and characters. */
private final QName fElementQName = new QName();
private final QName fAttributeQName = new QName();
private final XMLAttributesImpl fAttributes = new XMLAttributesImpl();
private final XMLString fTempString = new XMLString();
private final ArrayList fDeclaredPrefixes = new ArrayList();
private final XMLStringBuffer fStringBuffer = new XMLStringBuffer();
private int fDepth;
public StAXSchemaParser() {
fNamespaceContext.setDeclaredPrefixes(fDeclaredPrefixes);
}
public void reset(SchemaDOMParser schemaDOMParser, SymbolTable symbolTable) {
fSchemaDOMParser = schemaDOMParser;
fSymbolTable = symbolTable;
fNamespaceContext.setSymbolTable(fSymbolTable);
fNamespaceContext.reset();
}
public Document getDocument() {
return fSchemaDOMParser.getDocument();
}
public void parse(XMLEventReader input) throws XMLStreamException, XNIException {
XMLEvent currentEvent = input.peek();
if (currentEvent != null) {
int eventType = currentEvent.getEventType();
if (eventType != XMLStreamConstants.START_DOCUMENT &&
eventType != XMLStreamConstants.START_ELEMENT) {
throw new XMLStreamException();
}
fLocationWrapper.setLocation(currentEvent.getLocation());
fSchemaDOMParser.startDocument(fLocationWrapper, null, fNamespaceContext, null);
loop: while (input.hasNext()) {
currentEvent = input.nextEvent();
eventType = currentEvent.getEventType();
switch (eventType) {
case XMLStreamConstants.START_ELEMENT:
++fDepth;
StartElement start = currentEvent.asStartElement();
fillQName(fElementQName, start.getName());
fLocationWrapper.setLocation(start.getLocation());
fNamespaceContext.setNamespaceContext(start.getNamespaceContext());
fillXMLAttributes(start);
fillDeclaredPrefixes(start);
addNamespaceDeclarations();
fNamespaceContext.pushContext();
fSchemaDOMParser.startElement(fElementQName, fAttributes, null);
break;
case XMLStreamConstants.END_ELEMENT:
EndElement end = currentEvent.asEndElement();
fillQName(fElementQName, end.getName());
fillDeclaredPrefixes(end);
fLocationWrapper.setLocation(end.getLocation());
fSchemaDOMParser.endElement(fElementQName, null);
fNamespaceContext.popContext();
--fDepth;
if (fDepth <= 0) {
break loop;
}
break;
case XMLStreamConstants.CHARACTERS:
sendCharactersToSchemaParser(currentEvent.asCharacters().getData(), false);
break;
case XMLStreamConstants.SPACE:
sendCharactersToSchemaParser(currentEvent.asCharacters().getData(), true);
break;
case XMLStreamConstants.CDATA:
fSchemaDOMParser.startCDATA(null);
sendCharactersToSchemaParser(currentEvent.asCharacters().getData(), false);
fSchemaDOMParser.endCDATA(null);
break;
case XMLStreamConstants.PROCESSING_INSTRUCTION:
ProcessingInstruction pi = (ProcessingInstruction)currentEvent;
fillProcessingInstruction(pi.getData());
fSchemaDOMParser.processingInstruction(pi.getTarget(), fTempString, null);
break;
case XMLStreamConstants.DTD:
/* There shouldn't be a DTD in the schema */
break;
case XMLStreamConstants.ENTITY_REFERENCE:
/* Not needed for schemas */
break;
case XMLStreamConstants.COMMENT:
/* No point in sending comments */
break;
case XMLStreamConstants.START_DOCUMENT:
fDepth++;
/* We automatically call startDocument before the loop */
break;
case XMLStreamConstants.END_DOCUMENT:
/* We automatically call endDocument after the loop */
break;
}
}
fLocationWrapper.setLocation(null);
fNamespaceContext.setNamespaceContext(null);
fSchemaDOMParser.endDocument(null);
}
}
public void parse(XMLStreamReader input) throws XMLStreamException, XNIException {
if (input.hasNext()) {
int eventType = input.getEventType();
if (eventType != XMLStreamConstants.START_DOCUMENT &&
eventType != XMLStreamConstants.START_ELEMENT) {
throw new XMLStreamException();
}
fLocationWrapper.setLocation(input.getLocation());
fSchemaDOMParser.startDocument(fLocationWrapper, null, fNamespaceContext, null);
boolean first = true;
loop: while (input.hasNext()) {
if (!first) {
eventType = input.next();
}
else {
first = false;
}
switch (eventType) {
case XMLStreamConstants.START_ELEMENT:
++fDepth;
fLocationWrapper.setLocation(input.getLocation());
fNamespaceContext.setNamespaceContext(input.getNamespaceContext());
fillQName(fElementQName, input.getNamespaceURI(),
input.getLocalName(), input.getPrefix());
fillXMLAttributes(input);
fillDeclaredPrefixes(input);
addNamespaceDeclarations();
fNamespaceContext.pushContext();
fSchemaDOMParser.startElement(fElementQName, fAttributes, null);
break;
case XMLStreamConstants.END_ELEMENT:
fLocationWrapper.setLocation(input.getLocation());
fNamespaceContext.setNamespaceContext(input.getNamespaceContext());
fillQName(fElementQName, input.getNamespaceURI(),
input.getLocalName(), input.getPrefix());
fillDeclaredPrefixes(input);
fSchemaDOMParser.endElement(fElementQName, null);
fNamespaceContext.popContext();
--fDepth;
if (fDepth <= 0) {
break loop;
}
break;
case XMLStreamConstants.CHARACTERS:
fTempString.setValues(input.getTextCharacters(),
input.getTextStart(), input.getTextLength());
fSchemaDOMParser.characters(fTempString, null);
break;
case XMLStreamConstants.SPACE:
fTempString.setValues(input.getTextCharacters(),
input.getTextStart(), input.getTextLength());
fSchemaDOMParser.ignorableWhitespace(fTempString, null);
break;
case XMLStreamConstants.CDATA:
fSchemaDOMParser.startCDATA(null);
fTempString.setValues(input.getTextCharacters(),
input.getTextStart(), input.getTextLength());
fSchemaDOMParser.characters(fTempString, null);
fSchemaDOMParser.endCDATA(null);
break;
case XMLStreamConstants.PROCESSING_INSTRUCTION:
fillProcessingInstruction(input.getPIData());
fSchemaDOMParser.processingInstruction(input.getPITarget(), fTempString, null);
break;
case XMLStreamConstants.DTD:
/* There shouldn't be a DTD in the schema */
break;
case XMLStreamConstants.ENTITY_REFERENCE:
/* Not needed for schemas */
break;
case XMLStreamConstants.COMMENT:
/* No point in sending comments */
break;
case XMLStreamConstants.START_DOCUMENT:
++fDepth;
/* We automatically call startDocument before the loop */
break;
case XMLStreamConstants.END_DOCUMENT:
/* We automatically call endDocument after the loop */
break;
}
}
fLocationWrapper.setLocation(null);
fNamespaceContext.setNamespaceContext(null);
fSchemaDOMParser.endDocument(null);
}
}
/** Send characters to the validator in CHUNK_SIZE character chunks. */
private void sendCharactersToSchemaParser(String str, boolean whitespace) {
if (str != null) {
final int length = str.length();
final int remainder = length & CHUNK_MASK;
if (remainder > 0) {
str.getChars(0, remainder, fCharBuffer, 0);
fTempString.setValues(fCharBuffer, 0, remainder);
if (whitespace) {
fSchemaDOMParser.ignorableWhitespace(fTempString, null);
}
else {
fSchemaDOMParser.characters(fTempString, null);
}
}
int i = remainder;
while (i < length) {
str.getChars(i, i += CHUNK_SIZE, fCharBuffer, 0);
fTempString.setValues(fCharBuffer, 0, CHUNK_SIZE);
if (whitespace) {
fSchemaDOMParser.ignorableWhitespace(fTempString, null);
}
else {
fSchemaDOMParser.characters(fTempString, null);
}
}
}
}
// processing instructions must be sent all in one chunk
private void fillProcessingInstruction(String data) {
final int dataLength = data.length();
char [] charBuffer = fCharBuffer;
if (charBuffer.length < dataLength) {
// toCharArray() creates a newly allocated array, so it's okay
// to keep a reference to it.
charBuffer = data.toCharArray();
}
else {
data.getChars(0, dataLength, charBuffer, 0);
}
fTempString.setValues(charBuffer, 0, dataLength);
}
private void fillXMLAttributes(StartElement event) {
fAttributes.removeAllAttributes();
final Iterator attrs = event.getAttributes();
while (attrs.hasNext()) {
Attribute attr = (Attribute) attrs.next();
fillQName(fAttributeQName, attr.getName());
String type = attr.getDTDType();
int idx = fAttributes.getLength();
fAttributes.addAttributeNS(fAttributeQName,
(type != null) ? type : XMLSymbols.fCDATASymbol, attr.getValue());
fAttributes.setSpecified(idx, attr.isSpecified());
}
}
private void fillXMLAttributes(XMLStreamReader input) {
fAttributes.removeAllAttributes();
final int len = input.getAttributeCount();
for (int i = 0; i < len; ++i) {
fillQName(fAttributeQName, input.getAttributeNamespace(i),
input.getAttributeLocalName(i), input.getAttributePrefix(i));
String type = input.getAttributeType(i);
fAttributes.addAttributeNS(fAttributeQName,
(type != null) ? type : XMLSymbols.fCDATASymbol, input.getAttributeValue(i));
fAttributes.setSpecified(i, input.isAttributeSpecified(i));
}
}
private void addNamespaceDeclarations() {
String prefix = null;
String localpart = null;
String rawname = null;
String nsPrefix = null;
String nsURI = null;
final Iterator iter = fDeclaredPrefixes.iterator();
while (iter.hasNext()) {
nsPrefix = (String) iter.next();
nsURI = fNamespaceContext.getURI(nsPrefix);
if (nsPrefix.length() > 0) {
prefix = XMLSymbols.PREFIX_XMLNS;
localpart = nsPrefix;
fStringBuffer.clear();
fStringBuffer.append(prefix);
fStringBuffer.append(':');
fStringBuffer.append(localpart);
rawname = fSymbolTable.addSymbol(fStringBuffer.ch, fStringBuffer.offset, fStringBuffer.length);
}
else {
prefix = XMLSymbols.EMPTY_STRING;
localpart = XMLSymbols.PREFIX_XMLNS;
rawname = XMLSymbols.PREFIX_XMLNS;
}
fAttributeQName.setValues(prefix, localpart, rawname, NamespaceContext.XMLNS_URI);
fAttributes.addAttribute(fAttributeQName, XMLSymbols.fCDATASymbol,
(nsURI != null) ? nsURI : XMLSymbols.EMPTY_STRING);
}
}
/** Fills in the list of declared prefixes. */
private void fillDeclaredPrefixes(StartElement event) {
fillDeclaredPrefixes(event.getNamespaces());
}
/** Fills in the list of declared prefixes. */
private void fillDeclaredPrefixes(EndElement event) {
fillDeclaredPrefixes(event.getNamespaces());
}
/** Fills in the list of declared prefixes. */
private void fillDeclaredPrefixes(Iterator namespaces) {
fDeclaredPrefixes.clear();
while (namespaces.hasNext()) {
Namespace ns = (Namespace) namespaces.next();
String prefix = ns.getPrefix();
fDeclaredPrefixes.add(prefix != null ? prefix : "");
}
}
/** Fills in the list of declared prefixes. */
private void fillDeclaredPrefixes(XMLStreamReader reader) {
fDeclaredPrefixes.clear();
final int len = reader.getNamespaceCount();
for (int i = 0; i < len; ++i) {
String prefix = reader.getNamespacePrefix(i);
fDeclaredPrefixes.add(prefix != null ? prefix : "");
}
}
/** Fills in a QName object. */
private void fillQName(QName toFill, javax.xml.namespace.QName toCopy) {
fillQName(toFill, toCopy.getNamespaceURI(), toCopy.getLocalPart(), toCopy.getPrefix());
}
/** Fills in a QName object. */
final void fillQName(QName toFill, String uri, String localpart, String prefix) {
uri = (uri != null && uri.length() > 0) ? fSymbolTable.addSymbol(uri) : null;
localpart = (localpart != null) ? fSymbolTable.addSymbol(localpart) : XMLSymbols.EMPTY_STRING;
prefix = (prefix != null && prefix.length() > 0) ? fSymbolTable.addSymbol(prefix) : XMLSymbols.EMPTY_STRING;
String raw = localpart;
if (prefix != XMLSymbols.EMPTY_STRING) {
fStringBuffer.clear();
fStringBuffer.append(prefix);
fStringBuffer.append(':');
fStringBuffer.append(localpart);
raw = fSymbolTable.addSymbol(fStringBuffer.ch, fStringBuffer.offset, fStringBuffer.length);
}
toFill.setValues(prefix, localpart, raw, uri);
}
} // StAXSchemaParser