/** * * 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.sdo.util.resource; import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; import java.util.List; import java.util.NoSuchElementException; import javax.xml.XMLConstants; import javax.xml.namespace.NamespaceContext; import javax.xml.namespace.QName; import javax.xml.stream.Location; import javax.xml.stream.XMLStreamException; import javax.xml.stream.XMLStreamReader; import org.eclipse.emf.ecore.xmi.XMLResource; import org.xml.sax.Attributes; import org.xml.sax.Locator; /** * This special purpose XMLStreamReader is used to produce a StAX event stream corresponding to a list of events * recorded earlier. The recorded events are generated by the inner class RecordedEventXMLStreamReader.Tag, * which records the events in either of 2 ways: * * 1) in conjunction with class SDOXMLLoadImpl, it records events corresponding to the SAX events being * handled by the SDOXMLLoadImpl when loading XML using SDOXMLResourceImpl. * 2) when Tag.record() is called (see class ChangeSummaryStreamDeserializer), it walks through and records * the StAX events produced by another XMLStreamReader. * * This class is used by the SDO StAX-based ChangeSummaryType-property loader, class * ChangeSummaryStreamDeserializer, which is inoked by and uses (for loading deleted object XML fragments) * the SAX-based loader class XMLResourceImpl. */ public abstract class RecordedEventXMLStreamReader implements XMLStreamReader { static private class Event { int type; public NamespaceContext nameSpaceContext; Location location; protected final void initialize(XMLStreamReader reader) { nameSpaceContext = reader.getNamespaceContext(); location = reader.getLocation(); } protected final void location(final Locator locator) { location = new Location() { public int getCharacterOffset() { return -1; } public int getColumnNumber() { return locator.getColumnNumber(); } public int getLineNumber() { return locator.getLineNumber(); } public String getPublicId() { return locator.getPublicId(); } public String getSystemId() { return locator.getSystemId(); } }; } } static class ValueEvent extends Event { final String value; protected ValueEvent(String v) { value = v; } } static protected class Reference extends ValueEvent { final String target; protected Reference(String name, String data) { super(data); target = name; } } static protected final class AttributeEvent extends Reference { final QName name; final String nameSpace, prefix; int attributes; final boolean specified; protected AttributeEvent(XMLStreamReader reader) { super(reader.getAttributeType(0), reader.getAttributeValue(0)); attributes = reader.getAttributeCount(); name = reader.getAttributeName(0); nameSpace = reader.getAttributeNamespace(0); prefix = reader.getAttributePrefix(0); specified = reader.isAttributeSpecified(0); } } static protected final class NameSpaceEvent extends Reference { int nameSpaces; protected NameSpaceEvent(XMLStreamReader reader) { super(reader.getNamespacePrefix(0), reader.getNamespaceURI(0)); nameSpaces = reader.getNamespaceCount(); } } static protected String prefix(String qName, String nameSpace) { int delimiter = qName.indexOf(':'); if (delimiter != -1) return qName.substring(0, delimiter); if (nameSpace.length() != 0) return XMLConstants.DEFAULT_NS_PREFIX; // if (nameSpaceContext.getNamespaceURI(XMLConstants.DEFAULT_NS_PREFIX) != null || xsdHelper.getGlobalProperty(null, name, element) == null) return null; } static class EndElement extends Event { List nameSpaces/* = null */; public final QName name; public final String nameSpace; final String prefix; protected EndElement(XMLStreamReader reader) { name = reader.getName(); nameSpace = reader.getNamespaceURI(); prefix = reader.getPrefix(); int count = reader.getNamespaceCount(); if (count == 0) return; nameSpaces = new ArrayList(count); int index = 0; do Tag.bind(reader.getNamespacePrefix(index), reader.getNamespaceURI(index), nameSpaces); while (++index != count); } protected EndElement(String uri, String local, String p, Locator locator) { if (p == null) { name = new QName(uri, local, XMLConstants.DEFAULT_NS_PREFIX); nameSpace = null; } else { name = new QName(uri, local, p); nameSpace = uri; } prefix = p; location(locator); } } static class NameSpace { final String prefix, uri; protected NameSpace(String p, String nameSpace) { prefix = p; uri = nameSpace; } } static final class Attribute extends NameSpace { final String type, value; final QName qName; boolean specified/* = false */; protected Attribute(String t, String v, QName name, String prefix, String nameSpace) { super(prefix, nameSpace); type = t; value = v; qName = name; } } static final class AttributeList /* implements Attributes */{// TODO exclude XMLConstants.XMLNS_ATTRIBUTE final List attributes; protected AttributeList(int size) { attributes = new ArrayList(size); } /* * @param uri Never null */ public final int getIndex(String uri, String localName) { for (int index = getLength(); index != 0;) if (getLocalName(--index).equals(localName) && uri.equals(getURI(index))) return index; return -1; } public final int getLength() { return attributes.size(); } protected final Attribute attribute(int index) { return (Attribute) attributes.get(index); } public final String getLocalName(int index) { return attribute(index).qName.getLocalPart(); } public final String getType(int index) { return attribute(index).type; } /*public String getType(String uri, String localName) { int index = getIndex(uri, localName); return index == -1 ? null: getType(index); } */ public final String getURI(int index) { return attribute(index).uri; } public final String getValue(int index) { return attribute(index).value; } /* * @param uri Never null */ public final String getValue(String uri, String localName) { int index = getIndex(uri, localName); return index == -1 ? null : getValue(index); } } static protected class StartElement extends EndElement { final AttributeList attributes; protected StartElement(XMLStreamReader reader) { super(reader); int count = reader.getAttributeCount(); if (count == 0) attributes = null; else { attributes = new AttributeList(count); int index = 0; do { Attribute attribute = new Attribute(reader.getAttributeType(index), reader.getAttributeValue(index), reader .getAttributeName(index), reader.getAttributePrefix(index), reader.getAttributeNamespace(index)); attribute.specified = reader.isAttributeSpecified(index); attributes.attributes.add(attribute); } while (++index != count); } } protected StartElement(String nameSpace, String local, String prefix, Attributes attributeArray, Locator locator, List bindings, final NamespaceContext context) { super(nameSpace, local, prefix, locator); nameSpaces = bindings; nameSpaceContext = bindings == null || bindings.isEmpty() ? context : new NamespaceContext() { public String getNamespaceURI(String prefix) { for (int index = nameSpaces.size(); index != 0;) { NameSpace binding = (NameSpace) nameSpaces.get(--index); if (binding.prefix.equals(prefix)) return binding.uri; } return context.getNamespaceURI(prefix); } public String getPrefix(String namespaceURI) { for (int index = nameSpaces.size(); index != 0;) { NameSpace binding = (NameSpace) nameSpaces.get(--index); if (binding.uri.equals(namespaceURI)) return binding.prefix; } return context.getPrefix(namespaceURI); } public Iterator getPrefixes(final String namespaceURI) { final Iterator iterator = context.getPrefixes(namespaceURI); return new Iterator() { Iterator bindings = nameSpaces.iterator(); NameSpace binding/* = null */; protected final boolean prefix() { while (bindings.hasNext()) { binding = (NameSpace) bindings.next(); if (binding.uri.equals(namespaceURI)) return true; } bindings = null; return false; } public boolean hasNext() { return bindings != null && prefix() || iterator.hasNext(); } protected NameSpace nameSpace; public Object next() { if (bindings == null || binding == null && !prefix()) return iterator.next(); nameSpace = binding; binding = null; return nameSpace.prefix; } public void remove() { if (bindings == null) iterator.remove(); else nameSpaces.remove(nameSpace); } }; } }; int count = attributeArray.getLength(); if (count == 0) attributes = null; else { attributes = new AttributeList(count); int index = 0; do { QName name; nameSpace = attributeArray.getURI(index); local = attributeArray.getLocalName(index); prefix = prefix(attributeArray.getQName(index), nameSpace); if (prefix == null) { name = new QName(nameSpace, local, XMLConstants.DEFAULT_NS_PREFIX); nameSpace = null; } else name = new QName(nameSpace, local, prefix); attributes.attributes.add(new Attribute(attributeArray.getType(index), attributeArray.getValue(index), name, prefix, nameSpace)); } while (++index != count); } } } static public class Tag extends StartElement { public Tag(XMLStreamReader reader) { super(reader); initialize(reader); } public List events/* = null */; // may be empty protected final void events() { events = new ArrayList(); } public Tag(String nameSpace, String local, String prefix, Attributes attributes, Locator locator, NamespaceContext context, List bindings) { super(nameSpace, local, prefix, attributes, locator, bindings, context); events(); } static public void bind(String prefix, String nameSpace, Collection nameSpaces) { nameSpaces.add(new NameSpace(prefix, nameSpace)); } protected int nest/* = 0 */; public final void start(String nameSpace, String local, String qName, Attributes attributes, Locator locator, List bindings) { Event event; for (int index = events.size();/* true */;) { if (index == 0) { event = this; break; } event = (Event) events.get(--index); if (event.type != END_ELEMENT) break; siblings: for (int nest = 0;/* true */;) switch (((Event) events.get(--index)).type) { case START_ELEMENT: if (nest == 0) break siblings; --nest; break; case END_ELEMENT: ++nest; } } Event start = new StartElement(nameSpace, local, prefix(qName, nameSpace), attributes, locator, bindings, event.nameSpaceContext); start.type = START_ELEMENT; events.add(start); ++nest; } protected final void add (Event event) { int index = events.size(); event.nameSpaceContext = index == 0 ? nameSpaceContext : ((Event) events.get(--index)).nameSpaceContext; events.add(event); } public final void text(int type, String value, Locator locator) { Event event = new ValueEvent(value); event.type = type; event.location(locator); //int index = events.size(); add(event); } public final boolean end(String nameSpace, String local, String qName, Locator locator) { Event end = new EndElement(nameSpace, local, prefix(qName, nameSpace), locator); end.type = END_ELEMENT; add(end); if (nest == 0) return true; --nest; return false; } public final XMLStreamReader play(final XMLResource resource) { return new RecordedEventXMLStreamReader(this) { public void close() { } public String getCharacterEncodingScheme() { return null; // TODO } public String getEncoding() { return resource.getEncoding(); } public Object getProperty(String property) { return null; // TODO javax.xml.stream.notations & javax.xml.stream.entities for DTD } public String getVersion() { return resource.getXMLVersion(); } public boolean isStandalone() { return false; // TODO } public boolean standaloneSet() { return false; // TODO } }; } protected final void add(Event event, int type, XMLStreamReader reader) { event.type = type; event.initialize(reader); events.add(event); } public final boolean record(XMLStreamReader reader) throws XMLStreamException { events(); for (int nest = 0; reader.hasNext();) { Event event; int type = reader.next(); switch (type) { case CHARACTERS: case CDATA: case COMMENT: case SPACE: case DTD: event = new ValueEvent(reader.getText()); break; case ENTITY_REFERENCE: event = new Reference(reader.getLocalName(), reader.getText()); break; case PROCESSING_INSTRUCTION: event = new Reference(reader.getPITarget(), reader.getPIData()); break; case ATTRIBUTE: event = new AttributeEvent(reader); break; case NAMESPACE: event = new NameSpaceEvent(reader); break; case START_ELEMENT: ++nest; event = new StartElement(reader); break; case END_ELEMENT: add(new EndElement(reader), type, reader); if (nest == 0) return false; --nest; continue; case END_DOCUMENT: return true; // report error? default: // new type event = new Event(); } add(event, type, reader); } return true; // report error? } public final XMLStreamReader play(final XMLStreamReader reader) { return new RecordedEventXMLStreamReader(this) { public void close() throws XMLStreamException { reader.close(); } public String getCharacterEncodingScheme() { return reader.getCharacterEncodingScheme(); } public String getEncoding() { return reader.getEncoding(); } public Object getProperty(String property) { return reader.getProperty(property); // TODO javax.xml.stream.notations & javax.xml.stream.entities for DTD } public String getVersion() { return reader.getVersion(); } public boolean isStandalone() { return reader.isStandalone(); } public boolean standaloneSet() { return reader.standaloneSet(); } }; } } Event event; final List events; final int size; protected RecordedEventXMLStreamReader(Tag tag) { event = tag; tag.type = START_ELEMENT; events = tag.events; size = events.size(); } public int getAttributeCount() { switch (getEventType()) { case START_ELEMENT: AttributeList attributes = ((StartElement) event).attributes; return attributes == null ? 0 : attributes.getLength(); case ATTRIBUTE: return ((AttributeEvent) event).attributes; } throw new IllegalStateException("Neither START_ELEMENT nor ATTRIBUTE"); } protected final AttributeList attributes() { if (getEventType() == START_ELEMENT) return ((StartElement) event).attributes; throw new IllegalStateException("Neither START_ELEMENT nor ATTRIBUTE"); } public String getAttributeLocalName(int index) { return attributes().getLocalName(index); } static Attribute attribute(AttributeList attributes, int index) { return (Attribute) attributes.attributes.get(index); } public QName getAttributeName(int index) { return getEventType() == ATTRIBUTE ? ((AttributeEvent) event).name : attribute(attributes(), index).qName; } public String getAttributeNamespace(int index) { return getEventType() == ATTRIBUTE ? ((AttributeEvent) event).nameSpace : attributes().getURI(index); } public String getAttributePrefix(int index) { return getEventType() == ATTRIBUTE ? ((AttributeEvent) event).prefix : attribute(attributes(), index).prefix; } public String getAttributeType(int index) { return getEventType() == ATTRIBUTE ? ((Reference) event).target : attributes().getType(index); } public String getAttributeValue(int index) { return getEventType() == ATTRIBUTE ? ((ValueEvent) event).value : attributes().getValue(index); } public boolean isAttributeSpecified(int index) { if (getEventType() == ATTRIBUTE) return ((AttributeEvent) event).specified; AttributeList attributes = attributes(); return attribute(attributes, index).specified; } public String getAttributeValue(String nameSpace, String name) { if (getEventType() == ATTRIBUTE) { AttributeEvent attribute = (AttributeEvent) event; return !attribute.name.getLocalPart().equals(name) ? null : nameSpace == null ? (attribute.nameSpace == null ? attribute.value : null) : nameSpace.equals(attribute.nameSpace) ? attribute.value : null; } AttributeList attributes = attributes(); return attributes == null ? null : attributes.getValue(nameSpace == null ? "" : nameSpace, name); } protected StringBuffer buffer/* = null */; public String getElementText() { if (buffer != null) buffer.delete(0, buffer.length()); for (;;) switch (next()) { case END_ELEMENT: return buffer == null ? null : buffer.toString(); default: if (buffer == null) buffer = new StringBuffer(); buffer.append(getText()); case PROCESSING_INSTRUCTION: case COMMENT: } } public final int getEventType() { return event.type; } public String getLocalName() { if (getEventType() == ENTITY_REFERENCE) return ((Reference) event).target; if (event instanceof EndElement) return ((EndElement) event).name.getLocalPart(); throw new IllegalStateException("Neither START_ELEMENT, END_ELEMENT nor ENTITY_REFERENCE"); } public final Location getLocation() { return event.location; } public QName getName() { if (hasName()) return ((EndElement) event).name; throw new IllegalStateException("Neither START_ELEMENT nor END_ELEMENT"); } public final NamespaceContext getNamespaceContext() { return event.nameSpaceContext; } public int getNamespaceCount() { if (getEventType() == NAMESPACE) return ((NameSpaceEvent) event).nameSpaces; if (!(event instanceof EndElement)) throw new IllegalStateException("Neither START_ELEMENT, END_ELEMENT nor NAMESPACE"); Collection nameSpaces = ((EndElement) event).nameSpaces; return nameSpaces == null ? 0 : nameSpaces.size(); } protected final NameSpace getNameSpace(int index) { if (event instanceof EndElement) return (NameSpace) ((EndElement) event).nameSpaces.get(index); throw new IllegalStateException("Neither START_ELEMENT, END_ELEMENT nor NAMESPACE"); } public String getNamespacePrefix(int index) { return getEventType() == NAMESPACE ? ((Reference) event).target : getNameSpace(index).prefix; } public final String getNamespaceURI() { switch (getEventType()) { case ATTRIBUTE: return ((AttributeEvent) event).nameSpace; case NAMESPACE: return ((ValueEvent) event).value; } return event instanceof EndElement ? ((EndElement) event).nameSpace : null; } public String getNamespaceURI(String prefix) { return getNamespaceContext().getNamespaceURI(prefix); } public String getNamespaceURI(int index) { return getEventType() == NAMESPACE ? ((ValueEvent) event).value : getNameSpace(index).uri; } public String getPIData() { return getEventType() == PROCESSING_INSTRUCTION ? ((ValueEvent) event).value : null; } public String getPITarget() { return getEventType() == PROCESSING_INSTRUCTION ? ((Reference) event).target : null; } public String getPrefix() { switch (getEventType()) { case ATTRIBUTE: return ((AttributeEvent) event).prefix; case NAMESPACE: return ((Reference) event).target; } return event instanceof EndElement ? ((EndElement) event).prefix : null; } public final String getText() { if (hasText()) return ((ValueEvent) event).value; throw new IllegalStateException("Neither CHARACTERS, CDATA, COMMENT, SPACE, ENTITY_REFERENCE nor DTD"); } public final char[] getTextCharacters() { switch (getEventType()) { case CHARACTERS: case CDATA: case COMMENT: case SPACE: return ((ValueEvent) event).value.toCharArray(); } throw new IllegalStateException("Neither CHARACTERS, CDATA, COMMENT nor SPACE"); } public int getTextCharacters(int sourceStart, char[] target, int targetStart, int length) { char[] source = getTextCharacters(); if (sourceStart > source.length) throw new IndexOutOfBoundsException("source start > source length"); int sourceLen = source.length - sourceStart; if (length > sourceLen) length = sourceLen; System.arraycopy(source, sourceStart, target, targetStart, length); return sourceLen; } public int getTextLength() { return getTextCharacters().length; } public int getTextStart() { return 0; } public final boolean hasName() { return event instanceof EndElement; } protected int next/* = 0 */; public final boolean hasNext() { return next != size; } public final boolean hasText() { switch (getEventType()) { case CHARACTERS: case CDATA: case COMMENT: case SPACE: case ENTITY_REFERENCE: case DTD: return true; } return false; } public boolean isCharacters() { switch (getEventType()) { case CHARACTERS: case CDATA: case SPACE: return true; } return false; } public boolean isEndElement() { return getEventType() == END_ELEMENT; } public boolean isStartElement() { return getEventType() == START_ELEMENT; } protected final boolean areWhiteSpace() { String text = getText(); for (int index = text.length(); index != 0;) if (!Character.isWhitespace(text.charAt(--index))) return false; return true; } public boolean isWhiteSpace() { switch (getEventType()) { case CHARACTERS: case CDATA: return areWhiteSpace(); case SPACE: return true; } return false; } public final int next() { if (!hasNext()) throw new NoSuchElementException(); event = (Event) events.get(next++); return event.type; } protected final void throwXMLStreamException(String message) throws XMLStreamException { throw new XMLStreamException(message, getLocation()); } public int nextTag() throws XMLStreamException { for (;;) { int type = next(); switch (type) { case CHARACTERS: case CDATA: if (!areWhiteSpace()) break; case SPACE: case PROCESSING_INSTRUCTION: case COMMENT: continue; case START_ELEMENT: case END_ELEMENT: return type; } throwXMLStreamException("expected start or end tag"); } } public void require(int type, String nameSpace, String name) throws XMLStreamException { if (getEventType() != type) throwXMLStreamException("type not matched"); if (nameSpace != null && !nameSpace.equals(getNamespaceURI())) throwXMLStreamException("Name Space not matched"); if (name != null && !(getEventType() == ATTRIBUTE ? name.equals(((AttributeEvent) event).name.getLocalPart()) : event instanceof EndElement && name.equals(((EndElement) event).name.getLocalPart()))) throwXMLStreamException("name not matched"); } }