/* * 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.contribution.processor; import static javax.xml.stream.XMLStreamConstants.END_ELEMENT; import static javax.xml.stream.XMLStreamConstants.START_ELEMENT; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.StringTokenizer; 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; import org.apache.tuscany.sca.assembly.Extensible; import org.apache.tuscany.sca.assembly.Extension; import org.apache.tuscany.sca.assembly.ExtensionFactory; import org.apache.tuscany.sca.contribution.service.ContributionReadException; import org.apache.tuscany.sca.contribution.service.ContributionWriteException; /** * A base class with utility methods for the other artifact processors in this module. * * @version $Rev$ $Date$ */ public abstract class BaseStAXArtifactProcessor { /** * Returns a QName from a string. * @param reader * @param value * @return */ protected QName getQNameValue(XMLStreamReader reader, String value) { if (value != null) { int index = value.indexOf(':'); String prefix = index == -1 ? "" : value.substring(0, index); String localName = index == -1 ? value : value.substring(index + 1); String ns = reader.getNamespaceContext().getNamespaceURI(prefix); if (ns == null) { ns = ""; } return new QName(ns, localName, prefix); } else { return null; } } /** * Returns the boolean value of an attribute. * @param reader * @param name * @return */ protected boolean getBoolean(XMLStreamReader reader, String name) { String value = reader.getAttributeValue(null, name); if (value == null) { return false; } return Boolean.valueOf(value); } /** * Returns the QName value of an attribute. * @param reader * @param name * @return */ protected QName getQName(XMLStreamReader reader, String name) { String qname = reader.getAttributeValue(null, name); return getQNameValue(reader, qname); } /** * Returns the value of an attribute as a list of QNames. * @param reader * @param name * @return */ protected List getQNames(XMLStreamReader reader, String name) { String value = reader.getAttributeValue(null, name); if (value != null) { List qnames = new ArrayList(); for (StringTokenizer tokens = new StringTokenizer(value); tokens.hasMoreTokens();) { qnames.add(getQName(reader, tokens.nextToken())); } return qnames; } else { return Collections.emptyList(); } } /** * Returns the string value of an attribute. * @param reader * @param name * @return */ protected String getString(XMLStreamReader reader, String name) { return reader.getAttributeValue(null, name); } /** * Test if an attribute is explicitly set * @param reader * @param name * @return */ protected boolean isSet(XMLStreamReader reader, String name) { return reader.getAttributeValue(null, name) != null; } /** * Returns the value of xsi:type attribute * @param reader The XML stream reader * @return The QName of the type, if the attribute is not present, null is * returned. */ protected QName getXSIType(XMLStreamReader reader) { String qname = reader.getAttributeValue(XMLConstants.W3C_XML_SCHEMA_INSTANCE_NS_URI, "type"); return getQNameValue(reader, qname); } /** * Parse the next child element. * @param reader * @return * @throws XMLStreamException */ protected boolean nextChildElement(XMLStreamReader reader) throws XMLStreamException { while (reader.hasNext()) { int event = reader.next(); if (event == END_ELEMENT) { return false; } if (event == START_ELEMENT) { return true; } } return false; } /** * Advance the stream to the next END_ELEMENT event skipping any nested * content. * @param reader the reader to advance * @throws XMLStreamException if there was a problem reading the stream */ protected void skipToEndElement(XMLStreamReader reader) throws XMLStreamException { int depth = 0; while (reader.hasNext()) { int event = reader.next(); if (event == XMLStreamConstants.START_ELEMENT) { depth++; } else if (event == XMLStreamConstants.END_ELEMENT) { if (depth == 0) { return; } depth--; } } } /** * * @param writer * @param uri * @throws XMLStreamException */ private String writeElementPrefix(XMLStreamWriter writer, String uri) throws XMLStreamException { if (uri == null) { return null; } String prefix = writer.getPrefix(uri); if (prefix != null) { return null; } else { // Find an available prefix and bind it to the given URI NamespaceContext nsc = writer.getNamespaceContext(); for (int i=1; ; i++) { prefix = "ns" + i; if (nsc.getNamespaceURI(prefix) == null) { break; } } // writer.setPrefix(prefix, uri); return prefix; } } /** * Start an element. * @param uri * @param name * @param attrs * @throws XMLStreamException */ protected void writeStart(XMLStreamWriter writer, String uri, String name, XAttr... attrs) throws XMLStreamException { String prefix = writeElementPrefix(writer, uri); writer.writeStartElement(uri, name); if (prefix != null){ writer.writeNamespace(prefix,uri); } writeAttributePrefixes(writer, attrs); writeAttributes(writer, attrs); } /** * Start an element. * @param qname * @param attrs * @throws XMLStreamException */ protected void writeStart(XMLStreamWriter writer, QName qname, XAttr... attrs) throws XMLStreamException { writeStart(writer, qname.getNamespaceURI(), qname.getLocalPart(), attrs); } /** * End an element. * @param writer * @throws XMLStreamException */ protected void writeEnd(XMLStreamWriter writer) throws XMLStreamException { writer.writeEndElement(); } /** * Start a document. * @param writer * @throws XMLStreamException */ protected void writeStartDocument(XMLStreamWriter writer, String uri, String name, XAttr... attrs) throws XMLStreamException { writer.writeStartDocument(); writer.setDefaultNamespace(uri); writeStart(writer, uri, name, attrs); writer.writeDefaultNamespace(uri); } /** * Start a document. * @param writer * @param qname * @param attrs * @throws XMLStreamException */ protected void writeStartDocument(XMLStreamWriter writer, QName qname, XAttr... attrs) throws XMLStreamException { writeStartDocument(writer, qname.getNamespaceURI(), qname.getLocalPart(), attrs); } /** * End a document. * @param writer * @throws XMLStreamException */ protected void writeEndDocument(XMLStreamWriter writer) throws XMLStreamException { writer.writeEndDocument(); } /** * Write attributes to the current element. * @param writer * @param attrs * @throws XMLStreamException */ protected void writeAttributes(XMLStreamWriter writer, XAttr... attrs) throws XMLStreamException { for (XAttr attr : attrs) { if (attr != null) attr.write(writer); } } /** * Write attribute prefixes to the current element. * @param writer * @param attrs * @throws XMLStreamException */ protected void writeAttributePrefixes(XMLStreamWriter writer, XAttr... attrs) throws XMLStreamException { for (XAttr attr : attrs) { if (attr != null) attr.writePrefix(writer); } } /** * * @param reader * @param elementName * @param extensible * @param extensionAttributeProcessor * @param extensionAttributeProcessor * @param extensionFactory * @throws ContributionReadException * @throws XMLStreamException */ protected void readExtendedAttributes(XMLStreamReader reader, Extensible extensible, StAXAttributeProcessor extensionAttributeProcessor, ExtensionFactory extensionFactory) throws ContributionReadException, XMLStreamException { QName elementName = reader.getName(); for (int a = 0; a < reader.getAttributeCount(); a++) { QName attributeName = reader.getAttributeName(a); if (attributeName.getNamespaceURI() != null && attributeName.getNamespaceURI().length() > 0) { if (!elementName.getNamespaceURI().equals(attributeName.getNamespaceURI())) { Object attributeValue = extensionAttributeProcessor.read(attributeName, reader); Extension attributeExtension; if (attributeValue instanceof Extension) { attributeExtension = (Extension)attributeValue; } else { attributeExtension = extensionFactory.createExtension(attributeName, attributeValue, true); } extensible.getAttributeExtensions().add(attributeExtension); } } } } /** * * @param attributeModel * @param writer * @param extensibleElement * @param extensionAttributeProcessor * @throws ContributionWriteException * @throws XMLStreamException */ protected void writeExtendedAttributes(XMLStreamWriter writer, Extensible extensibleElement, StAXAttributeProcessor extensionAttributeProcessor) throws ContributionWriteException, XMLStreamException { for (Extension extension : extensibleElement.getAttributeExtensions()) { if (extension.isAttribute()) { extensionAttributeProcessor.write(extension, writer); } } } protected void readExtendedElement(XMLStreamReader reader, Extensible extensible, StAXArtifactProcessor extensionProcessor) throws ContributionReadException, XMLStreamException { Object ext = extensionProcessor.read(reader); if (extensible != null) { extensible.getExtensions().add(ext); } } protected void writeExtendedElements(XMLStreamWriter writer, Extensible extensible, StAXArtifactProcessor extensionProcessor) throws ContributionWriteException, XMLStreamException { for (Object ext : extensible.getExtensions()) { extensionProcessor.write(ext, writer); } } /** * Represents an XML attribute that needs to be written to a document. */ public static class XAttr { private static final String SCA10_NS = "http://www.osoa.org/xmlns/sca/1.0"; private String uri = SCA10_NS; private String name; private Object value; public XAttr(String uri, String name, String value) { this.uri = uri; this.name = name; this.value = value; } public XAttr(String name, String value) { this(null, name, value); } public XAttr(String uri, String name, List values) { this.uri = uri; this.name = name; this.value = values; } public XAttr(String name, List values) { this(null, name, values); } public XAttr(String uri, String name, Boolean value) { this.uri = uri; this.name = name; this.value = value; } public XAttr(String name, Boolean value) { this(null, name, value); } public XAttr(String uri, String name, Integer value) { this.uri = uri; this.name = name; this.value = value; } public XAttr(String name, Integer value) { this(null, name, value); } public XAttr(String uri, String name, Double value) { this.uri = uri; this.name = name; this.value = value; } public XAttr(String name, Double value) { this(null, name, value); } public XAttr(String uri, String name, QName value) { this.uri = uri; this.name = name; this.value = value; } public XAttr(String name, QName value) { this(null, name, value); } /** * Writes a string from a QName and registers a prefix for its namespace. * @param reader * @param value * @return */ private String writeQNameValue(XMLStreamWriter writer, QName qname) throws XMLStreamException { if (qname != null) { String prefix = qname.getPrefix(); String uri = qname.getNamespaceURI(); prefix = writer.getPrefix(uri); if (prefix != null) { // Use the prefix already bound to the given URI if (prefix.length() > 0) { return prefix + ":" + qname.getLocalPart(); } else { // Empty prefix, just return the local part of the given qname return qname.getLocalPart(); } } else { // Find an available prefix and bind it to the given URI NamespaceContext nsc = writer.getNamespaceContext(); for (int i=1; ; i++) { prefix = "ns" + i; if (nsc.getNamespaceURI(prefix) == null) { break; } } // writer.setPrefix(prefix, uri); writer.writeNamespace(prefix, uri); return prefix + ":" + qname.getLocalPart(); } } else { return null; } } /** * Registers a prefix for the namespace of a QName. * @param reader * @param value * @return */ private void writeQNamePrefix(XMLStreamWriter writer, QName qname) throws XMLStreamException { if (qname != null) { String prefix = qname.getPrefix(); String uri = qname.getNamespaceURI(); prefix = writer.getPrefix(uri); if (prefix != null) { return; } else { // Find an available prefix and bind it to the given URI NamespaceContext nsc = writer.getNamespaceContext(); for (int i=1; ; i++) { prefix = "ns" + i; if (nsc.getNamespaceURI(prefix) == null) { break; } } // writer.setPrefix(prefix, uri); writer.writeNamespace(prefix, uri); } } } /** * Write to document * @param writer * @throws XMLStreamException */ public void write(XMLStreamWriter writer) throws XMLStreamException { String str; if (value instanceof QName) { // Write a QName str = writeQNameValue(writer, (QName)value); } else if (value instanceof List) { // Write a list of values List values = (List)value; if (values.isEmpty()) { return; } StringBuffer buffer = new StringBuffer(); for (Object v: values) { if (v == null) { // Skip null values continue; } if (v instanceof XAttr) { // Write an XAttr value ((XAttr)v).write(writer); continue; } if (buffer.length() != 0) { buffer.append(' '); } if (v instanceof QName) { // Write a QName value buffer.append(writeQNameValue(writer, (QName)v)); } else { // Write value as a string buffer.append(String.valueOf(v)); } } str = buffer.toString(); } else { // Write a string if (value == null) { return; } str = String.valueOf(value); } if (str.length() == 0) { return; } // Write the attribute if (uri != null && !uri.equals(SCA10_NS)) { writer.writeAttribute(uri, name, str); } else { writer.writeAttribute(name,str); } } /** * Registers a prefix for the namespace of a QName or list of QNames * @param writer * @throws XMLStreamException */ public void writePrefix(XMLStreamWriter writer) throws XMLStreamException { if (value instanceof QName) { // Write prefix for a single QName value writeQNamePrefix(writer, (QName)value); } else if (value instanceof List) { // Write prefixes for a list of values for (Object v: (List)value) { if (v instanceof QName) { // Write prefix for a QName value writeQNamePrefix(writer, (QName)v); } else if (v instanceof XAttr) { // Write prefix for an XAttr value ((XAttr)v).writePrefix(writer); } } } } } }