/* * Copyright 2011, 2012 Odysseus Software GmbH * * Licensed 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.synapse.commons.staxon.core.event; import java.io.IOException; import java.io.StringWriter; import java.io.Writer; import java.util.ArrayList; import java.util.Collections; import java.util.Iterator; import java.util.List; import javax.xml.XMLConstants; import javax.xml.namespace.NamespaceContext; 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 javax.xml.stream.events.Attribute; import javax.xml.stream.events.Characters; import javax.xml.stream.events.Comment; import javax.xml.stream.events.EndDocument; import javax.xml.stream.events.EndElement; import javax.xml.stream.events.EntityDeclaration; import javax.xml.stream.events.EntityReference; import javax.xml.stream.events.Namespace; import javax.xml.stream.events.ProcessingInstruction; import javax.xml.stream.events.StartDocument; import javax.xml.stream.events.StartElement; import javax.xml.stream.events.XMLEvent; import javax.xml.stream.util.XMLEventAllocator; import javax.xml.stream.util.XMLEventConsumer; /** * Simple implementation of {@link XMLEventAllocator}. */ public class SimpleXMLEventAllocator implements XMLEventAllocator { static abstract class AbstractXMLEvent implements XMLEvent { static <E> List<E> toList(Iterator<E> iterator) { if (iterator == null || !iterator.hasNext()) { return Collections.emptyList(); } else { List<E> list = new ArrayList<E>(); while (iterator.hasNext()) { list.add(iterator.next()); } return list; } } final int eventType; final Location location; AbstractXMLEvent(int eventType, Location location) { this.eventType = eventType; this.location = location; } @Override public Characters asCharacters() { return (Characters) this; } @Override public StartElement asStartElement() { return (StartElement) this; } @Override public EndElement asEndElement() { return (EndElement) this; } @Override public int getEventType() { return eventType; } @Override public Location getLocation() { return location; } @Override public QName getSchemaType() { return null; } @Override public boolean isAttribute() { return eventType == XMLStreamConstants.ATTRIBUTE; } @Override public boolean isCharacters() { return eventType == XMLStreamConstants.CHARACTERS || eventType == XMLStreamConstants.CDATA; } @Override public boolean isEndDocument() { return eventType == XMLStreamConstants.END_DOCUMENT; } @Override public boolean isEndElement() { return eventType == XMLStreamConstants.END_ELEMENT; } @Override public boolean isEntityReference() { return eventType == XMLStreamConstants.ENTITY_REFERENCE; } @Override public boolean isNamespace() { return eventType == XMLStreamConstants.NAMESPACE; } @Override public boolean isProcessingInstruction() { return eventType == XMLStreamConstants.PROCESSING_INSTRUCTION; } @Override public boolean isStartDocument() { return eventType == XMLStreamConstants.START_DOCUMENT; } @Override public boolean isStartElement() { return eventType == XMLStreamConstants.START_ELEMENT; } @Override public String toString() { try { Writer writer = new StringWriter(); writer.write(getClass().getSimpleName()); writer.write('('); writeAsEncodedUnicodeInternal(writer); writer.write(')'); return writer.toString(); } catch (IOException e) { return super.toString(); } } @Override public void writeAsEncodedUnicode(Writer writer) throws XMLStreamException { try { writeAsEncodedUnicodeInternal(writer); } catch (IOException e) { throw new XMLStreamException(e); } } abstract void writeAsEncodedUnicodeInternal(Writer writer) throws IOException; } static class AttributeEvent extends AbstractXMLEvent implements Attribute { final QName name; final String value; final boolean specified; AttributeEvent(Location location, QName name, String value, boolean specified) { this(XMLStreamConstants.ATTRIBUTE, location, name, value, specified); } AttributeEvent(int eventType, Location location, QName name, String value, boolean specified) { super(eventType, location); assert eventType == XMLStreamConstants.ATTRIBUTE || eventType == XMLStreamConstants.NAMESPACE; this.name = name; this.value = value; this.specified = specified; } @Override public String getDTDType() { return "CDATA"; } @Override public QName getName() { return name; } @Override public String getValue() { return value; } @Override public boolean isSpecified() { return specified; } @Override void writeAsEncodedUnicodeInternal(Writer writer) throws IOException { if (!XMLConstants.DEFAULT_NS_PREFIX.equals(name.getPrefix())) { writer.write(name.getPrefix()); writer.write(':'); } writer.write(name.getLocalPart()); writer.write('='); writer.write('"'); for (int i = 0; i < value.length(); i++) { char c = value.charAt(i); switch (c) { case '<': writer.write("<"); break; case '>': writer.write(">"); break; case '&': writer.write("&"); break; case '"': writer.write("""); break; default: writer.write(c); } } writer.write('"'); } } static class CharactersEvent extends AbstractXMLEvent implements Characters { final String data; final boolean whitespace; CharactersEvent(XMLStreamReader reader) { this(reader.getEventType(), reader.getLocation(), reader.getText(), reader.isWhiteSpace()); } CharactersEvent(int eventType, Location location, String data, boolean whitespace) { super(eventType, location); assert eventType == XMLStreamConstants.CHARACTERS || eventType == XMLStreamConstants.CDATA || eventType == XMLStreamConstants.SPACE; this.data = data; this.whitespace = whitespace; } @Override public String getData() { return data; } @Override public boolean isCData() { return eventType == XMLStreamConstants.CDATA; } @Override public boolean isIgnorableWhiteSpace() { return eventType == XMLStreamConstants.SPACE; } @Override public boolean isWhiteSpace() { return whitespace; } @Override void writeAsEncodedUnicodeInternal(Writer writer) throws IOException { if (isCData()) { writer.write("<![CDATA["); writer.write(data); writer.write("]]>"); } else if (!isIgnorableWhiteSpace()) { // API doc: No indentation or whitespace should be "outputted". for (int i = 0; i < data.length(); i++) { char c = data.charAt(i); switch (c) { case '<': writer.write("<"); break; case '>': writer.write(">"); break; case '&': writer.write("&"); break; default: writer.write(c); } } } } } static class CommentEvent extends AbstractXMLEvent implements Comment { final String text; CommentEvent(XMLStreamReader reader) { super(reader.getEventType(), reader.getLocation()); assert eventType == XMLStreamConstants.COMMENT; text = reader.getText(); } CommentEvent(Location location, String text) { super(XMLStreamConstants.COMMENT, location); this.text = text; } @Override public String getText() { return text; } @Override void writeAsEncodedUnicodeInternal(Writer writer) throws IOException { writer.write("<!--"); writer.write(text); writer.write("-->"); } } static class EndDocumentEvent extends AbstractXMLEvent implements EndDocument { EndDocumentEvent(XMLStreamReader reader) { super(reader.getEventType(), reader.getLocation()); assert eventType == XMLStreamConstants.END_DOCUMENT; } EndDocumentEvent(Location location) { super(XMLStreamConstants.END_DOCUMENT, location); } @Override void writeAsEncodedUnicodeInternal(Writer writer) { // silence } } static class EndElementEvent extends AbstractXMLEvent implements EndElement { final QName name; final List<NamespaceEvent> namespaces; EndElementEvent(XMLStreamReader reader) { super(reader.getEventType(), reader.getLocation()); assert eventType == XMLStreamConstants.END_ELEMENT; name = reader.getName(); if (reader.getNamespaceCount() == 0) { namespaces = Collections.emptyList(); } else { namespaces = new ArrayList<NamespaceEvent>(reader.getNamespaceCount()); for (int i = 0; i < reader.getNamespaceCount(); i++) { namespaces.add(new NamespaceEvent(location, reader.getNamespaceURI(i), reader.getNamespacePrefix(i))); } } } EndElementEvent(Location location, QName name, Iterator<NamespaceEvent> namespaces) { super(XMLStreamConstants.END_ELEMENT, location); this.name = name; this.namespaces = toList(namespaces); } @Override public QName getName() { return name; } @Override public Iterator<?> getNamespaces() { return namespaces.iterator(); } @Override void writeAsEncodedUnicodeInternal(Writer writer) throws IOException { writer.write("</"); if (!XMLConstants.DEFAULT_NS_PREFIX.equals(name.getPrefix())) { writer.write(name.getPrefix()); writer.write(':'); } writer.write(name.getLocalPart()); writer.write('>'); } } static class EntityReferenceEvent extends AbstractXMLEvent implements EntityReference { final String name; final EntityDeclaration declaration; EntityReferenceEvent(XMLStreamReader reader) { super(reader.getEventType(), reader.getLocation()); assert eventType == XMLStreamConstants.ENTITY_REFERENCE; name = reader.getText(); declaration = null; // TODO } EntityReferenceEvent(Location location, String name, EntityDeclaration declaration) { super(XMLStreamConstants.ENTITY_REFERENCE, location); this.name = name; this.declaration = declaration; } @Override public EntityDeclaration getDeclaration() { return declaration; } @Override public String getName() { return name; } @Override void writeAsEncodedUnicodeInternal(Writer writer) throws IOException { writer.write('&'); writer.write(name); writer.write(';'); } } static class NamespaceEvent extends AttributeEvent implements Namespace { static QName createName(String prefix) { if (prefix == null || XMLConstants.DEFAULT_NS_PREFIX.equals(prefix)) { return new QName(XMLConstants.XMLNS_ATTRIBUTE_NS_URI, XMLConstants.XMLNS_ATTRIBUTE); } else { return new QName(XMLConstants.XMLNS_ATTRIBUTE_NS_URI, prefix, XMLConstants.XMLNS_ATTRIBUTE); } } NamespaceEvent(Location location, String namespaceURI, String prefix) { super(XMLStreamConstants.NAMESPACE, location, createName(prefix), namespaceURI, true); } @Override public String getPrefix() { return isDefaultNamespaceDeclaration() ? XMLConstants.DEFAULT_NS_PREFIX : getName().getLocalPart(); } @Override public String getNamespaceURI() { return getValue(); } @Override public boolean isDefaultNamespaceDeclaration() { return XMLConstants.DEFAULT_NS_PREFIX.equals(getName().getPrefix()); } } static class ProcessingInstructionEvent extends AbstractXMLEvent implements ProcessingInstruction { final String target; final String data; ProcessingInstructionEvent(XMLStreamReader reader) { super(reader.getEventType(), reader.getLocation()); assert eventType == XMLStreamConstants.PROCESSING_INSTRUCTION; target = reader.getPITarget(); data = reader.getPIData(); } ProcessingInstructionEvent(Location location, String target, String data) { super(XMLStreamConstants.PROCESSING_INSTRUCTION, location); this.target = target; this.data = data; } @Override public String getTarget() { return target; } @Override public String getData() { return data; } @Override void writeAsEncodedUnicodeInternal(Writer writer) throws IOException { writer.write("<?"); writer.write(target); if (data != null) { writer.write(' '); writer.write(data.trim()); } writer.write("?>"); } } static class StartDocumentEvent extends AbstractXMLEvent implements StartDocument { final String encodingScheme; final String version; final Boolean standalone; StartDocumentEvent(XMLStreamReader reader) { super(reader.getEventType(), reader.getLocation()); assert eventType == XMLStreamConstants.START_DOCUMENT; encodingScheme = reader.getCharacterEncodingScheme(); version = reader.getVersion() == null ? "1.0" : reader.getVersion(); standalone = reader.standaloneSet() ? Boolean.valueOf(reader.isStandalone()) : null; } StartDocumentEvent(Location location, String encoding, String version, Boolean standalone) { super(XMLStreamConstants.START_DOCUMENT, location); this.encodingScheme = encoding; this.version = version == null ? "1.0" : version; this.standalone = standalone; } @Override public String getCharacterEncodingScheme() { return encodingSet() ? encodingScheme : "UTF-8"; } @Override public boolean encodingSet() { return encodingScheme != null; } @Override public String getVersion() { return version; } @Override public boolean isStandalone() { return standaloneSet() ? standalone.booleanValue() : false; } @Override public boolean standaloneSet() { return standalone != null; } @Override public String getSystemId() { return location.getSystemId(); } @Override void writeAsEncodedUnicodeInternal(Writer writer) throws IOException { writer.write("<?xml version=\""); writer.write(version); writer.write('"'); if (encodingSet()) { writer.write(" encoding=\""); writer.write(encodingScheme); writer.write('"'); } if (standaloneSet()) { writer.write(" standalone=\""); writer.write(standalone ? "yes" : "no"); writer.write('"'); } writer.write("?>"); } } static class StartElementEvent extends AbstractXMLEvent implements StartElement { final QName name; final List<AttributeEvent> attributes; final List<NamespaceEvent> namespaces; final NamespaceContext context; StartElementEvent(XMLStreamReader reader) { super(reader.getEventType(), reader.getLocation()); assert eventType == XMLStreamConstants.START_ELEMENT; name = reader.getName(); context = reader.getNamespaceContext(); if (reader.getAttributeCount() == 0) { attributes = Collections.emptyList(); } else { attributes = new ArrayList<AttributeEvent>(reader.getAttributeCount()); for (int i = 0; i < reader.getAttributeCount(); i++) { attributes.add(new AttributeEvent(location, reader.getAttributeName(i), reader.getAttributeValue(i), reader.isAttributeSpecified(i))); } } if (reader.getNamespaceCount() == 0) { namespaces = Collections.emptyList(); } else { namespaces = new ArrayList<NamespaceEvent>(reader.getNamespaceCount()); for (int i = 0; i < reader.getNamespaceCount(); i++) { namespaces.add(new NamespaceEvent(location, reader.getNamespaceURI(i), reader.getNamespacePrefix(i))); } } } StartElementEvent(Location location, QName name, Iterator<AttributeEvent> attributes, Iterator<NamespaceEvent> namespaces, NamespaceContext context) { super(XMLStreamConstants.START_ELEMENT, location); this.name = name; this.context = context; this.attributes = toList(attributes); this.namespaces = toList(namespaces); } @Override public Attribute getAttributeByName(QName name) { for (Attribute attribute : attributes) { if (attribute.getName().equals(name)) { return attribute; } } return null; } @Override public Iterator<?> getAttributes() { return attributes.iterator(); } @Override public QName getName() { return name; } @Override public NamespaceContext getNamespaceContext() { return context; } @Override public Iterator<?> getNamespaces() { return namespaces.iterator(); } @Override public String getNamespaceURI(String prefix) { for (Namespace namespace : namespaces) { if (namespace.getPrefix().equals(prefix)) { return namespace.getNamespaceURI(); } } return null; } @Override void writeAsEncodedUnicodeInternal(Writer writer) throws IOException { writer.write('<'); if (!XMLConstants.DEFAULT_NS_PREFIX.equals(name.getPrefix())) { writer.write(name.getPrefix()); writer.write(':'); } writer.write(name.getLocalPart()); for (NamespaceEvent namespace : namespaces) { writer.write(' '); namespace.writeAsEncodedUnicodeInternal(writer); } for (AttributeEvent attribute : attributes) { if (attribute.isSpecified()) { writer.write(' '); attribute.writeAsEncodedUnicodeInternal(writer); } } writer.write('>'); } } @Override public XMLEventAllocator newInstance() { return this; } @Override public XMLEvent allocate(XMLStreamReader reader) throws XMLStreamException { switch (reader.getEventType()) { case XMLStreamConstants.CDATA: case XMLStreamConstants.CHARACTERS: case XMLStreamConstants.SPACE: return new CharactersEvent(reader); case XMLStreamConstants.COMMENT: return new CommentEvent(reader); case XMLStreamConstants.DTD: throw new UnsupportedOperationException(); case XMLStreamConstants.END_DOCUMENT: return new EndDocumentEvent(reader); case XMLStreamConstants.END_ELEMENT: return new EndElementEvent(reader); case XMLStreamConstants.ENTITY_REFERENCE: return new EntityReferenceEvent(reader); case XMLStreamConstants.NOTATION_DECLARATION: throw new UnsupportedOperationException(); case XMLStreamConstants.PROCESSING_INSTRUCTION: return new ProcessingInstructionEvent(reader); case XMLStreamConstants.START_DOCUMENT: return new StartDocumentEvent(reader); case XMLStreamConstants.START_ELEMENT: return new StartElementEvent(reader); default: throw new XMLStreamException("Unexpected event type: " + reader.getEventType()); } } @Override public void allocate(XMLStreamReader reader, XMLEventConsumer consumer) throws XMLStreamException { consumer.add(allocate(reader)); } }