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 +++++++++++++++++++++ 1 file changed, 663 insertions(+) create mode 100644 branches/sdo-1.0-incubating/impl/src/main/java/org/apache/tuscany/sdo/util/resource/ChangeSummaryStreamDeserializer.java (limited to 'branches/sdo-1.0-incubating/impl/src/main/java/org/apache/tuscany/sdo/util/resource/ChangeSummaryStreamDeserializer.java') diff --git a/branches/sdo-1.0-incubating/impl/src/main/java/org/apache/tuscany/sdo/util/resource/ChangeSummaryStreamDeserializer.java b/branches/sdo-1.0-incubating/impl/src/main/java/org/apache/tuscany/sdo/util/resource/ChangeSummaryStreamDeserializer.java new file mode 100644 index 0000000000..6dc461a2fe --- /dev/null +++ b/branches/sdo-1.0-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; + } +} -- cgit v1.2.3