diff options
Diffstat (limited to 'sca-java-2.x/branches/2.0/modules/common-xml/src/main/java/org/apache/tuscany/sca/common/xml/stax/impl')
2 files changed, 624 insertions, 0 deletions
diff --git a/sca-java-2.x/branches/2.0/modules/common-xml/src/main/java/org/apache/tuscany/sca/common/xml/stax/impl/StAX2SAXAdapter.java b/sca-java-2.x/branches/2.0/modules/common-xml/src/main/java/org/apache/tuscany/sca/common/xml/stax/impl/StAX2SAXAdapter.java new file mode 100644 index 0000000000..df0fc069b6 --- /dev/null +++ b/sca-java-2.x/branches/2.0/modules/common-xml/src/main/java/org/apache/tuscany/sca/common/xml/stax/impl/StAX2SAXAdapter.java @@ -0,0 +1,256 @@ +/* + * 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.tuscany.sca.common.xml.stax.impl; + +import javax.xml.namespace.QName; +import javax.xml.stream.Location; +import javax.xml.stream.XMLStreamConstants; +import javax.xml.stream.XMLStreamException; +import javax.xml.stream.XMLStreamReader; + +import org.xml.sax.Attributes; +import org.xml.sax.ContentHandler; +import org.xml.sax.Locator; +import org.xml.sax.SAXException; +import org.xml.sax.helpers.AttributesImpl; + +/** + * Adapter that converts from StAX to SAX event streams. Currently the following + * SAX events are not generated: + * <ul> + * <li>ignorableWhitespace</li> + * <li>skippedEntity</li> + * <ul> + * Also the following StAX events are not mapped: + * <ul> + * <li>CDATA</li> + * <li>COMMENT</li> + * <li>DTD</li> + * <li>ENTITY_DECLARATION</li> + * <li>ENTITY_REFERENCE</li> + * <li>NOTATION_DECLARATION</li> + * <li>SPACE</li> + * </ul> + * StAX ATTRIBUTE events are ignored but the equivalent attributes (derived from + * the START_ELEMENT event) are supplied in the SAX startElement event's + * Attributes parameter. If the adaptor is configured to pass namespace prefixes + * then namespace information will also be included in the Attributes; StAX + * NAMESPACE events are ignored. <p/> Another issue is namespace processing. If + * the reader is positioned at a sub-node, we cannot capture all the in-scope + * namespace bindings. Therefore we cannot re-create a proper SAX event stream + * from a StAX parser. <p/> For example <p/> <a:root xmlns:a="foo" + * xmlns:b="bar"><b:sub>a:foo</b:sub></a:root> <p/> And if + * you are handed a parser at <b:sub>, then your SAX events should look + * like: <p/> <b:sub xmlns:a="foo" xmlns:b="bar">a:foo</b:sub> <p/> + * not: <p/> <b:sub>a:foo</b:sub> <p/> <p/> Proposal: we change the + * receiver of SAX events (SDOXMLResourceImpl) so that it uses NamespaceContext + * to resolve prefix (as opposed to record start/endPrefixMappings and use it + * for resolution.) + * + * @version $Rev$ $Date$ + */ +public class StAX2SAXAdapter { + private final boolean namespacePrefixes; + + /** + * Construct a new StAX to SAX adapter that will convert a StAX event stream + * into a SAX event stream. + * + * @param namespacePrefixes whether xmlns attributes should be included in + * startElement events; + */ + public StAX2SAXAdapter(boolean namespacePrefixes) { + this.namespacePrefixes = namespacePrefixes; + } + + /** + * Pull events from the StAX stream and dispatch to the SAX ContentHandler. + * The StAX stream would typically be located on a START_DOCUMENT or + * START_ELEMENT event and when this method returns it will be located on + * the associated END_DOCUMENT or END_ELEMENT event. Behaviour with other + * start events is undefined. + * + * @param reader StAX event source to read + * @param handler SAX ContentHandler for processing events + * @throws XMLStreamException if there was a problem reading the stream + * @throws SAXException passed through from the ContentHandler + */ + public void parse(XMLStreamReader reader, ContentHandler handler) throws XMLStreamException, SAXException { + handler.setDocumentLocator(new LocatorAdaptor(reader.getLocation())); + + // remembers the nest level of elements to know when we are done + int level = 0; + int event = reader.getEventType(); + while (true) { + switch (event) { + case XMLStreamConstants.START_DOCUMENT: + level++; + handler.startDocument(); + break; + case XMLStreamConstants.START_ELEMENT: + level++; + handleStartElement(reader, handler); + break; + case XMLStreamConstants.PROCESSING_INSTRUCTION: + handler.processingInstruction(reader.getPITarget(), reader.getPIData()); + break; + case XMLStreamConstants.CHARACTERS: + handler.characters(reader.getTextCharacters(), reader.getTextStart(), reader + .getTextLength()); + break; + case XMLStreamConstants.END_ELEMENT: + handleEndElement(reader, handler); + level--; + if (level == 0) { + return; + } + break; + case XMLStreamConstants.END_DOCUMENT: + handler.endDocument(); + return; + /* + * uncomment to handle all events rather than just mapped + * ones // StAX events that are not mapped to SAX case + * XMLStreamConstants.COMMENT: case + * XMLStreamConstants.SPACE: case + * XMLStreamConstants.ENTITY_REFERENCE: case + * XMLStreamConstants.DTD: case XMLStreamConstants.CDATA: + * case XMLStreamConstants.NOTATION_DECLARATION: case + * XMLStreamConstants.ENTITY_DECLARATION: break; // StAX + * events handled in START_ELEMENT case + * XMLStreamConstants.ATTRIBUTE: case + * XMLStreamConstants.NAMESPACE: break; default: throw new + * AssertionError("Unknown StAX event: " + event); + */ + } + event = reader.next(); + } + } + + private void handleStartElement(XMLStreamReader reader, ContentHandler handler) throws SAXException { + // send startPrefixMapping events immediately before startElement event + int nsCount = reader.getNamespaceCount(); + for (int i = 0; i < nsCount; i++) { + String prefix = reader.getNamespacePrefix(i); + if (prefix == null) { // true for default namespace + prefix = ""; + } + handler.startPrefixMapping(prefix, reader.getNamespaceURI(i)); + } + + // fire startElement + QName qname = reader.getName(); + String prefix = qname.getPrefix(); + String rawname; + if (prefix == null || prefix.length() == 0) { + rawname = qname.getLocalPart(); + } else { + rawname = prefix + ':' + qname.getLocalPart(); + } + Attributes attrs = getAttributes(reader); + handler.startElement(qname.getNamespaceURI(), qname.getLocalPart(), rawname, attrs); + } + + private static void handleEndElement(XMLStreamReader reader, ContentHandler handler) throws SAXException { + // fire endElement + QName qname = reader.getName(); + handler.endElement(qname.getNamespaceURI(), qname.getLocalPart(), qname.toString()); + + // send endPrefixMapping events immediately after endElement event + // we send them in the opposite order to that returned but this is not + // actually required by SAX + int nsCount = reader.getNamespaceCount(); + for (int i = nsCount - 1; i >= 0; i--) { + String prefix = reader.getNamespacePrefix(i); + if (prefix == null) { // true for default namespace + prefix = ""; + } + handler.endPrefixMapping(prefix); + } + } + + /** + * Get the attributes associated with the current START_ELEMENT event. + * + * @return the StAX attributes converted to org.xml.sax.Attributes + */ + private Attributes getAttributes(XMLStreamReader reader) { + assert reader.getEventType() == XMLStreamConstants.START_ELEMENT; + + AttributesImpl attrs = new AttributesImpl(); + + // add namespace declarations if required + if (namespacePrefixes) { + for (int i = 0; i < reader.getNamespaceCount(); i++) { + String prefix = reader.getNamespacePrefix(i); + String uri = reader.getNamespaceURI(i); + attrs.addAttribute(null, prefix, "xmlns:" + prefix, "CDATA", uri); + } + } + + // Regular attributes + for (int i = 0; i < reader.getAttributeCount(); i++) { + String uri = reader.getAttributeNamespace(i); + if (uri == null) { + uri = ""; + } + String localName = reader.getAttributeLocalName(i); + String prefix = reader.getAttributePrefix(i); + String qname; + if (prefix == null || prefix.length() == 0) { + qname = localName; + } else { + qname = prefix + ':' + localName; + } + String type = reader.getAttributeType(i); + String value = reader.getAttributeValue(i); + + attrs.addAttribute(uri, localName, qname, type, value); + } + + return attrs; + } + + /** + * Adaptor for mapping Locator information. + */ + private static final class LocatorAdaptor implements Locator { + private final Location location; + + private LocatorAdaptor(Location location) { + this.location = location; + } + + public int getColumnNumber() { + return location == null ? 0 : location.getColumnNumber(); + } + + public int getLineNumber() { + return location == null ? 0 : location.getLineNumber(); + } + + public String getPublicId() { + return location == null ? "" : location.getPublicId(); + } + + public String getSystemId() { + return location == null ? "" : location.getSystemId(); + } + } +} diff --git a/sca-java-2.x/branches/2.0/modules/common-xml/src/main/java/org/apache/tuscany/sca/common/xml/stax/impl/XMLStreamSerializer.java b/sca-java-2.x/branches/2.0/modules/common-xml/src/main/java/org/apache/tuscany/sca/common/xml/stax/impl/XMLStreamSerializer.java new file mode 100644 index 0000000000..883199e4dd --- /dev/null +++ b/sca-java-2.x/branches/2.0/modules/common-xml/src/main/java/org/apache/tuscany/sca/common/xml/stax/impl/XMLStreamSerializer.java @@ -0,0 +1,368 @@ +/* + * 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.tuscany.sca.common.xml.stax.impl; + +import static javax.xml.XMLConstants.DEFAULT_NS_PREFIX; +import static javax.xml.XMLConstants.NULL_NS_URI; + +import javax.xml.XMLConstants; +import javax.xml.namespace.NamespaceContext; +import javax.xml.namespace.QName; +import javax.xml.stream.XMLStreamConstants; +import javax.xml.stream.XMLStreamException; +import javax.xml.stream.XMLStreamReader; +import javax.xml.stream.XMLStreamWriter; + +/** + * The XMLStreamSerializer pulls events from the XMLStreamReader and dumps into the XMLStreamWriter + * + * @version $Rev$ $Date$ + */ +public class XMLStreamSerializer implements XMLStreamConstants { + public static final String NAMESPACE_PREFIX = "ns"; + private static int namespaceSuffix; + + /* + * The behavior of the Serializer is such that it returns when it encounters the starting element for the second + * time. The depth variable tracks the depth of the Serializer and tells it when to return. Note that it is assumed + * that this Serialization starts on an Element. + */ + + /** + * Field depth + */ + private int depth; + + /** + * A flag to tell if the writer has javax.xml.stream.isRepairingNamespaces set to true + */ + private boolean isRepairingNamespaces = true; + + /** + * @param isRepairingNamespaces + */ + public XMLStreamSerializer(boolean isRepairingNamespaces) { + super(); + this.isRepairingNamespaces = isRepairingNamespaces; + } + + public XMLStreamSerializer() { + this(true); + } + + /** + * Generates a unique namespace prefix that is not in the scope of the NamespaceContext + * + * @param nsCtxt + * @return string + */ + private String generateUniquePrefix(NamespaceContext nsCtxt) { + String prefix = NAMESPACE_PREFIX + namespaceSuffix++; + // null should be returned if the prefix is not bound! + while (nsCtxt.getNamespaceURI(prefix) != null) { + prefix = NAMESPACE_PREFIX + namespaceSuffix++; + } + + return prefix; + } + + /** + * Method serialize. + * + * @param node + * @param writer + * @throws XMLStreamException + */ + public void serialize(XMLStreamReader node, XMLStreamWriter writer) throws XMLStreamException { + serializeNode(node, writer); + } + + /** + * @param reader + * @param writer + * @throws XMLStreamException + */ + protected void serializeAttributes(XMLStreamReader reader, XMLStreamWriter writer) throws XMLStreamException { + int count = reader.getAttributeCount(); + String prefix; + String namespaceName; + String localName; + String value; + for (int i = 0; i < count; i++) { + prefix = reader.getAttributePrefix(i); + namespaceName = reader.getAttributeNamespace(i); + localName = reader.getAttributeLocalName(i); + value = reader.getAttributeValue(i); + + writeAttribute(writer, prefix, localName, namespaceName, value); + + } + } + + public void writeAttribute(XMLStreamWriter writer, QName name, String value) throws XMLStreamException { + writeAttribute(writer, name.getPrefix(), name.getLocalPart(), name.getNamespaceURI(), value); + } + + public String writeAttribute(XMLStreamWriter writer, + String prefix, + String localName, + String namespaceURI, + String value) throws XMLStreamException { + String writerPrefix; + /* + * Due to parser implementations returning null as the namespace URI (for the empty namespace) we need to + * make sure that we deal with a namespace name that is not null. The best way to work around this issue is + * to set the namespace URI to "" if it is null + */ + if (namespaceURI == null) { + namespaceURI = NULL_NS_URI; + } + + if (prefix == null) { + prefix = DEFAULT_NS_PREFIX; + } + + if (isRepairingNamespaces) { + writer.writeAttribute(prefix, namespaceURI, localName, value); + return writer.getPrefix(namespaceURI); + } + + writerPrefix = writer.getPrefix(namespaceURI); + + if (!NULL_NS_URI.equals(namespaceURI)) { + if (writerPrefix != null && isDefaultNSPrefix(prefix)) { + // prefix has already being declared but this particular attrib has a + // no prefix attached. So use the prefix provided by the writer + + writer.writeAttribute(writerPrefix, namespaceURI, localName, value); + return writerPrefix; + + } else if (!isDefaultNSPrefix(prefix) && !prefix.equals(writerPrefix)) { + // writer prefix is available but different from the current + // prefix of the attrib. We should be declaring the new prefix + // as a namespace declaration + + writer.writeNamespace(prefix, namespaceURI); + writer.writeAttribute(prefix, namespaceURI, localName, value); + return prefix; + + } else if (isDefaultNSPrefix(prefix)) { + // prefix is null (or empty), but the namespace name is valid! it has not + // being written previously also. So we need to generate a prefix here + + prefix = generateUniquePrefix(writer.getNamespaceContext()); + writer.writeNamespace(prefix, namespaceURI); + writer.writeAttribute(prefix, namespaceURI, localName, value); + return prefix; + } else { + writer.writeAttribute(prefix, namespaceURI, localName, value); + return prefix; + } + } else { + // empty namespace is equal to no namespace! + writer.writeAttribute(localName, value); + return prefix; + } + } + + private boolean isDefaultNSPrefix(String prefix) { + return (prefix == null || prefix.equals(DEFAULT_NS_PREFIX)); + } + + /** + * Method serializeCData. + * + * @param reader + * @param writer + * @throws XMLStreamException + */ + protected void serializeCData(XMLStreamReader reader, XMLStreamWriter writer) throws XMLStreamException { + writer.writeCData(reader.getText()); + } + + /** + * Method serializeComment. + * + * @param reader + * @param writer + * @throws XMLStreamException + */ + protected void serializeComment(XMLStreamReader reader, XMLStreamWriter writer) throws XMLStreamException { + writer.writeComment(reader.getText()); + } + + /** + * @param reader + * @param writer + * @throws XMLStreamException + */ + protected void serializeElement(XMLStreamReader reader, XMLStreamWriter writer) throws XMLStreamException { + writeStartElement(writer, reader.getName()); + + // add the namespaces + int count = reader.getNamespaceCount(); + String namespacePrefix; + for (int i = 0; i < count; i++) { + namespacePrefix = reader.getNamespacePrefix(i); + serializeNamespace(namespacePrefix, reader.getNamespaceURI(i), writer); + } + + // add attributes + serializeAttributes(reader, writer); + + } + + public void writeStartElement(XMLStreamWriter writer, QName name) throws XMLStreamException { + writeStartElement(writer, name.getPrefix(), name.getLocalPart(), name.getNamespaceURI()); + } + + public void writeStartElement(XMLStreamWriter writer, String prefix, String localName, String namespaceURI) + throws XMLStreamException { + + if (namespaceURI == null) { + namespaceURI = NULL_NS_URI; + } + if (prefix == null) { + prefix = DEFAULT_NS_PREFIX; + } + + if (isRepairingNamespaces) { + writer.writeStartElement(prefix, localName, namespaceURI); + return; + } + + String writerPrefix = writer.getPrefix(namespaceURI); + if (writerPrefix != null) { + // Namespace is bound + writer.writeStartElement(writerPrefix, localName, namespaceURI); + } else { + // Namespace is not bound + if (NULL_NS_URI.equals(namespaceURI)) { + writer.writeStartElement(localName); + String defaultNS = writer.getNamespaceContext().getNamespaceURI(DEFAULT_NS_PREFIX); + if (defaultNS != null && !NULL_NS_URI.equals(defaultNS)) { + writer.writeNamespace(prefix, namespaceURI); + } + } else { + writer.writeStartElement(prefix, localName, namespaceURI); + // writeNamespace() will call setPrefix() + writer.writeNamespace(prefix, namespaceURI); + } + } + } + + /** + * Method serializeEndElement. + * + * @param writer + * @throws XMLStreamException + */ + protected void serializeEndElement(XMLStreamWriter writer) throws XMLStreamException { + writer.writeEndElement(); + } + + /** + * Method serializeNamespace. + * + * @param prefix + * @param uri + * @param writer + * @throws XMLStreamException + */ + private void serializeNamespace(String prefix, String uri, XMLStreamWriter writer) throws XMLStreamException { + writeNamespace(writer, prefix, uri); + } + + public String writeNamespace(XMLStreamWriter writer, String prefix, String uri) throws XMLStreamException { + if (uri == null) { + uri = NULL_NS_URI; + } + String prefix1 = writer.getPrefix(uri); + if (prefix1 == null) { + if (prefix == null) { + prefix = DEFAULT_NS_PREFIX; + } + if (DEFAULT_NS_PREFIX.equals(prefix) && !XMLConstants.NULL_NS_URI.equals(uri)) { + String ns = writer.getNamespaceContext().getNamespaceURI(prefix); + if (ns != null) { + prefix = generateUniquePrefix(writer.getNamespaceContext()); + } + } + writer.writeNamespace(prefix, uri); + return prefix; + } else { + return prefix1; + } + } + + /** + * Method serializeNode. + * + * @param reader + * @param writer + * @throws XMLStreamException + */ + protected void serializeNode(XMLStreamReader reader, XMLStreamWriter writer) throws XMLStreamException { + while (true) { + int event = reader.getEventType(); + if (event == START_ELEMENT) { + serializeElement(reader, writer); + depth++; + } else if (event == ATTRIBUTE) { + serializeAttributes(reader, writer); + } else if (event == CHARACTERS) { + serializeText(reader, writer); + } else if (event == COMMENT) { + serializeComment(reader, writer); + } else if (event == CDATA) { + serializeCData(reader, writer); + } else if (event == END_ELEMENT) { + serializeEndElement(writer); + depth--; + } else if (event == START_DOCUMENT) { + depth++; // if a start document is found then increment + writer.writeStartDocument(); + // the depth + } else if (event == END_DOCUMENT) { + if (depth != 0) { + depth--; // for the end document - reduce the depth + } + writer.writeEndDocument(); + } + if (depth == 0) { + break; + } + if (reader.hasNext()) { + reader.next(); + } else { + break; + } + } + } + + /** + * @param reader + * @param writer + * @throws XMLStreamException + */ + protected void serializeText(XMLStreamReader reader, XMLStreamWriter writer) throws XMLStreamException { + writer.writeCharacters(reader.getText()); + } +} |