From bdd0a41aed7edf21ec2a65cfa17a86af2ef8c48a Mon Sep 17 00:00:00 2001 From: dims Date: Tue, 17 Jun 2008 00:23:01 +0000 Subject: Move Tuscany from Incubator to top level. git-svn-id: http://svn.us.apache.org/repos/asf/tuscany@668359 13f79535-47bb-0310-9956-ffa450edef68 --- .../resource/ChangeSummaryStreamDeserializer.java | 663 ++++++++ .../resource/ChangeSummaryStreamSerializer.java | 690 +++++++++ .../util/resource/DataObjectXMLStreamReader.java | 1579 ++++++++++++++++++++ .../resource/RecordedEventXMLStreamReader.java | 868 +++++++++++ .../tuscany/sdo/util/resource/SDODeserializer.java | 340 +++++ .../sdo/util/resource/SDOURIConverterImpl.java | 60 + .../util/resource/SDOXMLResourceFactoryImpl.java | 47 + .../sdo/util/resource/SDOXMLResourceImpl.java | 1009 +++++++++++++ .../sdo/util/resource/XMLDocumentStreamReader.java | 438 ++++++ .../sdo/util/resource/XMLFragmentStreamReader.java | 44 + .../sdo/util/resource/XMLStreamSerializer.java | 414 +++++ 11 files changed, 6152 insertions(+) create mode 100644 branches/sdo-1.1.1-incubating/impl/src/main/java/org/apache/tuscany/sdo/util/resource/ChangeSummaryStreamDeserializer.java create mode 100644 branches/sdo-1.1.1-incubating/impl/src/main/java/org/apache/tuscany/sdo/util/resource/ChangeSummaryStreamSerializer.java create mode 100644 branches/sdo-1.1.1-incubating/impl/src/main/java/org/apache/tuscany/sdo/util/resource/DataObjectXMLStreamReader.java create mode 100644 branches/sdo-1.1.1-incubating/impl/src/main/java/org/apache/tuscany/sdo/util/resource/RecordedEventXMLStreamReader.java create mode 100644 branches/sdo-1.1.1-incubating/impl/src/main/java/org/apache/tuscany/sdo/util/resource/SDODeserializer.java create mode 100644 branches/sdo-1.1.1-incubating/impl/src/main/java/org/apache/tuscany/sdo/util/resource/SDOURIConverterImpl.java create mode 100644 branches/sdo-1.1.1-incubating/impl/src/main/java/org/apache/tuscany/sdo/util/resource/SDOXMLResourceFactoryImpl.java create mode 100644 branches/sdo-1.1.1-incubating/impl/src/main/java/org/apache/tuscany/sdo/util/resource/SDOXMLResourceImpl.java create mode 100644 branches/sdo-1.1.1-incubating/impl/src/main/java/org/apache/tuscany/sdo/util/resource/XMLDocumentStreamReader.java create mode 100644 branches/sdo-1.1.1-incubating/impl/src/main/java/org/apache/tuscany/sdo/util/resource/XMLFragmentStreamReader.java create mode 100644 branches/sdo-1.1.1-incubating/impl/src/main/java/org/apache/tuscany/sdo/util/resource/XMLStreamSerializer.java (limited to 'branches/sdo-1.1.1-incubating/impl/src/main/java/org/apache/tuscany/sdo/util/resource') diff --git a/branches/sdo-1.1.1-incubating/impl/src/main/java/org/apache/tuscany/sdo/util/resource/ChangeSummaryStreamDeserializer.java b/branches/sdo-1.1.1-incubating/impl/src/main/java/org/apache/tuscany/sdo/util/resource/ChangeSummaryStreamDeserializer.java new file mode 100644 index 0000000000..697de1c7ad --- /dev/null +++ b/branches/sdo-1.1.1-incubating/impl/src/main/java/org/apache/tuscany/sdo/util/resource/ChangeSummaryStreamDeserializer.java @@ -0,0 +1,663 @@ +/** + * + * 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.Comparator; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import javax.xml.namespace.NamespaceContext; +import javax.xml.stream.XMLStreamException; +import javax.xml.stream.XMLStreamReader; + +import org.apache.tuscany.sdo.SDOFactory; +import org.apache.tuscany.sdo.helper.SDOAnnotations; +import org.apache.tuscany.sdo.impl.ChangeSummaryImpl; +import org.apache.tuscany.sdo.impl.ClassImpl; +import org.eclipse.emf.ecore.EClass; +import org.eclipse.emf.ecore.EObject; +import org.eclipse.emf.ecore.EStructuralFeature; +import org.eclipse.emf.ecore.ETypedElement; +import org.eclipse.emf.ecore.change.ChangeDescription; +import org.eclipse.emf.ecore.change.ChangeFactory; +import org.eclipse.emf.ecore.change.ChangeKind; +import org.eclipse.emf.ecore.change.FeatureChange; +import org.eclipse.emf.ecore.change.FeatureMapEntry; +import org.eclipse.emf.ecore.change.ListChange; +import org.eclipse.emf.ecore.util.FeatureMap; +import org.eclipse.emf.ecore.util.FeatureMapUtil; + +import commonj.sdo.ChangeSummary; +import commonj.sdo.DataObject; +import commonj.sdo.Property; +import commonj.sdo.Type; +import commonj.sdo.helper.HelperContext; + +/** + * ChangeSummary StAX Deserializer whose input conforms to the SDO Java/C++/PHP specifications. The instance isn't thread-safe, however it's safe to + * use the instance any times on the same thread. + */ +public class ChangeSummaryStreamDeserializer extends SDODeserializer { + static final class ForwardReference extends Ref { + final String unset; + + ForwardReference(String ref, NamespaceContext nameSpaces, String u) { + super(ref, nameSpaces); + unset = u; + } + + Collection attributes/* = null */, qualifiedAttributes/* = null */, tags/* = null */; // may be null, never empty + } + + protected Collection forwardReferences/* = null */; + + static private final class ElementChange extends Ref { + private final Object containing, containment; + private ElementChange(String ref, NamespaceContext nameSpaces, Object property, Object propertyInSequence) { + super(ref, nameSpaces); + containing = property; + containment = propertyInSequence; + } + } + + static private class PropertyMapChanges { + Map/* Property,List */lists/* = null */;// may be null + + Collection put(Object property) { + Collection list = new ArrayList(); + lists.put(property, list); + return list; + } + + protected final Collection get(Object property) { + Object list = lists.get(property); + return list == null ? put(property) : (Collection) list; + } + + protected final Collection newList(Object property) { + lists = new HashMap(); + return put(property); + } + } + + static final class ObjectChanges extends PropertyMapChanges { + Collection elementChanges/* = null */,// may be null, never empty + featureChanges; + + protected final void newElementChanges() { + elementChanges = new ArrayList(); + } + } + + Collection objectChangesCollection/* = null */, objectMapChanges, deletedDataObjects; + + protected final ObjectChanges newObjectChanges(Collection featureChanges) { + ObjectChanges objectChanges = new ObjectChanges(); + objectChanges.featureChanges = featureChanges; + if (objectChangesCollection == null) + objectChangesCollection = new ArrayList(); + objectChangesCollection.add(objectChanges); + return objectChanges; + } + + private ChangeFactory changeFactory; + + private SDOFactory changeSettingFactory; + + private void logPropertyChange(Collection featureChanges, EStructuralFeature feature, Object value, boolean set) { + if (changeSettingFactory == null) + featureChanges.add(changeFactory.createFeatureChange(feature, value, set)); + else + featureChanges.add(changeSettingFactory.createChangeSummarySetting(feature, value, set)); + } + + void unsetProperty(Collection featureChanges, String unset, int begin, int index, Type type) { + logPropertyChange(featureChanges, (EStructuralFeature) type.getProperty(unset.substring(begin, index)), null, false); + } + + static boolean isWhitespace(String unset, int index) { + return Character.isWhitespace(unset.charAt(index)); + } + + protected final Collection unsetProperties(EObject referent, String unset, Type type) { + Map.Entry entry = changeFactory.createEObjectToChangesMapEntry(referent); + objectMapChanges.add(entry); + Collection featureChanges = (Collection) entry.getValue(); + if (unset == null) + return featureChanges; + int end = unset.length(); + if (end != 0) + for (int begin = 0, index = 1;/* true */; ++index) { + if (index == end) { + unsetProperty(featureChanges, unset, begin, index, type); + break; + } + if (isWhitespace(unset, index)) { + unsetProperty(featureChanges, unset, begin, index, type); + while (true) { + if (++index != end) + return featureChanges; + if (!isWhitespace(unset, index)) { + begin = index; + break; + } + } + } + } + return featureChanges; + } + + private Object value(EStructuralFeature containing, Object containment, Object value) { + return FeatureMapUtil.isFeatureMap(containing) ? changeFactory.createFeatureMapEntry((EStructuralFeature) containment, value) : value; + } + + private void logPropertyChange(Collection featureChanges, Object containing, Object containment, Object value) { + EStructuralFeature feature = (EStructuralFeature) containing; + logPropertyChange(featureChanges, feature, value(feature, containment, value), true); + } + + private void logPropertyChange(Collection featureChanges, Object property, Object value) { + logPropertyChange(featureChanges, property, propertyInSequence, value); + } + + void logAttributeChange(Collection featureChanges, Property property, String literal, NamespaceContext nameSpaces) { + logPropertyChange(featureChanges, property, value(property.getType(), literal, nameSpaces)); + } + + protected final void logAttributeChange(Collection featureChanges, String property, Type type, String value, NamespaceContext nameSpaces) { + logAttributeChange(featureChanges, getProperty(type, property), value, nameSpaces); + } + + protected final void logAttributeChange(Collection featureChanges, String nameSpace, String name, Type type, String value, + NamespaceContext nameSpaces) { + logAttributeChange(featureChanges, getProperty(type, nameSpace, name, false), value, nameSpaces); + } + + protected final String ref() { + return reader.getAttributeValue(SDOAnnotations.COMMONJ_SDO_NS, ChangeSummaryStreamSerializer.REF_ATTRIBUTE); + } + + ChangeSummaryImpl changeSummary; + + protected Object load(XMLStreamReader reader, Map options) throws XMLStreamException { + Object value = super.load(reader, options); + deletedDataObjects.add(value); + return value; + } + + protected final void getChangeSummary(DataObject rootObject) { + changeSummary = (ChangeSummaryImpl) rootObject.getChangeSummary(); // DynamicDataObjectImpl(EClass) + } + + static protected final class Tag extends RecordedEventXMLStreamReader.Tag { + protected String ref; + + protected Object value; + + protected Tag(XMLStreamReader reader) { + super(reader); + } + } + + protected final void addPropertyChange(Collection list, Object value, Object containing) { + list.add(value((EStructuralFeature) containing, propertyInSequence, value)); + } + + protected boolean logging; + + /** + * Imports ChangeSummary 2-1. Forward references will be resolved by {@link #end()}. + * + * @param reader + * Never null + * @throws XMLStreamException + */ + public final void begin(DataObject rootObject, HelperContext scope, XMLStreamReader reader) throws XMLStreamException { + /* + * 3-1. Instantiate ChangeSummary input: reader (xsi:type), factory, rootObject output: changeSummary, changeDescription + */ + initialize(reader, scope, rootObject); + if (typedXSI()) { + changeSummary = (ChangeSummaryImpl) scope.getDataFactory().create(nameSpace, name); + if (changeSummary == null) + getChangeSummary(rootObject); + else { + Property csp = ((ClassImpl) rootObject.getType()).getChangeSummaryProperty(); + rootObject.set(csp, changeSummary); + changeSummary.setDataObject(rootObject); + } + } else + getChangeSummary(rootObject); + ChangeDescription changeDescription = (ChangeDescription) changeSummary; + + /* + * 3-2. "logging" attribute input: reader output: logging + */ + logging = Boolean.valueOf(reader.getAttributeValue(null, "logging")).booleanValue(); + + /* + * 3-3. Modified DataObjects input: changeDescription + */ + if (forwardReferences != null) + forwardReferences.clear(); + if (objectChangesCollection != null) + objectChangesCollection.clear(); + if (START_ELEMENT == reader.nextTag()) { + objectMapChanges = changeDescription.getObjectChanges(); + deletedDataObjects = changeDescription.getObjectsToAttach(); + Object factory = changeDescription.eClass().getEPackage().getEFactoryInstance(); + changeFactory = factory instanceof ChangeFactory ? (ChangeFactory) factory : ChangeFactory.eINSTANCE; + changeSettingFactory = factory instanceof SDOFactory ? (SDOFactory) factory : null; + do { + /* + * Modified DataObject + */ + String ref = ref(), unset = reader.getAttributeValue(SDOAnnotations.COMMONJ_SDO_NS, ChangeSummaryStreamSerializer.UNSET); + int attributes = reader.getAttributeCount(); + NamespaceContext nameSpaces = reader.getNamespaceContext(); + EObject referent = referent(ref, nameSpaces); + if (referent == null) { + /* + * Forward-referenced(unresolved) modified DataObject + */ + ForwardReference forwardReference = new ForwardReference(ref, nameSpaces, unset); + if (forwardReferences == null) + forwardReferences = new ArrayList(); + forwardReferences.add(forwardReference); + do // what about xmlns="NS1" a1="qName" xmlns="NS2" a2="qName" ? + { + /* + * Record property old value as attribute for end() + */ + String nameSpace = reader.getAttributeNamespace(--attributes), name = reader.getAttributeLocalName(attributes), value = reader + .getAttributeValue(attributes); + if (nameSpace == null) { + /* + * Local attribute + */ + Attribute attribute = new Attribute(); + attribute.name = name; + attribute.value = value; + if (forwardReference.attributes == null) + forwardReference.attributes = new ArrayList(); + forwardReference.attributes.add(attribute); + } else if (!SDOAnnotations.COMMONJ_SDO_NS.equals(nameSpace) || !ChangeSummaryStreamSerializer.REF_ATTRIBUTE.equals(name) + && !ChangeSummaryStreamSerializer.UNSET.equals(name)) { + /* + * Qualified(global) attribute + */ + QualifiedAttribute attribute = new QualifiedAttribute(); + attribute.name = name; + attribute.value = value; + attribute.nameSpace = nameSpace; + if (forwardReference.qualifiedAttributes == null) + forwardReference.qualifiedAttributes = new ArrayList(); + forwardReference.qualifiedAttributes.add(attribute); + } + } while (attributes != 0); + while (START_ELEMENT == reader.nextTag()) { + /* + * Record property old value as element for end() + */ + Tag tag = new Tag(reader); + if (forwardReference.tags == null) + forwardReference.tags = new ArrayList(); + forwardReference.tags.add(tag); + tag.ref = ref(); + if (tag.ref != null) + continue; + Type xsi = typeXSI(); + if (xsi == null) { + if (tag.nameSpace != null) + tag.value = value(globalElementType(tag.nameSpace, tag.name.getLocalPart())); // TODO substitutionGroup type if null + else if (tag.record(reader)) + break; + } else + tag.value = value(xsi); + } + } else { + /* + * Resolved(back-referenced) modified DataObject + */ + Type type = ((DataObject) referent).getType(); + Collection featureChanges = unsetProperties(referent, unset, type); + do // what about xmlns="NS1" a1="qName" xmlns="NS2" a2="qName" ? + { + /* + * Log property old value as attribute + */ + String nameSpace = reader.getAttributeNamespace(--attributes), name = reader.getAttributeLocalName(attributes), value = reader + .getAttributeValue(attributes); + if (nameSpace == null) + logAttributeChange(featureChanges, name, type, value, nameSpaces); + else if (!SDOAnnotations.COMMONJ_SDO_NS.equals(nameSpace) || !ChangeSummaryStreamSerializer.REF_ATTRIBUTE.equals(name) + && !ChangeSummaryStreamSerializer.UNSET.equals(name)) + logAttributeChange(featureChanges, nameSpace, name, type, value, nameSpaces); + } while (attributes != 0); + if (START_ELEMENT == reader.nextTag()) { + ObjectChanges objectChanges = null; + do { + /* + * Log property old value as element + */ + String nameSpace = reader.getNamespaceURI(), name = reader.getLocalName(); + Property property = getProperty(nameSpace, name, type); + boolean many = property.isMany(); + Object value; + ref = ref(); + if (ref == null) { + /* + * Contained property old value + */ + Type xsi = typeXSI(); + if (xsi != null) + value = value(xsi); + else if (nameSpace == null) + value = value(reader); + else { + xsi = globalElementType(nameSpace, name); + value = value(xsi == null ? propertyInSequence.getType() : xsi); + } + } else { + /* + * Referenced child DataObject + */ + nameSpaces = reader.getNamespaceContext(); + reader.nextTag()/* END_ELEMENT */; + value = referent(ref, nameSpaces); + if (value == null) { + /* + * Forward-referenced(unresolved) child DataObject + */ + if (!many) { + ElementChange elementChange = new ElementChange(ref, nameSpaces, property, propertyInSequence); + if (objectChanges == null) { + objectChanges = newObjectChanges(featureChanges); + objectChanges.newElementChanges(); + } else if (objectChanges.elementChanges == null) + objectChanges.newElementChanges(); + objectChanges.elementChanges.add(elementChange); + continue; + } + value = new Ref(ref, nameSpaces); + } + } + if (many) { + Collection list; + if (objectChanges == null) { + objectChanges = newObjectChanges(featureChanges); + list = objectChanges.newList(property); + } else if (objectChanges.lists == null) + list = objectChanges.newList(property); + else + list = objectChanges.get(property); + addPropertyChange(list, value, property); + } else + logPropertyChange(featureChanges, property, value); + } while (START_ELEMENT == reader.nextTag()); + } + } + } while (START_ELEMENT == reader.nextTag()); + } + } + + static private boolean sequence(Object listChanges) { + return FeatureMapUtil.isFeatureMap(((FeatureChange) ((EStructuralFeature.Setting) listChanges).getEObject()).getFeature()); + } + + private ListChange createListChange(ChangeKind changeKind, int index, Collection listChanges) { + ListChange listChange = changeFactory.createListChange(); + listChange.setKind(changeKind); + listChange.setIndex(index); + listChanges.add(listChange); + return listChange; + } + + Collection add(Collection adds, int change, Collection listChanges, Object value) { + if (adds == null) { + ListChange listChange = createListChange(ChangeKind.ADD_LITERAL, change, listChanges); + adds = sequence(listChanges) ? listChange.getFeatureMapEntryValues() : listChange.getValues(); + } + adds.add(value); + return adds; + } + + private int remove(int change, Collection listChanges, List list, int begin, int end) { + ListChange listChange = createListChange(ChangeKind.REMOVE_LITERAL, change, listChanges); + if (sequence(listChanges)) { + Collection removes = listChange.getFeatureMapEntryValues(); + do { + FeatureMap.Entry fme = (FeatureMap.Entry) list.get(begin); + removes.add(changeFactory.createFeatureMapEntry(fme.getEStructuralFeature(), fme.getValue())); + } while (++begin != end); + return begin; + } + Collection removes = listChange.getValues(); + do + removes.add(list.get(begin)); + while (++begin != end); + return begin; + } + + int remove(int begin, int end, int change, Collection listChanges, List list) { + return begin == end ? begin : remove(change, listChanges, list, begin, end); + } + + protected final void logManyChanges(PropertyMapChanges propertyMapChanges, EObject referent, Collection featureChanges) { + for (Iterator lists = propertyMapChanges.lists.entrySet().iterator(); lists.hasNext();) { + /* + * Compute ListChanges out of comparision of old and new list + */ + Map.Entry entry = (Map.Entry) lists.next(); + EStructuralFeature property = (EStructuralFeature) entry.getKey(); + Iterator values = ((Collection) entry.getValue()).iterator(); // old list + Object value = referent.eGet(property); + List list = value instanceof FeatureMap.Internal.Wrapper ? ((FeatureMap.Internal.Wrapper) value).featureMap() : (List) value; // new + int change = 0; + FeatureChange featureChange = changeSettingFactory == null ? changeFactory.createFeatureChange() : (FeatureChange) changeSettingFactory + .createChangeSummarySetting(); + featureChange.setFeature(property); + Collection listChanges = featureChange.getListChanges(), adds = null; + featureChanges.add(featureChange); + if (property.getEType() instanceof EClass) { + /* + * Log child DataObject changes + */ + int begin = 0, end = list.size(); + while (values.hasNext()) { + value = values.next(); + if (value.getClass() == Ref.class) { + value = referent((Ref) value); + if (value == null) + continue;// report error? + } + // values.remove(); + for (int index = begin;/* true */; ++index) + if (index == end) { + adds = add(adds, change, listChanges, value); + break; + } else if (list.get(index) == value) // List#indexOf uses equals + { + begin = remove(begin, index, change, listChanges, list); + ++begin; + adds = null; + break; + } + ++change; + } + remove(begin, end, change, listChanges, list); + } else if (FeatureMapUtil.isFeatureMap(property)) { + /* + * Log Sequence changes + */ + int begin = 0, end = list.size(); + while (values.hasNext()) { + FeatureMapEntry featureMapEntry = (FeatureMapEntry) values.next(); + value = featureMapEntry.getValue(); + if (value.getClass() == Ref.class) { + value = referent((Ref) value); + if (value == null) + continue;// report error? + } + // values.remove(); + Comparator equality; + ETypedElement feature = featureMapEntry.getFeature(); + if (((Type) feature.getEType()).isDataType()) + if (value == null) + equality = EQUAL_NULL; + else + equality = EQUAL; + else + equality = SAME; + for (int index = begin;/* true */; ++index) + if (index == end) { + adds = add(adds, change, listChanges, featureMapEntry); + break; + } else { + FeatureMap.Entry fme = (FeatureMap.Entry) list.get(index); + if (feature == fme.getEStructuralFeature() && equality.compare(fme.getValue(), value) == 0) { + begin = remove(begin, index, change, listChanges, list); + ++begin; + adds = null; + break; + } + } + ++change; + } + remove(begin, end, change, listChanges, list); + } else { + /* + * Log simple value changes + */ + while (values.hasNext()) { + value = values.next(); + // values.remove(); + int index = list.indexOf(value); + switch (index) { + case -1: + adds = add(adds, change, listChanges, value); + break; + default: + remove(change, listChanges, list, 0, index); + case 0: + list = list.subList(++index, list.size()); + adds = null; + } + ++change; + } + remove(0, list.size(), change, listChanges, list); + } + } + } + + protected PropertyMapChanges propertyMapChanges/* = null */; + + /** + * Imports ChangeSummary 2-2. Resolves forward references from {@link #begin} and resumes logging if necessary. If it's invoked from + * patching/resolving, try to make it last since logging may be turned on. + * + * @see #begin + */ + public final ChangeSummary end() throws XMLStreamException { + if (forwardReferences != null) + for (Iterator iterator = forwardReferences.iterator(); iterator.hasNext();) { + /* + * Forward-referenced(unresolved) modified DataObject from begin(...) + */ + ForwardReference forwardReference = (ForwardReference) iterator.next(); + EObject referent = referent(forwardReference); + if (referent == null) + continue; // report error? + // iterator.remove(); + Type type = ((DataObject) referent).getType(); + Collection featureChanges = unsetProperties(referent, forwardReference.unset, type); + if (forwardReference.attributes != null) + for (Iterator attributes = forwardReference.attributes.iterator(); attributes.hasNext();) { + /* + * Log property old value as local attribute from begin(...) + */ + Attribute attribute = (Attribute) attributes.next(); + logAttributeChange(featureChanges, attribute.name, type, attribute.value, forwardReference.nameSpaces); + } + if (forwardReference.qualifiedAttributes != null) + for (Iterator attributes = forwardReference.qualifiedAttributes.iterator(); attributes.hasNext();) { + /* + * Log property old value as qualified/global attribute from begin(...) + */ + QualifiedAttribute attribute = (QualifiedAttribute) attributes.next(); + logAttributeChange(featureChanges, attribute.nameSpace, attribute.name, type, attribute.value, forwardReference.nameSpaces); + } + if (forwardReference.tags != null) { + if (propertyMapChanges != null) + propertyMapChanges.lists.clear(); + for (Iterator tags = forwardReference.tags.iterator(); tags.hasNext();) { + /* + * Log property old value as element from begin(...) + */ + Tag tag = (Tag) tags.next(); + Property property = getProperty(tag.nameSpace, tag.name.getLocalPart(), type); + if (tag.ref != null) + tag.value = referent(tag.ref, tag.nameSpaceContext); + // if (tag.value == null) report error? + else if (tag.events != null) + tag.value = value(play(tag)); + if (property.isMany()) { + Collection list; + if (propertyMapChanges == null) { + propertyMapChanges = new PropertyMapChanges(); + list = propertyMapChanges.newList(property); + } else + list = propertyMapChanges.get(property); + addPropertyChange( list, tag.value, property); + } else + logPropertyChange(featureChanges, property, tag.value); + } + if (propertyMapChanges != null) + logManyChanges(propertyMapChanges, referent, featureChanges); + } + } + if (objectChangesCollection != null) + for (Iterator iterator = objectChangesCollection.iterator(); iterator.hasNext();) { + /* + * Forward-referenced(unresolved) child DataObject from begin(...) + */ + ObjectChanges objectChanges = (ObjectChanges) iterator.next(); + if (objectChanges.elementChanges != null) + for (Iterator elementChanges = objectChanges.elementChanges.iterator(); elementChanges.hasNext();) { + ElementChange elementChange = (ElementChange) elementChanges.next(); + Object value = referent(elementChange); + if (value == null) + continue; // report error? + // iterator.remove(); + logPropertyChange(objectChanges.featureChanges, elementChange.containing, elementChange.containment, value); + } + if (objectChanges.lists != null) + logManyChanges(objectChanges, (EObject) ((Map.Entry) ((EStructuralFeature.Setting) objectChanges.featureChanges).getEObject()) + .getKey(), objectChanges.featureChanges); + } + if (logging) + changeSummary.resumeLogging(); + return changeSummary; + } +} diff --git a/branches/sdo-1.1.1-incubating/impl/src/main/java/org/apache/tuscany/sdo/util/resource/ChangeSummaryStreamSerializer.java b/branches/sdo-1.1.1-incubating/impl/src/main/java/org/apache/tuscany/sdo/util/resource/ChangeSummaryStreamSerializer.java new file mode 100644 index 0000000000..ef5af8ab57 --- /dev/null +++ b/branches/sdo-1.1.1-incubating/impl/src/main/java/org/apache/tuscany/sdo/util/resource/ChangeSummaryStreamSerializer.java @@ -0,0 +1,690 @@ +/** + * + * 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.Collection; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import javax.xml.namespace.NamespaceContext; +import javax.xml.namespace.QName; +import javax.xml.stream.XMLOutputFactory; +import javax.xml.stream.XMLStreamException; +import javax.xml.stream.XMLStreamReader; +import javax.xml.stream.XMLStreamWriter; + +import org.apache.tuscany.sdo.SDOPackage; +import org.apache.tuscany.sdo.helper.HelperContextImpl; +import org.apache.tuscany.sdo.helper.SDOAnnotations; +import org.apache.tuscany.sdo.helper.XSDHelperImpl; +import org.apache.tuscany.sdo.impl.ChangeSummaryImpl; +import org.apache.tuscany.sdo.model.ModelFactory; +import org.apache.tuscany.sdo.model.impl.ModelFactoryImpl; +import org.apache.tuscany.sdo.util.SDOUtil; +import org.eclipse.emf.common.util.EMap; +import org.eclipse.emf.ecore.EClassifier; +import org.eclipse.emf.ecore.EDataType; +import org.eclipse.emf.ecore.EObject; +import org.eclipse.emf.ecore.EStructuralFeature; +import org.eclipse.emf.ecore.change.ChangeDescription; +import org.eclipse.emf.ecore.change.ChangeKind; +import org.eclipse.emf.ecore.change.FeatureChange; +import org.eclipse.emf.ecore.change.ListChange; +import org.eclipse.emf.ecore.util.EcoreUtil; +import org.eclipse.emf.ecore.util.ExtendedMetaData; +import org.eclipse.emf.ecore.util.FeatureMap; +import org.eclipse.emf.ecore.util.FeatureMapUtil; +import org.eclipse.emf.ecore.xmi.XMLResource; + +import commonj.sdo.ChangeSummary; +import commonj.sdo.DataObject; +import commonj.sdo.Property; +import commonj.sdo.helper.XSDHelper; + +/** + * ChangeSummary StAX Serializer whose output conforms to the SDO Java/C++/PHP specifications. The instance isn't thread-safe, however it's safe to + * use the instance any times on the same thread. + */ +public class ChangeSummaryStreamSerializer { + private XMLStreamWriter writer; + + private String writeNamespace(String prefix, String nameSpace) throws XMLStreamException { + writer.writeNamespace(prefix, nameSpace); + writer.setPrefix(prefix, nameSpace); + return prefix; + } + + private int nsPrefixSuffix; + + private String prefix(String nameSpace, String preference) throws XMLStreamException { + if (preference != null) { + String bound = writer.getNamespaceContext().getNamespaceURI(preference); + if (bound == null) { + String prefix = writer.getPrefix(nameSpace); + return prefix == null ? writeNamespace(preference, nameSpace) : prefix/* or null */; + } + if (bound.equals(nameSpace)) + return preference; + } + Object automaticNsPrefix = writer.getProperty(XMLOutputFactory.IS_REPAIRING_NAMESPACES); + if (automaticNsPrefix != null && automaticNsPrefix.getClass() == Boolean.class // faster than instanceof since Boolean is final + && ((Boolean) automaticNsPrefix).booleanValue()) + return null; + String prefix = writer.getPrefix(nameSpace); + if (prefix != null) + return prefix; // or null + NamespaceContext nameSpaces = writer.getNamespaceContext(); + do + prefix = "CS" + nsPrefixSuffix++; + while (nameSpaces.getNamespaceURI(prefix) != null); + return writeNamespace(prefix, nameSpace); + } + + void writeGlobalAttribute(String prefix, String nameSpace, String name, String value) throws XMLStreamException { + prefix = prefix(nameSpace, prefix); + if (prefix == null) + writer.writeAttribute(nameSpace, name, value); + else + writer.writeAttribute(prefix, nameSpace, name, value); + } + + XSDHelper xsdHelper; + + protected final void writeAttribute(Property property, String value) throws XMLStreamException { + String name = xsdHelper.getLocalName(property), nameSpace = xsdHelper.getNamespaceURI(property); + // TODO "" for no-NameSpace global attribute + if (nameSpace == null) + writer.writeAttribute(name, value); + else + writeGlobalAttribute(null, nameSpace, name, value); + } + + private String lineBreak, indent, margin; + + private int nest; + + private void breakLine() throws XMLStreamException { + writer.writeCharacters(lineBreak); + + if (margin != null) + writer.writeCharacters(margin); + + if (indent != null) + for (int count = nest; count != 0; --count) + writer.writeCharacters(indent); + } + + private Map options; + + static private final String STRING_OPTION = "String option"; + + void startElement() throws XMLStreamException { + if (options == null) + return; + if (lineBreak == STRING_OPTION) + lineBreak = (String) options.get(SDOUtil.XML_SAVE_LineBreak); + if (lineBreak == null) + return; + if (margin == STRING_OPTION) + margin = (String) options.get(SDOUtil.XML_SAVE_MARGIN); + if (indent == STRING_OPTION) + indent = (String) options.get(SDOUtil.XML_SAVE_INDENT); + breakLine(); + } + + void writeStartElement(String prefix, String nameSpace, String name) throws XMLStreamException { + startElement(); + if (nameSpace == null) + writer.writeStartElement(name); + else { + prefix = prefix(nameSpace, prefix); + if (prefix == null) + writer.writeStartElement(nameSpace, name); + else + writer.writeStartElement(prefix, name, nameSpace); + } + } + + void writeStartElement(Property property) throws XMLStreamException { + ++nest; + writeStartElement(null, xsdHelper.getNamespaceURI(property),// TODO "" for no-NameSpace global element + xsdHelper.getLocalName(property)); + } + + static protected final String CREATE_ATTRIBUTE = "create", DELETE_ATTRIBUTE = "delete", LOGGING_ATTRIBUTE = "logging", REF_ATTRIBUTE = "ref", UNSET = "unset"; + + private StringBuffer step(String nameSpace, String name, StringBuffer path) throws XMLStreamException { + if (nameSpace != null) { + nameSpace = writer.getPrefix(nameSpace); + if (nameSpace != null && nameSpace.length() != 0) + return path.append(nameSpace).append(':').append(name); // *:name[namespace-uri()='nameSpace'] + } + return path.append(name); + } + + private StringBuffer step(Property containmentProperty, StringBuffer path) throws XMLStreamException { + return step(xsdHelper.getNamespaceURI(containmentProperty),// TODO "" for no-NameSpace global element + xsdHelper.getLocalName(containmentProperty), path); + } + + private StringBuffer step(Property containmentProperty) throws XMLStreamException { + return step(containmentProperty, new StringBuffer()); + } + + private DataObject dataObject; + + private StringBuffer step(Object container) throws XMLStreamException { + Property containmentProperty = dataObject.getContainmentProperty(); + StringBuffer step = step(containmentProperty); + if (containmentProperty.isMany() || ((EObject) dataObject).eContainingFeature() != containmentProperty) + step.append('[').append(((DataObject) container).getList(containmentProperty).indexOf(dataObject) + 1).append(']'); + return step; + } + + private String pathRootObject; + private DataObject rootObject; + + private EObject container(EObject object) { + final EObject container = object.eContainer(); + if (!(container instanceof DataObject)) + return null; + String name = extendedMetaData.getName(container.eClass()); + return name != null && name.length() == 0 // DocumentRoot + ? null : container; + } + + private String path() throws XMLStreamException { + if (pathRootObject == STRING_OPTION) + pathRootObject = options == null ? null : (String) options.get(OPTION_RootObject_PATH); + if (pathRootObject != null && dataObject == rootObject) + return null; + EObject container = container((EObject) dataObject); + if (container == null) + return null; + StringBuffer step = step(container); + while (true) { + String path = step.toString(); + dataObject = (DataObject) container; + if (pathRootObject != null && container == rootObject) + return path; + container = container(container); + if (container == null) + return path; + step = step(container).append('/').append(path); + } + } + + /* + * not to support DataGraph 3-1 private org.eclipse.emf.ecore.resource.Resource rootResource; + */ + + protected String rootElementNS; + + String rootElementName; + + ExtendedMetaData extendedMetaData; + + protected final String rootElementName() { + if (rootElementNS != null) + return rootElementName; + QName rootElement = (QName) options.get(OPTION_ROOT_ELEMENT); + if (rootElement != null) { + rootElementNS = rootElement.getNamespaceURI(); + return rootElementName = rootElement.getLocalPart(); + } + EStructuralFeature element = ((EObject) rootObject).eContainingFeature(); + if (element == null) { + rootElementNS = ""; + return rootElementName = "descendant-or-self::node()"; + } + rootElementNS = extendedMetaData.getNamespace(element); + if (rootElementNS == null) + rootElementNS = ""; + return rootElementName = extendedMetaData.getName(element); + } + + String ref() throws XMLStreamException { + /* + * not to support DataGraph 3-2 if (rootResource != null) return rootResource.getURIFragment((EObject) dataObject); + */ + String id = EcoreUtil.getID((EObject) dataObject); + if (id != null) + return id; + id = path(); + if (pathRootObject == null) + return id == null ? "#/" + rootElementName() // descendant-or-self::node() + : "#//" + id; + return id == null ? pathRootObject/* + "."*/ : pathRootObject + id; + } + + void writeRef(String ref) throws XMLStreamException { + writer.writeAttribute(SDOAnnotations.COMMONJ_SDO_NS, REF_ATTRIBUTE, ref); + } + + void writeRef() throws XMLStreamException { + writeRef(ref()); + } + + void writeEndElement(String lineBreak) throws XMLStreamException { + if (lineBreak != null) + breakLine(); + writer.writeEndElement(); + --nest; + } + + private StringBuffer pathDeleted/* = null */; + + private Collection modifiedDataObjects; + + private int lengthDeleted; + + private String changeSummaryElementNS, changeSummaryElementName; + + private ChangeSummary changeSummary; + + protected boolean skipDeletedModification(DataObject modifiedDataObject) { + return changeSummary.isDeleted(modifiedDataObject); + } + + String refDeleted() throws XMLStreamException { + String id = EcoreUtil.getID((EObject) dataObject); + if (id != null) + return id; + id = path(); // "dataObject" is updated too!! + DataObject deletedDataObject = dataObject; + + /* + * construct "#//...changeSummary/" + * output: pathDeleted + */ + if (lengthDeleted == -1) { + String path = pathRootObject == null ? "#//" : pathRootObject; + if (pathDeleted == null) + pathDeleted = new StringBuffer(path); + else + pathDeleted.replace(0, pathDeleted.length(), path); + dataObject = rootObject; + path = path(); + if (path != null) + pathDeleted.append(path).append('/'); + step(changeSummaryElementNS, changeSummaryElementName, pathDeleted).append('/'); + lengthDeleted = pathDeleted.length(); + } else + pathDeleted.delete(lengthDeleted, pathDeleted.length()); + + dataObject = changeSummary.getOldContainer(deletedDataObject); + Property containmentProperty = dataObject.getContainmentProperty(); + String name = containmentProperty == null ? rootElementName() : xsdHelper.getLocalName(containmentProperty); + int index = 1; + for (Iterator iterator = modifiedDataObjects.iterator(); iterator.hasNext();) { + DataObject modifiedDataObject = (DataObject) iterator.next(); + if (skipDeletedModification(modifiedDataObject)) + continue; + if (modifiedDataObject == dataObject) + break; + Property property = modifiedDataObject.getContainmentProperty(); + if (property == containmentProperty || name.equals(property == null ? rootElementName() : xsdHelper.getLocalName(property))) + ++index; + } + pathDeleted/*.append("*:")*/.append(name).append('[').append(index).append("]/"); + containmentProperty = changeSummary.getOldContainmentProperty(deletedDataObject); + // assert containmentProperty != null; + step(containmentProperty, pathDeleted); + Object f; + if (containmentProperty.isMany() + || (f = extendedMetaData.getAffiliation(((EObject) dataObject).eClass(), (EStructuralFeature) containmentProperty)) != null + && f != containmentProperty) + pathDeleted.append('[').append( + ((List) changeSummary.getOldValue(dataObject, containmentProperty).getValue()).indexOf(deletedDataObject) + 1).append(']'); + if (id != null) + pathDeleted.append('/').append(id); + return pathDeleted.toString(); + } + + static String convertToString(Property property, Object value) { + return EcoreUtil.convertToString((EDataType) property.getType(), value); + } + + void writeRefDeleted() throws XMLStreamException { + writeRef(refDeleted()); + } + + protected final void writeDeletedObject(Property property) throws XMLStreamException { + ++nest; + startElement(); + --nest; + DataObject oldDataObject = ((ChangeSummaryImpl)changeSummary).getOldDataObject(dataObject); + XMLStreamReader reader = new DataObjectXMLStreamReader(property, oldDataObject, null, xsdHelper); + new XMLStreamSerializer().serialize(new XMLDocumentStreamReader(reader), writer); + } + + static public final Object ChangeSummary_TYPE = ((ModelFactoryImpl) ModelFactory.INSTANCE).getChangeSummaryType(); + + Collection deletedDataObjects; + + protected final void writeElement(Object value, Property property) throws XMLStreamException { + if (value == null) { + writeStartElement(property); + writeGlobalAttribute(ExtendedMetaData.XSI_PREFIX, ExtendedMetaData.XSI_URI, XMLResource.NIL, "true"); + writeEndElement(null); + } else if (value instanceof DataObject) { + dataObject = (DataObject) value; + if (!changeSummary.isDeleted(dataObject)) { + writeStartElement(property); + writeRef(); + writeEndElement(null); + } else if (property.isContainment() && deletedDataObjects.contains(dataObject)) { + writeDeletedObject(property); + } else { + writeStartElement(property); + writeRefDeleted(); + writeEndElement(null); + } + } else { + Object type = property.getType(); + if (type == ChangeSummary_TYPE) + return; + writeStartElement(property); + writer.writeCharacters(EcoreUtil.convertToString((EDataType) type, value)); + writeEndElement(null); + } + } + + protected final void writeElement(Object value) throws XMLStreamException { + FeatureMap.Entry entry = (FeatureMap.Entry) value; + writeElement(entry.getValue(), (Property)entry.getEStructuralFeature()); + } + + static protected List optimize(List values, Object featureChange, int size) { + int fromIndex = size, toIndex = 0; + for (Iterator changes = ((FeatureChange) featureChange).getListChanges().iterator(); changes.hasNext();) { + ListChange change = (ListChange) changes.next(); + Object kind = change.getKind(); + if (kind == ChangeKind.MOVE_LITERAL) + return values; + int index = change.getIndex(); + if (kind == ChangeKind.ADD_LITERAL) { + if (index == 0) { + fromIndex = 0; + if (toIndex == 0) + toIndex = 1; + } else { + int to = index; + if (--index < fromIndex) + fromIndex = index; + if (++to > toIndex) + toIndex = to; + else if (to < toIndex) + ++toIndex; + } + ++size; + } else { + --size; + if (index < fromIndex) + fromIndex = index; + if (index < toIndex) + --toIndex; + else if (index > toIndex && index != size) + toIndex = index; + } + } + return values.subList(fromIndex, toIndex); + } + + static protected final Object CHANGE_SUMMARY = SDOPackage.eINSTANCE.getChangeSummary(); + + /** + * Root Object path String such as "#", etc. Absent/null is the default (automatic computation) + */ + static public final String OPTION_RootObject_PATH = "RootObject path", + /** + * Boolean to optimize sequence/list/array. Absent/null/Boolean.FALSE is the default (no optimization) + */ + OPTION_OPTIMIZE_LIST = "optimize sequence/list/array", + /** + * Element QName if the changeSummary Root Object is a XML root; the NameSpace can be empty, never null; the prefix is ignored. + * Absent/null is the default (automatic computation from DocumentRoot if any) + */ + OPTION_ROOT_ELEMENT = "Root Element"; + + /** + * Exports ChangeSummary + * + * @param changeSummary + * Never null + * @param changeSummaryElement + * changeSummary element; the NameSpace can be empty if no NameSpace, or null if local element; the prefix can be null(no preference) + * @param writer + * Never null + * @param options + * {@link SDOUtil#XML_SAVE_LineBreak} (absence/null is the default i.e. no Line Breaking), {@link SDOUtil#XML_SAVE_INDENT} (absence/null is the default i.e. no indentation), {@link SDOUtil#XML_SAVE_MARGIN}, {@link #OPTION_RootObject_PATH}, {@link #OPTION_OPTIMIZE_LIST} and XMLResource.OPTION_EXTENDED_META_DATA; can be null or empty + */ + public final void saveChangeSummary(ChangeSummary changeSummary, QName changeSummaryElement, XMLStreamWriter writer, Map options) + throws XMLStreamException { + /* + * 6-1. Group created, deleted and modified DataObjects + * input: changeSummary output: createdDataObjects, deletedDataObjects & modifiedDataObjects + * implement: careful if compute from changeSummary.getChangedDataObjects() since it also includes children of deleted objects (thank Frank) + */ + if (changeSummary.isLogging()) + ((ChangeSummaryImpl) changeSummary).summarize(); + ChangeDescription changeDescription = (ChangeDescription) changeSummary; + Iterator createdDataObjects = changeDescription.getObjectsToDetach().iterator(); + deletedDataObjects = changeDescription.getObjectsToAttach(); + EMap objectChanges = changeDescription.getObjectChanges(); + modifiedDataObjects = objectChanges.keySet(); // may contain DO(s) from createdDataObjects and/or deletedDataObjects + + /* + * 6-2. Prepare to compute (X)Path + * input: changeSummary + * output: rootResource + */ + /*not to support DataGraph 3-3 + Object dataGraph = changeSummary.getDataGraph(); + if (dataGraph == null) { + DataObject rootObject = changeSummary.getRootObject(); + // assert rootObject != null; + rootResource = rootObject.getContainer() == null ? ((EObject) rootObject).eResource() // Can be null since this *StAX* writer does NOT + // require rootObject contained by an *EMF* Resource + : null; // eResource() direct content may not necessarily always be the XML document + } else + // assert dataGraph instanceof DataGraphImpl; + rootResource = ((org.apache.tuscany.sdo.impl.DataGraphImpl) dataGraph).getRootResource(); */ + + /* + * 6-2. Start ChangeSummary element + * input: writer, options, elementCS, changeSummary & changeDescription (6-1) + */ + nsPrefixSuffix = 0; + this.writer = writer; + this.options = options; + lineBreak = ""; + indent = margin = pathRootObject = STRING_OPTION; + nest = 0; + changeSummaryElementNS = changeSummaryElement.getNamespaceURI(); + changeSummaryElementName = changeSummaryElement.getLocalPart(); + writeStartElement(changeSummaryElement.getPrefix(), changeSummaryElementNS, changeSummaryElementName); + lineBreak = STRING_OPTION; + rootObject = changeSummary.getRootObject(); + extendedMetaData = (ExtendedMetaData) options.get(XMLResource.OPTION_EXTENDED_META_DATA); + if (extendedMetaData == null) + { + extendedMetaData = ExtendedMetaData.INSTANCE; + xsdHelper = XSDHelper.INSTANCE; + } + else{ + xsdHelper = (new HelperContextImpl(extendedMetaData, false)).getXSDHelper(); + } + Property declaration = changeSummaryElementNS == null + ? rootObject.getType().getProperty(changeSummaryElementName) + : xsdHelper.getGlobalProperty(changeSummaryElementNS, changeSummaryElementName, true); + if (declaration != null) + { + EClassifier type = changeDescription.eClass(); + if (type != declaration.getType() && type != CHANGE_SUMMARY) + writeGlobalAttribute(ExtendedMetaData.XSI_PREFIX, ExtendedMetaData.XSI_URI, XMLResource.TYPE, new StringBuffer(prefix(extendedMetaData.getNamespace(type), null)) + .append(':').append(extendedMetaData.getName(type)).toString()); + } + + /* + * 6-3. "create" attribute + * input: createdDataObjects (6-1), rootResource (6-2), changeSummary & writer + */ + rootElementNS = null; + this.changeSummary = changeSummary; + if (createdDataObjects.hasNext()) { + StringBuffer buffer = new StringBuffer(); + while (true) { + dataObject = (DataObject) createdDataObjects.next(); + buffer.append(ref()); + if (!createdDataObjects.hasNext()) + break; + buffer.append(' '); + } + writer.writeAttribute(CREATE_ATTRIBUTE, buffer.toString()); + } + + /* + * 6-4. "delete" attribute + * input: deletedDataObjects (6-1), modifiedDataObjects (6-1) & writer + */ + Iterator iterator = deletedDataObjects.iterator(); + if (iterator.hasNext()) { + lengthDeleted = -1; + StringBuffer buffer = null; + do { + dataObject = (DataObject) iterator.next(); + if (skipDeletedModification(changeSummary.getOldContainer(dataObject))) + continue; + if (buffer == null) + buffer = new StringBuffer(); + else + buffer.append(' '); + buffer.append(refDeleted()); + } while (iterator.hasNext()); + writer.writeAttribute(DELETE_ATTRIBUTE, buffer.toString()); + } + + /* + * 6-5. "logging" attribute + * input: changeSummary & writer + */ + writer.writeAttribute(LOGGING_ATTRIBUTE, changeSummary.isLogging() ? "true" : "false"); + + /* + * 6-6. Modified DataObjects + * input: modifiedDataObjects (6-1), rootResource (6-2), changeSummary & writer + */ + iterator = modifiedDataObjects.iterator(); + if (iterator.hasNext()) { + boolean optimizeList; + if (options == null) + optimizeList = false; + else + { + Object option = options.get(OPTION_OPTIMIZE_LIST); + optimizeList = option == null ? false : ((Boolean)option).booleanValue(); + } + prefix(SDOAnnotations.COMMONJ_SDO_NS, SDOPackage.eNS_PREFIX); + do { + DataObject dataObject = (DataObject) iterator.next(); + if (skipDeletedModification(dataObject)) + continue; + Property containmentProperty = dataObject.getContainmentProperty(); + if (containmentProperty == null) { + ++nest; + startElement(); + rootElementName(); + writer.writeStartElement(rootElementNS, rootElementName); + } else + writeStartElement(containmentProperty); + this.dataObject = dataObject; + writeRef(); + + String lineBreak = null; + Collection oldValues = (Collection) objectChanges.get(dataObject); // changeSummary.getOldValues repeats Sequence changes + Iterator settings = oldValues.iterator(); + if (settings.hasNext()) { + do { + ChangeSummary.Setting oldValue = (ChangeSummary.Setting) settings.next(); + if (oldValue.isSet()) + continue; + StringBuffer unset = step(oldValue.getProperty()); + while (settings.hasNext()) { + oldValue = (ChangeSummary.Setting) settings.next(); + if (!oldValue.isSet()) + step(oldValue.getProperty(), unset.append(' ')); + } + writer.writeAttribute(SDOAnnotations.COMMONJ_SDO_NS, UNSET, unset.toString()); + break; + } while (settings.hasNext()); + for (settings = oldValues.iterator(); settings.hasNext();) { + ChangeSummary.Setting oldValue = (ChangeSummary.Setting) settings.next(); + Property property = oldValue.getProperty(); + if (oldValue.isSet() && xsdHelper.isAttribute(property)) + // assert ! property.isMany(); + writeAttribute(property, convertToString(property, oldValue.getValue())); + } + for (settings = oldValues.iterator(); settings.hasNext();) { + ChangeSummary.Setting oldValue = (ChangeSummary.Setting) settings.next(); + Property property = oldValue.getProperty(); + if (!xsdHelper.isAttribute(property)) + if (property.isMany()) { + Object value = oldValue.getValue(); + List list = (List) value; + if (FeatureMapUtil.isFeatureMap((EStructuralFeature) property)) { + if (optimizeList) + list = optimize(list, oldValue, dataObject.getSequence(property).size()); + Iterator values = list.iterator(); + if (!values.hasNext()) + continue; + do + writeElement(values.next()); + while (values.hasNext()); + } else { + if (optimizeList) + list = optimize(list, oldValue, dataObject.getList(property).size()); + Iterator values = list.iterator(); + if (!values.hasNext()) + continue; + do + writeElement(values.next(), property); + while (values.hasNext()); + } + lineBreak = this.lineBreak; + } else if (oldValue.isSet()) { + Object value = oldValue.getValue(); + if (value instanceof FeatureMap.Entry) + writeElement(value); + else + writeElement(value, property); + lineBreak = this.lineBreak; + } + } + } + writeEndElement(lineBreak); + } while (iterator.hasNext()); + writeEndElement(lineBreak); + } else + writeEndElement(null); + writer.flush(); + } +} diff --git a/branches/sdo-1.1.1-incubating/impl/src/main/java/org/apache/tuscany/sdo/util/resource/DataObjectXMLStreamReader.java b/branches/sdo-1.1.1-incubating/impl/src/main/java/org/apache/tuscany/sdo/util/resource/DataObjectXMLStreamReader.java new file mode 100644 index 0000000000..06099fe6a3 --- /dev/null +++ b/branches/sdo-1.1.1-incubating/impl/src/main/java/org/apache/tuscany/sdo/util/resource/DataObjectXMLStreamReader.java @@ -0,0 +1,1579 @@ +/** + * + * 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.Arrays; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import javax.xml.namespace.NamespaceContext; +import javax.xml.namespace.QName; +import javax.xml.stream.Location; +import javax.xml.stream.XMLStreamException; + +import org.apache.tuscany.sdo.impl.AttributeImpl; +import org.apache.tuscany.sdo.impl.ReferenceImpl; +import org.apache.tuscany.sdo.util.SDOUtil; +import org.eclipse.emf.ecore.EClass; +import org.eclipse.emf.ecore.EDataType; +import org.eclipse.emf.ecore.EReference; +import org.eclipse.emf.ecore.EStructuralFeature; +import org.eclipse.emf.ecore.util.ExtendedMetaData; + +import commonj.sdo.DataObject; +import commonj.sdo.Property; +import commonj.sdo.Sequence; +import commonj.sdo.Type; +import commonj.sdo.helper.TypeHelper; +import commonj.sdo.helper.XMLDocument; +import commonj.sdo.helper.XSDHelper; + +public class DataObjectXMLStreamReader implements XMLFragmentStreamReader { + private static final QName XSI_TYPE_QNAME = new QName("http://www.w3.org/2001/XMLSchema-instance", "type", "xsi"); + private Property rootElement = null; + private DataObject dataObject; + + private String rootElementURI; + + private String rootElementName; + + private DataObject serializeRoot; + + private TypeHelper typeHelper; + + private XSDHelper xsdHelper; + + private Map.Entry[] properties; + + private Map.Entry[] attributes; + + private QName elementQName; + + // we always create a new namespace context + private NameSpaceContext namespaceContext; + + private Map declaredNamespaceMap = new HashMap(); + + // states for this pullparser - it can only have three states + private static final int START_ELEMENT_STATE = 0; + + private static final int END_ELEMENT_STATE = 1; + + private static final int DELEGATED_STATE = 2; + + private static final int TEXT_STATE = 3; + + // integer field that keeps the state of this + // parser. + private int state = START_ELEMENT_STATE; + + // reference to the child reader + private XMLFragmentStreamReader childReader; + + // current property index + // initialized at zero + private int currentPropertyIndex = 0; + + public DataObjectXMLStreamReader(DataObject dataObject, String rootElmentURI, String rootElementName) { + this(dataObject, rootElmentURI, rootElementName, null, null); + } + + public DataObjectXMLStreamReader(DataObject dataObject, String rootElmentURI, String rootElementName, TypeHelper typeHelper) { + this(dataObject, rootElmentURI, rootElementName, typeHelper, null); + } + + public DataObjectXMLStreamReader(DataObject dataObject, String rootElmentURI, String rootElementName, TypeHelper typeHelper, XSDHelper xsdHelper) { + this.dataObject = dataObject; + this.rootElementURI = rootElmentURI; + this.rootElementName = rootElementName; + this.serializeRoot = dataObject; + this.typeHelper = typeHelper == null ? TypeHelper.INSTANCE : typeHelper; + this.xsdHelper = (xsdHelper != null) ? xsdHelper : ((typeHelper == null) ? XSDHelper.INSTANCE : SDOUtil.createXSDHelper(typeHelper)); + rootElement = this.xsdHelper.getGlobalProperty(rootElmentURI, rootElementName, true); + namespaceContext = new NameSpaceContext(); + populateProperties(); + } + + protected DataObjectXMLStreamReader(TypeHelper typeHelper, XSDHelper xsdHelper, Property rootElement, DataObject dataObject) { + this.typeHelper = typeHelper == null ? TypeHelper.INSTANCE : typeHelper; + this.xsdHelper = (xsdHelper != null) ? xsdHelper : ((typeHelper == null) ? XSDHelper.INSTANCE : SDOUtil.createXSDHelper(typeHelper)); + this.rootElement = rootElement; + this.dataObject = dataObject; + this.rootElementURI = xsdHelper.getNamespaceURI(rootElement); + this.rootElementName = xsdHelper.getLocalName(rootElement); + } + + protected DataObjectXMLStreamReader(TypeHelper typeHelper, XSDHelper xsdHelper, Property rootElement, DataObject dataObject, DataObject serializeRoot) { + this.typeHelper = typeHelper == null ? TypeHelper.INSTANCE : typeHelper; + this.xsdHelper = (xsdHelper != null) ? xsdHelper : ((typeHelper == null) ? XSDHelper.INSTANCE : SDOUtil.createXSDHelper(typeHelper)); + this.rootElement = rootElement; + this.dataObject = dataObject; + this.serializeRoot = serializeRoot; + this.rootElementURI = xsdHelper.getNamespaceURI(rootElement); + this.rootElementName = xsdHelper.getLocalName(rootElement); + } + public DataObjectXMLStreamReader(Property rootElement, DataObject dataObject, TypeHelper typeHelper, XSDHelper xsdHelper) { + this(typeHelper, xsdHelper, rootElement, dataObject); + namespaceContext = new NameSpaceContext(); + populateProperties(); + } + + public DataObjectXMLStreamReader(XMLDocument document, TypeHelper typeHelper) { + this.dataObject = document.getRootObject(); + this.rootElementName = document.getRootElementName(); + this.rootElementURI = document.getRootElementURI(); + this.serializeRoot = this.dataObject; + this.typeHelper = typeHelper; + this.xsdHelper = typeHelper == null ? XSDHelper.INSTANCE : SDOUtil.createXSDHelper(typeHelper); + namespaceContext = new NameSpaceContext(); + populateProperties(); + } + + /* + * we need to pass in a namespace context since when delegated, we've no idea of the current namespace context. So it needs to be passed on here! + */ + protected DataObjectXMLStreamReader(QName elementQName, Map.Entry[] properties, Map.Entry[] attributes) { + // validate the lengths, since both the arrays are supposed + // to have + this.properties = properties; + this.elementQName = elementQName; + this.attributes = attributes; + namespaceContext = new NameSpaceContext(); + } + + private void addProperty(Property property, Object value, List propertyList) { + if (property.isMany() && property.getContainingType().isOpen() && value instanceof Sequence) { + addSequenceValue(propertyList, (Sequence) value); + } else if (SDOUtil.isMany(property, dataObject) && value instanceof List) { + addListValue(propertyList, property, (List) value); + } else { + // Complex Type + addSingleValue(propertyList, property, value); + } + } + + void addProperty(List propertyList, Property property, Object value, Object type) { + if (!isTransient(property, type)) + addProperty(property, value, propertyList); + } + + private void addSequenceValue(List elements, Sequence seq) { + if (seq != null && seq.size() > 0) { + for (int j = 0; j < seq.size(); j++) { + Object o = seq.getValue(j); + Property p = seq.getProperty(j); + addSingleValue(elements, p, o); + } + } + } + + static private boolean isTransient(Property property, Object type) { + // HACK: We need some SDOUtil extension to understand a property is derived + EStructuralFeature feature = (EStructuralFeature) property; + if (ExtendedMetaData.INSTANCE.getGroup(feature) != null) + return false; + feature = ExtendedMetaData.INSTANCE.getAffiliation((EClass) type, feature); + if (feature != null && feature != property) + return false; + if (property instanceof ReferenceImpl) { + ReferenceImpl r = (ReferenceImpl) property; + if (r.isTransient()) + return true; + EReference opposite = r.getEOpposite(); + if (opposite != null && opposite.isContainment()) { + return true; + } + } else if (property instanceof AttributeImpl) { + AttributeImpl a = (AttributeImpl) property; + if (a.isTransient()) + return true; + EDataType d = (EDataType) a.getEType(); + if (!d.isSerializable()) { + return true; + } + } + return false; + } + + private void addListValue(List propertyList, Property property, List objList) { + if (objList != null) { + for (int j = 0; j < objList.size(); j++) { + Object object = objList.get(j); + addSingleValue(propertyList, property, object); + } + } + } + + private void addSingleValue(List propertyList, Property property, Object value) { + String uri = xsdHelper.getNamespaceURI(property); + String name = xsdHelper.getLocalName(property); + QName qname = namespaceContext.createQName(uri, name); + Type propertyType = property.getType(); + + if (property.getName().equals("value") && uri == null && name.equals(":0")) { + // "value" is special property containing the value of simpleContent + Map.Entry entry = new NameValuePair(ELEMENT_TEXT, value); + propertyList.add(entry); + } else + + // FIXME: We need to deal with non-containment properties + if (value == null) { + // Creating xsi:nil="true" for elements + registerNamespace("xsi", "http://www.w3.org/2001/XMLSchema-instance"); + Map.Entry entry = new NameValuePair(qname, null); + propertyList.add(entry); + } else if (propertyType.isDataType()) { + Map.Entry entry = new NameValuePair(qname, SDOUtil.convertToString(propertyType, value)); + propertyList.add(entry); + } else if (property.isContainment() && value == serializeRoot) { + // do not create the childReader because a containmentCycle exists and this is the second + // time this DataObject has been encountered + } else { + DataObjectXMLStreamReader childReader = new DataObjectXMLStreamReader(typeHelper, xsdHelper, property, (DataObject) value, serializeRoot); + childReader.namespaceContext = namespaceContext; + childReader.populateProperties(); + childReader.rootElement = property; + Map.Entry entry = new NameValuePair(qname, childReader); + propertyList.add(entry); + } + } + + public void populateProperties() { + /*declaredNamespaceMap.put("xml", "http://www.w3.org/XML/1998/namespace"); + declaredNamespaceMap.put("xmlns", "http://www.w3.org/2000/xmlns/"); + declaredNamespaceMap.put("xsi", "http://www.w3.org/2001/XMLSchema-instance"); + */ + + if (properties != null) + return; + if (elementQName == null) + elementQName = namespaceContext.createQName(this.rootElementURI, this.rootElementName); + else + elementQName = namespaceContext.createQName(elementQName.getNamespaceURI(), elementQName.getLocalPart()); + + List elementList = new ArrayList(); + List attributeList = new ArrayList(); + if (dataObject == null) { + return; + } + Type type = dataObject.getType(); + + // Add xsi:type if rootElement doesn't exist or the type is different + if (rootElement == null || (rootElement != null && rootElement.getType() != type)) { + // FIXME: XSDHelper.getLocalName() for annoymous type returns null? + String typeName = xsdHelper.getLocalName(type); + if (typeName != null) { + QName realTypeName = namespaceContext.createQName(type.getURI(), typeName); + String typeQName = realTypeName.getPrefix() + ":" + realTypeName.getLocalPart(); + registerNamespace("xsi", "http://www.w3.org/2001/XMLSchema-instance"); + registerNamespace(realTypeName.getPrefix(), realTypeName.getNamespaceURI()); + attributeList.add(new NameValuePair(XSI_TYPE_QNAME, typeQName)); + } + } + + if (type.isSequenced()) { + Sequence sequence = dataObject.getSequence(); + for (int i = 0; i < sequence.size(); i++) { + Property property = sequence.getProperty(i); + Object value = sequence.getValue(i); + if (property == null) { + // property == null for text in mixed content + elementList.add(new NameValuePair(ELEMENT_TEXT, value)); + } else { + addProperty(property, value, elementList); + } + } + // Attributes are not in the sequence + List properties = dataObject.getInstanceProperties(); + for (Iterator i = properties.iterator(); i.hasNext();) { + Property property = (Property) i.next(); + if (xsdHelper.isAttribute(property)) { + // FIXME: How to handle nilable=true? + if (!dataObject.isSet(property)) + continue; + Object value = dataObject.get(property); + addProperty(attributeList, property, value, type); + } + } + } else { + List properties = dataObject.getInstanceProperties(); + for (Iterator i = properties.iterator(); i.hasNext();) { + Property property = (Property) i.next(); + // FIXME: How to handle nilable=true? + if (!dataObject.isSet(property)) + continue; + Object value = dataObject.get(property); + if (xsdHelper.isAttribute(property)) + addProperty(attributeList, property, value, type); + else + addProperty(elementList, property, value, type); + } + } + properties = (Map.Entry[]) elementList.toArray(new Map.Entry[0]); + attributes = (Map.Entry[]) attributeList.toArray(new Map.Entry[0]); + } + + public DataObject getDataObject() { + return dataObject; + } + + /** + * we need to split out the calling to the populate namespaces seperately since this needs to be done *after* setting the parent namespace + * context. We cannot assume it will happen at construction! + */ + public void init() { + // here we have an extra issue to attend to. we need to look at the + // prefixes and uris (the combination) and populate a hashmap of + // namespaces. The hashmap of namespaces will be used to serve the + // namespace context + + populateNamespaceContext(); + } + + /** + * + * @param key + * @return + * @throws IllegalArgumentException + */ + public Object getProperty(String key) throws IllegalArgumentException { + if (state == START_ELEMENT_STATE || state == END_ELEMENT_STATE) { + return null; + } else if (state == TEXT_STATE) { + return null; + } else if (state == DELEGATED_STATE) { + return childReader.getProperty(key); + } else { + return null; + } + + } + + public int next() throws XMLStreamException { + return updateStatus(); + } + + public void require(int i, String string, String string1) throws XMLStreamException { + throw new UnsupportedOperationException(); + } + + /** + * todo implement the right contract for this + * + * @return + * @throws XMLStreamException + */ + public String getElementText() throws XMLStreamException { + if (state == DELEGATED_STATE) { + return childReader.getElementText(); + } else { + return null; + } + + } + + /** + * todo implement this + * + * @return + * @throws XMLStreamException + */ + public int nextTag() throws XMLStreamException { + return 0; + } + + /** + * @return + * @throws XMLStreamException + */ + public boolean hasNext() throws XMLStreamException { + if (state == DELEGATED_STATE) { + if (childReader.isEndOfFragment()) { + // the child reader is done. We shouldn't be getting the + // hasnext result from the child pullparser then + return true; + } else { + return childReader.hasNext(); + } + } else { + return (state == START_ELEMENT_STATE || state == TEXT_STATE); + + } + } + + public void close() throws XMLStreamException { + // do nothing here - we have no resources to free + } + + public String getNamespaceURI(String prefix) { + return namespaceContext.getNamespaceURI(prefix); + } + + public boolean isStartElement() { + if (state == START_ELEMENT_STATE) { + return true; + } else if (state == END_ELEMENT_STATE) { + return false; + } + return childReader.isStartElement(); + } + + public boolean isEndElement() { + if (state == START_ELEMENT_STATE) { + return false; + } else if (state == END_ELEMENT_STATE) { + return true; + } + return childReader.isEndElement(); + } + + public boolean isCharacters() { + if (state == START_ELEMENT_STATE || state == END_ELEMENT_STATE) { + return false; + } + return childReader.isCharacters(); + } + + public boolean isWhiteSpace() { + if (state == START_ELEMENT_STATE || state == END_ELEMENT_STATE) { + return false; + } + return childReader.isWhiteSpace(); + } + + // ///////////////////////////////////////////////////////////////////////// + // / attribute handling + // ///////////////////////////////////////////////////////////////////////// + + public String getAttributeValue(String nsUri, String localName) { + + int attribCount = getAttributeCount(); + String returnValue = null; + QName attribQualifiedName; + for (int i = 0; i < attribCount; i++) { + attribQualifiedName = getAttributeName(i); + if (nsUri == null) { + if (localName.equals(attribQualifiedName.getLocalPart())) { + returnValue = getAttributeValue(i); + break; + } + } else { + if (localName.equals(attribQualifiedName.getLocalPart()) && nsUri.equals(attribQualifiedName.getNamespaceURI())) { + returnValue = getAttributeValue(i); + break; + } + } + + } + + return returnValue; + } + + public int getAttributeCount() { + return (state == DELEGATED_STATE) ? childReader.getAttributeCount() + : ((attributes != null) && (state == START_ELEMENT_STATE) ? attributes.length : 0); + } + + /** + * @param i + * @return + */ + public QName getAttributeName(int i) { + if (state == DELEGATED_STATE) { + return childReader.getAttributeName(i); + } else if (state == START_ELEMENT_STATE) { + if (attributes == null) { + return null; + } else { + if ((i >= (attributes.length)) || i < 0) { // out of range + return null; + } else { + // get the attribute pointer + Object attribPointer = attributes[i].getKey(); + // case one - attrib name is null + // this should be the pointer to the OMAttribute then + if (attribPointer instanceof String) { + return new QName((String) attribPointer); + } else if (attribPointer instanceof QName) { + return (QName) attribPointer; + } else { + return null; + } + } + } + } else { + throw new IllegalStateException();// as per the api contract + } + + } + + public String getAttributeNamespace(int i) { + if (state == DELEGATED_STATE) { + return childReader.getAttributeNamespace(i); + } else if (state == START_ELEMENT_STATE) { + QName name = getAttributeName(i); + if (name == null) { + return null; + } else { + return name.getNamespaceURI(); + } + } else { + throw new IllegalStateException(); + } + } + + public String getAttributeLocalName(int i) { + if (state == DELEGATED_STATE) { + return childReader.getAttributeLocalName(i); + } else if (state == START_ELEMENT_STATE) { + QName name = getAttributeName(i); + if (name == null) { + return null; + } else { + return name.getLocalPart(); + } + } else { + throw new IllegalStateException(); + } + } + + public String getAttributePrefix(int i) { + if (state == DELEGATED_STATE) { + return childReader.getAttributePrefix(i); + } else if (state == START_ELEMENT_STATE) { + QName name = getAttributeName(i); + if (name == null) { + return null; + } else { + return name.getPrefix(); + } + } else { + throw new IllegalStateException(); + } + } + + public String getAttributeType(int i) { + return null; // not supported + } + + public String getAttributeValue(int i) { + if (state == DELEGATED_STATE) { + return childReader.getAttributeValue(i); + } else if (state == START_ELEMENT_STATE) { + if (attributes == null) { + return null; + } else { + if ((i >= (attributes.length)) || i < 0) { // out of range + return null; + } else { + // get the attribute pointer + Object attribPointer = attributes[i].getKey(); + Object omAttribObj = attributes[i].getValue(); + + // Handle xsd:QName/SDO URI type property + // Before save, convert # to : + String propertyName = null; + if (attribPointer instanceof String) + propertyName = (String)attribPointer; + else if (attribPointer instanceof QName) + propertyName = ((QName)attribPointer).getLocalPart(); + else + return null; + + String attrValue = (String)omAttribObj; + + Property property = dataObject.getType().getProperty(propertyName); + // property can be null for xsi:type + if (property != null && "URI".equals(property.getType().getName())) { + String namespace = null; + String localPart = attrValue; + + int index = attrValue.indexOf('#'); + if (index == -1) { + return localPart; + } + else { + namespace = localPart.substring(0, index); + localPart = localPart.substring(index+1); + + String prefix = namespaceContext.getPrefix(namespace); + if (prefix == null || prefix.length() == 0) + return localPart; + + return prefix + ":" + localPart; + } + } + else { + return attrValue; + } + } + } + } else { + throw new IllegalStateException(); + } + + } + + public boolean isAttributeSpecified(int i) { + return false; // not supported + } + + // ///////////////////////////////////////////////////////////////////////// + // //////////// end of attribute handling + // ///////////////////////////////////////////////////////////////////////// + + // ////////////////////////////////////////////////////////////////////////// + // //////////// namespace handling + // ////////////////////////////////////////////////////////////////////////// + + public int getNamespaceCount() { + if (state == DELEGATED_STATE) { + return childReader.getNamespaceCount(); + } else { + return declaredNamespaceMap.size(); + } + } + + /** + * @param i + * @return + */ + public String getNamespacePrefix(int i) { + if (state == DELEGATED_STATE) { + return childReader.getNamespacePrefix(i); + } else if (state != TEXT_STATE) { + // order the prefixes + String[] prefixes = makePrefixArray(); + if ((i >= prefixes.length) || (i < 0)) { + return null; + } else { + return prefixes[i]; + } + + } else { + throw new IllegalStateException(); + } + + } + + /** + * Get the prefix list from the hastable and take that into an array + * + * @return + */ + private String[] makePrefixArray() { + String[] prefixes = (String[]) declaredNamespaceMap.keySet().toArray(new String[declaredNamespaceMap.size()]); + Arrays.sort(prefixes); + return prefixes; + } + + public String getNamespaceURI(int i) { + if (state == DELEGATED_STATE) { + return childReader.getNamespaceURI(i); + } else if (state != TEXT_STATE) { + String namespacePrefix = getNamespacePrefix(i); + return namespacePrefix == null ? null : (String) declaredNamespaceMap.get(namespacePrefix); + } else { + throw new IllegalStateException(); + } + + } + + public NamespaceContext getNamespaceContext() { + if (state == DELEGATED_STATE) { + return childReader.getNamespaceContext(); + } else { + return namespaceContext; + } + + } + + // ///////////////////////////////////////////////////////////////////////// + // /////// end of namespace handling + // ///////////////////////////////////////////////////////////////////////// + + public int getEventType() { + if (state == START_ELEMENT_STATE) { + return START_ELEMENT; + } else if (state == END_ELEMENT_STATE) { + return END_ELEMENT; + } else { // this is the delegated state + return childReader.getEventType(); + } + + } + + public String getText() { + if (state == DELEGATED_STATE) { + return childReader.getText(); + } else if (state == TEXT_STATE) { + return (String) properties[currentPropertyIndex - 1].getValue(); + } else { + throw new IllegalStateException(); + } + } + + public char[] getTextCharacters() { + if (state == DELEGATED_STATE) { + return childReader.getTextCharacters(); + } else if (state == TEXT_STATE) { + return getTextData(); + } else { + throw new IllegalStateException(); + } + } + + private char[] getTextData() { + return properties[currentPropertyIndex - 1].getValue() == null ? new char[0] : ((String) properties[currentPropertyIndex - 1].getValue()) + .toCharArray(); + } + + private int copy(int sourceStart, char[] target, int targetStart, int length) { + char[] source = getTextData(); + 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 getTextCharacters(int i, char[] chars, int i1, int i2) throws XMLStreamException { + if (state == DELEGATED_STATE) { + return childReader.getTextCharacters(i, chars, i1, i2); + } else if (state == TEXT_STATE) { + return copy(i, chars, i1, i2); + } else { + throw new IllegalStateException(); + } + } + + public int getTextStart() { + if (state == DELEGATED_STATE) { + return childReader.getTextStart(); + } else if (state == TEXT_STATE) { + return 0;// assume text always starts at 0 + } else { + throw new IllegalStateException(); + } + } + + public int getTextLength() { + if (state == DELEGATED_STATE) { + return childReader.getTextLength(); + } else if (state == TEXT_STATE) { + return getTextData().length; + } else { + throw new IllegalStateException(); + } + } + + public String getEncoding() { + if (state == DELEGATED_STATE) { + return childReader.getEncoding(); + } else { + // we've no idea what the encoding is going to be in this case + // perhaps we ought to return some constant here, which the user might + // have access to change! + return null; + } + } + + /** + * check the validity of this implementation + * + * @return + */ + public boolean hasText() { + if (state == DELEGATED_STATE) { + return childReader.hasText(); + } else if (state == TEXT_STATE) { + return true; + } else { + return false; + } + + } + + /** + * @return + */ + public Location getLocation() { + // return a default location + return new Location() { + public int getLineNumber() { + return 0; + } + + public int getColumnNumber() { + return 0; + } + + public int getCharacterOffset() { + return 0; + } + + public String getPublicId() { + return null; + } + + public String getSystemId() { + return null; + } + }; + } + + public QName getName() { + if (state == DELEGATED_STATE) { + return childReader.getName(); + } else if (state != TEXT_STATE) { + return elementQName; + } else { + throw new IllegalStateException(); + } + + } + + public String getLocalName() { + if (state == DELEGATED_STATE) { + return childReader.getLocalName(); + } else if (state != TEXT_STATE) { + return elementQName.getLocalPart(); + } else { + throw new IllegalStateException(); + } + } + + public boolean hasName() { + // since this parser always has a name, the hasname + // has to return true if we are still navigating this element + // if not we should ask the child reader for it. + if (state == DELEGATED_STATE) { + return childReader.hasName(); + } else if (state != TEXT_STATE) { + return true; + } else { + return false; + } + } + + public String getNamespaceURI() { + if (state == DELEGATED_STATE) { + return childReader.getNamespaceURI(); + } else if (state == TEXT_STATE) { + return null; + } else { + return elementQName.getNamespaceURI(); + } + } + + public String getPrefix() { + if (state == DELEGATED_STATE) { + return childReader.getPrefix(); + } else if (state == TEXT_STATE) { + return null; + } else { + return elementQName.getPrefix(); + } + } + + public String getVersion() { + return null; + } + + public boolean isStandalone() { + return true; + } + + public boolean standaloneSet() { + return true; + } + + public String getCharacterEncodingScheme() { + return null; // todo - should we return something for this ? + } + + public String getPITarget() { + throw new UnsupportedOperationException("Yet to be implemented !!"); + } + + public String getPIData() { + throw new UnsupportedOperationException("Yet to be implemented !!"); + } + + // ///////////////////////////////////////////////////////////////////////// + // / Other utility methods + // //////////////////////////////////////////////////////////////////////// + + /** + * Populates a namespace context + */ + private void populateNamespaceContext() { + + // first add the current element namespace to the namespace context + // declare it if not found + registerNamespace(elementQName.getPrefix(), elementQName.getNamespaceURI()); + + // traverse through the attributes and populate the namespace context + // the attrib list can be of many combinations + // the valid combinations are + // String - String + // QName - QName + // null - OMAttribute + + if (attributes != null) { + for (int i = 0; i < attributes.length; i++) { // jump in two + Object attribName = attributes[i].getKey(); + if (attribName instanceof String) { + // ignore this case - Nothing to do + } else if (attribName instanceof QName) { + QName attribQName = ((QName) attribName); + registerNamespace(attribQName.getPrefix(), attribQName.getNamespaceURI()); + + } + } + } + + } + + /** + * @param prefix + * @param uri + */ + private void registerNamespace(String prefix, String uri) { + if (!uri.equals(namespaceContext.getNamespaceURI(prefix))) { + namespaceContext.registerMapping(prefix, uri); + declaredNamespaceMap.put(prefix, uri); + } + } + + /** + * By far this should be the most important method in this class this method changes the state of the parser according to the change in the + */ + private int updateStatus() throws XMLStreamException { + int returnEvent = -1; // invalid state is the default state + switch (state) { + case START_ELEMENT_STATE: + // current element is start element. We should be looking at the + // property list and making a pullparser for the property value + if (properties == null || properties.length == 0) { + // no properties - move to the end element state straightaway + state = END_ELEMENT_STATE; + returnEvent = END_ELEMENT; + } else { + // there are properties. now we should delegate this task to a + // child reader depending on the property type + returnEvent = processProperties(); + + } + break; + case END_ELEMENT_STATE: + // we've reached the end element already. If the user tries to push + // further ahead then it is an exception + throw new XMLStreamException("Trying to go beyond the end of the pullparser"); + + case DELEGATED_STATE: + if (childReader.isEndOfFragment()) { + // we've reached the end! + if (currentPropertyIndex > (properties.length - 1)) { + state = END_ELEMENT_STATE; + returnEvent = END_ELEMENT; + } else { + returnEvent = processProperties(); + } + } else { + returnEvent = childReader.next(); + } + break; + + case TEXT_STATE: + // if there are any more event we should be delegating to + // processProperties. if not we just return an end element + if (currentPropertyIndex > (properties.length - 1)) { + state = END_ELEMENT_STATE; + returnEvent = END_ELEMENT; + } else { + returnEvent = processProperties(); + } + break; + } + return returnEvent; + } + + /** + * A convenient method to reuse the properties + * + * @return event to be thrown + * @throws XMLStreamException + */ + private int processProperties() throws XMLStreamException { + // move to the next property depending on the current property + // index + Object propPointer = properties[currentPropertyIndex].getKey(); + QName propertyQName = null; + boolean textFound = false; + if (propPointer == null) { + throw new XMLStreamException("property key cannot be null!"); + } else if (propPointer instanceof String) { + // propPointer being a String has a special case + // that is it can be a the special constant ELEMENT_TEXT that + // says this text event + if (ELEMENT_TEXT.equals(propPointer)) { + textFound = true; + } else { + propertyQName = new QName((String) propPointer); + } + } else if (propPointer instanceof QName) { + propertyQName = (QName) propPointer; + } else { + // oops - we've no idea what kind of key this is + throw new XMLStreamException("unidentified property key!!!" + propPointer); + } + + // ok! we got the key. Now look at the value + Object propertyValue = properties[currentPropertyIndex].getValue(); + // cater for the special case now + if (textFound) { + // no delegation here - make the parser null and immediately + // return with the event characters + childReader = null; + state = TEXT_STATE; + currentPropertyIndex++; + return CHARACTERS; + } else if (propertyValue == null || propertyValue instanceof String) { + // strings are handled by the NameValuePairStreamReader + childReader = new SimpleElementStreamReader(propertyQName, (String) propertyValue, namespaceContext); + childReader.init(); + } else if (propertyValue instanceof DataObjectXMLStreamReader) { + // ADBbean has it's own method to get a reader + XMLFragmentStreamReader reader = (DataObjectXMLStreamReader) propertyValue; + // we know for sure that this is an ADB XMLStreamreader. + // However we need to make sure that it is compatible + childReader = reader; + childReader.init(); + } else { + // all special possiblilities has been tried! Let's treat + // the thing as a bean and try generating events from it + throw new UnsupportedOperationException("Not supported"); + // childReader = new WrappingXMLStreamReader(BeanUtil.getPullParser(propertyValue, propertyQName)); + // we cannot register the namespace context here + } + + // set the state here + state = DELEGATED_STATE; + // we are done with the delegation + // increment the property index + currentPropertyIndex++; + return childReader.getEventType(); + } + + /** + * are we done ? + * + * @return + */ + public boolean isEndOfFragment() { + return (state == END_ELEMENT_STATE); + } + + protected static class NameValuePair implements Map.Entry { + private Object key; + + private Object value; + + public NameValuePair(Object key, Object value) { + this.key = key; + this.value = value; + } + + public Object getKey() { + return key; + } + + public Object getValue() { + return value; + } + + public Object setValue(Object value) { + Object v = this.value; + this.value = value; + return v; + } + + } + + protected static class SimpleElementStreamReader implements XMLFragmentStreamReader { + + private static final int START_ELEMENT_STATE = 0; + + private static final int TEXT_STATE = 1; + + private static final int END_ELEMENT_STATE = 2; + + private static final int START_ELEMENT_STATE_WITH_NULL = 3; + + private static final QName XSI_NIL_QNAME = new QName("http://www.w3.org/2001/XMLSchema-instance", "nil", "xsi"); + + private final NameSpaceContext namespaceContext; + + private QName name; + + private String value; + + private int state = START_ELEMENT_STATE; + + public SimpleElementStreamReader(QName name, String value, NameSpaceContext nameSpaces) { + this.name = name; + this.value = value; + if (value == null) + state = START_ELEMENT_STATE_WITH_NULL; + namespaceContext = nameSpaces; + } + + public Object getProperty(String key) throws IllegalArgumentException { + return null; + } + + public int next() throws XMLStreamException { + switch (state) { + case START_ELEMENT_STATE: + state = TEXT_STATE; + return CHARACTERS; + case START_ELEMENT_STATE_WITH_NULL: + state = END_ELEMENT_STATE; + return END_ELEMENT; + case END_ELEMENT_STATE: + // oops, not supposed to happen! + throw new XMLStreamException("end already reached!"); + case TEXT_STATE: + state = END_ELEMENT_STATE; + return END_ELEMENT; + default: + throw new XMLStreamException("unknown event type!"); + } + } + + public void require(int i, String string, String string1) throws XMLStreamException { + // not implemented + } + + public String getElementText() throws XMLStreamException { + if (state == START_ELEMENT) { + // move to the end state and return the value + state = END_ELEMENT_STATE; + return value; + } else { + throw new XMLStreamException(); + } + + } + + public int nextTag() throws XMLStreamException { + return 0;// todo + } + + public boolean hasNext() throws XMLStreamException { + return (state != END_ELEMENT_STATE); + } + + public void close() throws XMLStreamException { + // Do nothing - we've nothing to free here + } + + public String getNamespaceURI(String prefix) { + return namespaceContext.getNamespaceURI(prefix); + } + + public boolean isStartElement() { + return (state == START_ELEMENT_STATE || state == START_ELEMENT_STATE_WITH_NULL); + } + + public boolean isEndElement() { + return (state == END_ELEMENT_STATE); + } + + public boolean isCharacters() { + return (state == TEXT_STATE); + } + + public boolean isWhiteSpace() { + return false; // no whitespaces here + } + + public boolean isAttributeSpecified(int i) { + return false; // no attribs here + } + + public NamespaceContext getNamespaceContext() { + return this.namespaceContext; + } + + public int getEventType() { + switch (state) { + case START_ELEMENT_STATE: + case START_ELEMENT_STATE_WITH_NULL: + return START_ELEMENT; + case END_ELEMENT_STATE: + return END_ELEMENT; + case TEXT_STATE: + return CHARACTERS; + default: + throw new UnsupportedOperationException(); + // we've no idea what this is!!!!! + } + + } + + public String getText() { + if (state == TEXT_STATE) { + return value; + } else { + throw new IllegalStateException(); + } + } + + public char[] getTextCharacters() { + if (state == TEXT_STATE) { + return value.toCharArray(); + } else { + throw new IllegalStateException(); + } + } + + public int getTextCharacters(int i, char[] chars, int i1, int i2) throws XMLStreamException { + // not implemented + throw new UnsupportedOperationException(); + } + + public int getTextStart() { + if (state == TEXT_STATE) { + return 0; + } else { + throw new IllegalStateException(); + } + } + + public int getTextLength() { + if (state == TEXT_STATE) { + return value.length(); + } else { + throw new IllegalStateException(); + } + + } + + public String getEncoding() { + return "UTF-8"; + } + + public boolean hasText() { + return (state == TEXT_STATE); + } + + public Location getLocation() { + return new Location() { + public int getLineNumber() { + return 0; + } + + public int getColumnNumber() { + return 0; + } + + public int getCharacterOffset() { + return 0; + } + + public String getPublicId() { + return null; + } + + public String getSystemId() { + return null; + } + }; + } + + public QName getName() { + if (state != TEXT_STATE) { + return name; + } else { + return null; + } + } + + public String getLocalName() { + if (state != TEXT_STATE) { + return name.getLocalPart(); + } else { + return null; + } + } + + public boolean hasName() { + return (state != TEXT_STATE); + + } + + public String getNamespaceURI() { + if (state != TEXT_STATE) { + return name.getNamespaceURI(); + } else { + return null; + } + + } + + public String getPrefix() { + if (state != TEXT_STATE) { + return name.getPrefix(); + } else { + return null; + } + } + + public String getVersion() { + return null; // todo 1.0 ? + } + + public boolean isStandalone() { + return false; + } + + public boolean standaloneSet() { + return false; + } + + public String getCharacterEncodingScheme() { + return null; + } + + public String getPITarget() { + return null; + } + + public String getPIData() { + return null; + } + + public boolean isEndOfFragment() { + return (state == END_ELEMENT_STATE); + } + + public void init() { + // just add the current elements namespace and prefix to the this + // elements nscontext + registerNamespace(name.getPrefix(), name.getNamespaceURI()); + + } + + /** + * @param prefix + * @param uri + */ + private void registerNamespace(String prefix, String uri) { + // todo - need to fix this up to cater for cases where + // namespaces are having no prefixes + if (!uri.equals(namespaceContext.getNamespaceURI(prefix))) { + // this namespace is not there. Need to declare it + namespaceContext.registerMapping(prefix, uri); + } + } + + public int getAttributeCount() { + if (state == START_ELEMENT_STATE_WITH_NULL) + return 1; + if (state == START_ELEMENT_STATE) { + return 0; + } else { + throw new IllegalStateException(); + } + + } + + public String getAttributeLocalName(int i) { + if (state == START_ELEMENT_STATE_WITH_NULL && i == 0) + return XSI_NIL_QNAME.getLocalPart(); + if (state == START_ELEMENT_STATE) { + return null; + } else { + throw new IllegalStateException(); + } + } + + public QName getAttributeName(int i) { + if (state == START_ELEMENT_STATE_WITH_NULL && i == 0) + return XSI_NIL_QNAME; + if (state == START_ELEMENT_STATE) { + return null; + } else { + throw new IllegalStateException(); + } + } + + public String getAttributeNamespace(int i) { + if (state == START_ELEMENT_STATE_WITH_NULL && i == 0) + return XSI_NIL_QNAME.getNamespaceURI(); + if (state == START_ELEMENT_STATE) { + return null; + } else { + throw new IllegalStateException(); + } + } + + public String getAttributePrefix(int i) { + if (state == START_ELEMENT_STATE_WITH_NULL && i == 0) + return XSI_NIL_QNAME.getPrefix(); + if (state == START_ELEMENT_STATE) { + return null; + } else { + throw new IllegalStateException(); + } + } + + public String getAttributeType(int i) { + return null; // not implemented + } + + public String getAttributeValue(int i) { + if (state == START_ELEMENT_STATE_WITH_NULL && i == 0) + return "true"; + if (state == START_ELEMENT_STATE) { + return null; + } else { + throw new IllegalStateException(); + } + } + + public String getAttributeValue(String string, String string1) { + if (state == TEXT_STATE) { + // todo something + return null; + } else { + return null; + } + + } + + public int getNamespaceCount() { + if (state == START_ELEMENT_STATE_WITH_NULL && isXsiNamespacePresent()) + return 1; + else + return 0; + + } + + public String getNamespacePrefix(int i) { + if (state == START_ELEMENT_STATE_WITH_NULL && isXsiNamespacePresent() && i == 0) + return XSI_NIL_QNAME.getPrefix(); + else + return null; + } + + public String getNamespaceURI(int i) { + if (state == START_ELEMENT_STATE_WITH_NULL && isXsiNamespacePresent() && i == 0) + return XSI_NIL_QNAME.getNamespaceURI(); + else + return null; + } + + /** + * Test whether the xsi namespace is present + * + * @return + */ + private boolean isXsiNamespacePresent() { + return (namespaceContext.getNamespaceURI(XSI_NIL_QNAME.getPrefix()) != null); + } + + } + + protected class NameSpaceContext implements NamespaceContext { + private Map prefixToNamespaceMapping = new HashMap(); + + public NameSpaceContext() { + prefixToNamespaceMapping.put("xml", "http://www.w3.org/XML/1998/namespace"); + prefixToNamespaceMapping.put("xmlns", "http://www.w3.org/2000/xmlns/"); + prefixToNamespaceMapping.put("xsi", "http://www.w3.org/2001/XMLSchema-instance"); + } + + public String getNamespaceURI(String prefix) { + if (prefix == null) + throw new IllegalArgumentException("Prefix is null"); + + String ns = (String) prefixToNamespaceMapping.get(prefix); + if (ns != null) + return ns; + else + return null; + } + + public String getPrefix(String nsURI) { + if (nsURI == null) + throw new IllegalArgumentException("Namespace is null"); + for (Iterator i = prefixToNamespaceMapping.entrySet().iterator(); i.hasNext();) { + Map.Entry entry = (Map.Entry) i.next(); + if (entry.getValue().equals(nsURI)) { + return (String) entry.getKey(); + } + } + return null; + } + + public Iterator getPrefixes(String nsURI) { + List prefixList = new ArrayList(); + for (Iterator i = prefixToNamespaceMapping.entrySet().iterator(); i.hasNext();) { + Map.Entry entry = (Map.Entry) i.next(); + if (entry.getValue().equals(nsURI)) { + prefixList.add(entry.getKey()); + } + } + return prefixList.iterator(); + } + + public void registerMapping(String prefix, String nsURI) { + prefixToNamespaceMapping.put(prefix, nsURI); + } + + private int counter = 0; + + public synchronized QName createQName(String nsURI, String name) { + String prefix = nsURI != null ? (String) getPrefix(nsURI) : null; + if (prefix == null && nsURI != null && !nsURI.equals("")) + prefix = "p" + (counter++); + if (prefix == null) + prefix = ""; + if (nsURI != null) { + prefixToNamespaceMapping.put(prefix, nsURI); + declaredNamespaceMap.put(prefix, nsURI); + } + return new QName(nsURI, name, prefix); + } + + public void removeMapping(String prefix) { + prefixToNamespaceMapping.remove(prefix); + } + } + +} diff --git a/branches/sdo-1.1.1-incubating/impl/src/main/java/org/apache/tuscany/sdo/util/resource/RecordedEventXMLStreamReader.java b/branches/sdo-1.1.1-incubating/impl/src/main/java/org/apache/tuscany/sdo/util/resource/RecordedEventXMLStreamReader.java new file mode 100644 index 0000000000..a0da5d2609 --- /dev/null +++ b/branches/sdo-1.1.1-incubating/impl/src/main/java/org/apache/tuscany/sdo/util/resource/RecordedEventXMLStreamReader.java @@ -0,0 +1,868 @@ +/** + * + * 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"); + } +} diff --git a/branches/sdo-1.1.1-incubating/impl/src/main/java/org/apache/tuscany/sdo/util/resource/SDODeserializer.java b/branches/sdo-1.1.1-incubating/impl/src/main/java/org/apache/tuscany/sdo/util/resource/SDODeserializer.java new file mode 100644 index 0000000000..01c043b074 --- /dev/null +++ b/branches/sdo-1.1.1-incubating/impl/src/main/java/org/apache/tuscany/sdo/util/resource/SDODeserializer.java @@ -0,0 +1,340 @@ +/** + * + * 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.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import javax.xml.namespace.NamespaceContext; +import javax.xml.stream.XMLStreamException; +import javax.xml.stream.XMLStreamReader; + +import org.apache.tuscany.sdo.helper.HelperContextImpl; +import org.apache.tuscany.sdo.helper.TypeHelperImpl; +import org.apache.tuscany.sdo.helper.XMLStreamHelper; +import org.apache.tuscany.sdo.helper.XMLStreamHelperImpl; +import org.apache.tuscany.sdo.util.StreamDeserializer; +import org.eclipse.emf.common.notify.Notification; +import org.eclipse.emf.common.util.EList; +import org.eclipse.emf.common.util.TreeIterator; +import org.eclipse.emf.ecore.EClass; +import org.eclipse.emf.ecore.EObject; +import org.eclipse.emf.ecore.EReference; +import org.eclipse.emf.ecore.EStructuralFeature; +import org.eclipse.emf.ecore.resource.Resource; +import org.eclipse.emf.ecore.util.EcoreUtil; +import org.eclipse.emf.ecore.util.ExtendedMetaData; +import org.eclipse.emf.ecore.xmi.XMLResource; + +import commonj.sdo.Property; +import commonj.sdo.Type; +import commonj.sdo.helper.HelperContext; +import commonj.sdo.helper.TypeHelper; +import commonj.sdo.helper.XSDHelper; + +/** + * SDO StAX Deserializer. The instance isn't thread-safe, however it's safe to use the instance any times on the same thread. + */ +public class SDODeserializer extends StreamDeserializer { + private HelperContext hc; + protected final void initialize(XMLStreamReader stream, HelperContext scope, Object rootObject) { + hc = scope; + reader = stream; + xsdHelper = scope.getXSDHelper(); + typeHelper = scope.getTypeHelper(); + extendedMetaData = ((TypeHelperImpl) typeHelper).getExtendedMetaData(); + deserializer = (XMLStreamHelperImpl)((HelperContextImpl)scope).getXMLStreamHelper();; + root = (EObject) rootObject; + } + + XSDHelper xsdHelper; + + protected Property propertyInSequence; + + Property getProperty(Object type) { + EClass c = (EClass) type; + EStructuralFeature containment = (EStructuralFeature) propertyInSequence; + Object containing = extendedMetaData.getAffiliation(c, containment); + // if (containing == null) report error? + if (containment == containing && containment.isDerived()) { + containing = extendedMetaData.getMixedFeature(c); + if (containing == null) { + containing = extendedMetaData.getGroup(containment); + if (containing == null) + return propertyInSequence; + } + } + return (Property) containing; + } + + protected final Property getProperty(Type type, String nameSpace, String name, boolean element) { + for (Iterator iterator = type.getProperties().iterator(); iterator.hasNext();) { + propertyInSequence = (Property) iterator.next(); + if (name.equals(xsdHelper.getLocalName(propertyInSequence)) + && nameSpace.equals(xsdHelper.getNamespaceURI(propertyInSequence))) + return getProperty(type); + } + propertyInSequence = xsdHelper.getGlobalProperty(nameSpace, name, element); + // if (propertyInSequence == null) report error? + return getProperty(type); + } + + protected final Property getProperty(Type type, String name) { + propertyInSequence = type.getProperty(name); + // if (propertyInSequence == null) report error? + return getProperty(type); + } + + protected final Property getProperty(String nameSpace, String name, Type type) { + return null == nameSpace ? getProperty(type, name) : getProperty(type, nameSpace, name, true); + } + + XMLStreamHelperImpl deserializer; + + TypeHelper typeHelper; + + protected Object load(XMLStreamReader reader, Map options) throws XMLStreamException { + return deserializer.loadObject(reader, options); + } + + static Object value(Type type, XMLStreamReader reader) throws XMLStreamException { + return value(type, reader.getElementText(), reader.getNamespaceContext()); + } + + protected final Object value(Type type) throws XMLStreamException { + return "true".equals(reader.getAttributeValue(ExtendedMetaData.XSI_URI, XMLResource.NIL)) ? null : type.isDataType() ? value(type, reader) + : load(reader, null); + } + + Map options/* = null */; + + protected final Object value(XMLStreamReader reader) throws XMLStreamException { + Type propertyType = propertyInSequence.getType(); + if (propertyType.isDataType()) + return value(propertyType, reader); + if (options == null) + options = new HashMap(); + options.put(XMLStreamHelper.OPTION_DEFAULT_ROOT_TYPE, propertyType); + return load(reader, options); + } + + private boolean match(String name, String space, EStructuralFeature feature) { + return name.equals(extendedMetaData.getName(feature)) && space.equals(extendedMetaData.getNamespace(feature)); + } + + EObject step(String ref, int step, int index, EObject container, String prefix, NamespaceContext nameSpaces) { + String name = ref.substring(step, index); + Iterator iterator = container.eContents().iterator(); + if (iterator.hasNext()) + if (prefix == null) + do { + container = (EObject) iterator.next(); + // if( container == null )continue; + if (name.equals(extendedMetaData.getName(container.eContainmentFeature()))) + return container; + } while (iterator.hasNext()); + else { + prefix = nameSpaces.getNamespaceURI(prefix); + do { + container = (EObject) iterator.next(); + // if( container == null )continue; + if (match(name, prefix, container.eContainmentFeature())) + return container; + } while (iterator.hasNext()); + } + return null; + } + + EObject root; + + ExtendedMetaData extendedMetaData; + + protected final EObject referent(String ref, NamespaceContext nameSpaces) { + int length = ref.length(); + switch (length) { + case 0: + return null; + case 1: // # + return root; + } + EObject container; + int step; + if (ref.charAt(1) == '/') { + container = EcoreUtil.getRootContainer(root); + if (length == 2) + return container; + if (ref.charAt(2) == '/') { + for (Iterator iterator = container.eContents().iterator();/* true */;) { + if (!iterator.hasNext()) + return null; + container = (EObject) iterator.next(); + // if( container != null ) + break; + } + /*#// is invalid + if (length == 3) + return container; */ + step = 3; + } else + step = 2; + } else { + container = root; + step = 1; + } + String prefix = null; + for (int index = step; ++index != length;) { + switch (ref.charAt(index)) { + case '/': + container = step(ref, step, index, container, prefix, nameSpaces); + if (container == null) + return null; + break; + case ':': + prefix = ref.substring(step, index); + if (++index == length) + return container; // report error? + step = index; + default: + continue; + case '[': + name = ref.substring(step, index); + step = ref.indexOf(']', index + 2); + if (step == -1) + return container; // report error? + index = Integer.parseInt(ref.substring(++index, step)); + EStructuralFeature feature; + Iterator iterator = container.eContents().iterator(); + if (prefix == null) + do { + if (!iterator.hasNext()) + return null; + EObject content = (EObject) iterator.next(); + // if( content == null )continue; + feature = content.eContainmentFeature(); + } while (!name.equals(extendedMetaData.getName(feature))); + else { + prefix = nameSpaces.getNamespaceURI(prefix); + do { + if (!iterator.hasNext()) + return null; + EObject content = (EObject) iterator.next(); + // if( content == null )continue; + feature = content.eContainmentFeature(); + } while (!match(name, prefix, feature)); + } + Object value = container.eGet(feature); + if (value instanceof List) { + List values = (List) value; + if (index > values.size()) + return null; + container = (EObject) values.get(--index); + } else if (index == 1) + container = (EObject) value; + else + return null; + index = ref.indexOf('/', ++step); + if (index == -1) + return container; + } + if (++index == length) + return container; + step = index; + prefix = null; + } + return step(ref, step, length, container, prefix, nameSpaces); + } + + static protected class Ref implements EObject // FeatureMapEntry value + { + protected Ref(String path, NamespaceContext context) { + ref = path; + nameSpaces = context; + } + + final String ref; + + final NamespaceContext nameSpaces; + + public TreeIterator eAllContents() { + return null; + } + public EClass eClass() { + return null; + } + public EObject eContainer() { + return null; + } + public EStructuralFeature eContainingFeature() { + return null; + } + public EReference eContainmentFeature() { + return null; + } + public EList eContents() { + return null; + } + public EList eCrossReferences() { + return null; + } + public Object eGet(EStructuralFeature feature) { + return null; + } + public Object eGet(EStructuralFeature feature, boolean resolve) { + return null; + } + public boolean eIsProxy() { + return false; + } + public boolean eIsSet(EStructuralFeature feature) { + return false; + } + public Resource eResource() { + return null; + } + public void eSet(EStructuralFeature feature, Object newValue) { + } + public void eUnset(EStructuralFeature feature) { + } + public EList eAdapters() { + return null; + } + public boolean eDeliver() { + return false; + } + public void eNotify(Notification notification) { + } + public void eSetDeliver(boolean deliver) { + } + } + + protected final EObject referent(Ref path) { + return referent(path.ref, path.nameSpaces); + } + + protected final Type typeXSI() { + return typedXSI() ? typeHelper.getType(nameSpace, name) : null; + } + + protected final Type globalElementType(String nameSpace, String name) { + return xsdHelper.getGlobalProperty(nameSpace, name, true).getType(); + } +} diff --git a/branches/sdo-1.1.1-incubating/impl/src/main/java/org/apache/tuscany/sdo/util/resource/SDOURIConverterImpl.java b/branches/sdo-1.1.1-incubating/impl/src/main/java/org/apache/tuscany/sdo/util/resource/SDOURIConverterImpl.java new file mode 100644 index 0000000000..6110cd2305 --- /dev/null +++ b/branches/sdo-1.1.1-incubating/impl/src/main/java/org/apache/tuscany/sdo/util/resource/SDOURIConverterImpl.java @@ -0,0 +1,60 @@ +/** + * + * 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.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +import org.eclipse.emf.common.util.URI; +import org.eclipse.emf.ecore.resource.Resource; +import org.eclipse.emf.ecore.resource.impl.URIConverterImpl; + +public class SDOURIConverterImpl extends URIConverterImpl { + /** + * Disable going out to the wire. + */ + protected InputStream createURLInputStream(URI uri) throws IOException { + String scheme = uri.scheme(); + if ("http".equals(scheme) || "https".equals(scheme)) { + // TUSCANY 2240: We need to compromise if the remote loading is allowed or not + String ext = uri.fileExtension(); + if (!"wsdl".equalsIgnoreCase(ext) && !"xsd".equalsIgnoreCase(ext)) { + throw new Resource.IOWrappedException(new RuntimeException("Reading remote URL not supported: " + uri)); + } + } + return super.createURLInputStream(uri); + } + + /** + * Disable going out to the wire. + */ + protected OutputStream createURLOutputStream(URI uri) throws IOException { + String scheme = uri.scheme(); + if ("http".equals(scheme) || "https".equals(scheme)) { + // TUSCANY 2240: We need to compromise if the remote loading is allowed or not + String ext = uri.fileExtension(); + if (!"wsdl".equalsIgnoreCase(ext) && !"xsd".equalsIgnoreCase(ext)) { + throw new Resource.IOWrappedException(new RuntimeException("Reading remote URL not supported: " + uri)); + } + } + return super.createURLOutputStream(uri); + } +} diff --git a/branches/sdo-1.1.1-incubating/impl/src/main/java/org/apache/tuscany/sdo/util/resource/SDOXMLResourceFactoryImpl.java b/branches/sdo-1.1.1-incubating/impl/src/main/java/org/apache/tuscany/sdo/util/resource/SDOXMLResourceFactoryImpl.java new file mode 100644 index 0000000000..c8189f9766 --- /dev/null +++ b/branches/sdo-1.1.1-incubating/impl/src/main/java/org/apache/tuscany/sdo/util/resource/SDOXMLResourceFactoryImpl.java @@ -0,0 +1,47 @@ +/** + * + * 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 org.eclipse.emf.common.util.URI; +import org.eclipse.emf.ecore.resource.Resource; +import org.eclipse.emf.ecore.resource.impl.ResourceFactoryImpl; + +/** + * This class creates SDOXMLResourceImpl objects. + */ +public class SDOXMLResourceFactoryImpl extends ResourceFactoryImpl +{ + /** + * Constructor for SDOXMLResourceFactoryImpl. + */ + public SDOXMLResourceFactoryImpl() + { + super(); + } + + /** + * Creates an XMLResourceImpl and returns it. + */ + public Resource createResource(URI uri) + { + return new SDOXMLResourceImpl(uri); + } +} diff --git a/branches/sdo-1.1.1-incubating/impl/src/main/java/org/apache/tuscany/sdo/util/resource/SDOXMLResourceImpl.java b/branches/sdo-1.1.1-incubating/impl/src/main/java/org/apache/tuscany/sdo/util/resource/SDOXMLResourceImpl.java new file mode 100644 index 0000000000..172afb73db --- /dev/null +++ b/branches/sdo-1.1.1-incubating/impl/src/main/java/org/apache/tuscany/sdo/util/resource/SDOXMLResourceImpl.java @@ -0,0 +1,1009 @@ +/** + * + * 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.io.IOException; +import java.io.InputStream; +import java.io.Writer; +import java.net.URL; +import java.security.AccessController; +import java.security.PrivilegedActionException; +import java.security.PrivilegedExceptionAction; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import javax.xml.XMLConstants; +import javax.xml.namespace.NamespaceContext; +import javax.xml.namespace.QName; +import javax.xml.stream.XMLOutputFactory; +import javax.xml.stream.XMLStreamConstants; +import javax.xml.stream.XMLStreamException; +import javax.xml.stream.XMLStreamReader; +import javax.xml.stream.XMLStreamWriter; + +import org.apache.tuscany.sdo.SDOExtendedMetaData; +import org.apache.tuscany.sdo.helper.HelperContextImpl; +import org.apache.tuscany.sdo.helper.SDOExtendedMetaDataImpl; +import org.apache.tuscany.sdo.helper.XMLStreamHelper; +import org.apache.tuscany.sdo.helper.XSDHelperImpl; +import org.apache.tuscany.sdo.api.SDOHelper; +import org.apache.tuscany.sdo.api.SDOUtil; +import org.apache.tuscany.sdo.util.StAX2SAXAdapter; +import org.apache.tuscany.sdo.model.internal.InternalFactory; +import org.apache.tuscany.sdo.model.internal.impl.InternalFactoryImpl; +import org.eclipse.emf.common.util.EMap; +import org.eclipse.emf.common.util.URI; +import org.eclipse.emf.ecore.EClassifier; +import org.eclipse.emf.ecore.EDataType; +import org.eclipse.emf.ecore.EFactory; +import org.eclipse.emf.ecore.EObject; +import org.eclipse.emf.ecore.EPackage; +import org.eclipse.emf.ecore.EReference; +import org.eclipse.emf.ecore.EStructuralFeature; +import org.eclipse.emf.ecore.InternalEObject; +import org.eclipse.emf.ecore.resource.Resource; +import org.eclipse.emf.ecore.util.ExtendedMetaData; +import org.eclipse.emf.ecore.util.FeatureMap; +import org.eclipse.emf.ecore.xmi.XMIException; +import org.eclipse.emf.ecore.xmi.XMLHelper; +import org.eclipse.emf.ecore.xmi.XMLLoad; +import org.eclipse.emf.ecore.xmi.XMLOptions; +import org.eclipse.emf.ecore.xmi.XMLResource; +import org.eclipse.emf.ecore.xmi.XMLSave; +import org.eclipse.emf.ecore.xmi.impl.SAXXMLHandler; +import org.eclipse.emf.ecore.xmi.impl.XMLHelperImpl; +import org.eclipse.emf.ecore.xmi.impl.XMLLoadImpl; +import org.eclipse.emf.ecore.xmi.impl.XMLOptionsImpl; +import org.eclipse.emf.ecore.xmi.impl.XMLResourceImpl; +import org.eclipse.emf.ecore.xmi.impl.XMLSaveImpl; +import org.eclipse.emf.ecore.xmi.impl.XMLString; +import org.eclipse.emf.ecore.xmi.util.DefaultEcoreBuilder; +import org.eclipse.emf.ecore.xml.type.XMLTypePackage; +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.xml.sax.Attributes; +import org.xml.sax.ContentHandler; +import org.xml.sax.InputSource; +import org.xml.sax.SAXException; +import org.xml.sax.helpers.DefaultHandler; + +import commonj.sdo.ChangeSummary; +import commonj.sdo.DataObject; +import commonj.sdo.helper.XSDHelper; + +public class SDOXMLResourceImpl extends XMLResourceImpl { + private XMLStreamReader reader; + + /** + * [rfeng] Override the XMLHelperImpl to replace the NamespaceSupport so that it's aware of the NamespaceContext from the XMLStreamReader + */ + public static class SDOXMLHelperImpl extends XMLHelperImpl { + + /** + * EMF XMLResource (SAX) may be used to load from only a *portion* of a StAX stream + * which may reference (global) namespaces bound outside the (local) portion. + * This class extends EMF's NamespaceSupport to make {@link #getPrefix} and {@link #getURI} + * query these global binding(s) after first checking the local context(s). + */ + private static class StreamNamespaceSupport extends XMLHelperImpl.NamespaceSupport { + protected NamespaceContext nameSpaceContext; + + public String getPrefix(String uri) { + String prefix = super.getPrefix(uri); + if (prefix == null) + try { + prefix = nameSpaceContext.getPrefix(uri); + } catch (Exception e) { + // HACK: + // java.lang.UnsupportedOperationException + // at org.apache.axiom.om.impl.llom.OMStAXWrapper.getNamespaceContext(OMStAXWrapper.java:984) + } + return prefix; + } + + public String getURI(String prefix) { + String uri = super.getURI(prefix); + if (uri == null) + try { + uri = nameSpaceContext.getNamespaceURI(prefix); + } catch (Exception e) { + // HACK: + // java.lang.UnsupportedOperationException + // at org.apache.axiom.om.impl.llom.OMStAXWrapper.getNamespaceContext(OMStAXWrapper.java:984) + } + return uri; + } + + public StreamNamespaceSupport(XMLStreamReader reader) { + super(); + nameSpaceContext = reader.getNamespaceContext(); + } + + } + + public SDOXMLHelperImpl(XMLResource resource, XMLStreamReader reader) { + this(reader); + setResource(resource); + } + + public SDOXMLHelperImpl(XMLStreamReader reader) { + super(); + if (reader instanceof XMLDocumentStreamReader) // Only use StreamNamespaceSupport when loading from a *portion* of a StAX stream + namespaceSupport = new StreamNamespaceSupport(reader); + } + + private class NameSpaceContext implements NamespaceContext { // TODO Helper# pushContext() & popContext + public String getNamespaceURI(String prefix) { + return SDOXMLHelperImpl.this.getNamespaceURI(prefix); + } + + public String getPrefix(String namespaceURI) { + return SDOXMLHelperImpl.this.getPrefix(namespaceURI); + } + + public Iterator getPrefixes(String namespaceURI) { + return ((Collection) urisToPrefixes.get(namespaceURI)).iterator(); + } + } + + NameSpaceContext nameSpaceContext/* = null */; + + protected final NameSpaceContext nameSpaceContext() { + if (nameSpaceContext == null) + nameSpaceContext = new NameSpaceContext(); + return nameSpaceContext; + } + + private String xsdQName2SDOURI(String xsdQName) { + org.eclipse.emf.ecore.xml.type.internal.QName qname = new org.eclipse.emf.ecore.xml.type.internal.QName(xsdQName); + try { + updateQNameURI(qname); + } + catch (IllegalArgumentException e) { + return xsdQName; + } + String uri = qname.getNamespaceURI(); + if (uri != "") + return uri + "#" + qname.getLocalPart(); + else + return qname.getLocalPart(); + } + + private String getPrefixFromNamespaceURI(String nsURI) { + String nsPrefix = null; + + List prefixes = (List)urisToPrefixes.get(nsURI); + if (prefixes != null) + { + for (Iterator i = prefixes.iterator(); i.hasNext(); ) + { + nsPrefix = (String)i.next(); + if (nsPrefix.length() >= 0) { + // When the length is 0, it's the default namespace + return nsPrefix; + } + } + } + + nsPrefix = namespaceSupport.getPrefix(nsURI); + if (nsPrefix != null) + { + return nsPrefix; + } + + // Demand create a new package + EPackage ePackage = extendedMetaData.demandPackage(nsURI); + + if (ExtendedMetaData.XSI_URI.equals(nsURI)) { + ePackage.setNsPrefix(ExtendedMetaData.XSI_PREFIX); + } + + // getPrefix() will make sure all mapping tables are configured correctly + nsPrefix = getPrefix(ePackage, true); + + return nsPrefix; + } + + private String SDOURI2XsdQName(String sdoURI) { + String namespace = null; + String localPart = sdoURI; + + int index = sdoURI.indexOf('#'); + if (index == -1) { + return localPart; + } + else { + namespace = sdoURI.substring(0, index); + localPart = sdoURI.substring(index+1); + + String prefix = getPrefixFromNamespaceURI(namespace); + + if (prefix.length() == 0) + return localPart; + + return prefix + ":" + localPart; + } + } + + protected Object createFromString(EFactory eFactory, EDataType eDataType, String value) { + Object obj = super.createFromString(eFactory, eDataType, value); + if (eDataType == ((InternalFactoryImpl)InternalFactory.INSTANCE).getQName()) { + if (extendedMetaData != null) { + if (obj instanceof List) { + List list = (List)obj; + for (int i=0; i 0) { + xsiPrefix = typeQName.substring(0, index); + typeName = typeQName.substring(index + 1); + } else + typeName = typeQName; + eFactory = getFactoryForPrefix(xsiPrefix); + if (eFactory != null) + type = helper.getType(eFactory, typeName); + else if (XMLConstants.DEFAULT_NS_PREFIX.equals(xsiPrefix) && helper.getURI(xsiPrefix) == null) { + EPackage ePackage = handleMissingPackage(null); + if (ePackage == null) { + type = defaultRootType(prefix, name, isElement, peekObject, value); + if (type == null) + return; + eFactory = type.getEPackage().getEFactoryInstance(); + } else + type = helper.getType(eFactory = ePackage.getEFactoryInstance(), typeName); + } else { + type = defaultRootType(prefix, name, isElement, peekObject, value); + if (type == null) + return; + eFactory = type.getEPackage().getEFactoryInstance(); + } + } + root = helper.createObject(eFactory, type); + if (root != null) { + if (disableNotify) + root.eSetDeliver(false); + handleObjectAttribs(root); + processObject(root); + return; + } + } + super.handleUnknownFeature(prefix, name, isElement, peekObject, value); + } + + protected RecordedEventXMLStreamReader.Tag tag/* =null */; + + protected List nameSpaces/* = null */; + + public void startPrefixMapping(String prefix, String uri) { + if (nameSpaces == null) + nameSpaces = new ArrayList(); + RecordedEventXMLStreamReader.Tag.bind(prefix, uri, nameSpaces); + if (tag == null) + super.startPrefixMapping(prefix, uri); + } + + public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { + if (tag != null) { + tag.start(uri, localName, qName, attributes, locator, nameSpaces); + nameSpaces = null; + return; + } + EObject peekObject = objects.peekEObject(); + if (peekObject != null) { + String prefix = helper.getPrefix(uri.length() == 0 ? null : uri); + EStructuralFeature feature = getFeature(peekObject, prefix == null ? XMLConstants.DEFAULT_NS_PREFIX : prefix, localName, true); + if (feature != null && feature.getEType() == ChangeSummaryStreamSerializer.ChangeSummary_TYPE) { + tag = new RecordedEventXMLStreamReader.Tag(uri, localName, prefix, attributes, locator, ((SDOXMLHelperImpl) helper).nameSpaceContext(), + nameSpaces); + nameSpaces = null; + return; + } + } + if (nameSpaces != null) + nameSpaces.clear(); + super.startElement(uri, localName, qName, attributes); + } + + public void characters(char[] ch, int start, int length) { + if (tag == null) + super.characters(ch, start, length); + else + tag.text(XMLStreamConstants.CHARACTERS, new String(ch, start, length), locator); + } + + protected Collection changeSummaryDeserializers/* = null */; + + public void endElement(String uri, String localName, String qName) { + if (tag == null) + super.endElement(uri, localName, qName); + else if (tag.end(uri, localName, qName, locator)) { + if (changeSummaryDeserializers == null) + changeSummaryDeserializers = new ArrayList(); + ChangeSummaryStreamDeserializer changeSummaryDeserializer = new ChangeSummaryStreamDeserializer(); + try { + changeSummaryDeserializer.begin( + (DataObject) objects.peekEObject(), + new HelperContextImpl(extendedMetaData, false), + tag.play(xmlResource)); + changeSummaryDeserializers.add(changeSummaryDeserializer); + } catch (XMLStreamException e) { + xmlResource.getErrors().add(new XMIException(e)); + } + tag = null; + } + } + + public void endDocument() { + super.endDocument(); + if (changeSummaryDeserializers != null) + for (Iterator iterator = changeSummaryDeserializers.iterator(); iterator.hasNext();) + try { + ((ChangeSummaryStreamDeserializer) iterator.next()).end(); + // iterator.remove(); + } catch (XMLStreamException e) { + xmlResource.getErrors().add(new XMIException(e)); + } + } + } + + protected DefaultHandler makeDefaultHandler() { + return new XmlHandler(); + } + + /** + * Start parsing an XMLReader with the default handler. + */ + public void load(XMLResource resource, final XMLStreamReader reader, Map options) throws IOException { + this.resource = resource; + Map mergedOptions = new HashMap(defaultLoadOptions); + if (options != null) + mergedOptions.putAll(options); + + this.options = mergedOptions; + + final ContentHandler handler = makeDefaultHandler(); + + if (errors != null) { + errors.clear(); + } + + final StAX2SAXAdapter adapter = new StAX2SAXAdapter(true); + // Parse the XMLReader and generate SAX events + try { + AccessController.doPrivileged(new PrivilegedExceptionAction() { + public Object run() throws XMLStreamException, SAXException { + adapter.parse(reader, handler); + return null; + } + }); + } catch (PrivilegedActionException e) { + throw new Resource.IOWrappedException(e.getException()); + } + + helper = null; + if (!resource.getErrors().isEmpty()) { + Exception error = (Exception) resource.getErrors().get(0); + if (error instanceof XMIException) { + XMIException exception = (XMIException) error; + if (exception.getWrappedException() != null) { + throw new Resource.IOWrappedException(exception.getWrappedException()); + } + } + throw new Resource.IOWrappedException(error); + } + } + } + + public SDOXMLResourceImpl(URI uri) { + super(uri); + } + + protected XMLHelper createXMLHelper() { + return new SDOXMLHelperImpl(this, reader); + } + + /** + * @see org.eclipse.emf.ecore.xmi.impl.XMLResourceImpl#createXMLLoad() + */ + protected XMLLoad createXMLLoad() { + return new SDOXMLLoadImpl(createXMLHelper()); + } + + static protected int loadLaxForm; + static { + int defaultLaxForm = 0x4242; + String property = System.getProperty("XML.load.form.lax"); + if (property == null) + loadLaxForm = defaultLaxForm; + else + try { + loadLaxForm = Integer.decode(property).intValue(); + } catch (NumberFormatException eNumberFormat) { + loadLaxForm = defaultLaxForm; + } + } + + public void doLoad(InputSource inputSource, Map options) throws IOException { + if (options != null) { + /* + * Tolerates element/attribute malform unless indicated not to + */ + Object option = options.get(SDOHelper.XMLOptions.XML_LOAD_LAX_FORM); + int tolerance = option == null ? loadLaxForm : ((Number) option).intValue(); + option = options.get(OPTION_EXTENDED_META_DATA); + if (tolerance == 0) { + if (option instanceof SDOExtendedMetaData) + ((SDOExtendedMetaData) option).setFeatureNamespaceMatchingLax(false); + } else if (option instanceof SDOExtendedMetaData){ + ((SDOExtendedMetaData) option).setFeatureNamespaceMatchingLax(true); + } + else{ + options.put(OPTION_EXTENDED_META_DATA, option = new SDOExtendedMetaDataImpl()); // TODO copy (BasicExtendedMetaData)option + } + /* + * Loads schema if necessary + */ + if (Boolean.TRUE.equals(options.get(SDOHelper.XMLOptions.XML_LOAD_SCHEMA))){ + XMLOptions xmlOptions = (XMLOptions) options.get(OPTION_XML_OPTIONS); + if (xmlOptions == null) { + xmlOptions = new XMLOptionsImpl(); + options.put(OPTION_XML_OPTIONS, xmlOptions); + } + xmlOptions.setProcessSchemaLocations(true); + if (option == null){ + option = getDefaultLoadOptions().get(OPTION_EXTENDED_META_DATA); + } + ExtendedMetaData extendedMetaData; + final XSDHelper xsdHelper; + if (option == null) { + extendedMetaData = ExtendedMetaData.INSTANCE; + xsdHelper = XSDHelper.INSTANCE; + } else { + extendedMetaData = (ExtendedMetaData) option; + xsdHelper = (new HelperContextImpl(extendedMetaData, false)).getXSDHelper(); + } + xmlOptions.setEcoreBuilder(new DefaultEcoreBuilder(extendedMetaData) { + public Collection generate(Map targetNamespaceToURI) throws IOException { + for (Iterator iterator = targetNamespaceToURI.values().iterator(); iterator.hasNext();) { + String uri = iterator.next().toString(); + xsdHelper.define(uri.indexOf(":/") == -1 ? Thread.currentThread().getContextClassLoader().getResourceAsStream(uri) + : new URL(uri).openStream(), uri); + } + return null; // XMLHandler#processSchemaLocations doesn't take the result + } + }); + } + + if (Boolean.TRUE.equals(options.get(SDOHelper.XMLOptions.XML_LOAD_UNKNOWN_PROPERTIES))) { + options.put(OPTION_RECORD_UNKNOWN_FEATURE , Boolean.TRUE); + } + } + else if (loadLaxForm != 0) { + /* + * Tolerates element/attribute malform + */ + options = new HashMap(); + options.put(OPTION_EXTENDED_META_DATA, new SDOExtendedMetaDataImpl()); + } + super.doLoad(inputSource, options); + // TODO there is some thinking to be done about the restoration of options + } + + /** + * Loads the resource from a StAX XMLStreamReader. + */ + public void load(XMLStreamReader reader, Map options) throws IOException { + this.reader = reader; + SDOXMLLoadImpl xmlLoad = (SDOXMLLoadImpl) createXMLLoad(); + Map mergedOptions = new HashMap(defaultLoadOptions); + if (options != null) + mergedOptions.putAll(options); + xmlLoad.load(this, reader, mergedOptions); + } + + ChangeSummaryStreamSerializer changeSummarySerializer/* = null*/; + + static private final class LocalName extends QName { + private LocalName(String name) { + super(name); + } + + public String getNamespaceURI() { + return null; + } + } + + static final String INDENT = " ", LINE_SEPARATOR = System.getProperty("line.separator"); + + static final class XmlString extends XMLString { + XmlString(int lineWidth, String temporaryFileName) { + super(lineWidth, temporaryFileName); // setLineWidth & setTemporaryFileName + } + + XmlString(int lineWidth, String publicId, String systemId, String temporaryFileName) { + super(lineWidth, publicId, systemId, temporaryFileName); + } + + void setLineBreak(String lineBreak) { + lineSeparator = lineBreak; + } + + void margin(String margin) { + indents.set(0, margin); + } + + String indent = INDENT; + + protected String getElementIndent(int extra) { + int nesting = depth + extra - 1; + for (int i = indents.size() - 1; i < nesting; ++i) { + indents.add(indents.get(i) + indent); + } + return (String) indents.get(nesting); + } + + protected String getAttributeIndent() { + return getElementIndent(); + } + + public final boolean mixed() { + return isMixed; + } + + public void reset(String publicId, String systemId, int lineWidth, String temporaryFileName) { + super.reset(publicId, systemId, lineWidth, temporaryFileName); + setLineBreak(LINE_SEPARATOR); + indent = INDENT; + } + } + + static final char MARK = '\n'; + + static final String LINE_BREAK = new String(new char[] { MARK }); + + final class SDOXMLSaveImpl extends XMLSaveImpl { + SDOXMLSaveImpl(XMLHelper helper) { + super(helper); + } + + XmlString doc(XMLResource resource, Map options) { + if (doc instanceof XmlString) + return (XmlString) doc; + Object lineWidth = options.get(OPTION_LINE_WIDTH); + int width = lineWidth == null ? Integer.MAX_VALUE : ((Number) lineWidth).intValue(); + XmlString d = resource != null && Boolean.TRUE.equals(options.get(OPTION_SAVE_DOCTYPE)) ? new XmlString(width, resource.getPublicId(), + resource.getSystemId(), doc.getTemporaryFileName()) : new XmlString(width, doc.getTemporaryFileName()); + doc = d; + return d; + } + + Map changeSummaryOptions = new HashMap(); + String indent = INDENT, margin; + + protected void init(XMLResource resource, Map options) { + super.init(resource, options); + int unformat = 0; + String lineBreak = (String) options.get(SDOHelper.XMLOptions.XML_SAVE_LINE_BREAK); + if (lineBreak == null) + changeSummaryOptions.put(SDOHelper.XMLOptions.XML_SAVE_LINE_BREAK, LINE_BREAK); + else if (lineBreak.length() == 0) + ++unformat; + else { + changeSummaryOptions.put(SDOHelper.XMLOptions.XML_SAVE_LINE_BREAK, LINE_BREAK); + if (lineBreak.equals(LINE_SEPARATOR)) + lineBreak = null; + } + String indent = (String) options.get(SDOHelper.XMLOptions.XML_SAVE_INDENT); + if (indent == null) + changeSummaryOptions.put(SDOHelper.XMLOptions.XML_SAVE_INDENT, INDENT); + else if (indent.length() == 0) + ++unformat; + else { + changeSummaryOptions.put(SDOHelper.XMLOptions.XML_SAVE_INDENT, this.indent = indent); + if (indent.equals(INDENT)) + indent = null; + } + String margin = (String) options.get(SDOHelper.XMLOptions.XML_SAVE_MARGIN); + if (margin == null || margin.length() == 0) { + if (unformat == 2) + doc.setUnformatted(true); + else if (lineBreak != null) { + XmlString d = doc(resource, options); + d.setLineBreak(lineBreak); + if (indent != null) + d.indent = indent; + } else if (indent != null) + doc(resource, options).indent = indent; + this.margin = this.indent; + } else { + XmlString d = doc(resource, options); + d.margin(margin); + if (lineBreak != null) + d.setLineBreak(lineBreak); + if (indent != null) + d.indent = indent; + this.margin = margin + this.indent; + if (!toDOM && declareXML) + d.add(margin); + } + // changeSummaryOptions.put(ChangeSummaryStreamSerializer.OPTION_RootObject_PATH, "#"); + // changeSummaryOptions.put(ChangeSummaryStreamSerializer.OPTION_OPTIMIZE_LIST, Boolean.TRUE); + changeSummaryOptions.put(OPTION_EXTENDED_META_DATA, extendedMetaData); + } + + QName qName(EStructuralFeature f) { + if (extendedMetaData == null) + return new LocalName(f.getName()); + String nameSpace = extendedMetaData.getNamespace(f), name = extendedMetaData.getName(f); + return nameSpace == null ? new LocalName(name) : new QName(nameSpace, name); + } + + XMLStreamWriter xmlStreamWriter/* = null*/; + + void saveChangeSummary(EObject o, EStructuralFeature f, Object changeSummary) { + boolean notMixed; + if (doc instanceof XmlString) + notMixed = !((XmlString) doc).mixed(); + else if (extendedMetaData == null) + notMixed = true; + else + switch (extendedMetaData.getContentKind(o.eClass())) { + case ExtendedMetaData.MIXED_CONTENT: + case ExtendedMetaData.SIMPLE_CONTENT: + notMixed = false; + break; + default: + notMixed = true; + } + if (notMixed) { + StringBuffer margin = new StringBuffer(this.margin); + for (EObject container = o.eContainer(), grandContainer; (grandContainer = container.eContainer()) != null; container = grandContainer) + margin.append(indent); + changeSummaryOptions.put(SDOHelper.XMLOptions.XML_SAVE_MARGIN, margin.toString()); + } + try { + if (xmlStreamWriter == null) { + xmlStreamWriter = XMLOutputFactory.newInstance().createXMLStreamWriter(new Writer() { + public void close() { + } + + public void flush() { + } + + protected final void add(char[] cbuf, int index, int off) { + doc.addText(new String(cbuf, index, off - index)); + } + + public void write(char[] cbuf, int off, int len) { + if (len != 0) + for (;;) { + while (cbuf[off] == MARK) { + doc.addLine(); + if (--len == 0) + return; + ++off; + } + for (int index = off;/* true */;) { + ++off; + if (--len == 0) + add(cbuf, index, off); + else { + if (cbuf[off] != MARK) + continue; + add(cbuf, index, off); + doc.addLine(); + if (--len != 0) + break; + } + return; + } + ++off; + } + } + }); + xmlStreamWriter.setNamespaceContext(((SDOXMLHelperImpl) helper).new NameSpaceContext() { + public String getNamespaceURI(String prefix) { + return declareXSI && ExtendedMetaData.XSI_PREFIX.equals(prefix) ? ExtendedMetaData.XSI_URI : super + .getNamespaceURI(prefix); + } + + public String getPrefix(String namespaceURI) { + return declareXSI && ExtendedMetaData.XSI_URI.equals(namespaceURI) ? ExtendedMetaData.XSI_PREFIX : super + .getPrefix(namespaceURI); + } + + public Iterator getPrefixes(String namespaceURI) { + final Iterator iterator = super.getPrefixes(namespaceURI); + return ExtendedMetaData.XSI_URI.equals(namespaceURI) ? new Iterator() { + boolean first = true; + + public boolean hasNext() { + if (first) + if (declareXSI) // never from true to false + return true; + else + first = false; + return iterator.hasNext(); + } + + public Object next() { + if (first) { + first = false; + if (declareXSI) + return ExtendedMetaData.XSI_PREFIX; + } + return iterator.next(); + } + + public void remove() { + if (first) + declareXSI = false; + else + iterator.remove(); + } + } : iterator; + } + }); + for (Iterator iterator = helper.getPrefixToNamespaceMap().iterator(); iterator.hasNext();) { + Map.Entry entry = (Map.Entry) iterator.next(); + xmlStreamWriter.setPrefix((String) entry.getKey(), (String) entry.getValue()); + } + if (declareXSI) + xmlStreamWriter.setPrefix(ExtendedMetaData.XSI_PREFIX, ExtendedMetaData.XSI_URI); + if (changeSummarySerializer == null) + changeSummarySerializer = new ChangeSummaryStreamSerializer(); + } + changeSummarySerializer.saveChangeSummary((ChangeSummary) changeSummary, qName(f), xmlStreamWriter, changeSummaryOptions); + if (notMixed) + doc.addLine(); + } catch (XMLStreamException e) { + xmlResource.getErrors().add(new XMIException(e)); + } + } + + protected void saveDataTypeElementSingle(EObject o, EStructuralFeature f) { + if (f.getEType() == ChangeSummaryStreamSerializer.ChangeSummary_TYPE) + saveChangeSummary(o, f, helper.getValue(o, f)); + else + super.saveDataTypeElementSingle(o, f); + } + + /* + * TEMPORARILY COPIED FROM BASE CLASS - DO NOT EDIT - WILL BE REMOVED WHEN WE MOVE TO EMF 2.3 + */ + protected boolean saveElementFeatureMap(EObject o, EStructuralFeature f) + { + List values = (List)helper.getValue(o, f); + int size = values.size(); + for (int i = 0; i < size; i++) + { + FeatureMap.Entry entry = (FeatureMap.Entry)values.get(i); + EStructuralFeature entryFeature = entry.getEStructuralFeature(); + Object value = entry.getValue(); + if (entryFeature instanceof EReference) + { + if (value == null) + { + saveNil(o, entryFeature); + } + else + { + EReference referenceEntryFeature = (EReference)entryFeature; + if (referenceEntryFeature.isContainment()) + { + saveElement((InternalEObject)value, entryFeature); + } + else if (referenceEntryFeature.isResolveProxies()) + { + saveFeatureMapElementReference((EObject)value, referenceEntryFeature); + } + else + { + saveElementIDRef(o, (EObject)value, entryFeature); + } + } + } + else + { + if (entryFeature == XMLTypePackage.eINSTANCE.getXMLTypeDocumentRoot_Text()) + { + String svalue = value.toString(); + if (escape != null) + { + svalue = escape.convertText(svalue); + } + if (!toDOM) + { + doc.addText(svalue); + } + else + { + Node text = document.createTextNode(svalue); + currentNode.appendChild(text); + handler.recordValues(text, o, f, entry); + } + } + else if (entryFeature == XMLTypePackage.eINSTANCE.getXMLTypeDocumentRoot_CDATA()) + { + String stringValue = value.toString(); + if (escape != null) + { + stringValue = escape.convertLines(stringValue); + } + if (!toDOM) + { + doc.addCDATA(stringValue); + } + else + { + Node cdata = document.createCDATASection(stringValue); + currentNode.appendChild(cdata); + handler.recordValues(cdata, o, f, entry); + } + } + else if (entryFeature == XMLTypePackage.eINSTANCE.getXMLTypeDocumentRoot_Comment()) + { + String stringValue = value.toString(); + if (escape != null) + { + stringValue = escape.convertLines(stringValue); + } + if (!toDOM) + { + doc.addComment(stringValue); + } + else + { + // TODO comments are not sent to recordValues + currentNode.appendChild(document.createComment(stringValue)); + } + } + else + { + saveElement(o, value, entryFeature); + } + } + } + return size > 0; + } + + protected final void saveElement(EObject o, Object value, EStructuralFeature f) + { + if (f.getEType() == ChangeSummaryStreamSerializer.ChangeSummary_TYPE) + { + saveChangeSummary(o, f, value); + return; + } + /* super.saveElement(o, value, f); + * TEMPORARILY COPIED FROM BASE CLASS - DO NOT EDIT - WILL BE REMOVED WHEN WE MOVE TO EMF 2.3 + */ + if (value == null) + { + saveNil(o, f); + } + else + { + String svalue = getDatatypeValue(value, f, false); + if (!toDOM) + { + doc.saveDataValueElement(helper.getQName(f), svalue); + } + else + { + helper.populateNameInfo(nameInfo, f); + Element elem = document.createElementNS(nameInfo.getNamespaceURI(), nameInfo.getQualifiedName()); + Node text = document.createTextNode(svalue); + elem.appendChild(text); + currentNode.appendChild(elem); + handler.recordValues(elem, o, f, value); + handler.recordValues(text, o, f, value); + } + } + } + } + + protected XMLSave createXMLSave() { + return new SDOXMLSaveImpl(createXMLHelper()); + } +} diff --git a/branches/sdo-1.1.1-incubating/impl/src/main/java/org/apache/tuscany/sdo/util/resource/XMLDocumentStreamReader.java b/branches/sdo-1.1.1-incubating/impl/src/main/java/org/apache/tuscany/sdo/util/resource/XMLDocumentStreamReader.java new file mode 100644 index 0000000000..64088a0366 --- /dev/null +++ b/branches/sdo-1.1.1-incubating/impl/src/main/java/org/apache/tuscany/sdo/util/resource/XMLDocumentStreamReader.java @@ -0,0 +1,438 @@ +/** + * + * 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.NoSuchElementException; + +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; + +/** + * This class is derived from Apache Axis2 class + * org.apache.axis2.util.StreamWrapper. It's used wrap a XMLStreamReader to create a XMLStreamReader representing a document and it will produce + * START_DOCUMENT, END_DOCUMENT events. + * + */ +public class XMLDocumentStreamReader implements XMLStreamReader { + private static final int STATE_INIT = 0; // The wrapper will produce START_DOCUMENT + + private static final int STATE_SWITCHED = 1; // The real reader will produce events + + private static final int STATE_COMPLETE_AT_NEXT = 2; // The wrapper will produce END_DOCUMENT + + private static final int STATE_COMPLETED = 3; // Done + + private XMLStreamReader realReader = null; + + private int state = STATE_INIT; + + public XMLDocumentStreamReader(XMLStreamReader realReader) { + if (realReader == null) { + throw new UnsupportedOperationException("Reader cannot be null"); + } + + this.realReader = realReader; + + // If the real reader is positioned at START_DOCUMENT, always use the real reader + if (realReader.getEventType() == START_DOCUMENT) + state = STATE_SWITCHED; + } + + public void close() throws XMLStreamException { + realReader.close(); + } + + public int next() throws XMLStreamException { + int returnEvent = -1; + + switch (state) { + case STATE_SWITCHED: + returnEvent = realReader.next(); + if (returnEvent == END_DOCUMENT) { + state = STATE_COMPLETED; + } else if (!realReader.hasNext()) { + state = STATE_COMPLETE_AT_NEXT; + } + break; + case STATE_INIT: + state = STATE_SWITCHED; + returnEvent = realReader.getEventType(); + break; + case STATE_COMPLETE_AT_NEXT: + state = STATE_COMPLETED; + returnEvent = END_DOCUMENT; + break; + case STATE_COMPLETED: + // oops - no way we can go beyond this + throw new NoSuchElementException("End of stream has reached."); + default: + throw new UnsupportedOperationException(); + } + + return returnEvent; + } + + private boolean isDelegating() { + return state == STATE_SWITCHED || state == STATE_COMPLETE_AT_NEXT; + } + + public int nextTag() throws XMLStreamException { + if (isDelegating()) { + return realReader.nextTag(); + } else { + throw new XMLStreamException(); + } + } + + public void require(int i, String s, String s1) throws XMLStreamException { + if (isDelegating()) { + realReader.require(i, s, s1); + } + } + + public boolean standaloneSet() { + if (isDelegating()) { + return realReader.standaloneSet(); + } else { + return false; + } + } + + public int getAttributeCount() { + if (isDelegating()) { + return realReader.getAttributeCount(); + } else { + throw new IllegalStateException(); + } + } + + public String getAttributeLocalName(int i) { + if (isDelegating()) { + return realReader.getAttributeLocalName(i); + } else { + throw new IllegalStateException(); + } + } + + public QName getAttributeName(int i) { + if (isDelegating()) { + return realReader.getAttributeName(i); + } else { + throw new IllegalStateException(); + } + } + + public String getAttributeNamespace(int i) { + if (isDelegating()) { + return realReader.getAttributeNamespace(i); + } else { + throw new IllegalStateException(); + } + } + + public String getAttributePrefix(int i) { + if (isDelegating()) { + return realReader.getAttributePrefix(i); + } else { + throw new IllegalStateException(); + } + } + + public String getAttributeType(int i) { + if (isDelegating()) { + return realReader.getAttributeType(i); + } else { + throw new IllegalStateException(); + } + } + + public String getAttributeValue(int i) { + if (isDelegating()) { + return realReader.getAttributeValue(i); + } else { + throw new IllegalStateException(); + } + } + + public String getAttributeValue(String s, String s1) { + if (isDelegating()) { + return realReader.getAttributeValue(s, s1); + } else { + throw new IllegalStateException(); + } + } + + public String getCharacterEncodingScheme() { + return realReader.getCharacterEncodingScheme(); + } + + public String getElementText() throws XMLStreamException { + if (isDelegating()) { + return realReader.getElementText(); + } else { + throw new XMLStreamException(); + } + } + + public String getEncoding() { + return realReader.getEncoding(); + } + + public int getEventType() { + int event = -1; + switch (state) { + case STATE_SWITCHED: + case STATE_COMPLETE_AT_NEXT: + event = realReader.getEventType(); + break; + case STATE_INIT: + event = START_DOCUMENT; + break; + case STATE_COMPLETED: + event = END_DOCUMENT; + break; + } + return event; + } + + public Location getLocation() { + if (isDelegating()) { + return realReader.getLocation(); + } else { + return null; + } + } + + public QName getName() { + if (isDelegating()) { + return realReader.getName(); + } else { + throw new IllegalStateException(); + } + } + + public String getLocalName() { + if (isDelegating()) { + return realReader.getLocalName(); + } else { + throw new IllegalStateException(); + } + } + + public NamespaceContext getNamespaceContext() { + return realReader.getNamespaceContext(); + } + + public int getNamespaceCount() { + if (isDelegating()) { + return realReader.getNamespaceCount(); + } else { + throw new IllegalStateException(); + } + } + + public String getNamespacePrefix(int i) { + if (isDelegating()) { + return realReader.getNamespacePrefix(i); + } else { + throw new IllegalStateException(); + } + } + + public String getNamespaceURI() { + if (isDelegating()) { + return realReader.getNamespaceURI(); + } else { + throw new IllegalStateException(); + } + } + + public String getNamespaceURI(int i) { + if (isDelegating()) { + return realReader.getNamespaceURI(i); + } else { + throw new IllegalStateException(); + } + } + + public String getNamespaceURI(String s) { + if (isDelegating()) { + return realReader.getNamespaceURI(s); + } else { + throw new IllegalStateException(); + } + } + + public String getPIData() { + if (isDelegating()) { + return realReader.getPIData(); + } else { + throw new IllegalStateException(); + } + } + + public String getPITarget() { + if (isDelegating()) { + return realReader.getPITarget(); + } else { + throw new IllegalStateException(); + } + } + + public String getPrefix() { + if (isDelegating()) { + return realReader.getPrefix(); + } else { + throw new IllegalStateException(); + } + } + + public Object getProperty(String s) throws IllegalArgumentException { + return realReader.getProperty(s); + } + + public String getText() { + if (isDelegating()) { + return realReader.getText(); + } else { + throw new IllegalStateException(); + } + } + + public char[] getTextCharacters() { + if (isDelegating()) { + return realReader.getTextCharacters(); + } else { + throw new IllegalStateException(); + } + } + + public int getTextCharacters(int i, char[] chars, int i1, int i2) throws XMLStreamException { + if (isDelegating()) { + return realReader.getTextCharacters(i, chars, i1, i2); + } else { + throw new IllegalStateException(); + } + } + + public int getTextLength() { + if (isDelegating()) { + return realReader.getTextLength(); + } else { + throw new IllegalStateException(); + } + } + + public int getTextStart() { + if (isDelegating()) { + return realReader.getTextStart(); + } else { + throw new IllegalStateException(); + } + } + + public String getVersion() { + if (isDelegating()) { + return realReader.getVersion(); + } else { + return null; + } + } + + public boolean hasName() { + if (isDelegating()) { + return realReader.hasName(); + } else { + return false; + } + } + + public boolean hasNext() throws XMLStreamException { + if (state == STATE_COMPLETE_AT_NEXT) { + return true; + } else if (state == STATE_COMPLETED) { + return false; + } else if (state == STATE_SWITCHED) { + return realReader.hasNext(); + } else { + return true; + } + } + + public boolean hasText() { + if (isDelegating()) { + return realReader.hasText(); + } else { + return false; + } + } + + public boolean isAttributeSpecified(int i) { + if (isDelegating()) { + return realReader.isAttributeSpecified(i); + } else { + return false; + } + } + + public boolean isCharacters() { + if (isDelegating()) { + return realReader.isCharacters(); + } else { + return false; + } + } + + public boolean isEndElement() { + if (isDelegating()) { + return realReader.isEndElement(); + } else { + return false; + } + } + + public boolean isStandalone() { + if (isDelegating()) { + return realReader.isStandalone(); + } else { + return false; + } + } + + public boolean isStartElement() { + if (isDelegating()) { + return realReader.isStartElement(); + } else { + return false; + } + } + + public boolean isWhiteSpace() { + if (isDelegating()) { + return realReader.isWhiteSpace(); + } else { + return false; + } + } +} \ No newline at end of file diff --git a/branches/sdo-1.1.1-incubating/impl/src/main/java/org/apache/tuscany/sdo/util/resource/XMLFragmentStreamReader.java b/branches/sdo-1.1.1-incubating/impl/src/main/java/org/apache/tuscany/sdo/util/resource/XMLFragmentStreamReader.java new file mode 100644 index 0000000000..66d8fbdc6a --- /dev/null +++ b/branches/sdo-1.1.1-incubating/impl/src/main/java/org/apache/tuscany/sdo/util/resource/XMLFragmentStreamReader.java @@ -0,0 +1,44 @@ +/** + * + * 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 javax.xml.stream.XMLStreamReader; + +interface XMLFragmentStreamReader extends XMLStreamReader { + + // this will help to handle Text within the current element. + // user should pass the element text to the property list as this + // ELEMENT_TEXT as the key. This key deliberately has a space in it + // so that it is not a valid XML name + static final String ELEMENT_TEXT = "Element Text"; + + /** + * Extra method to query the state of the pullparser + * @return + */ + boolean isEndOfFragment(); + + /** + * Initiate the parser - this will do whatever the needed + * tasks to initiate the parser and must be called before + * attempting any specific parsing using this parser + */ + void init(); +} diff --git a/branches/sdo-1.1.1-incubating/impl/src/main/java/org/apache/tuscany/sdo/util/resource/XMLStreamSerializer.java b/branches/sdo-1.1.1-incubating/impl/src/main/java/org/apache/tuscany/sdo/util/resource/XMLStreamSerializer.java new file mode 100644 index 0000000000..2c2dc87aa4 --- /dev/null +++ b/branches/sdo-1.1.1-incubating/impl/src/main/java/org/apache/tuscany/sdo/util/resource/XMLStreamSerializer.java @@ -0,0 +1,414 @@ +/* + * Copyright 2004,2005 The Apache Software Foundation. + * + * 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.tuscany.sdo.util.resource; + +import java.util.Map; +import javax.xml.namespace.NamespaceContext; +import javax.xml.stream.XMLOutputFactory; +import javax.xml.stream.XMLStreamConstants; +import javax.xml.stream.XMLStreamException; +import javax.xml.stream.XMLStreamReader; +import javax.xml.stream.XMLStreamWriter; + +import org.apache.tuscany.sdo.api.SDOHelper; +import org.apache.tuscany.sdo.api.SDOUtil; +/** + * The XMLStreamSerializer pulls events from the XMLStreamReader and dumps into the XMLStreamWriter + */ +public class XMLStreamSerializer implements XMLStreamConstants { + + private static int namespaceSuffix = 0; + + public static final String NAMESPACE_PREFIX = "ns"; + + /* + * 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 serilizer and tells it when to return. Note that it is assumed that this serialization starts on an Element. + */ + + /** + * Field depth + */ + private int depth = 0; + + /** + * Method serialize. + * + * @param node + * @param writer + * @throws XMLStreamException + */ + public void serialize(XMLStreamReader node, XMLStreamWriter writer) throws XMLStreamException { + serializeNode(node, writer); + } + + public void serialize(XMLStreamReader node, XMLStreamWriter writer, Map options) throws XMLStreamException { + this.options = options; + serializeNode(node, writer); + } + /** + * Method serializeNode. + * + * @param reader + * @param writer + * @throws XMLStreamException + */ + protected void serializeNode(XMLStreamReader reader, XMLStreamWriter writer) throws XMLStreamException { + // TODO We get the StAXWriter at this point and uses it hereafter assuming that this is the only entry point to this class. + // If there can be other classes calling methodes of this we might need to change methode signatures to OMOutputer + while (reader.hasNext()) { + int event = reader.next(); + if (event == START_ELEMENT) { + if(options == null){ + serializeElement(reader, writer); + } + else{ + serializeElementWithOptions(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) { + if(options == null){ + serializeEndElement(writer); + }else{ + serializeEndElementWithOptions(writer); + } + depth--; + } else if (event == START_DOCUMENT) { + depth++; // if a start document is found then increment the depth + } else if (event == END_DOCUMENT) { + if (depth != 0) + depth--; // for the end document - reduce the depth + try { + if(options == null){ + serializeEndElement(writer); + } + else{ + serializeEndElementWithOptions(writer); + } + } catch (Exception e) { + // TODO: log exceptions + } + } + if (depth == 0) { + break; + } + } + } + + /** + * @param reader + * @param writer + * @throws XMLStreamException + */ + protected void serializeElement(XMLStreamReader reader, XMLStreamWriter writer) throws XMLStreamException { + String prefix = reader.getPrefix(); + String nameSpaceName = reader.getNamespaceURI(); + if (nameSpaceName != null) { + String writer_prefix = writer.getPrefix(nameSpaceName); + if (writer_prefix != null) { + writer.writeStartElement(nameSpaceName, reader.getLocalName()); + } else { + if (prefix != null) { + writer.writeStartElement(prefix, reader.getLocalName(), nameSpaceName); + writer.writeNamespace(prefix, nameSpaceName); + writer.setPrefix(prefix, nameSpaceName); + } else { + writer.writeStartElement(nameSpaceName, reader.getLocalName()); + writer.writeDefaultNamespace(nameSpaceName); + writer.setDefaultNamespace(nameSpaceName); + } + } + } else { + writer.writeStartElement(reader.getLocalName()); + } + + // add the namespaces + int count = reader.getNamespaceCount(); + String namespacePrefix; + for (int i = 0; i < count; i++) { + namespacePrefix = reader.getNamespacePrefix(i); + if (namespacePrefix != null && namespacePrefix.length() == 0) + continue; + + serializeNamespace(namespacePrefix, reader.getNamespaceURI(i), writer); + } + + // add attributes + serializeAttributes(reader, writer); + + } + /** + * @param reader + * @param writer + * @throws XMLStreamException + */ + protected void serializeElementWithOptions(XMLStreamReader reader, XMLStreamWriter writer) throws XMLStreamException { + ++nest; + boolean brk=false; + if(oldNest != nest){ + if(oldNest < nest) brk = true; + oldNest = nest; + } + startElement(writer); + String prefix = reader.getPrefix(); + String nameSpaceName = reader.getNamespaceURI(); + if (nameSpaceName != null) { + String writer_prefix = writer.getPrefix(nameSpaceName); + + if (writer_prefix != null) { + if(brk && nest != 1){ + breakLine(writer, true, true); + } + else{ + breakLine(writer, false, true); + } + + writer.writeStartElement(nameSpaceName, reader.getLocalName()); + } else { + if (prefix != null) { + if(brk && nest == 1){ + breakLine(writer, false, true); + } + writer.writeStartElement(prefix, reader.getLocalName(), nameSpaceName); + writer.writeNamespace(prefix, nameSpaceName); + writer.setPrefix(prefix, nameSpaceName); + } else { + if(brk && nest == 1){ + breakLine(writer, false, true); + } + writer.writeStartElement(nameSpaceName, reader.getLocalName()); + writer.writeDefaultNamespace(nameSpaceName); + writer.setDefaultNamespace(nameSpaceName); + if(brk && nest == 1){ + breakLine(writer, false, true); + } + } + } + } else { + writer.writeStartElement(reader.getLocalName()); + } + + // add the namespaces + int count = reader.getNamespaceCount(); + String namespacePrefix; + for (int i = 0; i < count; i++) { + namespacePrefix = reader.getNamespacePrefix(i); + if (namespacePrefix != null && namespacePrefix.length() == 0) + continue; + + serializeNamespace(namespacePrefix, reader.getNamespaceURI(i), writer); + } + + // add attributes + serializeAttributes(reader, writer); + } + /** + * Method serializeEndElement. + * + * @param writer + * @throws XMLStreamException + */ + protected void serializeEndElement(XMLStreamWriter writer) throws XMLStreamException { + writer.writeEndElement(); + } + + /** + * Method serializeEndElement. + * + * @param writer + * @throws XMLStreamException + */ + protected void serializeEndElementWithOptions(XMLStreamWriter writer) throws XMLStreamException { + --nest; + if(oldNest > nest+1){ + nest++; + breakLine(writer, false, true); + writer.writeEndElement(); + breakLine(writer, true, false); + nest--; + } + else{ + writer.writeEndElement(); + breakLine(writer, true, false); + } + } + /** + * @param reader + * @param writer + * @throws XMLStreamException + */ + protected void serializeText(XMLStreamReader reader, XMLStreamWriter writer) throws XMLStreamException { + writer.writeCharacters(reader.getText()); + } + + /** + * 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 serializeAttributes(XMLStreamReader reader, XMLStreamWriter writer) + throws XMLStreamException { + int count = reader.getAttributeCount(); + String prefix = null; + String namespaceName = null; + String writerPrefix = null; + for (int i = 0; i < count; i++) { + prefix = reader.getAttributePrefix(i); + namespaceName = reader.getAttributeNamespace(i); + if (namespaceName != null) { + writerPrefix = writer.getNamespaceContext().getPrefix(namespaceName); + } + + if (!"".equals(namespaceName)) { + // prefix has already being declared but this particular attrib + // has a + // no prefix attached. So use the prefix provided by the writer + if (writerPrefix != null && (prefix == null || prefix.equals(""))) { + writer.writeAttribute(writerPrefix, + namespaceName, + reader.getAttributeLocalName(i), + reader.getAttributeValue(i)); + + // writer prefix is available but different from the current + // prefix of the attrib. We should be decalring the new + // prefix + // as a namespace declaration + } else if (prefix != null && !"".equals(prefix) && !prefix.equals(writerPrefix)) { + writer.writeNamespace(prefix, namespaceName); + writer.writeAttribute(prefix, namespaceName, reader.getAttributeLocalName(i), reader + .getAttributeValue(i)); + + // 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 + } else if (prefix == null || prefix.equals("")) { + prefix = generateUniquePrefix(writer.getNamespaceContext()); + writer.writeNamespace(prefix, namespaceName); + writer.writeAttribute(prefix, namespaceName, reader.getAttributeLocalName(i), reader + .getAttributeValue(i)); + } else { + writer.writeAttribute(prefix, namespaceName, reader.getAttributeLocalName(i), reader + .getAttributeValue(i)); + } + } else { + // empty namespace is equal to no namespace! + writer.writeAttribute(reader.getAttributeLocalName(i), reader.getAttributeValue(i)); + } + + } + } + + /** + * 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 serializeNamespace. + * + * @param prefix + * @param URI + * @param writer + * @throws XMLStreamException + */ + private void serializeNamespace(String prefix, String URI, XMLStreamWriter writer) throws XMLStreamException { + String prefix1 = writer.getPrefix(URI); + if (prefix1 == null) { + writer.writeNamespace(prefix, URI); + writer.setPrefix(prefix, URI); + } + } + private String lineBreak, indent, margin = null; + + private int nest; + private int oldNest; + + private void breakLine(XMLStreamWriter writer, boolean breakLine, boolean others) throws XMLStreamException { + if (options == null || (lineBreak==null && indent==null && margin==null) ) + return; + if(breakLine) + writer.writeCharacters(lineBreak); + + if (margin != null){ + if(others) + writer.writeCharacters(margin); + } + + if (indent != null && others){ + for (int count = nest; count != 1; --count){//!= 0 + writer.writeCharacters(indent); + } + } + } + + private Map options; + + static private final String STRING_OPTION = "String option"; + + void startElement(XMLStreamWriter writer) throws XMLStreamException { + if (options == null) + return; + lineBreak = (String) options.get(SDOHelper.XMLOptions.XML_SAVE_LINE_BREAK); + if (lineBreak == null) + return; + margin = (String) options.get(SDOHelper.XMLOptions.XML_SAVE_MARGIN); + indent = (String) options.get(SDOHelper.XMLOptions.XML_SAVE_INDENT); + } +} -- cgit v1.2.3