summaryrefslogtreecommitdiffstats
path: root/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com
diff options
context:
space:
mode:
Diffstat (limited to 'sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com')
-rw-r--r--sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/DataAccessService.java64
-rw-r--r--sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/DataMapper.java90
-rw-r--r--sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/ObjectFactory.java17
-rw-r--r--sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/ObjectPropertyVisitor.java29
-rw-r--r--sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/PartialDataFactory.java25
-rw-r--r--sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/PartialDataObject.java81
-rw-r--r--sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/Property.java56
-rw-r--r--sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/PropertyNotAvailableException.java21
-rw-r--r--sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/PropertyTree.java236
-rw-r--r--sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/ReachableDefinition.java56
-rw-r--r--sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/SelectClause.java173
-rw-r--r--sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/Snapshot.java44
-rw-r--r--sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/SnapshotDefinition.java18
-rw-r--r--sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/Type.java50
-rw-r--r--sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/core/IDataObjectService.java10
-rw-r--r--sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/helper/TypeHelper.java33
-rw-r--r--sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/helper/XsdScanner.java78
-rw-r--r--sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/impl/BytesTypeConverter.java32
-rw-r--r--sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/impl/DataAccessService.java11
-rw-r--r--sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/impl/EnumTypeConverter.java21
-rw-r--r--sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/impl/HelperProviderImpl.java114
-rw-r--r--sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/impl/SdoImplementationFactory.java58
-rw-r--r--sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/impl/SerializableTypeConverter.java42
-rw-r--r--sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/impl/TypeConverter.java58
-rw-r--r--sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/impl/TypeProvider.java29
-rw-r--r--sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/impl/XsdTypeProvider.java43
-rw-r--r--sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/implementation/AbstractDataObject.java560
-rw-r--r--sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/implementation/AbstractPartialDataObject.java211
-rw-r--r--sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/implementation/BinarySerializer.java115
-rw-r--r--sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/implementation/BulkProperty.java18
-rw-r--r--sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/implementation/ClassPathImplementationClassRepository.java46
-rw-r--r--sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/implementation/CompleteSerializer.java74
-rw-r--r--sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/implementation/ContainedDataObject.java13
-rw-r--r--sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/implementation/ContainmentSerializer.java71
-rw-r--r--sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/implementation/DataFactoryImpl.java52
-rw-r--r--sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/implementation/DataGraphImplementation.java51
-rw-r--r--sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/implementation/DataObjectConverter.java147
-rw-r--r--sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/implementation/DataObjectHelper.java61
-rw-r--r--sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/implementation/DataObjectImplementation.java56
-rw-r--r--sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/implementation/DataObjectList.java131
-rw-r--r--sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/implementation/DataObjectStreamer.java178
-rw-r--r--sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/implementation/DataObjectXmlParser.java146
-rw-r--r--sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/implementation/EqualityHelperImpl.java135
-rw-r--r--sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/implementation/IDataObjectList.java10
-rw-r--r--sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/implementation/ImplementationClassRepository.java129
-rw-r--r--sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/implementation/PropertyImplementation.java138
-rw-r--r--sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/implementation/SdoImplementationException.java26
-rw-r--r--sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/implementation/SnapshotImplementation.java377
-rw-r--r--sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/implementation/SnapshotUnion.java36
-rw-r--r--sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/implementation/TypeHelperImpl.java435
-rw-r--r--sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/implementation/TypeImplementation.java267
-rw-r--r--sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/implementation/XMLDocumentImpl.java85
-rw-r--r--sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/implementation/XMLHelperImpl.java102
-rw-r--r--sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/implementation/XSDHelperImpl.java616
-rw-r--r--sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/mapper/AbstractPropertyAccessor.java35
-rw-r--r--sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/mapper/BeanPropertyAccessor.java79
-rw-r--r--sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/mapper/BeanPropertyAccessorBuilder.java62
-rw-r--r--sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/mapper/DelegatingDataMapper.java71
-rw-r--r--sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/mapper/DelegatingPartialDataObjectMapper.java119
-rw-r--r--sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/mapper/ExtendablePropertyAccessorBuilder.java75
-rw-r--r--sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/mapper/FalsePropertyAccessor.java13
-rw-r--r--sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/mapper/FilteringPartialDataObjectMapper.java30
-rw-r--r--sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/mapper/JavaBeanMapper.java125
-rw-r--r--sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/mapper/ManyValuedBeanPropertyAccessor.java25
-rw-r--r--sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/mapper/ManyValuedNullPropertyAccessor.java23
-rw-r--r--sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/mapper/NullPropertyAccessor.java17
-rw-r--r--sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/mapper/PartialDataObjectMapper.java77
-rw-r--r--sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/mapper/PropertyAccessor.java26
-rw-r--r--sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/mapper/PropertyAccessorBuilder.java53
-rw-r--r--sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/mapper/TruePropertyAccessor.java13
-rw-r--r--sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/mapper/TypeMapper.java81
-rw-r--r--sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/model/CatalogIdentity.java51
-rw-r--r--sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/model/Identity.java60
-rw-r--r--sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/model/LongIdentity.java24
-rw-r--r--sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/model/ObjectReference.java105
-rw-r--r--sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/model/StringIdentity.java14
-rw-r--r--sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/osgi/BundleClassHolder.java28
-rw-r--r--sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/osgi/OsgiBasedImplementationClassRepository.java84
-rw-r--r--sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/osgi/SdoActivator.java90
-rw-r--r--sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/util/ClassForNameClassHolder.java24
-rw-r--r--sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/util/ClassHolder.java14
-rw-r--r--sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/util/ClassLoaderBasedClassHolder.java24
-rw-r--r--sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/util/ResultSet.java133
83 files changed, 7350 insertions, 0 deletions
diff --git a/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/DataAccessService.java b/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/DataAccessService.java
new file mode 100644
index 0000000000..4797db417f
--- /dev/null
+++ b/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/DataAccessService.java
@@ -0,0 +1,64 @@
+package com.agfa.hap.sdo;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+
+import com.agfa.hap.sdo.implementation.SnapshotImplementation;
+import com.agfa.hap.sdo.mapper.PartialDataObjectMapper;
+
+public class DataAccessService {
+ private final static PartialDataObjectMapper MAPPER = new PartialDataObjectMapper();
+ public static Snapshot createSnapShot(SnapshotDefinition def, PartialDataObject root) {
+ return createSnapShot(MAPPER, def, root);
+ }
+
+ public static <T> Snapshot createSnapShot(DataMapper<T> mapper, SnapshotDefinition def, T root) {
+ List<T> roots = new ArrayList<T>(1);
+ roots.add(root);
+ return new SnapshotImplementation(mapper, def, roots);
+ }
+
+ @SuppressWarnings("unchecked")
+ public static <T> Snapshot createMultiSnapShot(SnapshotDefinition def, Collection<? extends T> root) {
+ return createMultiSnapShot((DataMapper<T>) MAPPER, def, root);
+ }
+
+ public static <T> Snapshot createMultiSnapShot(DataMapper<T> mapper, SnapshotDefinition def, Collection<? extends T> root) {
+ return new SnapshotImplementation(mapper, def, root);
+ }
+
+ public static PartialDataObject getRootObject(Snapshot s) {
+ return getRootObject(MAPPER, s);
+ }
+
+ /**
+ * @return The first and only root object of the snapshot. This is always a different
+ * instance than the object that was used to create the snapshot.
+ * @throws RuntimeException if there are no root objects or if there is more than one root object
+ */
+ public static <T> T getRootObject(DataMapper<T> mapper, Snapshot s) {
+ List<T> roots = getRootObjects(mapper, s);
+ if (roots.isEmpty()){
+ throw new RuntimeException("snapshot is empty");
+ }
+ if (roots.size() > 1){
+ throw new RuntimeException("more than 1 rootobject:" + roots.size());
+ }
+ return roots.get(0);
+ }
+
+ public static List<PartialDataObject> getRootObjects(Snapshot s) {
+ return getRootObjects(MAPPER, s);
+ }
+
+ public static <T> List<T> getRootObjects(DataMapper<T> mapper, Snapshot s) {
+ return s.extract(mapper);
+ }
+
+ public static <T> Iterator<T> rootObjectsIterator(DataMapper<T> mapper, Snapshot s) {
+ return getRootObjects(mapper, s).iterator();
+ }
+
+}
diff --git a/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/DataMapper.java b/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/DataMapper.java
new file mode 100644
index 0000000000..e0987c72f4
--- /dev/null
+++ b/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/DataMapper.java
@@ -0,0 +1,90 @@
+package com.agfa.hap.sdo;
+
+import java.util.Collection;
+import java.util.Iterator;
+
+import commonj.sdo.DataObject;
+
+/**
+ * Interface that allows any object to be exposed as {@link DataObject} instances.
+ * The implementation is guaranteed to work for DataObject instances as well.
+ * @author AMOCZ
+ */
+public interface DataMapper<T> {
+
+ /**
+ * @return The sdo type that corresponds with this object. It is assumed
+ * that for all properties of this type, appropriate values can be
+ * retrieved from the instance.
+ * @exception throws {@link IllegalArgumentException} in case no
+ * corresponding sdo type can be found.
+ */
+ Type getType(T object);
+
+ /**
+ * @return The sdo type that corresponds with this class. It is assumed
+ * that for all properties of this type, appropriate values can be
+ * retrieved from the instance.
+ * @return null if no sdo type can be found
+ * @exception throws {@link IllegalArgumentException} in case no
+ * corresponding sdo type can be found.
+ * TODO 1 mechanism for indication no type was found
+ */
+ Type getCorrespondingType(Class clazz);
+
+ /**
+ * @return An iterator over all values of this property of the given
+ * Object. Property should be a many-valued property.
+ * returns an "empty" iterator in case the property is null
+ * (SDO doesn't have the concept of a many valued property that can be null)
+ */
+ Iterator<? extends T> getObjects(T object, Property property);
+
+ /**
+ * @return The value of the property for the given object.
+ */
+ Object getProperty(T object, Property property);
+
+ /**
+ * Assigns the given value to the property of the object. If the property
+ * is many-valued, adds the property to the collection of values.
+ */
+ void setProperty(T object, Property property, Object value);
+
+ /**
+ * @return if this property is a bulk property for the given implementation clazz.
+ * Bulk properties
+ * are accessed in bulk (@see {@link #getProperties(Collection, Property, SnapshotDefinition)}
+ * to allow more efficient retrieval.
+ */
+ boolean isBulkProperty(Class clazz, Property property);
+
+ /**
+ * Return the corresponding values for this bulk property for the given object.
+ * A snapshotdefinition is passed as indication for which child objects are
+ * needed as well.
+ */
+ Collection<T> getProperties(Collection<T> object, Property bulkProperty, SnapshotDefinition def);
+
+ /**
+ * Marks a property as unavailable ({@see {@link PartialDataObject#isAvailable(Property)}}.
+ */
+ void setUnavailable(T object, Property property);
+
+ boolean isProxy(T object);
+
+ /**
+ * @return A newly created instance of which the class corresponds to the given type.
+ * @return null if the datamapper is unable to create a class for the given type
+ * @see DataMapper#getCorrespondingType(Class)
+ */
+ T create(Type type);
+
+ /**
+ * Create a new proxy. The type is passed as parameter as this accessor might
+ * be usable for multiple types.
+ * @return null if the datamapper is unable to create a proxy for the given type
+ */
+ T newProxy(Type type, Object identity);
+
+} \ No newline at end of file
diff --git a/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/ObjectFactory.java b/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/ObjectFactory.java
new file mode 100644
index 0000000000..dfdd865670
--- /dev/null
+++ b/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/ObjectFactory.java
@@ -0,0 +1,17 @@
+package com.agfa.hap.sdo;
+
+
+public interface ObjectFactory<T> {
+
+ /**
+ * @return A newly created instance of which the class corresponds to the given type.
+ * @see DataMapper#getCorrespondingType(Class)
+ */
+ T create(Type type);
+
+ /**
+ * Create a new proxy. The type is passed as parameter as this accessor might
+ * be usable for multiple types.
+ */
+ T newProxy(Type type, Object identity);
+}
diff --git a/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/ObjectPropertyVisitor.java b/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/ObjectPropertyVisitor.java
new file mode 100644
index 0000000000..e64a2eab6e
--- /dev/null
+++ b/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/ObjectPropertyVisitor.java
@@ -0,0 +1,29 @@
+package com.agfa.hap.sdo;
+
+
+/**
+ * Visitor interface for visiting all properties belong to a snapshot.
+ * @author AMOCZ
+ * @see SnapshotDefinition
+ */
+public interface ObjectPropertyVisitor {
+ void startDataObject(Object instance, Type type) throws Exception;
+ /**
+ * @param instance
+ * @param property
+ * @param value
+ * @return true if value can be further visited recursively
+ * @throws Exception
+ */
+ boolean visitProperty(Object instance, Property property, Object value) throws Exception;
+ void visitProxyProperty(Object instance, Property property, Object identity) throws Exception;
+ void endDataObject(Object instance, Type type) throws Exception;
+
+ /**
+ * Bulk properties are properties that are heavy to retrieve and as such should
+ * be visited in bulk. A typical example is a property that can only be filled in
+ * by doing a query.
+ * Bulk properties are visited after all other objects have been visited.
+ */
+ void visitBulkProperty(Object instance, Property property, SnapshotDefinition subselect);
+}
diff --git a/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/PartialDataFactory.java b/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/PartialDataFactory.java
new file mode 100644
index 0000000000..6568b0e4dc
--- /dev/null
+++ b/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/PartialDataFactory.java
@@ -0,0 +1,25 @@
+package com.agfa.hap.sdo;
+
+import commonj.sdo.Type;
+import commonj.sdo.helper.DataFactory;
+
+/**
+ * DataFactory that creates {@link PartialDataObject} instances.
+ * @author AMOCZ
+ */
+public interface PartialDataFactory extends DataFactory {
+
+ PartialDataObject create(Type type);
+
+ PartialDataObject create(String uri, String typeName);
+
+ PartialDataObject createProxy(Type type, Object identity);
+
+ PartialDataObject createProxy(String uri, String typeName, Object identity);
+
+ /**
+ * The default PartialDataFactory.
+ */
+ PartialDataFactory INSTANCE = (PartialDataFactory) DataFactory.INSTANCE;
+
+}
diff --git a/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/PartialDataObject.java b/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/PartialDataObject.java
new file mode 100644
index 0000000000..f837dcbd9b
--- /dev/null
+++ b/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/PartialDataObject.java
@@ -0,0 +1,81 @@
+package com.agfa.hap.sdo;
+
+import commonj.sdo.DataObject;
+
+/**
+ * HAP-specific extension to {@link commonj.sdo.DataObject}.
+ *
+ * A {@link DataObject} of which only a subset of its properties
+ * are available. Getting or setting a property which is not available
+ * results in an {@link PropertyNotAvailableException} being thrown.
+ * <br />
+ * A PartialDataObject that only has its identity property available is called a
+ * <i>proxy</i>.
+ * <br />
+ * PartialDataObjects are typically used (1) to allow dataobjects to have only a subset of their
+ * many-valued properties available and (2) to enable data object proxies.
+ * <br />
+ * Properties that are not available are also not set (i.e. {@link
+ * DataObject#isSet(int)} return false).
+ *
+ * @see Snapshot
+ * @author AMOCZ
+ */
+public interface PartialDataObject extends commonj.sdo.DataObject {
+
+ boolean isAvailable(commonj.sdo.Property property);
+
+ /**
+ * Strips the property such that is no longer available.
+ */
+ void setUnavailable(commonj.sdo.Property property);
+
+ /**
+ * @return If this data object is a proxy. Proxies only have their identity property
+ * available.
+ */
+ boolean isProxy();
+
+ /**
+ * Marker value for unavailable properties
+ */
+ public static final Object UNAVAILABLE_PROPERTY = new Object();
+
+ /**
+ * @return The value of the {@link Type#getIdentityProperty() identity property} for this
+ * instance.
+ */
+ Object getIdentity();
+
+ /**
+ * Sets the value of the {@link Type#getIdentityProperty() identity property} for this
+ * instance.
+ */
+ void setIdentity(Object object);
+
+ Type getType();
+
+ Property getContainmentProperty();
+
+ PartialDataObject getContainer();
+
+ PartialDataObject getDataObject(String name);
+
+ PartialDataObject getDataObject(commonj.sdo.Property property);
+
+ PartialDataObject getDataObject(int index);
+
+ PartialDataObject createDataObject(String name);
+
+ PartialDataObject createDataObject(int index);
+
+ PartialDataObject createDataObject(commonj.sdo.Property property);
+
+ PartialDataObject createDataObject(String propertyName, String namespaceURI, String typeName);
+
+ PartialDataObject createDataObject(int propertyIndex, String namespaceURI, String typeName);
+
+ PartialDataObject createDataObject(commonj.sdo.Property property, commonj.sdo.Type type);
+
+}
+
diff --git a/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/Property.java b/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/Property.java
new file mode 100644
index 0000000000..54219f2c7d
--- /dev/null
+++ b/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/Property.java
@@ -0,0 +1,56 @@
+/**
+ * <copyright>
+ *
+ * Service Data Objects
+ * Version 2.0
+ * Licensed Materials - Property of BEA and IBM
+ *
+ * (c) Copyright BEA Systems, Inc. and International Business Machines Corp 2005. All rights reserved.
+ *
+ * </copyright>
+ *
+ */
+
+package com.agfa.hap.sdo;
+
+import java.util.List;
+
+/**
+ * A representation of a Property in the {@link Type type} of a {@link DataObject data object}.
+ *
+ * We extend {@link commonj.sdo.Property} to have type safety (through generics) and to
+ * add functionality such as {@link #getIndex()}.
+ */
+public interface Property extends commonj.sdo.Property {
+
+ /**
+ * Returns the type of the Property.
+ * @return the Property type.
+ */
+ Type getType();
+
+ /**
+ * Returns the containing type of this Property.
+ * @return the Property's containing type.
+ * @see Type#getProperties()
+ */
+ Type getContainingType();
+
+ /**
+ * Returns the opposite Property if the Property is bi-directional or null otherwise.
+ * @return the opposite Property if the Property is bi-directional or null
+ */
+ Property getOpposite();
+
+ /**
+ * Return a list of alias names for this Property.
+ * @return a list of alias names for this Property.
+ */
+ List<String> getAliasNames();
+
+ /**
+ * Return the index in the list of properties of its containingtype.
+ * @return
+ */
+ int getIndex();
+}
diff --git a/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/PropertyNotAvailableException.java b/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/PropertyNotAvailableException.java
new file mode 100644
index 0000000000..0003d2e4ae
--- /dev/null
+++ b/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/PropertyNotAvailableException.java
@@ -0,0 +1,21 @@
+package com.agfa.hap.sdo;
+
+
+import commonj.sdo.Property;
+
+/**
+ * Exception throw when a property that is not filled in in accessed
+ * on a {@link ProxyDataObject}.
+ * @author AMOCZ
+ *
+ */
+public class PropertyNotAvailableException extends RuntimeException {
+
+ private static final long serialVersionUID = -1662462690347023219L;
+
+ public PropertyNotAvailableException(PartialDataObject source, Property prop) {
+ super("Property " + prop.getName() + " is not available on this instance of " + source.getType().getName()
+ + (source.getType().getIdentityProperty()==null?" id=null":" id=" + source.getIdentity()));
+ }
+
+}
diff --git a/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/PropertyTree.java b/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/PropertyTree.java
new file mode 100644
index 0000000000..f004276658
--- /dev/null
+++ b/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/PropertyTree.java
@@ -0,0 +1,236 @@
+/**
+ *
+ */
+package com.agfa.hap.sdo;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+import java.util.Map.Entry;
+
+
+/**
+ * Helper class for {@link SelectClause} that represents
+ * a comma-separated list of properties in a structured form.
+ * @author AMOCZ
+ *
+ */
+public abstract class PropertyTree {
+
+ public abstract String asCommmaSeparatedString();
+
+ public abstract PropertyTree getProperty(String property);
+
+ public abstract PropertyTree getProperty(Property property);
+
+ public abstract boolean containsProperty(Property property);
+
+ public abstract boolean containsProperty(String property);
+
+ public static PropertyTree newPropertyTree(Type type, String selectClause) {
+ PropertyTree result = new MapBasedPropertyTree(type);
+ String[] parts = selectClause.split(",");
+ for (String part : parts) {
+ if (part.length() > 0) {
+ result = result.extend(part);
+ }
+ }
+ return result;
+ }
+
+ protected PropertyTree extend(String property) {
+ property = property.trim();
+ int index = property.indexOf('.');
+ if (index < 0) {
+ if (property.length() == 1 && property.charAt(0) == '*') {
+ return STAR_PROPERTYTREE;
+ }
+ if (property.length() == 2 && property.charAt(0) == '*' && property.charAt(1) == '*') {
+ return STARSTAR_PROPERTYTREE;
+ }
+ addSimpleProperty(property);
+ } else {
+ addComposedProperty(property.substring(0, index), property.substring(index+1));
+ }
+ return this;
+ }
+
+ protected void addSimpleProperty(String propertyName) {
+ // ok
+ }
+
+ protected void addComposedProperty(String ownerName, String childName) {
+ // ok
+ }
+
+ final static PropertyTree EMPTY_PROPERTYTREE = new PropertyTree() {
+ public PropertyTree getProperty(String property) {
+ return null;
+ }
+
+ public PropertyTree getProperty(Property property) {
+ return null;
+ }
+
+ public boolean containsProperty(Property property) {
+ return false;
+ }
+
+ public boolean containsProperty(String property) {
+ return false;
+ }
+
+ @Override
+ public String asCommmaSeparatedString() {
+ return "";
+ }
+
+ };
+
+ final static PropertyTree STAR_PROPERTYTREE = new PropertyTree() {
+ public PropertyTree getProperty(String property) {
+ return EMPTY_PROPERTYTREE;
+ }
+
+ public PropertyTree getProperty(Property property) {
+ return EMPTY_PROPERTYTREE;
+ }
+
+ public boolean containsProperty(Property property) {
+ return true;
+ }
+
+ public boolean containsProperty(String property) {
+ return true;
+ }
+
+ @Override
+ public String asCommmaSeparatedString() {
+ throw new UnsupportedOperationException();
+ }
+
+
+
+ };
+
+ final static PropertyTree STARSTAR_PROPERTYTREE = new PropertyTree() {
+ public PropertyTree getProperty(String property) {
+ return this;
+ }
+
+ public PropertyTree getProperty(Property property) {
+ return this;
+ }
+
+ public boolean containsProperty(Property property) {
+ return true;
+ }
+
+ public boolean containsProperty(String property) {
+ return true;
+ }
+
+ @Override
+ public String asCommmaSeparatedString() {
+ throw new UnsupportedOperationException();
+ }
+
+ };
+
+ static class MapBasedPropertyTree extends PropertyTree {
+ private Map<Property, PropertyTree> properties;
+ private Type type;
+
+ public MapBasedPropertyTree(Type type) {
+ this.type = type;
+ properties = new HashMap<Property, PropertyTree>();
+ }
+
+ public Type getType() {
+ return type;
+ }
+
+ public Set<Entry<Property, PropertyTree>> getProperties() {
+ return properties.entrySet();
+ }
+
+ public PropertyTree getProperty(String property) {
+ return properties.get(type.getProperty(property));
+ }
+
+ public PropertyTree getProperty(Property property) {
+ return properties.get(property);
+ }
+
+ public boolean containsProperty(Property property) {
+ return properties.containsKey(property);
+ }
+
+ public boolean containsProperty(String property) {
+ return properties.containsKey(type.getProperty(property));
+ }
+
+ @Override
+ protected void addSimpleProperty(String property) {
+ Property prop = type.getProperty(property);
+ if (prop == null) {
+ throw new IllegalArgumentException("Unknown property " + property + " in type " + type.getName());
+ }
+ if (!prop.getType().isDataType() && !properties.containsKey(prop)) {
+ properties.put(prop, new MapBasedPropertyTree(prop.getType()));
+ }
+ }
+
+ @Override
+ protected void addComposedProperty(String owner, String child) {
+ Property prop = type.getProperty(owner);
+ if (prop == null) {
+ throw new IllegalArgumentException("Unknown property " + owner + " in type " + type.getName());
+ }
+ if (!prop.getType().isDataType()) {
+ PropertyTree children = properties.get(prop);
+ if (children == null) {
+ children = newPropertyTree(prop.getType(), child);
+ properties.put(prop, children);
+ } else {
+ children.extend(child);
+ }
+ }
+ }
+
+ public Collection<String> propertyPaths() {
+ Collection<String> paths = new ArrayList<String>();
+ for (Entry<Property, PropertyTree> entry : this.getProperties()){
+ String rootPropName = entry.getKey().getName();
+ MapBasedPropertyTree tree = (MapBasedPropertyTree) entry.getValue();
+ Collection<String> childPaths = tree.propertyPaths();
+ if (childPaths.isEmpty()){
+ paths.add(rootPropName);
+ } else {
+ for (String childPath : childPaths){
+ paths.add(rootPropName + "." + childPath);
+ }
+ }
+ }
+ return paths;
+ }
+
+ public String asCommmaSeparatedString(){
+ Collection<String> propertyPaths = this.propertyPaths();
+ if (propertyPaths.isEmpty()){
+ return "";
+ }
+ StringBuilder builder = new StringBuilder();
+ for(String path : propertyPaths){
+ builder.append(path);
+ builder.append(',');
+ }
+ builder.setLength(builder.length()-1); //remove last ,
+ return builder.toString();
+ }
+
+ }
+
+} \ No newline at end of file
diff --git a/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/ReachableDefinition.java b/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/ReachableDefinition.java
new file mode 100644
index 0000000000..919f68c98d
--- /dev/null
+++ b/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/ReachableDefinition.java
@@ -0,0 +1,56 @@
+package com.agfa.hap.sdo;
+
+import java.util.Iterator;
+
+
+/**
+ * {@link SnapshotDefinition} that includes everything that is reachable
+ * from the root object.
+ * starting from the root object all child properties are visited
+ * properties that are considered "proxy" by the datamapper are
+ * visited as proxies and will be serialized as sdo . (ie when using a HibernateDataMapper lazily retrieved objects that are "unitialized"
+ * will be serialized as a sdo proxy)
+ * @author AMOCZ
+ */
+@Deprecated
+public class ReachableDefinition implements SnapshotDefinition {
+
+ public <T> void visit(DataMapper<T> mapper, ObjectPropertyVisitor visitor, T root) throws Exception {
+ Type type = mapper.getType(root);
+ visitor.startDataObject(root, type);
+ for (Property p : type.getProperties()) {
+ try {
+ if (p.isMany()) {
+ Iterator<? extends T> it = mapper.getObjects(root, p);
+ while (it.hasNext()) {
+ visitChild(mapper, visitor, root, p, it.next());
+ }
+ } else {
+ visitChild(mapper, visitor, root, p, mapper.getProperty(root, p));
+ }
+ } catch (PropertyNotAvailableException e) {
+ // ok;
+ }
+ }
+ visitor.endDataObject(root, type);
+ }
+
+ private <T> void visitChild(DataMapper<T> mapper, ObjectPropertyVisitor visitor, T parent, Property property, Object value) throws Exception {
+ if (value != null) {
+ if (!property.getType().isDataType()) {
+ T child = (T) value;
+ if (mapper.isProxy(child)) {
+ Type childType = mapper.getType(child);
+ Object identity = mapper.getProperty(child, childType.getIdentityProperty());
+ visitor.visitProxyProperty(parent, property, identity);
+ return;
+ }
+ }
+ boolean recurse = visitor.visitProperty(parent, property, value);
+ if (recurse) {
+ visit(mapper, visitor, (T) value);
+ }
+ }
+ }
+
+}
diff --git a/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/SelectClause.java b/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/SelectClause.java
new file mode 100644
index 0000000000..80457010af
--- /dev/null
+++ b/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/SelectClause.java
@@ -0,0 +1,173 @@
+package com.agfa.hap.sdo;
+
+import java.util.Iterator;
+
+import commonj.sdo.Type;
+
+
+/**
+ * SnapshotDefinition defined by a comma-separated list of
+ * property paths. Datatype properties (e.g. strings, dates etc) are included by default
+ * and should not be part of the select clause. Many-valued properties that are not part of
+ * the select string will be unavailable. Single-valued properties that are not part of
+ * the select string will be available as proxies when they have their Identity property filled
+ * in. Otherwise, they will not be part of the snapshot.
+ * <p />
+ * Using '*' as the name of a property indicates all properties of that type.
+ * Using '**' indicates all properties recursively. This means all child
+ * properties including all properties of those child properties (in case
+ * they are not datatypes) recursively. This is similar to {@link ReachableDefinition}.
+ * <p />
+ * Ex. address, serviceRequests, medicalCase.* for the Patient type will return all
+ * basic properties of Patient, its address, its serviceRequests. And also its complete
+ * medicalCase including many-valued properties and all its direct links.
+ * <p />
+ * Properties that are part of the definition but are not available when creating the snapshot
+ * will be accessed. That may result in {@link PropertyNotAvailableException} being thrown or
+ * might result in lazy retrieval of data (e.g. when working with Hibernate proxies).
+ * @author AMOCZ
+ */
+public class SelectClause implements SnapshotDefinition {
+
+ private PropertyTree propertyTree;
+
+ protected SelectClause(PropertyTree propertyTree) {
+ this.propertyTree = propertyTree;
+ }
+
+ public SelectClause(Type mainType) {
+ this(mainType, "");
+ }
+
+ public SelectClause(Type mainType, String clause) {
+ this.propertyTree = PropertyTree.newPropertyTree((com.agfa.hap.sdo.Type) mainType, clause);
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (!(other instanceof SelectClause)) {
+ return false;
+ }
+ return ((SelectClause) other).propertyTree == propertyTree;
+ }
+
+ @Override
+ public int hashCode() {
+ return propertyTree.hashCode();
+ }
+
+ public <T> void visit(DataMapper<T> mapper, ObjectPropertyVisitor visitor, T dataObject) throws Exception {
+ visit(mapper, visitor, dataObject, propertyTree);
+ }
+
+ private <T> void visit(DataMapper<T> mapper, ObjectPropertyVisitor visitor, T dataObject, PropertyTree propertyTree) throws Exception {
+ com.agfa.hap.sdo.Type type = mapper.getType(dataObject);
+ if (type == null) {
+ throw new IllegalArgumentException("Can't visit an object which doesn't have an sdo type. dataObject=" + dataObject);
+ }
+ visitor.startDataObject(dataObject, type);
+ if (propertyTree != null) {
+ for (Property property : type.getProperties()) {
+ if (property.getType().isDataType()) {
+ if (property.isMany()){
+ Iterator<? extends T> it = mapper.getObjects(dataObject, property);
+ while (it.hasNext()) {
+ visitChild(mapper, visitor, dataObject, property, it.next(), null);
+ }
+ } else {
+ visitChild(mapper, visitor, dataObject, property, null);
+ }
+ } else {
+ PropertyTree tree = propertyTree.getProperty(property);
+ if (tree == null) {
+ visitProxy(mapper, visitor, dataObject, property);
+ } else {
+ visitChildObject(mapper, visitor, dataObject, property, tree);
+ }
+ }
+ }
+ }
+ visitor.endDataObject(dataObject, type);
+ }
+
+ /**
+ * this method is called if the property is not in the select clause
+ * when used with the default SnapshotSerializer :
+ * returning directly will result in property with value = null,
+ * calling visitProxyProperty with a null identity will result in a not available property
+ * calling visitProxyProperty with a non null identity will result in a property with isProxy=true
+ * @param <T>
+ * @param mapper
+ * @param visitor
+ * @param dataObject
+ * @param property
+ * @throws Exception
+ */
+ protected <T> void visitProxy(DataMapper<T> mapper, ObjectPropertyVisitor visitor, T dataObject, Property property) throws Exception {
+ if (property.isMany()) {
+ visitor.visitProxyProperty(dataObject, property, null); //result -> property not available
+ return;
+ }
+ Property idProp = property.getType().getIdentityProperty();
+ if (idProp == null) {
+ visitor.visitProxyProperty(dataObject, property, null); //result -> property not available
+ return;
+ }
+ T child = (T) mapper.getProperty(dataObject, property);
+ if (child == null) {
+ return; //result -> property null
+ }
+ Object identity = mapper.getProperty(child, idProp);
+ if (identity == null) {
+ visitor.visitProxyProperty(dataObject, property, null);
+ return; // link doesn't have an identity, so one could argue that it does not really exist and therefore the property should be null
+ // but this would not be consistent with the nonproxy case : there we treat it as a normal property
+ // also, this seems to be the most convenient choice for agility ris (ProcedureDefinitionType of qdoc requested procedures)
+ //result -> property not available
+ }
+ visitor.visitProxyProperty(dataObject, property, identity);
+
+ }
+
+ protected <T> void visitChildObject(DataMapper<T> mapper, ObjectPropertyVisitor visitor, T dataObject, Property property, PropertyTree tree) throws Exception {
+ if (mapper.isBulkProperty(dataObject.getClass(), property)) {
+ visitor.visitBulkProperty(dataObject, property, new SelectClause(tree));
+ return;
+ }
+ if (property.isMany()) {
+ Iterator<? extends T> it = mapper.getObjects(dataObject, property);
+ while (it.hasNext()) {
+ visitChild(mapper, visitor, dataObject, property, it.next(), tree);
+ }
+ } else {
+ visitChild(mapper, visitor, dataObject, property, tree);
+ }
+ }
+
+ protected <T> void visitChild(DataMapper<T> mapper, ObjectPropertyVisitor visitor, T parent, Property property, PropertyTree propertyTree) throws Exception {
+ if (mapper.isBulkProperty(parent.getClass(), property)) {
+ visitor.visitBulkProperty(parent, property, new SelectClause(propertyTree));
+ return;
+ }
+ Object value = mapper.getProperty(parent, property);
+ visitChild(mapper, visitor, parent, property, value, propertyTree);
+ }
+
+ private <T> void visitChild(DataMapper<T> mapper, ObjectPropertyVisitor visitor, T parent, Property property, Object value, PropertyTree propertyTree) throws Exception {
+ if (value != null) {
+ boolean recurse = visitor.visitProperty(parent, property, value);
+ if (recurse) {
+ visit(mapper, visitor, (T) value, propertyTree);
+ }
+ }
+ }
+
+ public PropertyTree getPropertyTree() {
+ return propertyTree;
+ }
+
+ public String asCommaSeparatedString(){
+ return this.getPropertyTree().asCommmaSeparatedString();
+ }
+
+}
diff --git a/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/Snapshot.java b/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/Snapshot.java
new file mode 100644
index 0000000000..35e22d869d
--- /dev/null
+++ b/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/Snapshot.java
@@ -0,0 +1,44 @@
+package com.agfa.hap.sdo;
+
+import java.io.Serializable;
+import java.util.List;
+
+
+/**
+ * Collection of structured data at a given moment in time.
+ * You can think of this as a set of name-value pairs
+ * where the names correspond to various {@link Property} instances.
+ * The actual content (i.e. which properties are contained) has been defined based on
+ * a {@link SnapshotDefinition}.
+ * <br />
+ * The difference with a DataGraph is that a Snapshot contains the state of a
+ * set of dataobjects at a certain moment in time. As such it is immutable.
+ * Another difference is that is defines its content based on a definition as opposed
+ * to the datagraph that uses the containment relation
+ * defined by sdo ({@see {@link Property#isContainment()}).
+ * <br />
+ * The data can be extracted either as a set of {@link ProxyDataObject} instances.
+ * The data can also be returned as a set of objects (POJOs). The latter case assumes
+ * appropriate classes can be
+ * found that correspond to the different {@link Type}s that are used.
+ * <br />
+ * <strong>Warning:</strong>
+ * Serialized objects of this class will not be compatible with
+ * future hap.SDO releases. The current serialization support is
+ * appropriate for short term storage or RMI between applications running
+ * the same version of hap.sdo.
+ *
+ * @author AMOCZ
+ * @see DataAccessService
+ * @see DataMapper
+ */
+public interface Snapshot extends Serializable {
+
+ /**
+ * Extracts the objects from the snapshots
+ * @param factory
+ * @return A list containing new objects with appropriate properties filled in.
+ */
+ <T> List<T> extract(DataMapper<T> mapper);
+
+}
diff --git a/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/SnapshotDefinition.java b/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/SnapshotDefinition.java
new file mode 100644
index 0000000000..ed9ea88fdd
--- /dev/null
+++ b/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/SnapshotDefinition.java
@@ -0,0 +1,18 @@
+package com.agfa.hap.sdo;
+
+import org.omg.CORBA.Any;
+
+/**
+ * Defines which properties belong to the {@link Snapshot}.
+ * @author AMOCZ
+ */
+public interface SnapshotDefinition {
+
+ /**
+ * Visits all properties of the object that are part
+ * of the definition.
+ * @exception Any exception that is thrown by the visitor
+ */
+ <T> void visit(DataMapper<T> mapper, ObjectPropertyVisitor visitor, T root) throws Exception;
+
+}
diff --git a/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/Type.java b/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/Type.java
new file mode 100644
index 0000000000..a3c0813c10
--- /dev/null
+++ b/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/Type.java
@@ -0,0 +1,50 @@
+/**
+ * <copyright>
+ *
+ * Service Data Objects
+ * Version 2.0
+ * Licensed Materials - Property of BEA and IBM
+ *
+ * (c) Copyright BEA Systems, Inc. and International Business Machines Corp 2005. All rights reserved.
+ *
+ * </copyright>
+ *
+ */
+
+package com.agfa.hap.sdo;
+
+import java.util.List;
+
+/**
+ * A representation of the type of a {@link Property property} of a {@link DataObject data object}.
+ *
+ * We extend the standard sdo type to have a more type-safe (generics) interface and to add
+ * specific functionality such as the {@link #getIdentityProperty()}.
+ *
+ * @see com.agfa.hap.sdo.helper.TypeHelper
+ * @see commonj.sdo.Type
+ */
+public interface Type extends commonj.sdo.Type {
+
+ /**
+ * This is a non-standard SDO property (but rumors are that is will appear in SDO2.1).
+ * The identity property typically maps on the ID attribute in xml.
+ * @return the identity property. This property can be used to distinguish a DataObject
+ * from all other DataObject having the same type.
+ */
+ Property getIdentityProperty();
+
+ // improve type safety
+
+ List<Property> getProperties();
+
+ Property getProperty(String propertyName);
+
+ Property getProperty(int index);
+
+ List<? extends Type> getBaseTypes();
+
+ List<? extends Property> getDeclaredProperties();
+
+ List<String> getAliasNames();
+}
diff --git a/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/core/IDataObjectService.java b/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/core/IDataObjectService.java
new file mode 100644
index 0000000000..f7cd42b7b5
--- /dev/null
+++ b/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/core/IDataObjectService.java
@@ -0,0 +1,10 @@
+package com.agfa.hap.sdo.core;
+
+public interface IDataObjectService {
+
+ /**
+ * Returns an xsd defining the types for this uri.
+ */
+ String getTypes(String uri);
+
+}
diff --git a/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/helper/TypeHelper.java b/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/helper/TypeHelper.java
new file mode 100644
index 0000000000..a418243edb
--- /dev/null
+++ b/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/helper/TypeHelper.java
@@ -0,0 +1,33 @@
+package com.agfa.hap.sdo.helper;
+
+import com.agfa.hap.sdo.Type;
+
+
+/**
+ * Redefinition of {@link commonj.sdo.helper.TypeHelper} to provide type-safe
+ * access to types.
+ * @author AMOCZ
+ * @see commonj.sdo.helper.TypeHelper
+ */
+public interface TypeHelper extends commonj.sdo.helper.TypeHelper {
+
+ /**
+ * Return the Type specified by typeName with the given uri,
+ * or null if not found.
+ * @param uri The uri of the Type - type.getURI();
+ * @param typeName The name of the Type - type.getName();
+ * @return the Type specified by typeName with the given uri,
+ * or null if not found.
+ */
+ Type getType(String uri, String typeName);
+
+ /**
+ * Return the Type for this interfaceClass or null if not found.
+ * @param interfaceClass is the interface for the DataObject's Type -
+ * type.getInstanceClass();
+ * @return the Type for this interfaceClass or null if not found.
+ */
+ Type getType(Class interfaceClass);
+
+ TypeHelper INSTANCE = (TypeHelper) commonj.sdo.helper.TypeHelper.INSTANCE;
+}
diff --git a/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/helper/XsdScanner.java b/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/helper/XsdScanner.java
new file mode 100644
index 0000000000..9dc88ab9b7
--- /dev/null
+++ b/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/helper/XsdScanner.java
@@ -0,0 +1,78 @@
+package com.agfa.hap.sdo.helper;
+
+import java.io.File;
+import java.io.FileFilter;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.net.URLDecoder;
+import java.util.Enumeration;
+import java.util.jar.JarEntry;
+import java.util.jar.JarInputStream;
+
+import commonj.sdo.helper.XSDHelper;
+
+/**
+ * This class scans the classpath for all sdo/*.xsd files and registers them
+ * using the type helper.
+ * @author AMOCZ
+ */
+public class XsdScanner {
+
+ private static final String ENCODING_NAME = System.getProperty("file.encoding");
+
+ private ClassLoader classLoader;
+
+ public XsdScanner(ClassLoader classLoader) {
+ this.classLoader = classLoader;
+ }
+
+ public void scanClassPath(String path) throws IOException {
+ for (Enumeration<URL> e = classLoader.getResources(path); e.hasMoreElements();) {
+ URL next = e.nextElement();
+ String file = URLDecoder.decode(next.getFile(), ENCODING_NAME);
+ if ("jar".equals(next.getProtocol()) && file.startsWith("file:/")) {
+ scanArchive(file);
+ } else if ("file".equals(next.getProtocol())){
+ File classInfoDircectory = new File(file);
+ scanDirectory(classInfoDircectory);
+ }
+ }
+ }
+
+ protected void scanArchive(String file) throws IOException {
+ int exclamation = file.indexOf('!');
+ String library = file.substring(0, exclamation);
+ JarInputStream jar = new JarInputStream(new URL(library).openStream());
+ while (true) {
+ JarEntry nextEntry = jar.getNextJarEntry();
+ if (nextEntry == null) {
+ break;
+ }
+ String nextEntryName = nextEntry.getName();
+ if (nextEntryName.endsWith(".xsd")) {
+ URL url = classLoader.getResource(nextEntryName);
+ read(url.openStream());
+ }
+ }
+ }
+
+ protected void scanDirectory(File dir) throws IOException {
+ File[] files = dir.listFiles(new FileFilter() {
+ public boolean accept(File pathname) {
+ return pathname.getName().endsWith(".xsd");
+ }
+ });
+ if (files != null) {
+ for (int i = 0; i < files.length; i++) {
+ read(new FileInputStream(files[i]));
+ }
+ }
+ }
+
+
+ protected void read(InputStream is) {
+ XSDHelper.INSTANCE.define(is, null);
+ }
+}
diff --git a/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/impl/BytesTypeConverter.java b/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/impl/BytesTypeConverter.java
new file mode 100644
index 0000000000..a6590762aa
--- /dev/null
+++ b/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/impl/BytesTypeConverter.java
@@ -0,0 +1,32 @@
+package com.agfa.hap.sdo.impl;
+
+
+public class BytesTypeConverter extends TypeConverter<byte[]>{
+ //TODO suboptimal implementation
+ @Override
+ public byte[] parse(String hexString) {
+ byte[] bytes = new byte[hexString.length()/3];
+ for (int i = 0; i < bytes.length; i++){
+ String hexFor1Byte = hexString.substring(i*3, i*3+3);
+ if (hexFor1Byte.startsWith("0")){ //strip zero for -
+ hexFor1Byte = hexFor1Byte.substring(1);
+ }
+ bytes[i] = (byte) Integer.parseInt(hexFor1Byte, 16);
+ }
+ return bytes;
+ }
+
+ @Override
+ public String toString(byte[] instance) {
+ StringBuilder sb = new StringBuilder(instance.length * 2);
+ for(int x = 0 ; x < instance.length ; x++)
+ {
+ String hex = Integer.toString(instance[x], 16);
+ for (int i = hex.length(); i < 3; i++){
+ sb.append("0");
+ }
+ sb.append(hex);
+ }
+ return sb.toString();
+ }
+}
diff --git a/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/impl/DataAccessService.java b/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/impl/DataAccessService.java
new file mode 100644
index 0000000000..15f8c942c9
--- /dev/null
+++ b/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/impl/DataAccessService.java
@@ -0,0 +1,11 @@
+package com.agfa.hap.sdo.impl;
+
+import com.agfa.hap.sdo.implementation.DataGraphImplementation;
+import commonj.sdo.DataGraph;
+
+public class DataAccessService {
+
+ public static DataGraph createDataGraph() {
+ return new DataGraphImplementation();
+ }
+}
diff --git a/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/impl/EnumTypeConverter.java b/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/impl/EnumTypeConverter.java
new file mode 100644
index 0000000000..f4fc3b012e
--- /dev/null
+++ b/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/impl/EnumTypeConverter.java
@@ -0,0 +1,21 @@
+package com.agfa.hap.sdo.impl;
+
+public class EnumTypeConverter<T extends Enum> extends TypeConverter<T> {
+
+ private Class<T> enumClazz;
+
+ public EnumTypeConverter(Class<T> enumClazz) {
+ this.enumClazz = enumClazz;
+ }
+
+ @Override
+ public T parse(String str) {
+ return (T) Enum.<T>valueOf(enumClazz, str);
+ }
+
+ @Override
+ public String toString(T instance) {
+ return instance.name();
+ }
+
+}
diff --git a/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/impl/HelperProviderImpl.java b/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/impl/HelperProviderImpl.java
new file mode 100644
index 0000000000..1ea8e000d9
--- /dev/null
+++ b/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/impl/HelperProviderImpl.java
@@ -0,0 +1,114 @@
+/**
+ * <copyright>
+ *
+ * Service Data Objects
+ * Version 2.0
+ * Licensed Materials - Property of BEA and IBM
+ *
+ * (c) Copyright BEA Systems, Inc. and International Business Machines Corp 2005. All rights reserved.
+ *
+ * </copyright>
+ *
+ */
+
+package com.agfa.hap.sdo.impl;
+
+import com.agfa.hap.sdo.PartialDataObject;
+import com.agfa.hap.sdo.implementation.ContainmentSerializer;
+import com.agfa.hap.sdo.implementation.DataFactoryImpl;
+import com.agfa.hap.sdo.implementation.EqualityHelperImpl;
+import com.agfa.hap.sdo.implementation.TypeHelperImpl;
+import com.agfa.hap.sdo.implementation.XMLHelperImpl;
+import com.agfa.hap.sdo.implementation.XSDHelperImpl;
+import commonj.sdo.helper.CopyHelper;
+import commonj.sdo.helper.DataFactory;
+import commonj.sdo.helper.DataHelper;
+import commonj.sdo.helper.EqualityHelper;
+import commonj.sdo.helper.TypeHelper;
+import commonj.sdo.helper.XMLHelper;
+import commonj.sdo.helper.XSDHelper;
+import commonj.sdo.impl.ExternalizableDelegator.Resolvable;
+
+/**
+ * This class instantiates a HelperProviderImpl that returns concrete helpers.
+ * This class may be replaced by another implementation.
+ * <p>
+ * The current implementation tries to instantiate an instance of the class
+ * defined by the <i>commonj.sdo.helperprovider</i> system property. If that
+ * doesn't work it tries to instantiate an instance of
+ * <i>com.agfa.ris.client.sdo.impl.HelperProviderImpl</i>. If that doesn't work
+ * it tries <i>com.agfa.ris.server.sdo.HelperProviderImpl</i>. If all fails, it
+ * just uses {@link HelperProviderImpl}.
+ */
+public class HelperProviderImpl extends commonj.sdo.impl.HelperProvider {
+
+ protected TypeHelper typeHelper;
+ protected XSDHelper xsdHelper;
+ protected XMLHelper xmlHelper;
+ protected DataFactory dataFactory;
+ protected EqualityHelper equalityHelper;
+
+ protected HelperProviderImpl() {
+ typeHelper = createTypeHelper();
+ xsdHelper = createXSDHelper();
+ xmlHelper = createXMLHelper();
+ dataFactory = createDataFactory();
+ equalityHelper = createEqualityHelper();
+ }
+
+ protected EqualityHelper createEqualityHelper() {
+ return new EqualityHelperImpl();
+ }
+
+ protected DataFactory createDataFactory() {
+ return new DataFactoryImpl();
+ }
+
+ protected XMLHelper createXMLHelper() {
+ return new XMLHelperImpl();
+ }
+
+ protected XSDHelper createXSDHelper() {
+ return new XSDHelperImpl();
+ }
+
+ protected TypeHelper createTypeHelper() {
+ return new TypeHelperImpl();
+ }
+
+ protected CopyHelper copyHelper() {
+ return null;
+ }
+
+ protected DataFactory dataFactory() {
+ return dataFactory;
+ }
+
+ protected DataHelper dataHelper() {
+ return null;
+ }
+
+ protected EqualityHelper equalityHelper() {
+ return equalityHelper;
+ }
+
+ protected TypeHelper typeHelper() {
+ return typeHelper;
+ }
+
+ protected XMLHelper xmlHelper() {
+ return xmlHelper;
+ }
+
+ protected XSDHelper xsdHelper() {
+ return xsdHelper;
+ }
+
+ protected Resolvable resolvable() {
+ return new ContainmentSerializer();
+ }
+
+ protected Resolvable resolvable(Object target) {
+ return new ContainmentSerializer((PartialDataObject) target);
+ }
+}
diff --git a/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/impl/SdoImplementationFactory.java b/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/impl/SdoImplementationFactory.java
new file mode 100644
index 0000000000..ea1c5c7d02
--- /dev/null
+++ b/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/impl/SdoImplementationFactory.java
@@ -0,0 +1,58 @@
+package com.agfa.hap.sdo.impl;
+
+import org.apache.log4j.Logger;
+
+import commonj.sdo.impl.HelperProvider;
+
+/**
+ * Class that is responsible for finding and choosing the correct SDO implementation.
+ * @author AMOCZ
+ */
+public class SdoImplementationFactory {
+
+ private static Class<? extends HelperProvider> implementationClass;
+
+ /**
+ * Use this to inject a suitable implementation class. This must happen BEFORE
+ * the sdo system is initialized.
+ * @param implementationClass
+ */
+ public static void setImplementationClass(Class<? extends HelperProvider> implementationClass) {
+ SdoImplementationFactory.implementationClass = implementationClass;
+ }
+
+ public static HelperProvider getHelperProviderImplementation() {
+ Logger logger = Logger.getLogger(SdoImplementationFactory.class);
+ try {
+ if (implementationClass != null) {
+ return implementationClass.newInstance();
+ }
+ String helperProviderClassname = System.getProperty("commonj.sdo.helperprovider",
+ "com.agfa.hap.rcp.sdo.impl.HelperProviderImpl");
+ try {
+ logger.debug("trying to load " + helperProviderClassname);
+ return (HelperProviderImpl) Class.forName(helperProviderClassname).newInstance();
+ } catch (ClassNotFoundException cnfe) {
+ try {
+ // client-side try contextclassloader as well -- this is
+ // needed for maven build
+ logger.debug("trying to load " + helperProviderClassname
+ + " through the contextclassloader");
+ return (HelperProviderImpl) Class.forName(
+ helperProviderClassname, true,
+ Thread.currentThread().getContextClassLoader())
+ .newInstance();
+ } catch (ClassNotFoundException e) {
+ logger
+ .debug("trying to load com.agfa.ris.server.sdo.HelperProviderImpl");
+ return (HelperProviderImpl) Class.forName("com.agfa.ris.server.sdo.HelperProviderImpl")
+ .newInstance();
+ }
+ }
+ } catch (Exception e) {
+ logger.warn("unable to load a special helperprovider, fallback to the default one", e);
+ return new HelperProviderImpl();
+ }
+ }
+
+}
diff --git a/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/impl/SerializableTypeConverter.java b/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/impl/SerializableTypeConverter.java
new file mode 100644
index 0000000000..1956b151a5
--- /dev/null
+++ b/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/impl/SerializableTypeConverter.java
@@ -0,0 +1,42 @@
+package com.agfa.hap.sdo.impl;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.ObjectInput;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutput;
+import java.io.ObjectOutputStream;
+import java.io.Serializable;
+
+public class SerializableTypeConverter extends TypeConverter<Serializable> {
+
+ @Override
+ public Serializable parse(String str) {
+ byte[] bytes = bytesTypeConverter.parse(str);
+ ByteArrayInputStream bis = new ByteArrayInputStream(bytes);
+ try {
+ ObjectInput in = new ObjectInputStream(bis);
+ return (Serializable) in.readObject();
+ } catch(IOException e) {
+ throw new RuntimeException(e);
+ } catch (ClassNotFoundException cnfe) {
+ throw new RuntimeException(cnfe);
+ }
+ }
+
+ @Override
+ public String toString(Serializable instance) {
+ ByteArrayOutputStream bos = new ByteArrayOutputStream();
+ try {
+ ObjectOutput out = new ObjectOutputStream(bos);
+ out.writeObject(instance);
+ out.close();
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ return bytesTypeConverter.toString(bos.toByteArray());
+ }
+
+ private BytesTypeConverter bytesTypeConverter = new BytesTypeConverter();
+}
diff --git a/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/impl/TypeConverter.java b/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/impl/TypeConverter.java
new file mode 100644
index 0000000000..28f57600e6
--- /dev/null
+++ b/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/impl/TypeConverter.java
@@ -0,0 +1,58 @@
+package com.agfa.hap.sdo.impl;
+
+import java.io.Serializable;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import com.agfa.hap.sdo.Type;
+
+/**
+ * Class that provides
+ * an implementation to convert instances from and to a string. This is typically
+ * used in sdo xml conversions and snapshots.
+ * <P>
+ * A custom sdo basic type can register an appropriate typeConverter.
+ */
+public abstract class TypeConverter<T> {
+
+ /**
+ * Parse the string and return an instance of the implementation class
+ * with a value that is represented by the string parameter.
+ */
+ public abstract T parse(String str);
+
+ /**
+ * Convert the instance to a string. The instance is guaranteed to be of type
+ * T.
+ */
+ public abstract String toString(T instance);
+
+ /**
+ * @return The type converter instance registered with that name or null if nothing
+ * is found.
+ */
+ @SuppressWarnings("unchecked")
+ public static <U> TypeConverter<U> get(Type type) {
+ return registry.get(type);
+ }
+
+ public static void register(Type type, TypeConverter converter) {
+ registry.put(type, converter);
+ }
+
+ /**
+ * Returns the default TypeConverter for the given clazz. Checks for Enums
+ * and classes that implement {@link Serializable}.
+ */
+ public static <T> TypeConverter<T> getDefaultConverter(Class<T> clazz) {
+ if (clazz.isEnum()) {
+ return new EnumTypeConverter(clazz);
+ }
+ if (Serializable.class.isAssignableFrom(clazz)) {
+ return (TypeConverter<T>) new SerializableTypeConverter();
+ }
+ return null;
+ }
+
+ private static Map<Type, TypeConverter> registry = new ConcurrentHashMap<Type, TypeConverter>();
+}
diff --git a/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/impl/TypeProvider.java b/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/impl/TypeProvider.java
new file mode 100644
index 0000000000..a3bf901a81
--- /dev/null
+++ b/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/impl/TypeProvider.java
@@ -0,0 +1,29 @@
+package com.agfa.hap.sdo.impl;
+
+import commonj.sdo.impl.HelperProvider;
+
+
+public abstract class TypeProvider {
+
+ public static TypeProvider getInstance() {
+ if (instance == null) {
+ //TODO review is this still needed?
+ // force initialization of HelperProvider
+ HelperProvider.getTypeHelper();
+ }
+ return instance;
+ }
+
+ private static TypeProvider instance;
+
+ public static void setInstance(TypeProvider provider) {
+ instance = provider;
+ }
+
+ /**
+ * Returns the xsd describing the types for this uri.
+ * If the uri can't be interpreted, returns null.
+ */
+ public abstract String getTypes(String uri);
+
+}
diff --git a/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/impl/XsdTypeProvider.java b/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/impl/XsdTypeProvider.java
new file mode 100644
index 0000000000..f2423447d3
--- /dev/null
+++ b/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/impl/XsdTypeProvider.java
@@ -0,0 +1,43 @@
+package com.agfa.hap.sdo.impl;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * Simple TypeProvider that gets its type info from xsd files.
+ * TypeInfo is cached once it is read.
+ * This class is thread-safe; it can be used in a concurrent environment.
+ * @author AMOCZ
+ */
+public class XsdTypeProvider extends TypeProvider {
+
+ private Map<String, String> cache = new ConcurrentHashMap<String, String>();
+
+ public XsdTypeProvider(){
+ }
+
+ public void register(String uri, String xsd) {
+ cache.put(uri, xsd);
+ }
+
+ public void register(String uri, InputStream is) throws IOException {
+ InputStreamReader reader = new InputStreamReader(is);
+ StringBuilder builder = new StringBuilder();
+ char[] cbuf = new char[10000];
+ int bytesRead;
+ while ((bytesRead = reader.read(cbuf)) > 0) {
+ builder.append(cbuf, 0, bytesRead);
+ }
+ register(uri, builder.toString());
+ }
+
+ @Override
+ public String getTypes(String uri) {
+ return cache.get(uri);
+ }
+
+}
+
diff --git a/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/implementation/AbstractDataObject.java b/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/implementation/AbstractDataObject.java
new file mode 100644
index 0000000000..0b2d55be02
--- /dev/null
+++ b/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/implementation/AbstractDataObject.java
@@ -0,0 +1,560 @@
+package com.agfa.hap.sdo.implementation;
+
+import java.io.ObjectStreamException;
+import java.io.Serializable;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.util.Date;
+import java.util.List;
+
+import org.apache.log4j.Logger;
+
+import com.agfa.hap.sdo.PropertyNotAvailableException;
+import commonj.sdo.ChangeSummary;
+import commonj.sdo.DataGraph;
+import commonj.sdo.DataObject;
+import commonj.sdo.Property;
+import commonj.sdo.Sequence;
+import commonj.sdo.impl.ExternalizableDelegator;
+
+public abstract class AbstractDataObject implements DataObject, ContainedDataObject, Serializable {
+
+ public static Logger logger = Logger.getLogger(DataObject.class);
+
+ public AbstractDataObject(com.agfa.hap.sdo.Type type) {
+ this.type = type;
+ }
+
+ protected AbstractDataObject(com.agfa.hap.sdo.Type type, DataGraph dataGraph) {
+ this(type);
+ this.dataGraph = dataGraph;
+ }
+
+ public Object get(int propertyIndex) {
+ return get(type.getProperty(propertyIndex));
+ }
+
+ public Object get(String path) {
+ try {
+ Object propertyValue = get(getProperty(path));
+ return propertyValue;
+ } catch(PropertyNotAvailableException pna){
+ throw pna;
+ } catch (Exception e){
+ logger.error("unable to get value for property" + path + " for type=" + getType(), e);
+ return null; //ref SDO_2.1_DRAFT_20060726.pdf p26
+ }
+ }
+
+ public List getList(String path) {
+ try {
+ return getList(getProperty(path));
+ } catch(PropertyNotAvailableException pna){
+ throw pna;
+ } catch(Exception e){
+ logger.error("unable to get value for property" + path + " for type=" + getType(), e);
+ return null; //ref SDO_2.1_DRAFT_20060726.pdf p26
+ }
+ }
+
+ public void set(String path, Object value) {
+ try{
+ set(getProperty(path), value);
+ } catch(PropertyNotAvailableException pna){
+ throw pna;
+ }catch (Exception e){
+ logger.error("unable to set property " + path + "for type=" + getType(), e);
+ //ref SDO_2.1_DRAFT_20060726.pdf p26
+ }
+ }
+
+ public void set(int propertyIndex, Object value) {
+ set(getType().getProperties().get(propertyIndex), value);
+ }
+
+ public boolean isSet(String path) {
+ try {
+ return isSet(getProperty(path));
+ } catch (Exception e){
+ logger.warn("property=" + path + " type=" + getType(), e);
+ return false; //ref SDO_2.1_DRAFT_20060726.pdf p26
+ }
+ }
+
+ public boolean isSet(int propertyIndex) {
+ return isSet(type.getProperty(propertyIndex));
+ }
+
+ public void unset(int propertyIndex) {
+ unset(type.getProperty(propertyIndex));
+ }
+
+ public void unset(String path) {
+ unset(getProperty(path));
+ }
+
+ public List getList(int propertyIndex) {
+ return getList(type.getProperty(propertyIndex));
+ }
+
+ public List getList(Property property) {
+ return (List) get(property);
+ }
+
+
+ public BigDecimal getBigDecimal(int propertyIndex) {
+ return (BigDecimal) get(propertyIndex);
+ }
+
+ public BigDecimal getBigDecimal(Property property) {
+ return (BigDecimal) get(property);
+ }
+
+ public BigDecimal getBigDecimal(String path) {
+ return (BigDecimal) get(path);
+ }
+
+ public BigInteger getBigInteger(int propertyIndex) {
+ return (BigInteger) get(propertyIndex);
+ }
+
+ public BigInteger getBigInteger(Property property) {
+ return (BigInteger) get(property);
+ }
+
+ public BigInteger getBigInteger(String path) {
+ return (BigInteger) get(path);
+ }
+
+ public boolean getBoolean(int propertyIndex) {
+ return (Boolean) get(propertyIndex);
+ }
+
+ public boolean getBoolean(Property property) {
+ return (Boolean) get(property);
+ }
+
+ public boolean getBoolean(String path) {
+ return (Boolean) get(path);
+ }
+
+ public byte getByte(int propertyIndex) {
+ return (Byte) get(propertyIndex);
+ }
+
+ public byte getByte(Property property) {
+ return (Byte) get(property);
+ }
+
+ public byte getByte(String path) {
+ return (Byte) get(path);
+ }
+
+ public byte[] getBytes(int propertyIndex) {
+ return (byte[]) get(propertyIndex);
+ }
+
+ public byte[] getBytes(Property property) {
+ return (byte[]) get(property);
+ }
+
+ public byte[] getBytes(String path) {
+ return (byte[]) get(path);
+ }
+
+ public char getChar(int propertyIndex) {
+ return (Character) get(propertyIndex);
+ }
+
+ public char getChar(Property property) {
+ return (Character) get(property);
+ }
+
+ public char getChar(String path) {
+ return (Character) get(path);
+ }
+
+ public Date getDate(int propertyIndex) {
+ return (Date) get(propertyIndex);
+ }
+
+ public Date getDate(Property property) {
+ return (Date) get(property);
+ }
+
+ public Date getDate(String path) {
+ return (Date) get(path);
+ }
+
+ public double getDouble(int propertyIndex) {
+ return (Double) get(propertyIndex);
+ }
+
+ public double getDouble(Property property) {
+ return (Double) get(property);
+ }
+
+ public double getDouble(String path) {
+ return (Double) get(path);
+ }
+
+ public float getFloat(int propertyIndex) {
+ return (Float) get(propertyIndex);
+ }
+
+ public float getFloat(Property property) {
+ return (Float) get(property);
+ }
+
+ public float getFloat(String path) {
+ return (Float) get(path);
+ }
+
+ public List getInstanceProperties() {
+ throw new UnsupportedOperationException();
+ }
+
+ public Property getInstanceProperty(String propertyName) {
+ throw new UnsupportedOperationException();
+ }
+
+ public int getInt(int propertyIndex) {
+ return (Integer) get(propertyIndex);
+ }
+
+ public int getInt(Property property) {
+ return (Integer) get(property);
+ }
+
+ public int getInt(String path) {
+ return (Integer) get(path);
+ }
+
+ public long getLong(int propertyIndex) {
+ return (Long) get(propertyIndex);
+ }
+
+ public long getLong(Property property) {
+ return (Long) get(property);
+ }
+
+ public long getLong(String path) {
+ return (Long) get(path);
+ }
+
+ public Property getProperty(String propertyName) {
+ return getType().getProperty(propertyName);
+ }
+
+ public Sequence getSequence() {
+ throw new UnsupportedOperationException();
+ }
+
+ public Sequence getSequence(int propertyIndex) {
+ throw new UnsupportedOperationException();
+ }
+
+ public Sequence getSequence(Property property) {
+ throw new UnsupportedOperationException();
+ }
+
+ public Sequence getSequence(String path) {
+ throw new UnsupportedOperationException();
+ }
+
+ public short getShort(int propertyIndex) {
+ return (Short) get(propertyIndex);
+ }
+
+ public short getShort(Property property) {
+ return (Short) get(property);
+ }
+
+ public short getShort(String path) {
+ return (Short) get(path);
+ }
+
+ public String getString(int propertyIndex) {
+ return (String) get(propertyIndex);
+ }
+
+ public String getString(Property property) {
+ return (String) get(property);
+ }
+
+ public String getString(String path) {
+ return (String) get(path);
+ }
+
+ public void setBigDecimal(int propertyIndex, BigDecimal value) {
+ this.set(propertyIndex, value);
+ }
+
+ public void setBigDecimal(Property property, BigDecimal value) {
+ this.set(property, value);
+ }
+
+ public void setBigDecimal(String path, BigDecimal value) {
+ this.set(path, value);
+ }
+
+ public void setBigInteger(int propertyIndex, BigInteger value) {
+ this.set(propertyIndex, value);
+ }
+
+ public void setBigInteger(Property property, BigInteger value) {
+ this.set(property, value);
+ }
+
+ public void setBigInteger(String path, BigInteger value) {
+ this.set(path, value);
+ }
+
+ public void setBoolean(int propertyIndex, boolean value) {
+ this.set(propertyIndex, value);
+ }
+
+ public void setBoolean(Property property, boolean value) {
+ this.set(property, value);
+ }
+
+ public void setBoolean(String path, boolean value) {
+ this.set(path, value);
+ }
+
+ public void setByte(int propertyIndex, byte value) {
+ this.set(propertyIndex, value);
+ }
+
+ public void setByte(Property property, byte value) {
+ this.set(property, value);
+ }
+
+ public void setByte(String path, byte value) {
+ this.set(path, value);
+ }
+
+ public void setBytes(int propertyIndex, byte[] value) {
+ this.set(propertyIndex, value);
+ }
+
+ public void setBytes(Property property, byte[] value) {
+ this.set(property, value);
+ }
+
+ public void setBytes(String path, byte[] value) {
+ this.set(path, value);
+ }
+
+ public void setChar(int propertyIndex, char value) {
+ this.set(propertyIndex, value);
+ }
+
+ public void setChar(Property property, char value) {
+ this.set(property, value);
+ }
+
+ public void setChar(String path, char value) {
+ this.set(path, value);
+ }
+
+ public void setDataObject(int propertyIndex, DataObject value) {
+ this.set(propertyIndex, value);
+ }
+
+ public void setDataObject(Property property, DataObject value) {
+ this.set(property, value);
+ }
+
+ public void setDataObject(String path, DataObject value) {
+ this.set(path, value);
+ }
+
+ public void setDate(int propertyIndex, Date value) {
+ this.set(propertyIndex, value);
+ }
+
+ public void setDate(Property property, Date value) {
+ this.set(property, value);
+ }
+
+ public void setDate(String path, Date value) {
+ this.set(path, value);
+ }
+
+ public void setDouble(int propertyIndex, double value) {
+ this.set(propertyIndex, value);
+ }
+
+ public void setDouble(Property property, double value) {
+ this.set(property, value);
+ }
+
+ public void setDouble(String path, double value) {
+ this.set(path, value);
+ }
+
+ public void setFloat(int propertyIndex, float value) {
+ this.set(propertyIndex, value);
+ }
+
+ public void setFloat(Property property, float value) {
+ this.set(property, value);
+ }
+
+ public void setFloat(String path, float value) {
+ this.set(path, value);
+ }
+
+ public void setInt(int propertyIndex, int value) {
+ this.set(propertyIndex, value);
+ }
+
+ public void setInt(Property property, int value) {
+ this.set(property, value);
+ }
+
+ public void setInt(String path, int value) {
+ this.set(path, value);
+ }
+
+ public void setList(int propertyIndex, List value) {
+ this.setList(this.getType().getProperty(propertyIndex), value);
+ }
+
+ public void setList(Property property, List value) {
+ List list = this.getList(property);
+ list.clear();
+ list.addAll(value);
+ }
+
+ public void setList(String path, List value) {
+ this.setList(this.getProperty(path), value);
+ }
+
+ public void setLong(int propertyIndex, long value) {
+ this.set(propertyIndex, value);
+ }
+
+ public void setLong(Property property, long value) {
+ this.set(property, value);
+ }
+
+ public void setLong(String path, long value) {
+ this.set(path, value);
+ }
+
+ public void setShort(int propertyIndex, short value) {
+ this.set(propertyIndex, value);
+ }
+
+ public void setShort(Property property, short value) {
+ this.set(property, value);
+ }
+
+ public void setShort(String path, short value) {
+ this.set(path, value);
+ }
+
+ public void setString(int propertyIndex, String value) {
+ this.set(propertyIndex, value);
+ }
+
+ public void setString(Property property, String value) {
+ this.set(property, value);
+ }
+
+ public void setString(String path, String value) {
+ this.set(path, value);
+ }
+
+ public com.agfa.hap.sdo.Type getType() {
+ return type;
+ }
+
+ public String debugString(){
+ StringBuilder builder = new StringBuilder();
+ builder.append(this.toString());
+ builder.append(" {");
+ if (type != null){
+ builder.append("type=");
+ builder.append(type.getName());
+ builder.append(";properties=(");
+ for (Property property : type.getProperties()){
+ builder.append(property.getName());
+ builder.append("=");
+ Object value = get(property);
+ if (value instanceof AbstractDataObject){
+ AbstractDataObject dataObject = (AbstractDataObject) value;
+ builder.append(dataObject.debugString());
+ } else {
+ builder.append(value);
+ }
+ builder.append(",");
+ }
+ builder.append(")");
+ }
+
+ builder.append(" }");
+ return builder.toString() ;
+ }
+
+ public void delete() {
+ throw new UnsupportedOperationException();
+ }
+
+ public ChangeSummary getChangeSummary() {
+ throw new UnsupportedOperationException();
+ }
+
+ protected Object writeReplace() throws ObjectStreamException {
+ return new ExternalizableDelegator(this);
+ }
+
+ public void setDataGraph(DataGraph dataGraph) {
+ this.dataGraph = dataGraph;
+ }
+
+ public DataObject getContainer() {
+ return container;
+ }
+
+ public Property getContainmentProperty() {
+ return containmentProperty;
+ }
+
+ public DataGraph getDataGraph() {
+ return dataGraph;
+ }
+
+ public DataObject getRootObject() {
+ return container == null ? this : container.getRootObject();
+ }
+
+ public void detach() {
+ if (this.container != null) {
+ container.unset(containmentProperty);
+ }
+ this.container = null;
+ this.containmentProperty = null;
+ this.dataGraph = null;
+ }
+
+ protected void setContainment(AbstractDataObject container, Property prop) {
+ this.container = container;
+ this.containmentProperty = container == null ? null : prop;
+ DataGraph newDataGraph = container == null ? null : container.dataGraph;
+ dataGraph = newDataGraph;
+ }
+
+ protected boolean isContained(Property property) {
+ return property.isContainment();
+ }
+
+
+ protected com.agfa.hap.sdo.Type type;
+ private Property containmentProperty;
+ private DataObject container;
+ private DataGraph dataGraph;
+
+}
diff --git a/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/implementation/AbstractPartialDataObject.java b/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/implementation/AbstractPartialDataObject.java
new file mode 100644
index 0000000000..f923f3b1ae
--- /dev/null
+++ b/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/implementation/AbstractPartialDataObject.java
@@ -0,0 +1,211 @@
+package com.agfa.hap.sdo.implementation;
+
+import java.util.List;
+
+import com.agfa.hap.sdo.PartialDataFactory;
+import com.agfa.hap.sdo.PartialDataObject;
+import com.agfa.hap.sdo.PropertyNotAvailableException;
+
+import commonj.sdo.DataGraph;
+import commonj.sdo.DataObject;
+import commonj.sdo.Property;
+import commonj.sdo.Type;
+import commonj.sdo.helper.TypeHelper;
+
+public abstract class AbstractPartialDataObject extends AbstractDataObject implements PartialDataObject {
+
+ protected boolean isSettingProperty;
+
+ protected AbstractPartialDataObject(com.agfa.hap.sdo.Type type) {
+ super(type);
+ }
+
+ protected AbstractPartialDataObject(com.agfa.hap.sdo.Type type, DataGraph dataGraph) {
+ super(type, dataGraph);
+ }
+
+ /**
+ * Returns the value of the property. In case the property is not accessible
+ * returns {@link PartialDataObject#UNAVAILABLE_PROPERTY}.
+ */
+ protected abstract Object basicGet(Property property);
+
+ protected abstract void basicSet(Property property, Object value);
+
+ public Object get(Property property) {
+ Object result = basicGet(property);
+ if (result == PartialDataObject.UNAVAILABLE_PROPERTY) {
+ throw new PropertyNotAvailableException(this, property);
+ }
+ return result;
+ }
+
+ public void set(Property property, Object value) {
+ if (property.isMany()) {
+ this.setList(property, (List) value);
+ return;
+ }
+ if (property.isReadOnly()) {
+ throw new IllegalArgumentException("Setting readonly properties is not allowed!");
+ }
+ if (isSettingProperty) {
+ return;
+ }
+ try {
+ isSettingProperty = true;
+ Object oldValue = basicGet(property);
+ if (oldValue == value) {
+ return;
+ }
+ if (oldValue != null && isContained(property) && oldValue != PartialDataObject.UNAVAILABLE_PROPERTY) {
+ ((AbstractDataObject) oldValue).setContainment(null, property);
+ }
+ if (value != null && value != PartialDataObject.UNAVAILABLE_PROPERTY && isContained(property)) {
+ ((AbstractDataObject) value).setContainment(this, property);
+ }
+ basicSet(property, value);
+ if (property.getOpposite() != null) {
+ if (oldValue != null && oldValue != PartialDataObject.UNAVAILABLE_PROPERTY) {
+ if (property.getOpposite().isMany()) {
+ PartialDataObject oldOpposite = (PartialDataObject) oldValue;
+ if (oldOpposite.isAvailable(property.getOpposite())) {
+ ((IDataObjectList) oldOpposite.getList(property.getOpposite())).removeForBidirectionalUpdate(this);
+ }
+ } else {
+ ((DataObject) oldValue).set(property.getOpposite(), null);
+ }
+ }
+ if (value != null && value != PartialDataObject.UNAVAILABLE_PROPERTY) {
+ if (property.getOpposite().isMany()) {
+ PartialDataObject opposite = (PartialDataObject) value;
+ if (opposite.isAvailable(property.getOpposite())) {
+ ((IDataObjectList) opposite.getList(property.getOpposite())).addForBidirectionalUpdate(this);
+ }
+ } else {
+ ((DataObject) value).set(property.getOpposite(), this);
+ }
+ }
+ }
+ } finally {
+ isSettingProperty = false;
+ }
+ }
+
+ public PartialDataObject createDataObject(String propertyName) {
+ return createDataObject(getType().getProperty(propertyName));
+ }
+
+ public PartialDataObject createDataObject(int propertyIndex) {
+ return createDataObject(getType().getProperties().get(propertyIndex));
+ }
+
+ public PartialDataObject createDataObject(Property property) {
+ return createDataObject(property, property.getType());
+ }
+
+ public PartialDataObject createDataObject(String propertyName, String namespaceURI, String typeName) {
+ return createDataObject(getType().getProperty(propertyName), TypeHelper.INSTANCE.getType(namespaceURI, typeName));
+ }
+
+ public PartialDataObject createDataObject(int propertyIndex, String namespaceURI, String typeName) {
+ return createDataObject(getType().getProperties().get(propertyIndex), TypeHelper.INSTANCE.getType(namespaceURI, typeName));
+ }
+
+ public PartialDataObject createDataObject(Property property, Type type) {
+ PartialDataObject result = PartialDataFactory.INSTANCE.create(type);
+ if (property.isMany()) {
+ safeGetList(property).add(result);
+ } else {
+ set(property, result);
+ }
+ return result;
+ }
+
+ // also works in case property is not available
+ protected List<Object> safeGetList(Property property) {
+ Object result = basicGet(property);
+ if (result == PartialDataObject.UNAVAILABLE_PROPERTY) {
+ result = initializeWithEmptyList(property);
+ }
+ return (List<Object>) result;
+ }
+
+ protected abstract List initializeWithEmptyList(Property property);
+
+ @Override
+ public void setList(Property property, List value) {
+ if (!isAvailable(property)) {
+ if (property.getOpposite() != null) {
+ // we have to do this to avoid exceptions saying that the element
+ // is already a part of the collection
+ for (Object element : value) {
+ AbstractDataObject ado = (AbstractDataObject) element;
+ ado.set(property.getOpposite(), null);
+ }
+ }
+ this.safeGetList(property); // ensure that it is available
+ }
+ super.setList(property, value);
+ }
+
+ public PartialDataObject getDataObject(int propertyIndex) {
+ return (PartialDataObject) get(propertyIndex);
+ }
+
+ public PartialDataObject getDataObject(Property property) {
+ return (PartialDataObject) get(property);
+ }
+
+ public PartialDataObject getDataObject(String path) {
+ return (PartialDataObject) get(path);
+ }
+
+ public com.agfa.hap.sdo.Property getContainmentProperty() {
+ return (com.agfa.hap.sdo.Property) super.getContainmentProperty();
+ }
+
+ public PartialDataObject getContainer() {
+ return (PartialDataObject) super.getContainer();
+ }
+
+ public Object getIdentity() {
+ return this.get(getType().getIdentityProperty());
+ }
+
+ public void setIdentity(Object value) {
+ this.set(getType().getIdentityProperty(), value);
+ }
+
+ void becomeProxy(Object identity) {
+ for (Property property : getType().getProperties()) {
+ setUnavailable(property);
+ }
+ setIdentity(identity);
+ this.isProxy = true;
+ }
+
+ public boolean isAvailable(Property property) {
+ return basicGet(property) != PartialDataObject.UNAVAILABLE_PROPERTY;
+ }
+
+ public void setUnavailable(Property property) {
+ basicSet(property, PartialDataObject.UNAVAILABLE_PROPERTY);
+ }
+
+ public boolean isSet(Property property) {
+ //TODO many valued props ref SDO_2.1_DRAFT_20060726.pdf page 18
+ Object value = basicGet(property);
+ return value != null && value != PartialDataObject.UNAVAILABLE_PROPERTY;
+ }
+
+ public void unset(Property property) {
+ basicSet(property, null);
+ }
+
+ public boolean isProxy() {
+ return isProxy;
+ }
+
+ private boolean isProxy;
+
+}
diff --git a/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/implementation/BinarySerializer.java b/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/implementation/BinarySerializer.java
new file mode 100644
index 0000000000..70a8b53194
--- /dev/null
+++ b/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/implementation/BinarySerializer.java
@@ -0,0 +1,115 @@
+package com.agfa.hap.sdo.implementation;
+
+import java.io.IOException;
+import java.io.ObjectInput;
+import java.io.ObjectOutput;
+import java.io.ObjectStreamException;
+import java.util.Iterator;
+
+import org.apache.log4j.Logger;
+
+import com.agfa.hap.sdo.PartialDataFactory;
+import com.agfa.hap.sdo.PartialDataObject;
+import com.agfa.hap.sdo.Property;
+import com.agfa.hap.sdo.impl.TypeConverter;
+import commonj.sdo.impl.ExternalizableDelegator.Resolvable;
+
+public abstract class BinarySerializer implements Resolvable {
+ private static Logger logger = Logger.getLogger(BinarySerializer.class);
+ private static final long serialVersionUID = -6762854727094260430L;
+
+ public BinarySerializer() {
+ }
+
+ public BinarySerializer(PartialDataObject dataObject) {
+ this.dataObject = dataObject;
+ }
+
+ protected transient PartialDataObject dataObject;
+
+ public Object readResolve() throws ObjectStreamException {
+ return dataObject;
+ }
+
+ public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
+ dataObject = read(in);
+ }
+
+ protected PartialDataObject create(String uri, String type) {
+ return PartialDataFactory.INSTANCE.create(uri, type);
+ }
+
+ protected PartialDataObject read(ObjectInput in) throws IOException {
+ String uri = in.readUTF();
+ String type = in.readUTF();
+ PartialDataObject result = create(uri, type);
+ int propIndex = in.readInt();
+ while (propIndex >= 0) {
+ Property prop = result.getType().getProperty(propIndex);
+ Object value = read(in, result, prop);
+ if (value != null) {
+ // can happen in case of sdo-ref
+ if (prop.isMany()) {
+ result.getList(prop).add(value);
+ } else {
+ result.set(prop, value);
+ }
+ }
+ propIndex = in.readInt();
+ }
+ return result;
+ }
+
+ private Object read(ObjectInput in, PartialDataObject parent, Property prop) throws IOException {
+ if (prop.getType().isDataType()) {
+ String value = in.readUTF();
+ try {
+ return TypeConverter.get(prop.getType()).parse(value);
+ } catch (RuntimeException e){
+ logger.error("unable to parse " + value + "for property " + prop + " parent=" + parent, e);
+ throw e;
+ }
+ } else {
+ return readChildDataObject(in, prop, parent);
+ }
+
+ }
+
+ public void writeExternal(ObjectOutput out) throws IOException {
+ write(out, dataObject);
+ }
+
+ protected void write(ObjectOutput out, PartialDataObject dataObject) throws IOException {
+ out.writeUTF(dataObject.getType().getURI());
+ out.writeUTF(dataObject.getType().getName());
+ for (Property prop : dataObject.getType().getProperties()) {
+ if (dataObject.isSet(prop)) {
+ if (prop.isMany()) {
+ Iterator<PartialDataObject> it = dataObject.getList(prop).iterator();
+ while (it.hasNext()) {
+ write(out, prop, it.next());
+ }
+ } else {
+ write(out, prop, dataObject.get(prop));
+ }
+ }
+ }
+ out.writeInt(-1);
+ }
+
+ protected void write(ObjectOutput out, Property prop, Object value) throws IOException {
+ if (prop.getOpposite() != null && prop.getOpposite().isContainment()) {
+ return;
+ }
+ out.writeInt(prop.getIndex());
+ if (prop.getType().isDataType()) {
+ out.writeUTF(TypeConverter.get(prop.getType()).toString(value));
+ } else {
+ writeChildDataObject(out, prop, (PartialDataObject) value);
+ }
+ }
+
+ protected abstract void writeChildDataObject(ObjectOutput out, Property prop, PartialDataObject child) throws IOException;
+ protected abstract PartialDataObject readChildDataObject(ObjectInput in, Property prop, PartialDataObject parent) throws IOException;
+
+}
diff --git a/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/implementation/BulkProperty.java b/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/implementation/BulkProperty.java
new file mode 100644
index 0000000000..3763b16963
--- /dev/null
+++ b/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/implementation/BulkProperty.java
@@ -0,0 +1,18 @@
+package com.agfa.hap.sdo.implementation;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import com.agfa.hap.sdo.Property;
+import com.agfa.hap.sdo.SnapshotDefinition;
+
+public class BulkProperty<T> {
+ BulkProperty(Property property, SnapshotDefinition subselect) {
+ this.property = property;
+ this.subselect = subselect;
+ this.instances = new ArrayList<T>();
+ }
+ Property property;
+ List<T> instances;
+ SnapshotDefinition subselect;
+ } \ No newline at end of file
diff --git a/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/implementation/ClassPathImplementationClassRepository.java b/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/implementation/ClassPathImplementationClassRepository.java
new file mode 100644
index 0000000000..820d7a0714
--- /dev/null
+++ b/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/implementation/ClassPathImplementationClassRepository.java
@@ -0,0 +1,46 @@
+package com.agfa.hap.sdo.implementation;
+
+import java.io.IOException;
+import java.net.URL;
+import java.util.Enumeration;
+
+import com.agfa.hap.sdo.util.ClassHolder;
+import com.agfa.hap.sdo.util.ClassLoaderBasedClassHolder;
+
+/**
+ * {@link ImplementationClassRepository} that gets its resources by searching at start-up
+ * the classpath for sdo/implementationclasses.properties files.
+ * @author AMOCZ
+ */
+public class ClassPathImplementationClassRepository extends ImplementationClassRepository {
+
+ @Override
+ protected void initialize() {
+ // this is needed e.g. during the maven build
+ initializeImplementationClasses(Thread.currentThread().getContextClassLoader());
+ // this is needed during normal runtime
+ initializeImplementationClasses(TypeHelperImpl.class.getClassLoader());
+ }
+
+ private void initializeImplementationClasses(final ClassLoader classLoader) {
+ if (classLoader == null) {
+ return;
+ }
+ try {
+ Enumeration<URL> urls = classLoader.getResources(IMPLEMENTATIONCLASS_RESOURCEFILE);
+ while (urls.hasMoreElements()){
+ URL url = urls.nextElement();
+ addImplementationClasses(url, new ClassHolderFactory() {
+
+ public ClassHolder createClassHolder(String className) {
+ return new ClassLoaderBasedClassHolder(className, classLoader);
+ }
+
+ });
+ }
+ } catch (IOException e) {
+ getLogger().warn("Could not read implementation classes.", e);
+ }
+ }
+
+}
diff --git a/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/implementation/CompleteSerializer.java b/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/implementation/CompleteSerializer.java
new file mode 100644
index 0000000000..944dc0fba4
--- /dev/null
+++ b/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/implementation/CompleteSerializer.java
@@ -0,0 +1,74 @@
+package com.agfa.hap.sdo.implementation;
+
+import java.io.IOException;
+import java.io.ObjectInput;
+import java.io.ObjectOutput;
+import java.util.ArrayList;
+import java.util.List;
+
+import com.agfa.hap.sdo.PartialDataObject;
+import com.agfa.hap.sdo.Property;
+
+/**
+ * BinarySerializer that serializes every object that it can reach in the graph.
+ * Implementation note: DataObjects are inlined the first time they are encountered.
+ * The second time an object is encountered a reference is stored.
+ * @author AMOCZ
+ */
+public class CompleteSerializer extends BinarySerializer {
+
+ private transient List<PartialDataObject> instances;
+
+ public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
+ instances = new ArrayList<PartialDataObject>();
+ super.readExternal(in);
+ instances = null;
+ }
+
+ @Override
+ protected PartialDataObject readChildDataObject(ObjectInput in, Property prop,
+ PartialDataObject parent) throws IOException {
+ boolean isOnStream = in.readBoolean();
+ if (isOnStream) {
+ return read(in);
+ } else {
+ int index = in.readInt();
+ return instances.get(index);
+ }
+ }
+
+
+ @Override
+ protected PartialDataObject create(String uri, String type) {
+ PartialDataObject result = super.create(uri, type);
+ instances.add(result);
+ return result;
+ }
+
+ @Override
+ public void writeExternal(ObjectOutput out) throws IOException {
+ instances = new ArrayList<PartialDataObject>();
+ super.writeExternal(out);
+ instances = null;
+ }
+
+ @Override
+ protected void write(ObjectOutput out, PartialDataObject dataObject) throws IOException {
+ instances.add(dataObject);
+ super.write(out, dataObject);
+ }
+
+ @Override
+ protected void writeChildDataObject(ObjectOutput out, Property prop,
+ PartialDataObject child) throws IOException {
+ int index = instances.indexOf(child);
+ if (index >= 0) {
+ out.writeBoolean(false);
+ out.writeInt(index);
+ } else {
+ out.writeBoolean(true);
+ write(out, child);
+ }
+ }
+
+}
diff --git a/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/implementation/ContainedDataObject.java b/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/implementation/ContainedDataObject.java
new file mode 100644
index 0000000000..c512a15238
--- /dev/null
+++ b/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/implementation/ContainedDataObject.java
@@ -0,0 +1,13 @@
+package com.agfa.hap.sdo.implementation;
+
+import commonj.sdo.DataGraph;
+
+/**
+ * This interface is introduced to allow addition of dataobject to a datagraph in an
+ * implementation independent way.
+ */
+public interface ContainedDataObject {
+
+ void setDataGraph(DataGraph dataGraph);
+
+}
diff --git a/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/implementation/ContainmentSerializer.java b/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/implementation/ContainmentSerializer.java
new file mode 100644
index 0000000000..8a0b27a260
--- /dev/null
+++ b/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/implementation/ContainmentSerializer.java
@@ -0,0 +1,71 @@
+package com.agfa.hap.sdo.implementation;
+
+import java.io.IOException;
+import java.io.ObjectInput;
+import java.io.ObjectOutput;
+import java.io.ObjectStreamException;
+import java.util.ArrayList;
+import java.util.List;
+
+import com.agfa.hap.sdo.PartialDataObject;
+import com.agfa.hap.sdo.Property;
+import com.agfa.hap.sdo.implementation.DataObjectHelper.Link;
+
+/**
+ * BinarySerializer that serializes all instances that it can reach through contained properties
+ * (as dictated by the sdo spec).
+ * @author AMOCZ
+ */
+public class ContainmentSerializer extends BinarySerializer {
+
+ private transient List<Link> links;
+
+ public ContainmentSerializer() {
+ }
+
+ public ContainmentSerializer(PartialDataObject dataObject) {
+ super(dataObject);
+ }
+
+ public Object readResolve() throws ObjectStreamException {
+ processLinks();
+ return super.readResolve();
+ }
+
+ protected void writeChildDataObject(ObjectOutput out, Property prop, PartialDataObject child) throws IOException {
+ if (prop.isContainment()) {
+ write(out, child);
+ } else {
+ out.writeUTF(DataObjectHelper.getSdoRef(child));
+ }
+ }
+
+ @Override
+ protected PartialDataObject readChildDataObject(ObjectInput in, Property prop, PartialDataObject parent) throws IOException {
+ if (prop.isContainment()) {
+ return read(in);
+ } else {
+ String sdoRef = in.readUTF();
+ addLink(parent, prop, sdoRef);
+ return null;
+ }
+ }
+
+ private void addLink(PartialDataObject target, Property prop, String sdoRef) {
+ if (links == null) {
+ links = new ArrayList<Link>();
+ }
+ links.add(new Link(target, prop, sdoRef));
+ }
+
+ private void processLinks() {
+ if (links == null) {
+ return;
+ }
+ for (Link link : links) {
+ link.apply();
+ }
+ links = null;
+ }
+
+}
diff --git a/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/implementation/DataFactoryImpl.java b/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/implementation/DataFactoryImpl.java
new file mode 100644
index 0000000000..b8a62b3ca2
--- /dev/null
+++ b/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/implementation/DataFactoryImpl.java
@@ -0,0 +1,52 @@
+package com.agfa.hap.sdo.implementation;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+
+import com.agfa.hap.sdo.PartialDataFactory;
+import com.agfa.hap.sdo.PartialDataObject;
+import com.agfa.hap.sdo.Type;
+import commonj.sdo.helper.TypeHelper;
+
+public class DataFactoryImpl implements PartialDataFactory {
+
+ public PartialDataObject create(String uri, String typeName) {
+ return create(TypeHelper.INSTANCE.getType(uri, typeName));
+ }
+
+ public PartialDataObject create(Class interfaceClass) {
+ return create(TypeHelper.INSTANCE.getType(interfaceClass));
+ }
+
+ public PartialDataObject create(commonj.sdo.Type type) {
+ Constructor<? extends PartialDataObject> instanceConstructor = ((TypeImplementation) type).getInstanceConstructor();
+ if (instanceConstructor != null) {
+ try {
+ return instanceConstructor.newInstance(type);
+ } catch (InvocationTargetException e) {
+ throw new IllegalArgumentException("Can't instantiate objects of type " + type.getName(), e);
+ } catch (IllegalAccessException e) {
+ throw new IllegalArgumentException("Can't instantiate objects of type " + type.getName(), e);
+ } catch (InstantiationException e) {
+ throw new IllegalArgumentException("Can't instantiate objects of type " + type.getName(), e);
+ }
+ }
+ return createDefaultDataObjectInstance(type);
+ }
+
+ protected PartialDataObject createDefaultDataObjectInstance(commonj.sdo.Type type) {
+ return new DataObjectImplementation((Type) type);
+ }
+
+ public PartialDataObject createProxy(String uri, String typeName, Object identity) {
+ return createProxy(TypeHelper.INSTANCE.getType(uri, typeName), identity);
+ }
+
+ public PartialDataObject createProxy(commonj.sdo.Type type, Object identity) {
+ PartialDataObject result = create(type);
+ ((AbstractPartialDataObject) result).becomeProxy(identity);
+ return result;
+ }
+
+
+}
diff --git a/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/implementation/DataGraphImplementation.java b/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/implementation/DataGraphImplementation.java
new file mode 100644
index 0000000000..cef56d67a8
--- /dev/null
+++ b/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/implementation/DataGraphImplementation.java
@@ -0,0 +1,51 @@
+package com.agfa.hap.sdo.implementation;
+
+import java.io.Serializable;
+
+import commonj.sdo.ChangeSummary;
+import commonj.sdo.DataGraph;
+import commonj.sdo.DataObject;
+import commonj.sdo.Type;
+import commonj.sdo.helper.DataFactory;
+import commonj.sdo.helper.TypeHelper;
+
+public class DataGraphImplementation implements DataGraph, Serializable {
+
+ private static final long serialVersionUID = 6144199477779992429L;
+
+ public DataGraphImplementation() {
+ }
+
+ public DataGraphImplementation(DataObject rootObject) {
+ this.rootObject = rootObject;
+ ((ContainedDataObject) rootObject).setDataGraph(this);
+ }
+
+ public DataObject getRootObject() {
+ return rootObject;
+ }
+
+ public ChangeSummary getChangeSummary() {
+ throw new UnsupportedOperationException();
+ }
+
+ public commonj.sdo.Type getType(String uri, String typeName) {
+ return TypeHelper.INSTANCE.getType(uri, typeName);
+ }
+
+ public DataObject createRootObject(String namespaceURI, String typeName) {
+ return createRootObject(getType(namespaceURI, typeName));
+ }
+
+ public DataObject createRootObject(commonj.sdo.Type type) {
+ rootObject = DataFactory.INSTANCE.create(type);
+ ((ContainedDataObject) rootObject).setDataGraph(this);
+ return rootObject;
+ }
+
+ DataObject createObject(Type type) {
+ return DataFactory.INSTANCE.create(type);
+ }
+
+ private DataObject rootObject;
+}
diff --git a/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/implementation/DataObjectConverter.java b/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/implementation/DataObjectConverter.java
new file mode 100644
index 0000000000..6b8c90026d
--- /dev/null
+++ b/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/implementation/DataObjectConverter.java
@@ -0,0 +1,147 @@
+package com.agfa.hap.sdo.implementation;
+
+import java.io.IOException;
+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 com.agfa.hap.sdo.DataMapper;
+import com.agfa.hap.sdo.ObjectPropertyVisitor;
+import com.agfa.hap.sdo.PartialDataFactory;
+import com.agfa.hap.sdo.Property;
+import com.agfa.hap.sdo.SnapshotDefinition;
+import com.agfa.hap.sdo.Type;
+import commonj.sdo.DataObject;
+
+/**
+*
+* the same functionality as SnapshotSerializer except that this outputs to a list of DataObjects
+*/
+public class DataObjectConverter<T> implements ObjectPropertyVisitor{
+
+ private DataMapper<T> mapper;
+ private Map<Object, DataObject> source2do = new HashMap<Object, DataObject>();
+ private Map<Property, BulkProperty<T>> bulkProperties;
+
+ public DataObjectConverter(DataMapper<T> mapper){
+ this.mapper = mapper;
+ }
+
+ public void endDataObject(Object instance, Type type) throws Exception {
+ //do nothing
+ }
+
+ public void startDataObject(Object instance, Type type) throws Exception {
+ DataObject dataObject = source2do.get(instance);
+ if (dataObject == null){
+ dataObject = PartialDataFactory.INSTANCE.create(type);
+ source2do.put(instance, dataObject);
+ }
+ }
+
+ public void visitBulkProperty(Object instance, Property property, SnapshotDefinition subselect) {
+ getInstancesForBulkProperty(property, subselect).add((T)instance);
+ }
+
+ protected List<T> getInstancesForBulkProperty(Property property, SnapshotDefinition subselect) {
+ if (bulkProperties == null) {
+ bulkProperties = new HashMap<Property, BulkProperty<T>>();
+ }
+ BulkProperty<T> bp = bulkProperties.get(property);
+ if (bp == null) {
+ bp = new BulkProperty<T>(property, subselect);
+ bulkProperties.put(property, bp);
+ } else {
+ if (!bp.subselect.equals(subselect)) {
+ throw new IllegalArgumentException("Different subselects not yet supported!");
+ }
+ }
+ return bp.instances;
+ }
+
+ public boolean visitProperty(Object instance, Property property, Object value) throws Exception {
+ DataObject dataObject = source2do.get(instance);
+ if (property.getType().isDataType() || value == null){
+ dataObject.set(property, value); //if many valued and datatype, then value will be a List, so this is ok
+ return false;
+ } else {
+ DataObject convertedValue = source2do.get(value);
+ if (convertedValue == null){
+ source2do.put(value, dataObject.createDataObject(property));
+ return true;
+ } else {
+ if (property.isMany()){
+ List manyValuedValue = dataObject.getList(property);
+ if (!manyValuedValue.contains(value)){
+ manyValuedValue.add(convertedValue);
+ }
+ } else {
+ dataObject.set(property, convertedValue);
+ }
+ return false; //was already visited, so no need to recurse
+ }
+ }
+ }
+
+ public void visitProxyProperty(Object instance, Property property, Object identity) throws Exception {
+ if (identity == null){ //eg when it is a many valued property
+ return;
+ }
+ DataObject dataObject = source2do.get(instance);
+ dataObject.set(property, PartialDataFactory.INSTANCE.createProxy(property.getType(), identity));
+ }
+
+ public List<DataObject> convert(SnapshotDefinition def, Collection<? extends T> roots) throws Exception {
+ for (T root : roots){
+ def.visit(mapper, this, root);
+ }
+ processBulkProperties();
+ List<DataObject> pdos = new ArrayList<DataObject>(roots.size());
+ for (T root : roots){
+ pdos.add(source2do.get(root));
+ }
+ return pdos;
+ }
+
+ protected void processBulkProperties() throws Exception {
+ if (bulkProperties != null) {
+ while (!bulkProperties.isEmpty()){
+ //we can't just simply iterate over the bulkproperties.values, because while writing a bulkproperty we might visit an additional bulk property in the subselect
+ BulkProperty bp = bulkProperties.values().iterator().next();
+ BulkProperty emptyBulkProperty = new BulkProperty(bp.property, bp.subselect);
+ bulkProperties.put(bp.property, emptyBulkProperty); //replace the bulk property that we are going to write with a copy of itself, but withouth instances
+ processBulkProperty(bp);
+ if (emptyBulkProperty.instances.isEmpty()){
+ bulkProperties.remove(bp.property); //if no new instances were added (through some nested bulkproperties) than we can remove the bulkproperty from the map of bulk properties to be processed
+ }
+ }
+ }
+ }
+
+ private void processBulkProperty(BulkProperty bp) throws IOException, Exception {
+ Collection<T> values = this.mapper.getProperties(bp.instances, bp.property, bp.subselect);
+ Iterator<T> valuesIt = values.iterator();
+ Iterator<?> parents = bp.instances.iterator();
+ while (parents.hasNext()) {
+ Object parent = parents.next();
+ T value = valuesIt.next();
+ if (bp.property.isMany()) {
+ Collection<T> children = (Collection<T>) value;
+ for (T child : children) {
+ processBulkPropertyValue(bp, parent, child);
+ }
+ } else {
+ processBulkPropertyValue(bp, parent, value);
+ }
+ }
+ }
+
+ protected void processBulkPropertyValue(BulkProperty bp, Object parent, T value) throws IOException, Exception {
+ if (this.visitProperty(parent, bp.property, value)) {
+ bp.subselect.visit(mapper, this, value);
+ }
+ }
+}
diff --git a/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/implementation/DataObjectHelper.java b/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/implementation/DataObjectHelper.java
new file mode 100644
index 0000000000..be19099150
--- /dev/null
+++ b/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/implementation/DataObjectHelper.java
@@ -0,0 +1,61 @@
+package com.agfa.hap.sdo.implementation;
+
+import com.agfa.hap.sdo.PartialDataObject;
+import com.agfa.hap.sdo.Property;
+
+class DataObjectHelper {
+
+ public static String getSdoRef(PartialDataObject target) {
+ StringBuilder builder = new StringBuilder();
+ Property prop = target.getContainmentProperty();
+ while (prop != null) {
+ target.getContainmentProperty();
+ if (prop.isMany()) {
+ builder.insert(0,']');
+ builder.insert(0, target.getContainer().getList(prop).indexOf(target));
+ builder.insert(0,'[');
+ }
+ builder.insert(0, prop.getName());
+ builder.insert(0, '/');
+ target = target.getContainer();
+ prop = target.getContainmentProperty();
+ }
+ return builder.toString();
+ }
+
+ public static PartialDataObject deref(PartialDataObject obj, String sdoRef) {
+ if (obj.getContainer() != null) {
+ return deref(obj.getContainer(), sdoRef);
+ }
+ String[] props = sdoRef.split("/");
+ for (String propertyName : props) {
+ if (propertyName.length() > 0) {
+ int index = 0;
+ if (propertyName.endsWith("]")) {
+ int bracketIndex = propertyName.lastIndexOf('[');
+ index = Integer.parseInt(propertyName.substring(bracketIndex+1, propertyName.length()-1));
+ propertyName = propertyName.substring(0, bracketIndex);
+ }
+ Property prop = obj.getType().getProperty(propertyName);
+ obj = prop.isMany() ? (PartialDataObject) obj.getList(prop).get(index) : obj.getDataObject(prop);
+ }
+ }
+ return obj;
+ }
+
+ public static class Link {
+ private PartialDataObject target;
+ private Property property;
+ private String sdoRef;
+ Link(PartialDataObject target, Property property, String sdoRef) {
+ this.target = target;
+ this.property = property;
+ this.sdoRef = sdoRef;
+ }
+ void apply() {
+ PartialDataObject link = DataObjectHelper.deref(target, sdoRef);
+ target.set(property, link);
+ }
+ }
+
+}
diff --git a/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/implementation/DataObjectImplementation.java b/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/implementation/DataObjectImplementation.java
new file mode 100644
index 0000000000..1f0467451c
--- /dev/null
+++ b/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/implementation/DataObjectImplementation.java
@@ -0,0 +1,56 @@
+package com.agfa.hap.sdo.implementation;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import commonj.sdo.Property;
+
+public class DataObjectImplementation extends AbstractPartialDataObject {
+
+ private static final long serialVersionUID = -3807872778273860980L;
+
+ protected DataObjectImplementation(com.agfa.hap.sdo.Type type, DataGraphImplementation dataGraph) {
+ super(type, dataGraph);
+ }
+
+ protected DataObjectImplementation(com.agfa.hap.sdo.Type type) {
+ super(type);
+ this.properties = new Object[type.getProperties().size()];
+ }
+
+
+ @Override
+ protected Object basicGet(Property property) {
+ Object result = properties[property.getIndex()];
+ if (property.isMany() && result == null) {
+ if (property.getType().isDataType()){
+ result = new ArrayList();
+ } else {
+ result = new DataObjectList(this, property);
+ }
+ properties[property.getIndex()] = result;
+ }
+ return result;
+ }
+
+ @Override
+ protected void basicSet(Property property, Object value) {
+ properties[property.getIndex()] = value;
+ }
+
+ @Override
+ protected List initializeWithEmptyList(Property property) {
+ List emptyList;
+ if (property.getType().isDataType()){
+ emptyList = new ArrayList();
+ } else {
+ emptyList = new DataObjectList(this, property);
+ }
+ basicSet(property, emptyList);
+ return emptyList;
+ }
+
+ private Object[] properties;
+
+}
+
diff --git a/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/implementation/DataObjectList.java b/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/implementation/DataObjectList.java
new file mode 100644
index 0000000000..a12a3c6dba
--- /dev/null
+++ b/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/implementation/DataObjectList.java
@@ -0,0 +1,131 @@
+package com.agfa.hap.sdo.implementation;
+
+import java.util.ArrayList;
+import java.util.Collection;
+
+import commonj.sdo.Property;
+
+/**
+ * List that performs AbstractDataObject bidirectional updates. If a dataobject is added/removed
+ * from this list, the opposite property of the dataobject is also set/reset. Also
+ * updates the containment relationship.
+ * @author AMOCZ
+ *
+ */
+public class DataObjectList extends ArrayList<AbstractDataObject> implements IDataObjectList<AbstractDataObject>{
+
+ private static final long serialVersionUID = -3893408567235500837L;
+
+ private final AbstractDataObject owner;
+ private final Property listProperty;
+ private boolean bidirUpdateInProgress;
+
+ public DataObjectList(AbstractDataObject owner, Property listProperty) {
+ this.owner = owner;
+ this.listProperty = listProperty;
+ }
+
+ @Override
+ public AbstractDataObject set(int index, AbstractDataObject element) {
+ AbstractDataObject oldValue = super.set(index, element);
+ performBidirectionalUpdate(element, owner);
+ if (oldValue != null) {
+ performBidirectionalUpdate(oldValue, null);
+ }
+ return oldValue;
+ }
+
+ public void removeForBidirectionalUpdate(AbstractDataObject o) {
+ if (!bidirUpdateInProgress) {
+ super.remove(o);
+ }
+ }
+
+ public void addForBidirectionalUpdate(AbstractDataObject o) {
+ if (!bidirUpdateInProgress) {
+ super.add(o);
+ }
+ }
+
+ @Override
+ public boolean add(AbstractDataObject o) {
+ if (bidirUpdateInProgress) {
+ return false;
+ }
+ if (listProperty.getOpposite() != null && o.getDataObject(listProperty.getOpposite()) == owner) {
+ throw new IllegalArgumentException("Can't add this object to the list because it is already present.");
+ }
+ boolean result = super.add(o);
+ performBidirectionalUpdate(o, owner);
+ return result;
+ }
+
+ @Override
+ public void add(int index, AbstractDataObject element) {
+ super.add(index, element);
+ performBidirectionalUpdate(element, owner);
+ }
+
+ @Override
+ public AbstractDataObject remove(int index) {
+ AbstractDataObject result = super.remove(index);
+ if (result != null) {
+ performBidirectionalUpdate(result, null);
+ }
+ return result;
+ }
+
+ @Override
+ public boolean remove(Object o) {
+ if (bidirUpdateInProgress) {
+ return false;
+ }
+ boolean result = super.remove(o);
+ if (result) {
+ performBidirectionalUpdate((AbstractDataObject) o, null);
+ }
+ return result;
+ }
+
+ protected void performBidirectionalUpdate(AbstractDataObject source, AbstractDataObject target) {
+ bidirUpdateInProgress = true;
+ if (listProperty.getOpposite() != null) {
+ source.set(listProperty.getOpposite(), target);
+ }
+ if (listProperty.isContainment()) {
+ source.setContainment(target, listProperty);
+ }
+ bidirUpdateInProgress = false;
+ }
+
+ @Override
+ public void clear() {
+ for (AbstractDataObject d : this) {
+ performBidirectionalUpdate(d, null);
+ }
+ super.clear();
+ }
+
+ @Override
+ public boolean addAll(Collection<? extends AbstractDataObject> c) {
+ boolean result = super.addAll(c);
+ if (result) {
+ for (AbstractDataObject d : c) {
+ performBidirectionalUpdate(d, owner);
+ }
+ }
+ return result;
+ }
+
+ @Override
+ public boolean addAll(int index, Collection<? extends AbstractDataObject> c) {
+ boolean result = super.addAll(index, c);
+ if (result) {
+ for (AbstractDataObject d : c) {
+ performBidirectionalUpdate(d, owner);
+ }
+ }
+ return result;
+ }
+
+}
diff --git a/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/implementation/DataObjectStreamer.java b/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/implementation/DataObjectStreamer.java
new file mode 100644
index 0000000000..978c031dbe
--- /dev/null
+++ b/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/implementation/DataObjectStreamer.java
@@ -0,0 +1,178 @@
+package com.agfa.hap.sdo.implementation;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.Reader;
+import java.io.Writer;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.parsers.SAXParser;
+import javax.xml.parsers.SAXParserFactory;
+import javax.xml.transform.OutputKeys;
+import javax.xml.transform.Result;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerConfigurationException;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.sax.SAXTransformerFactory;
+import javax.xml.transform.sax.TransformerHandler;
+import javax.xml.transform.stream.StreamResult;
+
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+import org.xml.sax.helpers.AttributesImpl;
+
+import com.agfa.hap.sdo.PartialDataObject;
+import com.agfa.hap.sdo.Property;
+import com.agfa.hap.sdo.impl.TypeConverter;
+
+import commonj.sdo.DataObject;
+import commonj.sdo.Type;
+import commonj.sdo.helper.TypeHelper;
+import commonj.sdo.helper.XMLDocument;
+
+class DataObjectStreamer {
+
+ private static SAXParserFactory parserFactory;
+
+ static final String XSI_URI = "http://www.w3.org/2001/XMLSchema-instance";
+ static final String XMLNS_URI = "http://www.w3.org/2000/xmlns/";
+
+ static {
+ parserFactory = SAXParserFactory.newInstance();
+ parserFactory.setNamespaceAware(true);
+ }
+
+ public XMLDocument fromXml(Reader r) throws IOException, SAXException {
+ return fromXml(new InputSource(r));
+ }
+
+ public XMLDocument fromXml(InputStream is) throws IOException, SAXException {
+ return fromXml(new InputSource(is));
+ }
+
+ public XMLDocument fromXml(InputSource input) throws IOException, SAXException {
+ try {
+ SAXParser parser = parserFactory.newSAXParser();
+ DataObjectXmlParser dox = new DataObjectXmlParser();
+ parser.parse(input, dox);
+ return dox.getXMLDocument();
+ } catch (ParserConfigurationException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public void toXml(XMLDocument doc, Writer os) {
+ try {
+ toXml(doc, new StreamResult(os));
+ } catch (TransformerConfigurationException e) {
+ throw new RuntimeException(e);
+ } catch (SAXException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public void toXml(XMLDocument doc, OutputStream os) {
+ try {
+ toXml(doc, new StreamResult(os));
+ } catch (TransformerConfigurationException e) {
+ throw new RuntimeException(e);
+ } catch (SAXException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ void toXml(XMLDocument doc, Result result) throws SAXException, TransformerConfigurationException {
+ SAXTransformerFactory tf = (SAXTransformerFactory) TransformerFactory.newInstance();
+ TransformerHandler hd = tf.newTransformerHandler();
+ Transformer serializer = hd.getTransformer();
+ serializer.setOutputProperty(OutputKeys.ENCODING, doc.getEncoding());
+ // serializer.setOutputProperty(OutputKeys.DOCTYPE_SYSTEM,"users.dtd");
+ serializer.setOutputProperty(OutputKeys.INDENT,"yes");
+ hd.setResult(result);
+ hd.startDocument();
+// hd.startPrefixMapping("xmlns", XMLNS_URI);
+ // hd.startPrefixMapping("", doc.getRootElementURI());
+ hd.startPrefixMapping("xsi", XSI_URI);
+ AttributesImpl attrs = new AttributesImpl();
+ Map<String, String> uriToNamespace = createUriToNamespace();
+ if (doc.getRootElementURI() != null) {
+ uriToNamespace.put(doc.getRootElementURI(), "tns");
+ }
+ for (String uri : uriToNamespace.keySet()) {
+ String ns = uriToNamespace.get(uri);
+ attrs.addAttribute(XMLNS_URI, ns, "xmlns:" + ns, "CDATA", uri);
+ }
+ String qualifiedName = uriToNamespace.get(doc.getRootObject().getType().getURI());
+ if (qualifiedName == null) {
+ qualifiedName = "";
+ } else {
+ qualifiedName = qualifiedName + ":";
+ }
+ attrs.addAttribute(XSI_URI, "type", "xsi:type", "CDATA", qualifiedName + doc.getRootObject().getType().getName());
+ hd.startElement(doc.getRootElementURI(), doc.getRootElementName(), doc.getRootElementName(), attrs);
+ toXml(hd, (PartialDataObject) doc.getRootObject(), uriToNamespace);
+ hd.endElement(doc.getRootElementURI(), doc.getRootElementName(), doc.getRootElementName());
+ hd.endPrefixMapping("xsi");
+ // hd.endPrefixMapping("");
+// hd.endPrefixMapping("xmlns");
+ hd.endDocument();
+ }
+
+ Map<String, String> createUriToNamespace() {
+ Map<String, String> result = new HashMap<String, String>();
+ result.put("http://www.w3.org/2001/XMLSchema-instance", "xsi");
+ result.put("commonj.sdo", "sdo");
+ int i = 1;
+ for (String uri : ((TypeHelperImpl) TypeHelper.INSTANCE).getKnownUris()) {
+ if (!result.containsKey(uri)) {
+ result.put(uri, "ns" + i++);
+ }
+ }
+ return result;
+ }
+
+ void toXml(TransformerHandler hd, PartialDataObject dataObject, Map<String, String> uriToNamespace) throws SAXException {
+ if (dataObject == null) {
+ return;
+ }
+ for (Property prop : dataObject.getType().getProperties()) {
+ if (dataObject.isSet(prop) && (prop.getType().isDataType() || prop.isContainment() || prop.getOpposite() == null)) {
+ if (prop.isMany()) {
+ Iterator<PartialDataObject> it = dataObject.getList(prop).iterator();
+ while (it.hasNext()) {
+ toXml(hd, it.next(), prop, uriToNamespace);
+ }
+ } else {
+ toXml(hd, dataObject.get(prop), prop, uriToNamespace);
+ }
+ }
+ }
+ }
+
+ private void toXml(TransformerHandler hd, Object value, Property prop, Map<String, String> uriToNamespace) throws SAXException {
+ AttributesImpl attrs = new AttributesImpl();
+ if (!prop.isContainment() && !prop.getType().isDataType() && prop.getOpposite() == null) {
+ attrs.addAttribute("commonj.sdo", "ref", "sdo:ref", "CDATA", DataObjectHelper.getSdoRef((PartialDataObject) value));
+ }
+ if (!prop.getType().isDataType()) {
+ Type valueType = ((DataObject) value).getType();
+ if (prop.getType() != valueType) {
+ attrs.addAttribute(XSI_URI, "type", "xsi:type", "CDATA", uriToNamespace.get(valueType.getURI()) + ":" + valueType.getName());
+ }
+ }
+ hd.startElement("", prop.getName(), prop.getName(), attrs);
+ if (prop.getType().isDataType()) {
+ String output = value == null ? "" : TypeConverter.get(prop.getType()).toString(value);
+ hd.characters(output.toCharArray(), 0, output.length());
+ } else {
+ if (prop.isContainment()) {
+ toXml(hd, (PartialDataObject) value, uriToNamespace);
+ }
+ }
+ hd.endElement("", prop.getName(), prop.getName());
+ }
+}
diff --git a/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/implementation/DataObjectXmlParser.java b/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/implementation/DataObjectXmlParser.java
new file mode 100644
index 0000000000..8993286fb6
--- /dev/null
+++ b/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/implementation/DataObjectXmlParser.java
@@ -0,0 +1,146 @@
+package com.agfa.hap.sdo.implementation;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.xml.sax.Attributes;
+import org.xml.sax.SAXException;
+import org.xml.sax.helpers.DefaultHandler;
+
+import com.agfa.hap.sdo.PartialDataFactory;
+import com.agfa.hap.sdo.PartialDataObject;
+import com.agfa.hap.sdo.Property;
+import com.agfa.hap.sdo.impl.TypeConverter;
+import com.agfa.hap.sdo.implementation.DataObjectHelper.Link;
+
+import commonj.sdo.Type;
+import commonj.sdo.helper.TypeHelper;
+import commonj.sdo.helper.XMLDocument;
+
+class DataObjectXmlParser extends DefaultHandler {
+
+ private PartialDataObject currentObject;
+ private Property currentProperty;
+ private String nameSpaceURI;
+ private String rootElement;
+ private List<Link> links;
+ private StringBuilder currentValueCharacters;
+ private Map<String, String> prefixMapping;
+
+ public DataObjectXmlParser() {
+ prefixMapping = new HashMap<String, String>();
+ }
+
+
+ @Override
+ public void endPrefixMapping(String prefix) throws SAXException {
+ prefixMapping.remove(prefix);
+ }
+
+
+ @Override
+ public void startPrefixMapping(String prefix, String uri) throws SAXException {
+ prefixMapping.put(prefix, uri);
+ }
+
+
+ @Override
+ public void characters(char[] ch, int start, int length) throws SAXException {
+ if (currentProperty != null && currentProperty.getType().isDataType()) {
+ if (currentValueCharacters == null){
+ currentValueCharacters = new StringBuilder();
+ }
+ currentValueCharacters.append(ch, start, length);
+ }
+ }
+
+ @Override
+ public void endDocument() throws SAXException {
+ processLinks();
+ }
+
+ @Override
+ public void endElement(String uri, String localName, String qName) throws SAXException {
+ if (currentProperty == null) {
+ if (currentObject.getContainer() != null) {
+ currentObject = currentObject.getContainer();
+ }
+ } else {
+ Object currentValue = currentValueCharacters == null ? null : this.convert(currentValueCharacters.toString());
+ currentObject.set(currentProperty, currentValue);
+ currentProperty = null;
+ }
+ }
+
+ @Override
+ public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
+ if (rootElement == null) {
+ rootElement = localName;
+ nameSpaceURI = uri;
+ currentObject = PartialDataFactory.INSTANCE.create(resolve(attributes.getValue(DataObjectStreamer.XSI_URI, "type")));
+ } else {
+ currentValueCharacters = null;
+ currentProperty = currentObject.getType().getProperty(localName);
+ if (!currentProperty.getType().isDataType()) {
+ String sdoRef = attributes.getValue("sdo:ref");
+ if (sdoRef != null) {
+ addLink(sdoRef);
+ } else {
+ String xsiType = attributes.getValue("xsi:type");
+ Type type = currentProperty.getType();
+ if (xsiType != null) {
+ type = resolve(xsiType);
+ }
+ currentObject = currentObject.createDataObject(currentProperty, type);
+ currentProperty = null;
+ }
+ }
+ }
+ }
+
+ protected Type resolve(String xsiType) {
+ int index = xsiType.indexOf(':');
+ String uri = nameSpaceURI;
+ if (index >= 0) {
+ String prefix = xsiType.substring(0, index);
+ uri = prefixMapping.get(prefix);
+ xsiType = xsiType.substring(index+1);
+ }
+ return TypeHelper.INSTANCE.getType(uri, xsiType);
+ }
+
+ public XMLDocument getXMLDocument() {
+ return new XMLDocumentImpl(currentObject, nameSpaceURI, rootElement);
+ }
+
+ private Object convert(String value) {
+ if (value.length() == 0) {
+ return null;
+ }
+ TypeConverter type = TypeConverter.get(currentProperty.getType());
+ if (type == null) {
+ throw new IllegalArgumentException("No basic data type defined for type " + currentProperty.getType());
+ }
+ return type.parse(value);
+ }
+
+ private void addLink(String sdoRef) {
+ if (links == null) {
+ links = new ArrayList<Link>();
+ }
+ links.add(new Link(currentObject, currentProperty, sdoRef));
+ }
+
+ private void processLinks() {
+ if (links == null) {
+ return;
+ }
+ for (Link link : links) {
+ link.apply();
+ }
+ links = null;
+ }
+
+}
diff --git a/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/implementation/EqualityHelperImpl.java b/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/implementation/EqualityHelperImpl.java
new file mode 100644
index 0000000000..a9ef6d7e78
--- /dev/null
+++ b/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/implementation/EqualityHelperImpl.java
@@ -0,0 +1,135 @@
+package com.agfa.hap.sdo.implementation;
+
+import java.util.Iterator;
+
+import com.agfa.hap.sdo.PartialDataObject;
+import com.agfa.hap.sdo.Property;
+import commonj.sdo.DataObject;
+import commonj.sdo.helper.EqualityHelper;
+
+public class EqualityHelperImpl implements EqualityHelper {
+
+ public boolean equal(DataObject dataObject1, DataObject dataObject2) {
+ return equal((PartialDataObject) dataObject1, (PartialDataObject) dataObject2);
+ }
+
+ public boolean equal(PartialDataObject dataObject1, PartialDataObject dataObject2) {
+ if (dataObject1 == null) {
+ return dataObject2 == null;
+ }
+ if (dataObject2 == null) {
+ return false;
+ }
+ if (!dataObject1.getType().equals(dataObject2.getType())) {
+ return false;
+ }
+ for (Property prop : dataObject1.getType().getProperties()) {
+ if (!dataObject1.isSet(prop) && !dataObject2.isSet(prop)) {
+ continue;
+ }
+ if (!dataObject1.isAvailable(prop) && !dataObject2.isAvailable(prop)) {
+ continue;
+ }
+ if (prop.getType().isDataType()) {
+ Object value1 = dataObject1.get(prop);
+ Object value2 = dataObject2.get(prop);
+ if (value1 == null) {
+ if (value2 != null) {
+ return false;
+ }
+ } else {
+ if (value1 instanceof byte[]){
+ if (value2 instanceof byte[]){
+ if ((((byte[])value1).length) == (((byte[])value2).length)){
+ for (int i = 0; i < (((byte[])value1).length); i++ ){
+ if ((((byte[])value1)[i]) != (((byte[])value2)[i])){
+ return false;
+ }
+ }
+ }
+ } else {
+ return false;
+ }
+ } else if (!value1.equals(value2)) {
+ return false;
+ }
+ }
+ } else {
+ if (prop.getOpposite() != null && prop.getOpposite().isContainment()) {
+ continue;
+ }
+ if (prop.isMany()) {
+ Iterator<PartialDataObject> dataObject1It = dataObject1.getList(prop).iterator();
+ Iterator<PartialDataObject> dataObject2It = dataObject2.getList(prop).iterator();
+ while (dataObject1It.hasNext()) {
+ if (!dataObject2It.hasNext()) {
+ return false;
+ }
+ if (!compareChild(prop, dataObject1It.next(), dataObject2It.next())) {
+ return false;
+ }
+ }
+ if (dataObject2It.hasNext()) {
+ return false;
+ }
+ } else {
+ if (!compareChild(prop, dataObject1.getDataObject(prop), dataObject2.getDataObject(prop))) {
+ return false;
+ }
+ }
+ }
+ }
+ return true;
+ }
+
+ private boolean compareChild(Property prop, PartialDataObject child1, PartialDataObject child2) {
+ if (prop.isContainment()) {
+ if (!equal(child1, child2))
+ return false;
+ } else {
+ String id1 = DataObjectHelper.getSdoRef(child1);
+ String id2 = DataObjectHelper.getSdoRef(child2);
+ if (!id1.equals(id2)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ public boolean equalShallow(DataObject dataObject1, DataObject dataObject2) {
+ return equalShallow((PartialDataObject) dataObject1, (PartialDataObject) dataObject2);
+ }
+
+ public boolean equalShallow(PartialDataObject dataObject1, PartialDataObject dataObject2) {
+ if (dataObject1 == null) {
+ return dataObject2 == null;
+ }
+ if (dataObject2 == null) {
+ return false;
+ }
+ if (!dataObject1.getType().equals(dataObject2.getType())) {
+ return false;
+ }
+ for (Property prop : dataObject1.getType().getProperties()) {
+ if (!prop.getType().isDataType()) {
+ continue;
+ }
+ if (!dataObject1.isSet(prop) && !dataObject2.isSet(prop)) {
+ continue;
+ }
+ Object value1 = dataObject1.get(prop);
+ Object value2 = dataObject2.get(prop);
+ if (value1 == null) {
+ if (value2 != null) {
+ return false;
+ }
+ } else {
+ if (!value1.equals(value2)) {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+}
diff --git a/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/implementation/IDataObjectList.java b/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/implementation/IDataObjectList.java
new file mode 100644
index 0000000000..49211cb42f
--- /dev/null
+++ b/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/implementation/IDataObjectList.java
@@ -0,0 +1,10 @@
+package com.agfa.hap.sdo.implementation;
+
+import java.util.List;
+
+public interface IDataObjectList<T extends AbstractDataObject> extends List<T> {
+
+ void removeForBidirectionalUpdate(T o);
+ void addForBidirectionalUpdate(T o);
+
+}
diff --git a/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/implementation/ImplementationClassRepository.java b/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/implementation/ImplementationClassRepository.java
new file mode 100644
index 0000000000..d89c827e0d
--- /dev/null
+++ b/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/implementation/ImplementationClassRepository.java
@@ -0,0 +1,129 @@
+package com.agfa.hap.sdo.implementation;
+
+import java.net.URL;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Map.Entry;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.apache.log4j.Logger;
+
+import com.agfa.hap.sdo.util.ClassHolder;
+import commonj.sdo.Type;
+import commonj.sdo.helper.TypeHelper;
+
+/**
+ * Class that provides access to implementation classes for SDO types.
+ * @author AMOCZ
+ */
+public abstract class ImplementationClassRepository {
+
+ protected final String IMPLEMENTATIONCLASS_RESOURCEFILE = "sdo/implementationclasses.properties";
+
+ public interface ClassHolderFactory {
+ ClassHolder createClassHolder(String className);
+ }
+
+ private static ImplementationClassRepository INSTANCE;
+
+ public static ImplementationClassRepository getInstance() {
+ if (INSTANCE == null) {
+ setInstance(new ClassPathImplementationClassRepository());
+ }
+ return INSTANCE;
+ }
+
+ public static void setInstance(ImplementationClassRepository instance) {
+ INSTANCE = instance;
+ instance.initialize();
+ }
+
+ private Map<QualifiedName, ClassHolder> typeName2ImplementationClass;
+
+ public ImplementationClassRepository() {
+ typeName2ImplementationClass = new ConcurrentHashMap<QualifiedName, ClassHolder>();
+ }
+
+ public ClassHolder getImplementationClass(Type type) {
+ return getImplementationClass(type.getURI(), type.getName());
+ }
+
+ public ClassHolder getImplementationClass(String uri, String name) {
+ QualifiedName qname = new QualifiedName(uri, name);
+ return typeName2ImplementationClass.get(qname);
+ }
+
+ public Type getTypeForImplementationClass(String name) {
+ // could be optimized but might not be worth it
+ for (Entry<QualifiedName, ClassHolder> entry : typeName2ImplementationClass.entrySet()) {
+ if (entry.getValue().getClassName().equals(name)) {
+ return TypeHelper.INSTANCE.getType(entry.getKey().getUri(), entry.getKey().getName());
+ }
+ }
+ return null;
+ }
+
+ protected abstract void initialize();
+
+ protected static Logger getLogger() {
+ return Logger.getLogger(ImplementationClassRepository.class);
+ }
+
+ protected void addImplementationClasses(URL propertyFile, ClassHolderFactory factory) {
+ try {
+ Properties props = new Properties();
+ props.load(propertyFile.openStream());
+ addImplementationClasses(props, factory);
+ } catch (Exception e) {
+ getLogger().warn("Could not read implementation classes from stream " + propertyFile, e);
+ }
+ }
+
+ protected void addImplementationClasses(Properties instanceProps, ClassHolderFactory factory) {
+ for (Map.Entry<Object, Object> entry : instanceProps.entrySet()) {
+ this.registerImplementationClassForType(factory.createClassHolder((String) entry.getKey()), (String) entry.getValue());
+ }
+ }
+
+ public void registerImplementationClassForType(ClassHolder classHolder, String qname) {
+ this.typeName2ImplementationClass.put(new QualifiedName(qname), classHolder);
+ }
+
+ public void registerImplementationClassForType(ClassHolder classHolder, String uri, String name) {
+ this.typeName2ImplementationClass.put(new QualifiedName(uri, name), classHolder);
+ }
+
+ private static class QualifiedName {
+ private final String uri;
+ private final String name;
+ public QualifiedName(String qName) {
+ int index = qName.lastIndexOf('$');
+ this.uri = qName.substring(0, index);
+ this.name = qName.substring(index+1);
+ }
+ public QualifiedName(String uri, String name) {
+ this.uri = uri;
+ this.name = name;
+ }
+ public String getUri() {
+ return uri;
+ }
+ public String getName() {
+ return name;
+ }
+ public boolean equals(Object other) {
+ if (other == null || other.getClass() != QualifiedName.class) {
+ return false;
+ }
+ QualifiedName otherQN = (QualifiedName) other;
+ return uri.equals(otherQN.uri) && name.equals(otherQN.name);
+ }
+ public int hashCode() {
+ return uri.hashCode() + name.hashCode();
+ }
+ public String toString() {
+ return uri + "/" + name;
+ }
+ }
+
+}
diff --git a/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/implementation/PropertyImplementation.java b/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/implementation/PropertyImplementation.java
new file mode 100644
index 0000000000..eaddc6a324
--- /dev/null
+++ b/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/implementation/PropertyImplementation.java
@@ -0,0 +1,138 @@
+package com.agfa.hap.sdo.implementation;
+
+import java.io.Serializable;
+import java.util.Collections;
+import java.util.List;
+
+import com.agfa.hap.sdo.Property;
+import com.agfa.hap.sdo.Type;
+
+public class PropertyImplementation implements Property, Serializable {
+
+ private static final long serialVersionUID = 1234L;
+
+ public PropertyImplementation(String name, Type type, Type container, int index) {
+ this.name = name;
+ this.containingType = container;
+ this.type = type;
+ this.index = index;
+ }
+
+ public List<String> getAliasNames() {
+ return Collections.emptyList();
+ }
+
+ public Type getContainingType() {
+ return containingType;
+ }
+
+ public Object getDefault() {
+ return null;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public Property getOpposite() {
+ if (oppositeName != null && oppositeProperty == null) {
+ setOpposite((PropertyImplementation) getType().getProperty(oppositeName));
+ }
+ return oppositeProperty;
+ }
+
+ public Type getType() {
+ return type;
+ }
+
+ public boolean isContainment() {
+ return isContainment;
+ }
+
+ public void setIsContainment(boolean isContainment) {
+ this.isContainment = isContainment;
+ }
+
+ public boolean isMany() {
+ return isMany;
+ }
+
+ public void setIsMany(boolean isMany) {
+ this.isMany = isMany;
+ }
+
+ public boolean isReadOnly() {
+ return isReadOnly;
+ }
+
+ public void setIsReadOnly(boolean isReadOnly) {
+ this.isReadOnly = isReadOnly;
+ }
+
+ public int getIndex() {
+ return index;
+ }
+
+ void setIndex(int index) {
+ this.index = index;
+ }
+
+ public String toString() {
+ return getContainingType().getName() + "." + name;
+ }
+
+ public void setOppositeName(String oppositeName) {
+ this.oppositeName = oppositeName;
+ }
+
+ void setOpposite(PropertyImplementation opposite) {
+ this.oppositeProperty = opposite;
+ opposite.oppositeProperty = this;
+ }
+
+ public void setType(Type type) {
+ this.type = type;
+ }
+
+ void createOppositeProperty() {
+ if (oppositeProperty != null) {
+ return;
+ }
+ if (oppositeName == null || !isContainment()) {
+ return;
+ }
+ PropertyImplementation opp = (PropertyImplementation) type.getProperty(oppositeName);
+ if (opp == null) {
+ opp = ((TypeImplementation) type).addProperty(oppositeName, this.getContainingType());
+ }
+ setOpposite(opp);
+ }
+
+
+ public Object get(commonj.sdo.Property property) {
+ return null;
+ }
+
+ public List getInstanceProperties() {
+ return Collections.EMPTY_LIST;
+ }
+
+ public boolean isNullable() {
+ return true;
+ }
+
+ public boolean isOpenContent() {
+ return false;
+ }
+
+
+ private Property oppositeProperty;
+ private boolean isReadOnly;
+ private boolean isMany;
+ private boolean isContainment;
+ private Type type;
+ private String oppositeName;
+ private final Type containingType;
+ private final String name;
+ private int index;
+}
diff --git a/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/implementation/SdoImplementationException.java b/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/implementation/SdoImplementationException.java
new file mode 100644
index 0000000000..868e0a3597
--- /dev/null
+++ b/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/implementation/SdoImplementationException.java
@@ -0,0 +1,26 @@
+package com.agfa.hap.sdo.implementation;
+
+/**
+ * Thrown when something went wrong in the sdo implementation.
+ * @author AMOCZ
+ */
+public class SdoImplementationException extends RuntimeException {
+
+ private static final long serialVersionUID = -3690342870276215465L;
+
+ public SdoImplementationException() {
+ }
+
+ public SdoImplementationException(String message) {
+ super(message);
+ }
+
+ public SdoImplementationException(Throwable cause) {
+ super(cause);
+ }
+
+ public SdoImplementationException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+}
diff --git a/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/implementation/SnapshotImplementation.java b/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/implementation/SnapshotImplementation.java
new file mode 100644
index 0000000000..7e3685de22
--- /dev/null
+++ b/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/implementation/SnapshotImplementation.java
@@ -0,0 +1,377 @@
+package com.agfa.hap.sdo.implementation;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.DataInput;
+import java.io.DataInputStream;
+import java.io.DataOutput;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.io.StreamCorruptedException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.log4j.Logger;
+
+import com.agfa.hap.sdo.DataMapper;
+import com.agfa.hap.sdo.ObjectPropertyVisitor;
+import com.agfa.hap.sdo.Property;
+import com.agfa.hap.sdo.Snapshot;
+import com.agfa.hap.sdo.SnapshotDefinition;
+import com.agfa.hap.sdo.Type;
+import com.agfa.hap.sdo.helper.TypeHelper;
+import com.agfa.hap.sdo.impl.TypeConverter;
+
+public class SnapshotImplementation implements Snapshot {
+
+ private static Logger logger = Logger.getLogger(SnapshotImplementation.class);
+ private static final long serialVersionUID = 5403046473375751795L;
+ private static final Snapshot EMPTY_SNAPSHOT = new SnapshotImplementation(null, null, Collections.emptyList()) ;
+ private transient byte[] serializedContent;
+
+ public static Snapshot emptySnapshot(){
+ return EMPTY_SNAPSHOT;
+ }
+
+ //workaround for http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6554519
+ private void writeObject(java.io.ObjectOutputStream out) throws IOException {
+ out.writeInt(serializedContent.length);
+ out.write(serializedContent);
+ }
+
+// workaround for http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6554519
+ private void readObject(java.io.ObjectInputStream in) throws IOException {
+ int size = in.readInt();
+ serializedContent = new byte[size];
+ int index = 0;
+ int remaining = size;
+ int read = 0;
+ while (index < size && remaining > 0){
+ read = in.read(serializedContent, index, remaining);
+ index += read;
+ remaining -= read;
+ }
+ if (remaining != 0){
+ throw new StreamCorruptedException("read=" + read + " index=" + index +
+ " remaining=" + remaining + " size=" + size + " serializedcontent=" + Arrays.toString(serializedContent));
+ }
+ }
+
+ public <T> SnapshotImplementation(DataMapper<T> mapper, SnapshotDefinition definition, Collection<? extends T> roots) {
+ buildSerializedContent(mapper, definition, roots);
+ }
+
+ public <T> List<T> extract(DataMapper<T> mapper) {
+ return extractRoots(mapper);
+ }
+
+ private <T> List<T> extractRoots(DataMapper<T> mapper) {
+ ByteArrayInputStream bis = new ByteArrayInputStream(serializedContent);
+ DataInputStream dis = new DataInputStream(bis);
+ SnapshotSerializer<T> serializer = new SnapshotSerializer<T>(mapper);
+ try {
+ return serializer.read(dis);
+ } catch (IOException e) {
+ throw new SdoImplementationException(e);
+ }
+ }
+
+ private <T> void buildSerializedContent(DataMapper<T> mapper, SnapshotDefinition definition, Collection<? extends T> targets) {
+ ByteArrayOutputStream bos = new ByteArrayOutputStream();
+ DataOutputStream out = new DataOutputStream(bos);
+ SnapshotSerializer<T> serializer = new SnapshotSerializer<T>(mapper);
+ try {
+ serializer.write(definition, targets, out);
+ out.close();
+ } catch (Exception e) {
+ throw new SdoImplementationException("definition=" + definition + ":targets=" + targets, e);
+ }
+ serializedContent = bos.toByteArray();
+ }
+
+ static class SnapshotSerializer<T> implements ObjectPropertyVisitor {
+ //protected visibility for testing purposes
+ public static final int MAX_UTF_LENGTH = 65535;
+ public static final String ID_ISNULL = ""; //the assumption here is that this will never be a valid value for an identity
+ private DataOutput out;
+ private DataMapper<T> mapper;
+ private List<Object> serializedObjects;
+ private Map<Property, BulkProperty<T>> bulkProperties;
+
+ public SnapshotSerializer(DataMapper<T> mapper) {
+ this.mapper = mapper;
+ }
+
+ protected T create(Type type) {
+ T result = mapper.create(type);
+ serializedObjects.add(result);
+ return result;
+ }
+
+ public List<T> read(DataInput in) throws IOException {
+ this.serializedObjects = new ArrayList<Object>();
+ return readFromStream(in);
+ }
+
+ private List<T> readFromStream(DataInput in) throws IOException {
+ int nrOfObjects = in.readInt();
+ if (nrOfObjects < 1){
+ return Collections.<T>emptyList();
+ }
+ List<T> result = basicReadFromStream(in, nrOfObjects);
+ readBulkProperties(in);
+ return result;
+ }
+
+ private List<T> basicReadFromStream(DataInput in, int nrOfObjects) throws IOException {
+ List<T> result = new ArrayList<T>(nrOfObjects);
+ for (int i = 0; i < nrOfObjects; i++){
+ result.add(readObject(in));
+ }
+ return result;
+ }
+
+ private T readObject(DataInput in) throws IOException {
+ Type type = TypeHelper.INSTANCE.getType(in.readUTF(), in.readUTF());
+ T instance = create(type);
+ int propIndex = in.readInt();
+ while (propIndex != Integer.MIN_VALUE) {
+ if (propIndex < 0) {
+ Property property = type.getProperty(-propIndex-1);
+ if (property.isMany()) {
+ mapper.setUnavailable(instance, property);
+ } else if (property.getType().getIdentityProperty() == null){
+ mapper.setUnavailable(instance, property);
+ } else {
+ Object identity = readIdentityValue(in, null, property.getType().getIdentityProperty());
+ if (identity == null){
+ mapper.setUnavailable(instance, property);
+ } else {
+ mapper.setProperty(instance, property, mapper.newProxy(property.getType(), identity));
+ }
+ }
+ } else {
+ Property prop = type.getProperty(propIndex);
+ Object value = read(in, instance, prop);
+ mapper.setProperty(instance, prop, value);
+ }
+ propIndex = in.readInt();
+ }
+ return instance;
+ }
+
+ private Object read(DataInput in, Object parent, Property prop) throws IOException {
+ if (prop.getType().isDataType()) {
+ String value = this.readPotentiallyLongUTF(in);
+ try {
+ return TypeConverter.get(prop.getType()).parse(value);
+ } catch (RuntimeException e){
+ logger.error("unable to parse " + value + "for property " + prop + " parent=" + parent, e);
+ throw e;
+ }
+ } else {
+ return readChildObject(in, prop, parent);
+ }
+
+ }
+
+ private Object readIdentityValue(DataInput in, Object parent, Property prop) throws IOException {
+ String value = in.readUTF();
+ if (ID_ISNULL.equals(value)){
+ return null;
+ }
+ try {
+ return TypeConverter.get(prop.getType()).parse(value);
+ } catch (RuntimeException e){
+ logger.error("unable to parse " + value + "for property " + prop + " parent=" + parent, e);
+ throw e;
+ }
+ }
+
+ String readPotentiallyLongUTF(DataInput in) throws IOException {
+ //package visibility for testing purposes
+ StringBuilder builder = new StringBuilder();
+ while (true){
+ String buf = in.readUTF();
+ builder.append(buf);
+ if (buf.length() < MAX_UTF_LENGTH){
+ break;
+ }
+ String finalOrContinue = in.readUTF();
+ if ("F".equals(finalOrContinue)){
+ break;
+ }
+ }
+ return builder.toString();
+ }
+
+ protected Object readChildObject(DataInput in, Property prop, Object parent) throws IOException {
+ int index = in.readInt();
+ if (index < 0) {
+ return readObject(in);
+ } else {
+ return serializedObjects.get(index);
+ }
+ }
+
+ public void write(SnapshotDefinition def, Collection<? extends T> roots, DataOutput out) throws Exception {
+ this.out = out;
+ this.serializedObjects = new ArrayList<Object>();
+ out.writeInt(roots.size());
+ for (T root : roots){
+ def.visit(mapper, this, root);
+ }
+ writeBulkProperties(out);
+ }
+
+ protected void writeBulkProperties(DataOutput out) throws Exception {
+ if (bulkProperties != null) {
+ while (!bulkProperties.isEmpty()){
+ //we can't just simply iterate over the bulkproperties.values, because while writing a bulkproperty we might visit an additional bulk property in the subselect
+ BulkProperty bp = bulkProperties.values().iterator().next();
+ BulkProperty emptyBulkProperty = new BulkProperty(bp.property, bp.subselect);
+ bulkProperties.put(bp.property, emptyBulkProperty); //replace the bulk property that we are going to write with a copy of itself, but withouth instances
+ writeBulkProperty(bp);
+ if (emptyBulkProperty.instances.isEmpty()){
+ bulkProperties.remove(bp.property); //if no new instances were added (through some nested bulkproperties) than we can remove the bulkproperty from the map of bulk properties to be processed
+ }
+ }
+ }
+ out.writeInt(-1);
+ }
+
+ protected void writeBulkProperty(BulkProperty<T> bp) throws Exception {
+ Collection<T> values = this.mapper.getProperties(bp.instances, bp.property, bp.subselect);
+ Iterator<T> valuesIt = values.iterator();
+ Iterator<?> parents = bp.instances.iterator();
+ while (parents.hasNext()) {
+ Object parent = parents.next();
+ T value = valuesIt.next();
+ if (bp.property.isMany()) {
+ Collection<T> children = (Collection<T>) value;
+ for (T child : children) {
+ writeBulkPropertyValue(bp, parent, child);
+ }
+ } else {
+ writeBulkPropertyValue(bp, parent, value);
+ }
+ }
+ }
+
+ protected void writeBulkPropertyValue(BulkProperty bp, Object parent, T value) throws IOException, Exception {
+ out.writeInt(serializedObjects.indexOf(parent));
+ if (this.visitProperty(parent, bp.property, value)) {
+ bp.subselect.visit(mapper, this, value);
+ }
+ }
+
+ protected void readBulkProperties(DataInput in) throws IOException {
+ while (readBulkProperty(in)) {
+ }
+ }
+
+ protected boolean readBulkProperty(DataInput in) throws IOException {
+ int index = in.readInt();
+ if (index < 0) {
+ return false;
+ }
+ T parent = (T) serializedObjects.get(index);
+ index = in.readInt();
+ if (index < 0){ //null value
+ Property property = mapper.getType(parent).getProperty(-index-1);
+ mapper.setProperty(parent, property, null);
+ return true;
+ }
+ Property property = mapper.getType(parent).getProperty(index);
+ Object value = read(in, parent, property);
+ mapper.setProperty(parent, property, value);
+ return true;
+ }
+
+ public void startDataObject(Object dataObject, Type type) throws IOException {
+ serializedObjects.add(dataObject);
+ out.writeUTF(type.getURI());
+ out.writeUTF(type.getName());
+ }
+
+ public boolean visitProperty(Object dataObject, Property property, Object value) throws IOException {
+ if (value == null) {
+ out.writeInt(-property.getIndex()-1);
+ return false;
+ }
+ out.writeInt(property.getIndex());
+ if (property.getType().isDataType()) {
+ writePotentiallyLongStringUTF(TypeConverter.get(property.getType()).toString(value), out);
+ return false;
+ } else {
+ int index = serializedObjects.indexOf(value);
+ out.writeInt(index);
+ return index < 0;
+ }
+ }
+
+ void writePotentiallyLongStringUTF(String stringValue, DataOutput out) throws IOException {
+ //package visibilty for testing purposes
+ int totalLength = stringValue.length();
+ if (totalLength < MAX_UTF_LENGTH){
+ out.writeUTF(stringValue);
+ return;
+ }
+ int lastPossibleMaxSubstringBeginIndex = totalLength - MAX_UTF_LENGTH;
+ int substringBeginIndex = 0;
+ for (; substringBeginIndex <= lastPossibleMaxSubstringBeginIndex; substringBeginIndex += MAX_UTF_LENGTH ){
+ out.writeUTF(stringValue.substring(substringBeginIndex, substringBeginIndex + MAX_UTF_LENGTH));
+ if (substringBeginIndex == lastPossibleMaxSubstringBeginIndex){
+ out.writeUTF("F"); //final
+ } else {
+ out.writeUTF("C"); //continue
+ }
+ }
+ if (substringBeginIndex != totalLength -1){
+ out.writeUTF(stringValue.substring(substringBeginIndex, totalLength)) ;
+ }
+ }
+
+ public void visitProxyProperty(Object instance, Property property, Object identity) throws Exception {
+ out.writeInt(-property.getIndex()-1);
+ if (identity == null) {
+ if (property.getType().getIdentityProperty() != null && !property.isMany()){
+ out.writeUTF(ID_ISNULL); //a bit of a hack in order to be able to set a property unavailabe, where the property is of an type with an id but where the id value is null. The deserialization mechanism expects a value for the identity when reading a proxy for a type with an identityproperty
+ }
+ return;
+ }
+ out.writeUTF(TypeConverter.get(property.getType().getIdentityProperty().getType()).toString(identity));
+ }
+
+ public void endDataObject(Object dataObject, Type type) throws IOException {
+ out.writeInt(Integer.MIN_VALUE);
+ }
+
+ public void visitBulkProperty(Object instance, Property property, SnapshotDefinition subselect) {
+ getInstancesForBulkProperty(property, subselect).add((T)instance);
+ }
+
+ protected List<T> getInstancesForBulkProperty(Property property, SnapshotDefinition subselect) {
+ if (bulkProperties == null) {
+ bulkProperties = new HashMap<Property, BulkProperty<T>>();
+ }
+ BulkProperty<T> bp = bulkProperties.get(property);
+ if (bp == null) {
+ bp = new BulkProperty<T>(property, subselect);
+ bulkProperties.put(property, bp);
+ } else {
+ if (!bp.subselect.equals(subselect)) {
+ throw new IllegalArgumentException("Different subselects not yet supported!");
+ }
+ }
+ return bp.instances;
+ }
+
+ }
+}
diff --git a/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/implementation/SnapshotUnion.java b/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/implementation/SnapshotUnion.java
new file mode 100644
index 0000000000..c94517f506
--- /dev/null
+++ b/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/implementation/SnapshotUnion.java
@@ -0,0 +1,36 @@
+package com.agfa.hap.sdo.implementation;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.LinkedList;
+import java.util.List;
+
+import com.agfa.hap.sdo.DataMapper;
+import com.agfa.hap.sdo.Snapshot;
+
+public class SnapshotUnion implements Snapshot {
+
+ /**
+ *
+ */
+ private static final long serialVersionUID = 2888458945891090081L;
+ Collection<Snapshot> snapshots;
+
+ public SnapshotUnion(Collection<Snapshot> snapshots){
+ this.snapshots = snapshots;
+ }
+
+ public SnapshotUnion(Snapshot firstSnapshot, Snapshot secondSnapshot) {
+ this(new LinkedList(Arrays.asList(new Snapshot[]{firstSnapshot, secondSnapshot}))); //linkedlist instead of arraylist because of a jboss bug
+ }
+
+ public <T> List<T> extract(DataMapper<T> mapper) {
+ List<T> extraction = new ArrayList<T>();
+ for (Snapshot snapshot : snapshots){
+ extraction.addAll(snapshot.extract(mapper));
+ }
+ return extraction;
+ }
+
+}
diff --git a/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/implementation/TypeHelperImpl.java b/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/implementation/TypeHelperImpl.java
new file mode 100644
index 0000000000..c62e570522
--- /dev/null
+++ b/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/implementation/TypeHelperImpl.java
@@ -0,0 +1,435 @@
+package com.agfa.hap.sdo.implementation;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.CopyOnWriteArraySet;
+
+import org.apache.log4j.Logger;
+
+import com.agfa.hap.sdo.PartialDataObject;
+import com.agfa.hap.sdo.helper.TypeHelper;
+import com.agfa.hap.sdo.impl.BytesTypeConverter;
+import com.agfa.hap.sdo.impl.TypeConverter;
+import com.agfa.hap.sdo.impl.TypeProvider;
+import com.agfa.hap.sdo.model.CatalogIdentity;
+import com.agfa.hap.sdo.model.LongIdentity;
+import commonj.sdo.DataObject;
+import commonj.sdo.Property;
+import commonj.sdo.Type;
+import commonj.sdo.helper.XSDHelper;
+
+public class TypeHelperImpl implements TypeHelper {
+
+ private static Logger logger = Logger.getLogger(TypeHelperImpl.class);
+
+ protected static final String HAP_BASIC_TYPE_URI = "com.agfa.hap";
+ private static final String PROPERTY_OPPOSITE = "opposite";
+ private static final String PROPERTY_TYPE = "type";
+ private static final String PROPERTY_CONTAINMENT = "containment";
+ private static final String PROPERTY_MANY = "many";
+ private static final String TYPE_DATA_TYPE = "dataType";
+ private static final String TYPE_NAME = "name";
+ private static final String TYPE_ALIAS_NAME = "aliasName";
+ private static final String TYPE_PROPERTY = "property";
+ private static final String TYPE_BASE_TYPE = "baseType";
+ private static final String TYPE_IDENTIFIERPROPERTY = "identityProperty";
+ private static final String TYPE_URI = "uri";
+
+ public TypeHelperImpl() {
+ types = new ConcurrentHashMap<String, TypeImplementation>();
+ providedURIs = new HashSet<String>();
+ allURIs = new CopyOnWriteArraySet<String>();
+ instanceClass2Type = new HashMap<Class, TypeImplementation>();
+ initializeTypes();
+ }
+
+ private void initializeTypes() {
+ providedURIs.add("commonj.sdo");
+ registerBasicType(String.class, new TypeConverter<String>() {
+ @Override
+ public String parse(String str) {
+ return str;
+ }
+ @Override
+ public String toString(String instance) {
+ return instance;
+ }
+ });
+ registerBasicType(char.class, new TypeConverter<Character>() {
+ @Override
+ public Character parse(String str) {
+ return str.charAt(0);
+ }
+ @Override
+ public String toString(Character instance) {
+ return instance.toString();
+ }
+ });
+ registerBasicType(Integer.class, new TypeConverter<Integer>() {
+ @Override
+ public Integer parse(String str) {
+ return Integer.parseInt(str);
+ }
+ @Override
+ public String toString(Integer instance) {
+ return Integer.toString(instance.intValue());
+ }
+ });
+ registerBasicType(Long.class, new TypeConverter<Long>() {
+ @Override
+ public Long parse(String str) {
+ return Long.parseLong(str);
+ }
+ @Override
+ public String toString(Long instance) {
+ return Long.toString(instance.longValue());
+ }
+ });
+ registerBasicType(Short.class, new TypeConverter<Short>() {
+ @Override
+ public Short parse(String str) {
+ return Short.parseShort(str);
+ }
+ @Override
+ public String toString(Short instance) {
+ return Short.toString(instance.shortValue());
+ }
+ });
+ registerBasicType(Double.class, new TypeConverter<Double>() {
+ @Override
+ public Double parse(String str) {
+ return Double.parseDouble(str);
+ }
+ @Override
+ public String toString(Double instance) {
+ return Double.toString(instance.doubleValue());
+ }
+ });
+ registerBasicType(Float.class, new TypeConverter<Float>() {
+ @Override
+ public Float parse(String str) {
+ return Float.parseFloat(str);
+ }
+ @Override
+ public String toString(Float instance) {
+ return Float.toString(instance.floatValue());
+ }
+ });
+ registerBasicType(Boolean.class, new TypeConverter<Boolean>() {
+ @Override
+ public Boolean parse(String str) {
+ return Boolean.parseBoolean(str);
+ }
+ @Override
+ public String toString(Boolean instance) {
+ return Boolean.toString(instance.booleanValue());
+ }
+ });
+ registerBasicType(Byte.class, new TypeConverter<Byte>() {
+ @Override
+ public Byte parse(String str) {
+ return Byte.parseByte(str);
+ }
+ @Override
+ public String toString(Byte instance) {
+ return Byte.toString(instance.byteValue());
+ }
+ });
+ registerBasicType(URI.class, new TypeConverter<URI>() {
+ @Override
+ public URI parse(String str) {
+ try {
+ return new URI(str);
+ } catch (URISyntaxException e) {
+ throw new IllegalArgumentException("Illegal uri syntax: " + str);
+ }
+ }
+ @Override
+ public String toString(URI instance) {
+ return instance.toString();
+ }
+ });
+ registerBasicType(Date.class, new TypeConverter<Date>() {
+ @Override
+ public Date parse(String str) {
+ return new Date(Long.parseLong(str));
+ }
+ @Override
+ public String toString(Date instance) {
+ return Long.toString(instance.getTime());
+ }
+ });
+ this.registerBasicType("Bytes", "commonj.sdo", byte[].class, new BytesTypeConverter());
+ registerBasicType(LongIdentity.class, HAP_BASIC_TYPE_URI, new TypeConverter<LongIdentity>() {
+ @Override
+ public LongIdentity parse(String str) {
+ return LongIdentity.fromString(str);
+ }
+ @Override
+ public String toString(LongIdentity instance) {
+ return instance.toString();
+ }
+ });
+ registerBasicType(CatalogIdentity.class, HAP_BASIC_TYPE_URI, new TypeConverter<CatalogIdentity>() {
+ @Override
+ public CatalogIdentity parse(String str) {
+ return CatalogIdentity.fromString(str);
+ }
+ @Override
+ public String toString(CatalogIdentity instance) {
+ return CatalogIdentity.toString(instance);
+ }
+ });
+ providedURIs.add(HAP_BASIC_TYPE_URI);
+ // other basic types from the spec
+// sdoTypeNameToClass.put("DateTime", String.class);
+// sdoTypeNameToClass.put("Int", int.class);
+// sdoTypeNameToClass.put("Month", String.class);
+// sdoTypeNameToClass.put("MonthDay", String.class);
+// sdoTypeNameToClass.put("Object", Object.class);
+// sdoTypeNameToClass.put("Strings", List<String>.class);
+// sdoTypeNameToClass.put("Year", String.class);
+// sdoTypeNameToClass.put("YearMonth", String.class);
+// sdoTypeNameToClass.put("YearMonthDay", String.class);
+ register(new TypeImplementation(Object.class, "commonj.sdo", "Object"));
+ intializeTypeAndPropertyTypes();
+ intializeDataObjectType();
+ }
+
+ private void intializeTypeAndPropertyTypes() {
+ TypeImplementation typeType = define("commonj.sdo", "Type");
+ TypeImplementation propertyType = define("commonj.sdo", "Property");
+ typeType.addProperty(TYPE_BASE_TYPE, typeType).setIsMany(true);
+ PropertyImplementation typeProperties = typeType.addProperty(TYPE_PROPERTY, propertyType);
+ typeProperties.setIsMany(true);
+ typeType.addProperty(TYPE_ALIAS_NAME, getType(String.class)); // should be many
+ typeType.addProperty(TYPE_NAME, getType(String.class));
+ typeType.addProperty("uri", getType(String.class));
+ typeType.addProperty(TYPE_DATA_TYPE, getType(Boolean.class));
+ typeType.addProperty("open", getType(Boolean.class));
+ typeType.addProperty("sequenced", getType(Boolean.class));
+ typeType.addProperty("abstract", getType(Boolean.class));
+ typeType.addProperty(TYPE_IDENTIFIERPROPERTY, getType(String.class));
+ propertyType.addProperty(TYPE_ALIAS_NAME, getType(String.class)); // many=true ,
+ propertyType.addProperty(TYPE_NAME , getType(String.class));
+ propertyType.addProperty(PROPERTY_MANY , getType(Boolean.class));
+ propertyType.addProperty(PROPERTY_CONTAINMENT , getType(Boolean.class));
+ propertyType.addProperty(PROPERTY_TYPE , typeType);
+ typeProperties.setIsContainment(true);
+ propertyType.addProperty("default" , getType("commonj.sdo", "Object"));
+ propertyType.addProperty("readOnly", getType(Boolean.class));
+ propertyType.addProperty(PROPERTY_OPPOSITE, propertyType);
+ }
+
+ private void intializeDataObjectType() {
+ TypeImplementation type = define("commonj.sdo", "DataObject");
+ type.setInstanceClass(DataObject.class);
+ type.setIsDataType(false);
+ type.setIsAbstract(true);
+ }
+
+ public synchronized void register(Type type) {
+ TypeImplementation typeImpl = (TypeImplementation) type;
+ typeImpl.setImplementationClass(ImplementationClassRepository.getInstance().getImplementationClass(typeImpl));
+ types.put(typeImpl.getName(), typeImpl);
+ if (typeImpl.getInstanceClass() != null) {
+ instanceClass2Type.put(typeImpl.getInstanceClass(), typeImpl);
+ }
+ if (typeImpl.isDataType()) {
+ types.put(typeImpl.getInstanceClass().getName(), typeImpl);
+ }
+ allURIs.add(type.getURI());
+ }
+
+ protected void registerBasicType(Class clazz, TypeConverter type) {
+ String typeName = null;
+ if (clazz.getName().startsWith("java.lang.")){
+ typeName = clazz.getName().substring(10);
+ } else {
+ typeName= clazz.getName();
+ }
+ this.registerBasicType(typeName, "commonj.sdo", clazz, type);
+ }
+
+ protected void registerBasicType(Class clazz, String uri, TypeConverter type) {
+ this.registerBasicType(clazz.getName(), uri, clazz, type);
+ }
+
+ protected TypeImplementation registerBasicType(String typeName, String uri, Class instanceClass, TypeConverter type){
+ TypeImplementation typeImp = new TypeImplementation(instanceClass, uri, typeName);
+ register(typeImp);
+ TypeConverter.register(typeImp, type);
+ return typeImp;
+ }
+
+ void register(Iterator<Type> types) {
+ while (types.hasNext()) {
+ TypeImplementation type = (TypeImplementation) types.next();
+ register(type);
+ }
+ }
+
+ public com.agfa.hap.sdo.Type getType(String uri, String typeName) {
+ synchronized(this) {
+ com.agfa.hap.sdo.Type result = types.get(typeName);
+ if (result != null) {
+ return result;
+ }
+ }
+ return getTypeFromProvider(uri, typeName);
+ }
+
+ private TypeImplementation getTypeFromProvider(String uri, String typeName) {
+ if (uri == null) {
+ return null;
+ }
+ if (TypeProvider.getInstance() == null) {
+ return null;
+ }
+ synchronized(this) {
+ if (providedURIs.contains(uri)) {//ok, some other caller will have loaded the types just before us
+ return types.get(typeName);
+ }
+ Set<String> urisToLoad = new HashSet<String>();
+ urisToLoad.add(uri);
+ while (!urisToLoad.isEmpty()){
+ String nextUri = urisToLoad.iterator().next();
+ String xsd = TypeProvider.getInstance().getTypes(nextUri);
+ if (xsd == null) {
+ logger.debug("unable to load sdo types for uri " + nextUri + " from type provider " + TypeProvider.getInstance());
+ return null; //this is not always an exceptional situation (eg when defining a type with a dataobject)
+ }
+ List<com.agfa.hap.sdo.Type> newTypes =XSDHelper.INSTANCE.define(xsd);
+ Set<String> extraURIsToLoad = this.referencedURIs(newTypes);
+ extraURIsToLoad.removeAll(providedURIs);
+ urisToLoad.addAll(extraURIsToLoad);
+ urisToLoad.remove(nextUri);
+ providedURIs.add(uri);
+ }
+ }
+ return types.get(typeName);
+ }
+
+ private Set<String> referencedURIs(List<com.agfa.hap.sdo.Type> types) {
+ Set<String> uris = new HashSet<String>();
+ for (com.agfa.hap.sdo.Type type : types){
+ for (Type baseType : type.getBaseTypes()){
+ uris.add(baseType.getURI());
+ }
+ for (Property property : type.getDeclaredProperties()){
+ uris.add(property.getType().getURI());
+ }
+ }
+ return uris;
+ }
+
+
+
+ public com.agfa.hap.sdo.Type getType(Class interfaceClass) {
+ com.agfa.hap.sdo.Type result = getTypeForInstanceClass(interfaceClass);
+ if (result != null) {
+ return result;
+ }
+ result = getType(null, interfaceClass.getName());
+ if (result != null) {
+ return result;
+ }
+ return getTypeForImplementationClass(interfaceClass.getName());
+ }
+
+ protected synchronized TypeImplementation getTypeForInstanceClass(Class instanceClass) {
+ return instanceClass2Type.get(instanceClass);
+ }
+
+ public com.agfa.hap.sdo.Type getTypeForImplementationClass(String name) {
+ return (com.agfa.hap.sdo.Type) ImplementationClassRepository.getInstance().getTypeForImplementationClass(name);
+ }
+
+ public synchronized com.agfa.hap.sdo.Type define(DataObject type) {
+ TypeImplementation result = (TypeImplementation) getType((String) type.get(TYPE_URI), (String) type.get(TYPE_NAME));
+ if (result != null && result.getProperties().size() == 0) {
+ return result;
+ }
+ result = new TypeImplementation((String) type.get(TYPE_URI), (String) type.get(TYPE_NAME));
+ if (type.isSet(TYPE_DATA_TYPE)) {
+ result.setIsDataType((Boolean) type.get(TYPE_DATA_TYPE));
+ }
+ register(result);
+ Iterator<PartialDataObject> it = type.getList(TYPE_PROPERTY).iterator();
+ while (it.hasNext()) {
+ DataObject property = it.next();
+ com.agfa.hap.sdo.Type propType = define(property.getDataObject(PROPERTY_TYPE));
+ PropertyImplementation prop = result.addProperty((String) property.get(TYPE_NAME), propType);
+ Boolean isMany = (Boolean) property.get(PROPERTY_MANY);
+ prop.setIsMany(isMany != null && isMany.booleanValue());
+ Boolean containment = (Boolean) property.get(PROPERTY_CONTAINMENT);
+ prop.setIsContainment(containment != null && containment.booleanValue());
+ String opposite = (String) property.get(PROPERTY_OPPOSITE);
+ prop.setOppositeName(opposite);
+ }
+ String idProp = (String) type.get(TYPE_IDENTIFIERPROPERTY);
+ if (idProp != null) {
+ result.setIdentityProperty(result.getProperty(idProp));
+ }
+ return result;
+ }
+
+ synchronized com.agfa.hap.sdo.Type defineInstanceClass(String interfaceClassName) {
+ com.agfa.hap.sdo.Type result = getType(null, interfaceClassName);
+ if (result == null) {
+ throw new IllegalArgumentException("Couldn't find class " + interfaceClassName);
+ }
+ return result;
+ }
+
+ synchronized TypeImplementation define(String uri, String name) {
+ TypeImplementation result = types.get(name);
+ if (result == null) {
+ result = new TypeImplementation(uri, name);
+ register(result);
+ }
+ return result;
+ }
+
+ public synchronized List define(List types) {
+ Iterator it = types.iterator();
+ List<Type> result = new ArrayList<Type>();
+ while (it.hasNext()) {
+ result.add(define((DataObject) it.next()));
+ }
+ return result;
+ }
+
+
+ public Collection<String> getKnownUris() {
+ return allURIs;
+ }
+
+
+ public Property defineOpenContentProperty(String uri, DataObject property) {
+ throw new UnsupportedOperationException();
+ }
+
+
+
+ public Property getOpenContentProperty(String uri, String propertyName) {
+ throw new UnsupportedOperationException();
+ }
+
+
+ private final Map<String, TypeImplementation> types;
+ private final Set<String> providedURIs;
+ private final Set<String> allURIs;
+ private Map<Class, TypeImplementation> instanceClass2Type;
+
+}
diff --git a/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/implementation/TypeImplementation.java b/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/implementation/TypeImplementation.java
new file mode 100644
index 0000000000..3ac3e5ecca
--- /dev/null
+++ b/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/implementation/TypeImplementation.java
@@ -0,0 +1,267 @@
+package com.agfa.hap.sdo.implementation;
+
+import java.lang.reflect.Constructor;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.log4j.Logger;
+
+import com.agfa.hap.sdo.PartialDataObject;
+import com.agfa.hap.sdo.Property;
+import com.agfa.hap.sdo.Type;
+import com.agfa.hap.sdo.util.ClassHolder;
+
+import commonj.sdo.DataObject;
+
+public class TypeImplementation implements Type {
+ private static Logger logger = Logger.getLogger(TypeImplementation.class);
+
+ private static final Class[] CONSTRUCTOR_PARAMS = new Class[] { Type.class };
+
+ public TypeImplementation(Class instanceClass) {
+ this.instanceClass = instanceClass;
+ this.isDataType = true;
+ this.name = instanceClass.getName();
+ if (this.name.startsWith("java.lang.")) {
+ this.name = this.name.substring(10);
+ this.uri = "commonj.sdo";
+ } else {
+ logger.warn("no uri for sdo datatype " + instanceClass);
+ }
+ this.properties = Collections.emptyList();
+ this.declaredProperties = Collections.emptyList();
+ this.baseTypes = Collections.emptyList();
+ }
+
+ public TypeImplementation(Class instanceClass, String uri) {
+ this(instanceClass, uri, instanceClass.getName());
+ }
+
+ public TypeImplementation(Class instanceClass, String uri, String name) {
+ this.instanceClass = instanceClass;
+ this.isDataType = true;
+ this.name = name;
+ this.uri = uri;
+ this.properties = Collections.emptyList();
+ this.declaredProperties = Collections.emptyList();
+ this.baseTypes = Collections.emptyList();
+ }
+
+ public void setInstanceClass(Class instanceClass) {
+ this.instanceClass = instanceClass;
+ }
+
+
+
+ public TypeImplementation(String uri, String name) {
+ this.instanceClass = DataObject.class;
+ this.isDataType = false;
+ this.properties = new ArrayList<Property>();
+ this.declaredProperties = new ArrayList<Property>();
+ this.baseTypes = new ArrayList<TypeImplementation>();
+ this.name = name;
+ this.uri = uri;
+ }
+
+ public void setBaseType(TypeImplementation baseType) {
+ if (baseTypes.size() != 0) {
+ throw new IllegalArgumentException("Single inheritance only!");
+ }
+ baseTypes.add(baseType);
+ }
+
+ public void normalizeTypeHierarchy() {
+ if (baseTypes.size() == 0) {
+ return;
+ }
+ TypeImplementation baseType = baseTypes.get(0);
+ baseType.normalizeTypeHierarchy();
+ if (this.implementationClassHolder == null) {
+ this.implementationClassHolder = baseType.implementationClassHolder;
+ }
+ if (baseType.getProperties().size() == 0) {
+ return;
+ }
+ if (properties.size() > 0 && baseType.getProperties().get(0) == properties.get(0)) {
+ // already normalized
+ return;
+ }
+ for (Property prop: baseType.getProperties()) {
+ properties.add(prop.getIndex(), prop);
+ }
+ for (int i = 0; i < properties.size(); i++) {
+ ((PropertyImplementation) properties.get(i)).setIndex(i);
+ }
+ if (baseType.getIdentityProperty() != null && identityProperty == null) {
+ this.identityProperty = baseType.getIdentityProperty();
+ }
+ }
+
+ public PropertyImplementation addProperty(String propertyName, Type type) {
+ PropertyImplementation prop = new PropertyImplementation(propertyName, type, this, properties.size());
+ properties.add(prop);
+ declaredProperties.add(prop);
+ return prop;
+ }
+
+ void addProperty(PropertyImplementation prop) {
+ properties.add(prop);
+ declaredProperties.add(prop);
+ }
+
+ public List<String> getAliasNames() {
+ return Collections.emptyList();
+ }
+
+ public List<TypeImplementation> getBaseTypes() {
+ return baseTypes;
+ }
+
+ public List<Property> getDeclaredProperties() {
+ return declaredProperties;
+ }
+
+ public Class getInstanceClass() {
+ return instanceClass;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public List<Property> getProperties() {
+ return properties;
+ }
+
+ public Property getProperty(String propertyName) {
+ Iterator it = properties.iterator();
+ while (it.hasNext()) {
+ Property prop = (Property) it.next();
+ if (prop.getName().equals(propertyName)) {
+ return prop;
+ }
+ }
+ return null;
+ }
+
+ public Property getProperty(int index) {
+ return properties.get(index);
+ }
+
+ public String getURI() {
+ return uri;
+ }
+
+ public boolean isAbstract() {
+ return isAbstract;
+ }
+
+ public void setIsAbstract(boolean isAbstract) {
+ this.isAbstract = isAbstract;
+ }
+
+ public boolean isDataType() {
+ return isDataType;
+ }
+
+ public void setIsDataType(boolean isDataType) {
+ this.isDataType = isDataType;
+ }
+
+ public boolean isInstance(Object object) {
+ if (object instanceof DataObject) {
+ return ((DataObject) object).getType().equals(this);
+ }
+ return false;
+ }
+
+ public boolean isOpen() {
+ return false;
+ }
+
+ public boolean isSequenced() {
+ return false;
+ }
+
+ public String toString() {
+ return name;
+ }
+
+ public boolean equals(Object other) {
+ if (other == null) {
+ return false;
+ }
+ if (other.getClass() != TypeImplementation.class) {
+ return false;
+ }
+ return ((TypeImplementation) other).name.equals(name);
+ }
+
+ public int hashCode() {
+ return name.hashCode();
+ }
+
+ public void changePropertyTypes(Map<String, Type> typenameToSpecializingType){
+ for (Property prop : this.getProperties()){
+ if (typenameToSpecializingType.containsKey(prop.getType().getName())){
+ ((PropertyImplementation) prop).setType(typenameToSpecializingType.get(prop.getType().getName()));
+ }
+ }
+ }
+
+ /**
+ * Sets the implementation class. The datafactory will create instances of this
+ * class when creating objects of this type.
+ */
+ public void setImplementationClass(ClassHolder implementationClassName) {
+ this.implementationClassHolder = implementationClassName;
+ }
+
+ public ClassHolder getImplementationClass() {
+ return implementationClassHolder;
+ }
+
+ @SuppressWarnings("unchecked")
+ public Constructor<? extends PartialDataObject> getInstanceConstructor() {
+ if (instanceConstructor == null && implementationClassHolder != null) {
+ try {
+ this.instanceConstructor = implementationClassHolder.resolveClass().getConstructor(CONSTRUCTOR_PARAMS);
+ } catch (Exception e) {
+ throw new IllegalArgumentException("Could not find a constructor with a Type parameter for class " + implementationClassHolder);
+ }
+ }
+ return instanceConstructor;
+ }
+
+ public Property getIdentityProperty() {
+ return identityProperty;
+ }
+
+ public void setIdentityProperty(commonj.sdo.Property identityProperty) {
+ this.identityProperty = (Property) identityProperty;
+ }
+
+ public Object get(commonj.sdo.Property property) {
+ throw new UnsupportedOperationException();
+ }
+
+ public List getInstanceProperties() {
+ return Collections.EMPTY_LIST;
+ }
+
+ private boolean isDataType;
+ private transient Class instanceClass; //the instanceclasses don't get serialized, in order to avoid some potential client/server issues
+ private String name;
+ private String uri;
+ private List<Property> properties;
+ private List<Property> declaredProperties;
+ private List<TypeImplementation> baseTypes;
+ private boolean isAbstract;
+ private transient Constructor<? extends PartialDataObject> instanceConstructor;
+ private Property identityProperty;
+ private ClassHolder implementationClassHolder;
+
+}
diff --git a/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/implementation/XMLDocumentImpl.java b/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/implementation/XMLDocumentImpl.java
new file mode 100644
index 0000000000..e03e04435c
--- /dev/null
+++ b/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/implementation/XMLDocumentImpl.java
@@ -0,0 +1,85 @@
+package com.agfa.hap.sdo.implementation;
+
+import commonj.sdo.DataObject;
+import commonj.sdo.helper.XMLDocument;
+
+class XMLDocumentImpl implements XMLDocument {
+
+ private DataObject rootObject;
+ private String rootElementURI;
+ private String rootElementName;
+ private String encoding;
+
+
+ public XMLDocumentImpl(DataObject root, String uri, String name) {
+ this();
+ rootObject = root;
+ rootElementName = name;
+ rootElementURI = uri;
+ }
+
+ public XMLDocumentImpl() {
+ encoding = "UTF-8";
+ }
+
+ public DataObject getRootObject() {
+ return rootObject;
+ }
+
+ public String getRootElementURI() {
+ return rootElementURI;
+ }
+
+ public String getRootElementName() {
+ return rootElementName;
+ }
+
+ public String getEncoding() {
+ return encoding;
+ }
+
+ public void setEncoding(String encoding) {
+ this.encoding = encoding;
+ }
+
+ public boolean isXMLDeclaration() {
+ // TODO Auto-generated method stub
+ return false;
+ }
+
+ public void setXMLDeclaration(boolean xmlDeclaration) {
+ // TODO Auto-generated method stub
+
+ }
+
+ public String getXMLVersion() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ public void setXMLVersion(String xmlVersion) {
+ // TODO Auto-generated method stub
+
+ }
+
+ public String getSchemaLocation() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ public void setSchemaLocation(String schemaLocation) {
+ // TODO Auto-generated method stub
+
+ }
+
+ public String getNoNamespaceSchemaLocation() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ public void setNoNamespaceSchemaLocation(String schemaLocation) {
+ // TODO Auto-generated method stub
+
+ }
+
+}
diff --git a/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/implementation/XMLHelperImpl.java b/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/implementation/XMLHelperImpl.java
new file mode 100644
index 0000000000..f6a2d1aca5
--- /dev/null
+++ b/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/implementation/XMLHelperImpl.java
@@ -0,0 +1,102 @@
+package com.agfa.hap.sdo.implementation;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.Reader;
+import java.io.StringReader;
+import java.io.StringWriter;
+import java.io.Writer;
+
+import javax.xml.transform.Result;
+import javax.xml.transform.Source;
+
+import org.xml.sax.SAXException;
+
+import sun.reflect.generics.reflectiveObjects.NotImplementedException;
+
+import commonj.sdo.DataObject;
+import commonj.sdo.helper.XMLDocument;
+import commonj.sdo.helper.XMLHelper;
+
+public class XMLHelperImpl implements XMLHelper {
+
+ private DataObjectStreamer dataObjectStreamer;
+
+ public XMLHelperImpl() {
+ dataObjectStreamer = new DataObjectStreamer();
+ }
+
+ public XMLDocument load(String inputString) {
+ try {
+ return load(new StringReader(inputString), null, null);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public XMLDocument load(InputStream inputStream) throws IOException {
+ return load(inputStream, null, null);
+ }
+
+ public XMLDocument load(InputStream inputStream, String locationURI,
+ Object options) throws IOException {
+ try {
+ return dataObjectStreamer.fromXml(inputStream);
+ } catch (SAXException e) {
+ throw new IllegalArgumentException(e);
+ }
+ }
+
+ public XMLDocument load(Source inputSource, String locationURI, Object options) throws IOException {
+ throw new NotImplementedException();
+ }
+
+
+ public XMLDocument load(Reader inputReader, String locationURI,
+ Object options) throws IOException {
+ try {
+ return dataObjectStreamer.fromXml(inputReader);
+ } catch (SAXException e) {
+ throw new IllegalArgumentException(e);
+ }
+ }
+
+ public String save(DataObject dataObject, String rootElementURI,
+ String rootElementName) {
+ StringWriter stringWriter = new StringWriter();
+ try {
+ save(createDocument(dataObject, rootElementURI, rootElementName), stringWriter, null);
+ } catch (IOException e) {
+ throw new RuntimeException("Should not happen!");
+ }
+ return stringWriter.toString();
+ }
+
+ public void save(DataObject dataObject, String rootElementURI,
+ String rootElementName, OutputStream outputStream)
+ throws IOException {
+ save(createDocument(dataObject, rootElementURI, rootElementName), outputStream, null);
+ }
+
+ public void save(XMLDocument xmlDocument, OutputStream outputStream,
+ Object options) throws IOException {
+ dataObjectStreamer.toXml(xmlDocument, outputStream);
+ }
+
+ public void save(XMLDocument xmlDocument, Writer outputWriter,
+ Object options) throws IOException {
+ dataObjectStreamer.toXml(xmlDocument, outputWriter);
+ }
+
+ public XMLDocument createDocument(DataObject dataObject,
+ String rootElementURI, String rootElementName) {
+ return new XMLDocumentImpl(dataObject, rootElementURI, rootElementName);
+ }
+
+ public void save(XMLDocument xmlDocument, Result outputResult, Object options) throws IOException {
+ throw new NotImplementedException();
+ }
+
+
+}
diff --git a/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/implementation/XSDHelperImpl.java b/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/implementation/XSDHelperImpl.java
new file mode 100644
index 0000000000..4d8c0f3de6
--- /dev/null
+++ b/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/implementation/XSDHelperImpl.java
@@ -0,0 +1,616 @@
+package com.agfa.hap.sdo.implementation;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.Reader;
+import java.io.StringReader;
+import java.io.StringWriter;
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.transform.OutputKeys;
+import javax.xml.transform.Result;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.sax.SAXTransformerFactory;
+import javax.xml.transform.sax.TransformerHandler;
+import javax.xml.transform.stream.StreamResult;
+
+import org.apache.log4j.Logger;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+import org.xml.sax.helpers.AttributesImpl;
+
+import com.agfa.hap.sdo.helper.TypeHelper;
+import com.agfa.hap.sdo.impl.TypeConverter;
+import com.agfa.hap.sdo.util.ClassHolder;
+import commonj.sdo.Property;
+import commonj.sdo.Type;
+import commonj.sdo.helper.XSDHelper;
+
+public class XSDHelperImpl implements XSDHelper {
+
+ private static final String IDENTITY_PROPERTY = "identityProperty";
+
+ private static final String AGFA_SDO_URI = "com.agfa.ris/sdo";
+
+ private static Logger logger = Logger.getLogger(XSDHelperImpl.class);
+
+ final static String XML_SCHEMA_URI = "http://www.w3.org/2001/XMLSchema";
+ final static String SDO_JAVA_URI = "commonj.sdo/java";
+ final static String SDO_URI = "commonj.sdo/xml"; //ref SDO_2.1_DRAFT_20060726.pdf p82
+ final static String SDO_PROPERTYTYPE = "propertyType";
+ final static String XSD_ANYURI = "anyURI";
+
+ public XSDHelperImpl() {
+ initializeXsdTypes();
+ }
+
+ public String getLocalName(Type type) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ public String getLocalName(Property property) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ public String getNamespaceURI(Property property) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ public boolean isAttribute(Property property) {
+ // TODO Auto-generated method stub
+ return false;
+ }
+
+ public boolean isElement(Property property) {
+ // TODO Auto-generated method stub
+ return false;
+ }
+
+ public boolean isMixed(Type type) {
+ // TODO Auto-generated method stub
+ return false;
+ }
+
+ public boolean isXSD(Type type) {
+ // TODO Auto-generated method stub
+ return false;
+ }
+
+ public Property getGlobalProperty(String uri, String propertyName,
+ boolean isElement) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ public String getAppinfo(Type type, String source) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ public String getAppinfo(Property property, String source) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ public List define(String xsd) {
+ StringReader reader = new StringReader(xsd);
+ return define(reader, null);
+ }
+
+ public List define(Reader xsdReader, String schemaLocation) {
+ try {
+ return readXsd(new InputSource(xsdReader));
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public List define(InputStream xsdInputStream, String schemaLocation) {
+ try {
+ return readXsd(new InputSource(xsdInputStream));
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public String generate(List typeList) {
+ //ref SDO_2.1_DRAFT_20060726.pdf p109-114
+ List<com.agfa.hap.sdo.Type> types = typeList;
+ String uri;
+ if (types.isEmpty()){
+ uri = "";
+ } else {
+ uri = types.get(0).getURI();
+ }
+
+ Map<com.agfa.hap.sdo.Type, String> type2URIsToImport = new HashMap<com.agfa.hap.sdo.Type, String>();
+ Map<String, String> uri2Namespace = new HashMap<String, String>();
+ Set<com.agfa.hap.sdo.Type> typesToGenerate = new HashSet<com.agfa.hap.sdo.Type>();
+ for (com.agfa.hap.sdo.Type type : types){
+ this.collectTypes(type, typesToGenerate, type2URIsToImport, uri);
+ }
+ List<com.agfa.hap.sdo.Type> typesToGenerateAlphabetical = new ArrayList<com.agfa.hap.sdo.Type>(typesToGenerate);
+ Collections.sort(typesToGenerateAlphabetical, new Comparator<Type>(){
+ public int compare(Type type1, Type type2) {
+ return type1.getName().compareTo(type2.getName());
+ }});
+ StringWriter stringWriter = new StringWriter();
+ Result result = new StreamResult(stringWriter);
+ try {
+ SAXTransformerFactory tf = (SAXTransformerFactory) TransformerFactory.newInstance();
+ TransformerHandler hd = tf.newTransformerHandler();
+ Transformer serializer = hd.getTransformer();
+ serializer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
+ serializer.setOutputProperty(OutputKeys.INDENT,"yes");
+ hd.setResult(result);
+ hd.startDocument();
+ AttributesImpl schemaAttributes = new AttributesImpl();
+ String attribURI = "";
+ hd.startPrefixMapping("sdo", SDO_URI);
+ hd.startPrefixMapping("sdoJava", SDO_JAVA_URI);
+ hd.startPrefixMapping("agfaSdo", AGFA_SDO_URI);
+ if (uri != null){
+ schemaAttributes.addAttribute(attribURI, "targetNamespace", "targetNamespace", "CDATA", uri);
+ hd.startPrefixMapping("tns", uri);
+ }
+ Set<String> urisToImport = new HashSet<String>(type2URIsToImport.values());
+ logger.debug("URIs to import = " + urisToImport);
+ int i = 1;
+ for (String importedURI : urisToImport){
+ String namespace = "ns" + i;
+ hd.startPrefixMapping(namespace, importedURI);
+ uri2Namespace.put(importedURI, namespace);
+ i++;
+ }
+ uri2Namespace.put(uri, "tns");
+ hd.startElement(XML_SCHEMA_URI , "schema", "xsd:schema", schemaAttributes);
+ for (com.agfa.hap.sdo.Type type : typesToGenerateAlphabetical){
+ generateXsdForType(hd, type, uri2Namespace);
+ }
+ hd.endElement(XML_SCHEMA_URI, "schema", "xsd:schema");
+ hd.endDocument();
+ } catch (Exception e){
+ throw new SdoImplementationException("", e);
+ }
+ for (com.agfa.hap.sdo.Type type : typesToGenerate){
+ if (!types.contains(type)){
+ types.add(type);
+ }
+ }
+ return stringWriter.toString();
+ }
+
+ private void collectTypes(com.agfa.hap.sdo.Type type, Set<com.agfa.hap.sdo.Type> typesToGenerate, Map<com.agfa.hap.sdo.Type, String> typesToImport, String uri) {
+ if (typesToGenerate.contains(type)){
+ return;
+ }
+ if ("commonj.sdo".equals(type.getURI())){
+ return;
+ }
+ if (!uri.equals(type.getURI())){
+ typesToImport.put(type, type.getURI());
+ return;
+ }
+ typesToGenerate.add(type);
+ for (com.agfa.hap.sdo.Property prop : type.getDeclaredProperties()){
+ this.collectTypes(prop.getType(), typesToGenerate, typesToImport, uri);
+ }
+
+ }
+
+ private void generateXsdForType(TransformerHandler hd, com.agfa.hap.sdo.Type type, Map<String, String> uri2Namespace) throws SAXException {
+ String namespaceURI= "";
+ AttributesImpl typeAttributes = new AttributesImpl();
+ typeAttributes.addAttribute(namespaceURI, "name", "name", "CDATA", type.getName());
+ if (type.isAbstract()) {
+ typeAttributes.addAttribute(namespaceURI, "abstract", "abstract", "CDATA", Boolean.toString(type.isAbstract()));
+ }
+ if (type.isSequenced()){
+ typeAttributes.addAttribute(namespaceURI, "mixed", "mixed", "CData", "true");
+ }
+ if (type.getIdentityProperty() != null) {
+ typeAttributes.addAttribute(namespaceURI, IDENTITY_PROPERTY, IDENTITY_PROPERTY, "CData", type.getIdentityProperty().getName());
+ }
+ hd.startElement(namespaceURI, "complexType", "xsd:complexType", typeAttributes);
+ if (type.getBaseTypes() != null && !type.getBaseTypes().isEmpty()){
+ hd.startElement(namespaceURI, "complexContent", "xsd:complexContent", new AttributesImpl());
+ AttributesImpl extensionAttributes = new AttributesImpl();
+ Type baseType = type.getBaseTypes().get(0);
+ extensionAttributes.addAttribute(namespaceURI, "base", "base", "CDATA", uri2Namespace.get(baseType.getURI())+ ":" + baseType.getName());
+ hd.startElement(namespaceURI, "extension", "xsd:extension", extensionAttributes );
+ hd.endElement(namespaceURI, "extension", "xsd:extension");
+ hd.endElement(namespaceURI, "complexContent", "xsd:complexContent");
+ }
+ List<Property> elementProperties = new ArrayList<Property>();
+ List<Property> attributeProperties = new ArrayList<Property>();
+ for (Property prop : type.getDeclaredProperties()){
+ if (prop.isContainment() || prop.isMany()){
+ //create an element
+ elementProperties.add(prop);
+ } else if (prop.getOpposite() != null && prop.getOpposite().isContainment()){
+ //do nothing
+ } else {
+ //create an attribute
+ attributeProperties.add(prop);
+ }
+ }
+ if (!elementProperties.isEmpty()){
+ if (type.isSequenced()){
+ hd.startElement(namespaceURI, "sequence", "xsd:sequence", null);
+ } else {
+ AttributesImpl choiceAttributes = new AttributesImpl();
+ choiceAttributes.addAttribute(namespaceURI, "maxOccurs", "maxOccurs", "CDATA", "unbounded");
+ hd.startElement(namespaceURI, "choice", "xsd:choice", choiceAttributes);
+ }
+ for (Property prop : elementProperties){
+ AttributesImpl elementAttributes = new AttributesImpl();
+ elementAttributes.addAttribute(namespaceURI, "name", "name", "CDATA", prop.getName());
+ elementAttributes.addAttribute(namespaceURI, "minOccurs", "name", "CDATA", prop.getName());
+ if (!prop.getAliasNames().isEmpty()){
+ StringBuilder aliasNames = new StringBuilder();
+ for (Object aliasName : prop.getAliasNames()){
+ aliasNames.append(aliasName);
+ aliasNames.append(' ');
+ }
+ if (aliasNames.length() != 0) {
+ aliasNames.setLength(aliasNames.length() - 1);
+ }
+ elementAttributes.addAttribute(namespaceURI, "aliasName", "sdo:aliasName", "CDATA", aliasNames.toString());
+ }
+ elementAttributes.addAttribute(namespaceURI, "readOnly", "sdo:readOnly", "CDATA", Boolean.toString(prop.isReadOnly()));
+ if (prop.isMany()){
+ elementAttributes.addAttribute(namespaceURI, "maxOccurs", "maxOccurs", "CDATA", "unbounded");
+ }
+ if (prop.getType().isDataType()){
+ elementAttributes.addAttribute(namespaceURI, "type", "type", "CDATA", "xsd:" + this.xsdTypeForSDOType(prop.getType().getName()));
+ } else if (prop.isContainment()){
+ elementAttributes.addAttribute(namespaceURI, "type", "type", "CDATA", uri2Namespace.get(prop.getType().getURI()) + ":" + prop.getType().getName());
+ } else {
+ elementAttributes.addAttribute(namespaceURI, "type", "type", "CDATA", "xsd:anyUri");
+ elementAttributes.addAttribute(namespaceURI, "propertyType", "sdo:propertyType", "CDATA", uri2Namespace.get(prop.getType().getURI()) + ":" + prop.getType().getName());
+ }
+ if (prop.getOpposite() != null){
+ elementAttributes.addAttribute(namespaceURI, "oppositeProperty", "sdo:oppositeProperty", "CDATA", prop.getOpposite().getName());
+ }
+ hd.startElement(namespaceURI, "element", "xsd:element", elementAttributes);
+ hd.endElement(namespaceURI, "element", "xsd:element");
+ }
+ if (type.isSequenced()){
+ hd.endElement(namespaceURI, "sequence", "xsd:sequence");
+ } else {
+ hd.endElement(namespaceURI, "choice", "xsd:choice");
+ }
+ }
+
+ for (Property prop : attributeProperties){
+ AttributesImpl attributeAttributes = new AttributesImpl();
+ attributeAttributes.addAttribute(namespaceURI, "name", "name", "CDATA", prop.getName());
+ if (!prop.getAliasNames().isEmpty()){
+ StringBuilder aliasNames = new StringBuilder();
+ for (Object aliasName : prop.getAliasNames()){
+ aliasNames.append(aliasName);
+ aliasNames.append(' ');
+ }
+ if (aliasNames.length() != 0) {
+ aliasNames.setLength(aliasNames.length() - 1);
+ }
+ attributeAttributes.addAttribute(namespaceURI, "aliasName", "sdo:aliasName", "CDATA", aliasNames.toString());
+ }
+ attributeAttributes.addAttribute(namespaceURI, "readOnly", "sdo:readOnly", "CDATA", Boolean.toString(prop.isReadOnly()));
+ if (prop.getDefault() != null){
+ attributeAttributes.addAttribute(namespaceURI, "default", "default", "CDATA", prop.getDefault().toString());
+ }
+ if (prop.getType().isDataType()){
+ String xsdType = this.xsdTypeForSDOType(prop.getType().getName());
+ if (xsdType != null) {
+ attributeAttributes.addAttribute(namespaceURI, "type", "type", "CDATA", "xsd:" + xsdType);
+ } else {
+ attributeAttributes.addAttribute(namespaceURI, "instanceClass", "sdoJava:instanceClass", "CDATA", prop.getType().getName());
+ }
+ } else {
+ attributeAttributes.addAttribute(namespaceURI, "type", "type", "CDATA", "xsd:anyURI");
+ attributeAttributes.addAttribute(namespaceURI, "propertyType", "sdo:propertyType", "CDATA", uri2Namespace.get(prop.getType().getURI()) + ":" + prop.getType().getName());
+ }
+ if (prop.getOpposite() != null){
+ attributeAttributes.addAttribute(namespaceURI, "oppositeProperty", "sdo:oppositeProperty", "CDATA", prop.getOpposite().getName());
+ }
+
+ hd.startElement(namespaceURI, "attribute", "xsd:attribute", attributeAttributes);
+ hd.endElement(namespaceURI, "attribute", "xsd:attribute");
+ }
+ hd.endElement(namespaceURI, "complexType", "xsd:complexType");
+ }
+
+ public String generate(List types, Map namespaceToSchemaLocation) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ List<Type> readXsd(InputSource in) throws IOException, SAXException, ParserConfigurationException {
+ Document document = parse(in);
+ String uri = document.getDocumentElement().getAttributeNS(XML_SCHEMA_URI, "targetNamespace");
+ if (uri.length() == 0) {
+ uri = document.getDocumentElement().getAttribute("targetNamespace");
+ }
+ Map<String, String> uriPrefixMap = new HashMap<String, String>();
+ NamedNodeMap docAttributes = document.getDocumentElement().getAttributes();
+ String defaultNamespaceURI = null;
+ for (int i = 0; i < docAttributes.getLength(); i++){
+ Node attribute = docAttributes.item(i);
+ if ("http://www.w3.org/2000/xmlns/".equals(attribute.getNamespaceURI())){
+ if ("xmlns".equals(attribute.getNodeName())){
+ defaultNamespaceURI = attribute.getNodeValue();
+ } else {
+ uriPrefixMap.put(attribute.getNodeName().substring(attribute.getNodeName().lastIndexOf(':') + 1), attribute.getNodeValue());
+ }
+ }
+ }
+ NodeList types = document.getElementsByTagNameNS(XML_SCHEMA_URI, "simpleType");
+ List<Type> result = new ArrayList<Type>();
+ readTypes(uri, uriPrefixMap, defaultNamespaceURI, types, result);
+ types = document.getElementsByTagNameNS(XML_SCHEMA_URI, "complexType");
+ readTypes(uri, uriPrefixMap, defaultNamespaceURI, types, result);
+ createOppositeProperties(result);
+ resolveBaseTypes(result);
+ return result;
+ }
+
+ private void readTypes(String uri, Map<String, String> uriPrefixMap, String defaultNamespaceURI, NodeList types, List<Type> result) {
+ for (int i = 0; i < types.getLength(); i++) {
+ Element type = (Element) types.item(i);
+ result.add(readType(uri, type, defaultNamespaceURI, uriPrefixMap));
+ }
+ }
+
+ private void createOppositeProperties(List<Type> types) {
+ for (Type t : types) {
+ // no iterator to avoid ConcurrentModificationException
+ for (int i = 0; i < t.getDeclaredProperties().size(); i++) {
+ ((PropertyImplementation) t.getDeclaredProperties().get(i)).createOppositeProperty();
+ }
+ }
+ }
+
+ private void resolveBaseTypes(List<Type> types) {
+ for (Type t : types) {
+ ((TypeImplementation) t).normalizeTypeHierarchy();
+ }
+ }
+
+ private Document parse(InputSource in) throws ParserConfigurationException, SAXException, IOException {
+ Document document;
+ try {
+ DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
+ documentBuilderFactory.setNamespaceAware(true);
+ DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
+ document = documentBuilder.parse(in);
+ } finally {
+ if (in.getByteStream() != null) {
+ in.getByteStream().close();
+ }
+ if (in.getCharacterStream() != null) {
+ in.getCharacterStream().close();
+ }
+ }
+ return document;
+ }
+
+ Type readType(String uri, Element typeElement, String defaultNamespaceURI, Map<String, String> uriPrefixMap) {
+ if (typeElement.getNodeName().endsWith("simpleType")) {
+ return readSimpleType(typeElement, uri);
+ }
+ String name = typeElement.getAttribute("name");
+ TypeImplementation result = ((TypeHelperImpl) TypeHelper.INSTANCE).define(uri, name);
+ superType(typeElement, defaultNamespaceURI, uriPrefixMap, result);
+ NodeList nodeList = typeElement.getElementsByTagNameNS(XML_SCHEMA_URI, "attribute");
+ for (int i = 0; i < nodeList.getLength(); i++) {
+ createProperty(result, (Element) nodeList.item(i), defaultNamespaceURI, uriPrefixMap, uri);
+ }
+ nodeList = typeElement.getElementsByTagNameNS(XML_SCHEMA_URI, "element");
+ for (int i = 0; i < nodeList.getLength(); i++) {
+ createProperty(result, (Element) nodeList.item(i), defaultNamespaceURI, uriPrefixMap, uri);
+ }
+ if (typeElement.hasAttributeNS(SDO_JAVA_URI, "extendedInstanceClass")) {
+ try {
+ Class instanceClass = Class.forName(typeElement.getAttributeNS(SDO_JAVA_URI, "extendedInstanceClass"));
+ result.setInstanceClass(instanceClass);
+ } catch (ClassNotFoundException e) {
+ throw new RuntimeException("Unknown java class ", e);
+ }
+ }
+ String id = typeElement.getAttribute(IDENTITY_PROPERTY);
+ if (id != null) {
+ result.setIdentityProperty(result.getProperty(id));
+ }
+ return result;
+ }
+
+ private void superType(Element typeElement, String defaultNamespaceURI, Map<String, String> uriPrefixMap, TypeImplementation result) {
+ if (!result.getBaseTypes().isEmpty()){
+ return; //in case the type already has a base type, leave it as it is
+ //TODO review: alternative is to overwrite the existing base type with a new one
+ }
+ NodeList nodeList = typeElement.getElementsByTagNameNS(XML_SCHEMA_URI, "extension");
+ for (int i = 0; i < nodeList.getLength(); i++) {
+ String superTypeName = ((Element) nodeList.item(i)).getAttribute("base");
+ Type superType = resolveType(superTypeName, uriPrefixMap, defaultNamespaceURI);
+ result.setBaseType((TypeImplementation) superType);
+ }
+ }
+
+ private Type readSimpleType(Element type, String uri) {
+ String name = type.getAttribute("name");
+ try {
+ Class<?> instanceClass = null;
+ TypeConverter<?> typeConverter = null;
+ String instanceClassName = type.getAttributeNS(SDO_JAVA_URI, "instanceClass");
+ if (instanceClassName != null && instanceClassName.length() > 0) {
+ instanceClass = Class.forName(instanceClassName);
+ String typeConverterName = type.getAttributeNS(AGFA_SDO_URI, "typeConverter");
+ if (typeConverterName != null && typeConverterName.length() > 0) {
+ try {
+ typeConverter = (TypeConverter<?>) Class.forName(typeConverterName).newInstance();
+ } catch (Exception e) {
+ throw new RuntimeException("Unknown TypeConverter class " + typeConverterName, e);
+ }
+ }
+ }
+ if (instanceClass == null) {
+ ClassHolder classHolder = ImplementationClassRepository.getInstance().getImplementationClass(uri, name);
+ if (classHolder == null) {
+ throw new SdoImplementationException("No implementation class registered for type " + uri + ":" + name);
+ }
+ instanceClass = classHolder.resolveClass();
+ }
+ if (typeConverter == null) {
+ typeConverter = TypeConverter.getDefaultConverter(instanceClass);
+ }
+ return ((TypeHelperImpl) TypeHelper.INSTANCE).registerBasicType(name, uri, instanceClass, typeConverter);
+ } catch (ClassNotFoundException e) {
+ throw new RuntimeException("Unknown java class " , e);
+ }
+ }
+
+ private void createProperty(TypeImplementation type, Element node, String defaultNamespaceURI, Map<String, String> uriPrefixMap, String targetNameSpace) {
+ boolean isSdoRef = false;
+ String propertyName = node.getAttribute("name");
+ if (type.getProperty(propertyName) != null){
+ return; //property already exists, so don't create another one, since the property may already be referenced by other types (eg in case of an opposite property)
+ }
+ String xsdType = node.getAttribute("type");
+ com.agfa.hap.sdo.Type propertyType;
+ if (xsdType.length() > 0) {
+ int prefixIndex = xsdType.indexOf(':');
+ if (prefixIndex > 0) {
+ String namespacePrefix = xsdType.substring(0, prefixIndex);
+ String xsdTypeWithoutPrefix = xsdType.substring(prefixIndex+1);
+ String namespace = uriPrefixMap.get(namespacePrefix);
+ if (XML_SCHEMA_URI.equals(namespace)){
+ if (xsdTypeWithoutPrefix.equals(XSD_ANYURI)) {
+ String fullPropertyTypeName = node.getAttributeNS(SDO_URI, SDO_PROPERTYTYPE);
+ if (fullPropertyTypeName.length() == 0) {
+ propertyType = TypeHelper.INSTANCE.getType(URI.class);
+ } else {
+ isSdoRef = true;
+ int propIndex = fullPropertyTypeName.indexOf(':');
+ String sdoPropertyTypeNamespace = null;
+ String propertyTypeName = null;
+ if (propIndex > 0) {
+ String prefix = fullPropertyTypeName.substring(0, propIndex);
+ sdoPropertyTypeNamespace = uriPrefixMap.get(prefix);
+ propertyTypeName = fullPropertyTypeName.substring(propIndex + 1);
+ } else {
+ sdoPropertyTypeNamespace = defaultNamespaceURI;
+ }
+ if (sdoPropertyTypeNamespace == null){
+ logger.warn("no namespace for " + fullPropertyTypeName + " in " + type);
+ }
+ // define will return the existing type if it already exists, or create a new one otherwise,
+ // in the latter case, it will have 0 properties, but those will get filled in later
+ propertyType = ((TypeHelperImpl) TypeHelper.INSTANCE).define(sdoPropertyTypeNamespace, propertyTypeName);
+ }
+ } else {
+ propertyType = TypeHelper.INSTANCE.getType("commonj.sdo", this.sdoTypeForXSDType(xsdTypeWithoutPrefix));
+ }
+ } else {
+ propertyType = ((TypeHelperImpl) TypeHelper.INSTANCE).define(namespace, xsdTypeWithoutPrefix);
+ }
+ } else {
+ propertyType = ((TypeHelperImpl) TypeHelper.INSTANCE).define(type.getURI(), xsdType);
+ }
+ } else {
+ String instanceClassName = node.getAttributeNS(SDO_JAVA_URI, "instanceClass");
+ propertyType = ((TypeHelperImpl) TypeHelper.INSTANCE).defineInstanceClass(instanceClassName);
+ }
+ PropertyImplementation result = type.addProperty(propertyName, propertyType);
+ String oppositeProperty = node.getAttributeNS(SDO_URI, "oppositeProperty");
+ if (oppositeProperty != null && oppositeProperty.length() > 0) {
+ result.setOppositeName(oppositeProperty);
+ }
+ String nodePrefix = node.getPrefix();
+ if (nodePrefix != null && XML_SCHEMA_URI.equals(uriPrefixMap.get(nodePrefix))
+ && "element".equals(node.getNodeName().substring(nodePrefix.length() + 1))
+ &&!isSdoRef && !propertyType.isDataType()) {
+ result.setIsContainment(true);
+ }
+ if ("unbounded".equals(node.getAttribute("maxOccurs"))) {
+ result.setIsMany(true);
+ }
+
+ }
+
+ private Type resolveType(String xsdType, Map<String, String> uriPrefixMap, String defaultNamespaceURI) {
+ int prefixIndex = xsdType.indexOf(':');
+ if (prefixIndex > 0) {
+ String namespacePrefix = xsdType.substring(0, prefixIndex);
+ String xsdTypeWithoutPrefix = xsdType.substring(prefixIndex+1);
+ String namespace = uriPrefixMap.get(namespacePrefix);
+ if (XML_SCHEMA_URI.equals(namespace)){
+ return TypeHelper.INSTANCE.getType("commonj.sdo", this.sdoTypeForXSDType(xsdTypeWithoutPrefix));
+ }
+ return ((TypeHelperImpl) TypeHelper.INSTANCE).define(namespace, xsdTypeWithoutPrefix);
+ }
+ return ((TypeHelperImpl) TypeHelper.INSTANCE).define(defaultNamespaceURI, xsdType);
+ }
+
+ private String sdoTypeForXSDType(String actualType) {
+ return xsdType2SdoType.get(actualType);
+ }
+
+ private String xsdTypeForSDOType(String sdoTypename){
+ return sdoType2XsdTypes.get(sdoTypename);
+ }
+
+ protected void registerXsdType(String xsdSimpleType, Class javaClass) {
+ if (javaClass.getName().startsWith("java.lang.")){
+ this.registerXsdType(xsdSimpleType, javaClass, javaClass.getName().substring(javaClass.getName().lastIndexOf('.') + 1));
+ } else {
+ this.registerXsdType(xsdSimpleType, javaClass, javaClass.getName());
+ }
+ }
+
+ protected void initializeXsdTypes() {
+ //ref SDO_2_.1_DRAFT_20060726.pdf p97
+ registerXsdType("string", String.class);
+ registerXsdType("int", Integer.class);
+ registerXsdType("long", Long.class);
+ registerXsdType("double", Double.class);
+ registerXsdType("boolean", Boolean.class);
+ registerXsdType("short", Short.class);
+ registerXsdType("byte", Byte.class);
+ registerXsdType("float", Float.class);
+ registerXsdType("hexBinary", byte[].class, "Bytes");
+ registerXsdType("anyURI", URI.class);
+ }
+
+ protected void registerXsdType(String xsdSimpleType, Class javaClass, String sdoTypeName) {
+ sdoType2XsdTypes.put(sdoTypeName, xsdSimpleType);
+ xsdType2SdoType.put(xsdSimpleType, sdoTypeName);
+ }
+
+
+ private Map<String, String> sdoType2XsdTypes = new HashMap<String, String>();
+ private Map<String, String> xsdType2SdoType = new HashMap<String, String>();
+
+
+}
diff --git a/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/mapper/AbstractPropertyAccessor.java b/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/mapper/AbstractPropertyAccessor.java
new file mode 100644
index 0000000000..3b27452007
--- /dev/null
+++ b/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/mapper/AbstractPropertyAccessor.java
@@ -0,0 +1,35 @@
+package com.agfa.hap.sdo.mapper;
+
+import java.util.Collection;
+
+import sun.reflect.generics.reflectiveObjects.NotImplementedException;
+
+import com.agfa.hap.sdo.DataMapper;
+import com.agfa.hap.sdo.Property;
+import com.agfa.hap.sdo.SnapshotDefinition;
+
+/**
+ * Abstract shell implementation for {@link PropertyAccessor}.
+ * @author AMOCZ
+ */
+public abstract class AbstractPropertyAccessor implements PropertyAccessor {
+
+ public Object getValue(Object instance, Property property,
+ DataMapper dataMapper) {
+ return null;
+ }
+
+ public Collection<?> getValues(Collection<?> instances, Property property,
+ SnapshotDefinition def, DataMapper dataMapper) {
+ throw new NotImplementedException();
+ }
+
+ public boolean isBulkAccessor() {
+ return false;
+ }
+
+ public void setValue(Object instance, Property property, Object value,
+ DataMapper dataMapper) {
+ }
+
+}
diff --git a/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/mapper/BeanPropertyAccessor.java b/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/mapper/BeanPropertyAccessor.java
new file mode 100644
index 0000000000..71f7e8e3f5
--- /dev/null
+++ b/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/mapper/BeanPropertyAccessor.java
@@ -0,0 +1,79 @@
+package com.agfa.hap.sdo.mapper;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.Collection;
+
+import sun.reflect.generics.reflectiveObjects.NotImplementedException;
+
+import com.agfa.hap.sdo.DataMapper;
+import com.agfa.hap.sdo.Property;
+import com.agfa.hap.sdo.SnapshotDefinition;
+
+/**
+ * PropertyAccessor that follows bean conventions (i.e. getX() methods) to get values.
+ * @author AMOCZ
+ */
+public class BeanPropertyAccessor implements PropertyAccessor {
+
+ private static final Object[] EMPTY_ARGS = new Object[0];
+
+ public Object getValue(Object instance, Property property, DataMapper dataMapper) {
+ if (!isReadable()) {
+ throw new IllegalArgumentException(instance.getClass() + " doesn't have a getter for property " + property.getName());
+ }
+ try {
+ return getter.invoke(instance, EMPTY_ARGS);
+ } catch (InvocationTargetException e) {
+ throw new IllegalArgumentException("Couldn't access property " + property, e.getTargetException());
+ } catch (IllegalAccessException e) {
+ throw new IllegalArgumentException("Couldn't access property " + property, e);
+ }
+ }
+
+ /**
+ * Sets the value of this property for the specified Object.
+ * @throws IllegalArgumentException
+ */
+ public void setValue(Object instance, Property property, Object newValue, DataMapper dataMapper) {
+ if (!isWritable()) {
+ throw new IllegalArgumentException(instance.getClass() + " doesn't have a setter for property " + property.getName());
+ }
+ try {
+ setter.invoke(instance, new Object[] { newValue });
+ } catch (InvocationTargetException e) {
+ throw new IllegalArgumentException("Couldn't access property " + property, e.getTargetException());
+ } catch (IllegalAccessException e) {
+ throw new IllegalArgumentException("Couldn't access property " + property, e);
+ }
+ }
+
+ public void setGetter(Method method) {
+ this.getter = method;
+ }
+
+ public void setSetter(Method method) {
+ this.setter = method;
+ }
+
+ public boolean isReadable() {
+ return getter != null;
+ }
+
+ public boolean isWritable() {
+ return setter != null;
+ }
+
+
+ public Collection<?> getValues(Collection<?> instances, Property property, SnapshotDefinition def, DataMapper dataMapper) {
+ throw new NotImplementedException();
+ }
+
+ public boolean isBulkAccessor() {
+ return false;
+ }
+
+
+ private Method setter;
+ private Method getter;
+}
diff --git a/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/mapper/BeanPropertyAccessorBuilder.java b/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/mapper/BeanPropertyAccessorBuilder.java
new file mode 100644
index 0000000000..1ebb121177
--- /dev/null
+++ b/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/mapper/BeanPropertyAccessorBuilder.java
@@ -0,0 +1,62 @@
+package com.agfa.hap.sdo.mapper;
+
+import java.lang.reflect.Method;
+
+import com.agfa.hap.sdo.Property;
+import com.agfa.hap.sdo.Type;
+
+/**
+ * PropertyAccessorBuilder that uses Java Bean conventions to extract properties from an object.
+ * @author AMOCZ
+ */
+public class BeanPropertyAccessorBuilder extends PropertyAccessorBuilder {
+
+ public BeanPropertyAccessorBuilder() {
+ super();
+ }
+
+ public BeanPropertyAccessorBuilder(PropertyAccessorBuilder next) {
+ super(next);
+ }
+
+ @Override
+ protected boolean accepts(Class cls, Type type) {
+ return true;
+ }
+
+ @Override
+ protected PropertyAccessor createPropertyAccessor(Class cls, Property prop) {
+ String propertyName = Character.toUpperCase(prop.getName().charAt(0)) + prop.getName().substring(1);
+ String methodName;
+ if (prop.getType().getInstanceClass() == Boolean.class) {
+ if (propertyName.startsWith("Is")){
+ methodName = prop.getName();
+ } else {
+ methodName = "is" + propertyName;
+ }
+ } else {
+ methodName = "get" + propertyName;
+ }
+ BeanPropertyAccessor property = prop.isMany() ? new ManyValuedBeanPropertyAccessor() : new BeanPropertyAccessor();
+ try {
+ Method method = cls.getMethod(methodName, (Class[]) null);
+ property.setGetter(method);
+ method = getSetMethod(cls, propertyName);
+ property.setSetter(method);
+ return property;
+ } catch (NoSuchMethodException e) {
+ throw new IllegalArgumentException("Can't find property " + prop.getName() + " on class " + cls.getName(), e);
+ }
+ }
+
+ // we assume there is no type overloading
+ private Method getSetMethod(Class cls, String propertyName) {
+ String methodName = "set" + propertyName;
+ for (Method m : cls.getMethods()) {
+ if (m.getName().equals(methodName)) {
+ return m;
+ }
+ }
+ return null;
+ }
+}
diff --git a/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/mapper/DelegatingDataMapper.java b/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/mapper/DelegatingDataMapper.java
new file mode 100644
index 0000000000..dea136e15c
--- /dev/null
+++ b/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/mapper/DelegatingDataMapper.java
@@ -0,0 +1,71 @@
+package com.agfa.hap.sdo.mapper;
+
+import java.util.Collection;
+import java.util.Iterator;
+
+import com.agfa.hap.sdo.DataMapper;
+import com.agfa.hap.sdo.Property;
+import com.agfa.hap.sdo.SnapshotDefinition;
+import com.agfa.hap.sdo.Type;
+
+public class DelegatingDataMapper<T> implements DataMapper<T> {
+
+ private DataMapper<T> principalMapper;
+ private DataMapper<T> secondaryMapper;
+
+ public DelegatingDataMapper(DataMapper<T> principalMapper, DataMapper<T> secondaryMapper){
+ this.principalMapper = principalMapper;
+ this.secondaryMapper = secondaryMapper;
+ }
+
+ public T create(Type type) {
+ T newlyCreated = principalMapper.create(type);
+ if (newlyCreated == null){
+ return secondaryMapper.create(type);
+ }
+ return newlyCreated;
+ }
+
+ public Type getCorrespondingType(Class clazz) {
+ return principalMapper.getCorrespondingType(clazz);
+ }
+
+ public Iterator<? extends T> getObjects(T object, Property property) {
+ return principalMapper.getObjects(object, property);
+ }
+
+ public Object getProperty(T object, Property property) {
+ return principalMapper.getProperty(object, property);
+ }
+
+ public Type getType(T object) {
+ return principalMapper.getType(object);
+ }
+
+ public boolean isProxy(T object) {
+ return principalMapper.isProxy(object);
+ }
+
+ public T newProxy(Type type, Object identity) {
+ return principalMapper.newProxy(type, identity);
+ }
+
+ public void setProperty(T object, Property property, Object value) {
+ principalMapper.setProperty(object, property, value);
+
+ }
+
+ public void setUnavailable(T object, Property property) {
+ principalMapper.setUnavailable(object, property);
+
+ }
+
+ public Collection<T> getProperties(Collection<T> object, Property bulkProperty, SnapshotDefinition def) {
+ return principalMapper.getProperties(object, bulkProperty, def);
+ }
+
+ public boolean isBulkProperty(Class clazz, Property property) {
+ return principalMapper.isBulkProperty(clazz, property);
+ }
+
+}
diff --git a/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/mapper/DelegatingPartialDataObjectMapper.java b/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/mapper/DelegatingPartialDataObjectMapper.java
new file mode 100644
index 0000000000..a925a2d9db
--- /dev/null
+++ b/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/mapper/DelegatingPartialDataObjectMapper.java
@@ -0,0 +1,119 @@
+package com.agfa.hap.sdo.mapper;
+
+import java.util.Collection;
+import java.util.Iterator;
+
+import com.agfa.hap.sdo.DataMapper;
+import com.agfa.hap.sdo.PartialDataObject;
+import com.agfa.hap.sdo.Property;
+import com.agfa.hap.sdo.SnapshotDefinition;
+import com.agfa.hap.sdo.Type;
+
+/**
+ * @author awvjz
+ *
+ * this datamapper wraps another datamapper and delegates to this second mapper in case the object it has to work with are not partialdataobjects
+ *
+ */
+public class DelegatingPartialDataObjectMapper<T> implements DataMapper<T> {
+ private DataMapper<T> delegate;
+ private PartialDataObjectMapper defaultMapper = new PartialDataObjectMapper();
+
+ public DelegatingPartialDataObjectMapper(DataMapper<T> delegateDataMapper){
+ delegate = delegateDataMapper;
+ }
+
+ public T create(Type type) {
+ T instance = delegate.create(type);
+ if (instance == null){
+ return (T) defaultMapper.create(type);
+ }
+ return instance;
+ }
+
+ public Type getCorrespondingType(Class clazz) {
+ Type type = null;
+ try {
+ type = delegate.getCorrespondingType(clazz);
+ } catch (IllegalArgumentException e){
+ //no type was found
+ }
+ if (type == null){
+ return defaultMapper.getCorrespondingType(clazz);
+ }
+ return type;
+ }
+
+
+ public Object getProperty(T object, Property property) {
+ if (object instanceof PartialDataObject){
+ return defaultMapper.getProperty((PartialDataObject) object, property);
+ }
+ return delegate.getProperty(object, property);
+ }
+
+
+ public T newProxy(Type type, Object identity) {
+ T proxy = delegate.newProxy(type, identity);
+ if (proxy == null){
+ return (T) defaultMapper.newProxy(type, identity);
+ }
+ return proxy;
+ }
+
+
+ public Iterator<? extends T> getObjects(T object, Property property) {
+ if (object instanceof PartialDataObject){
+ return (Iterator<? extends T>) defaultMapper.getObjects((PartialDataObject) object, property);
+ }
+ return delegate.getObjects(object, property);
+ }
+
+
+ public Type getType(T object) {
+ if (object instanceof PartialDataObject){
+ return defaultMapper.getType((PartialDataObject) object);
+ }
+ return delegate.getType(object);
+ }
+
+
+ public boolean isProxy(T object) {
+ if (object instanceof PartialDataObject){
+ return defaultMapper.isProxy((PartialDataObject) object);
+ }
+ return delegate.isProxy(object);
+ }
+
+
+ public void setProperty(T object, Property property, Object value) {
+ if (object instanceof PartialDataObject){
+ defaultMapper.setProperty((PartialDataObject) object, property, value);
+ } else {
+ delegate.setProperty(object, property, value);
+ }
+ }
+
+
+ public void setUnavailable(T object, Property property) {
+ if (object instanceof PartialDataObject){
+ defaultMapper.setUnavailable((PartialDataObject) object, property);
+ } else {
+ delegate.setUnavailable(object, property);
+ }
+ }
+
+ public Collection<T> getProperties(Collection<T> object, Property bulkProperty, SnapshotDefinition def) {
+ return delegate.getProperties(object, bulkProperty, def);
+ }
+
+ public boolean isBulkProperty(Class clazz, Property property) {
+ if (PartialDataObject.class.isAssignableFrom(clazz)) {
+ return false;
+ }
+ return delegate.isBulkProperty(clazz, property);
+ }
+
+
+
+}
diff --git a/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/mapper/ExtendablePropertyAccessorBuilder.java b/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/mapper/ExtendablePropertyAccessorBuilder.java
new file mode 100644
index 0000000000..b395574c24
--- /dev/null
+++ b/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/mapper/ExtendablePropertyAccessorBuilder.java
@@ -0,0 +1,75 @@
+package com.agfa.hap.sdo.mapper;
+
+import java.io.IOException;
+import java.net.URL;
+import java.util.Enumeration;
+import java.util.Properties;
+
+import com.agfa.hap.sdo.Property;
+import com.agfa.hap.sdo.Type;
+
+/**
+ * {@link PropertyAccessorBuilder} that allows configuration of dedicated
+ * PropertyAccessor classes for properties. Each time such properties are accessed
+ * (read and written) the configured {@link PropertyAccessor}.
+ *
+ * Configuration of a property must be done in "sdo-propertyaccessors.properties" properties file.
+ * The name of a property should be the fully qualified class name followed by '.'
+ * followed by the name of the property. The value of the property is the fully
+ * qualified name of a class that implements {@link PropertyAccessor}.
+ *
+ * @author AMOCZ
+ */
+public class ExtendablePropertyAccessorBuilder extends PropertyAccessorBuilder {
+ private static final String SDOPROPERTYACCESSORS = "sdo-propertyaccessors.properties";
+ private Properties specialPropertyAccessors = new Properties();
+ public ExtendablePropertyAccessorBuilder(PropertyAccessorBuilder next){
+ super(next);
+ initializeClassPropertyAccessorMap();
+ }
+
+ private void initializeClassPropertyAccessorMap() {
+ try {
+ Enumeration<URL> props = this.getClass().getClassLoader().getResources(SDOPROPERTYACCESSORS);
+ while (props.hasMoreElements()) {
+ specialPropertyAccessors.load(props.nextElement().openStream());
+ }
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ @Override
+ protected boolean accepts(Class cls, Type type) {
+ return true;
+ }
+
+ @Override
+ protected PropertyAccessor createPropertyAccessor(Class cls, Property prop) {
+ //TODO performance: in most cases there will be no special property accessor
+ String accessor = null;
+ Class clazz = cls;
+ while (clazz != null){
+ String qualifiedProperty = clazz.getName() + "." + prop.getName();
+ accessor = specialPropertyAccessors.getProperty(qualifiedProperty);
+ if (accessor != null){
+ try {
+ return (PropertyAccessor) Class.forName(accessor).newInstance();
+ } catch (Exception e) {
+ throw new RuntimeException("while creating property accessor for " + qualifiedProperty + " accessor=" + accessor, e);
+ }
+ }
+ clazz = clazz.getSuperclass();
+ }
+
+ //no special accessor found
+ return next.createPropertyAccessorOrDelegate(cls, prop);
+
+
+ }
+
+ public void reinitialize() {
+ this.initializeClassPropertyAccessorMap();
+ }
+
+}
diff --git a/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/mapper/FalsePropertyAccessor.java b/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/mapper/FalsePropertyAccessor.java
new file mode 100644
index 0000000000..a64b69b87d
--- /dev/null
+++ b/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/mapper/FalsePropertyAccessor.java
@@ -0,0 +1,13 @@
+package com.agfa.hap.sdo.mapper;
+
+import com.agfa.hap.sdo.DataMapper;
+import com.agfa.hap.sdo.Property;
+
+public class FalsePropertyAccessor extends AbstractPropertyAccessor {
+
+ public Object getValue(Object instance, Property property,
+ DataMapper dataMapper) {
+ return Boolean.FALSE;
+ }
+
+}
diff --git a/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/mapper/FilteringPartialDataObjectMapper.java b/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/mapper/FilteringPartialDataObjectMapper.java
new file mode 100644
index 0000000000..4ccd81ca69
--- /dev/null
+++ b/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/mapper/FilteringPartialDataObjectMapper.java
@@ -0,0 +1,30 @@
+package com.agfa.hap.sdo.mapper;
+
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Set;
+
+import com.agfa.hap.sdo.PartialDataObject;
+import com.agfa.hap.sdo.Type;
+
+/**
+ * ObjectMapper that always considers certain types to be proxies
+ *
+ */
+public class FilteringPartialDataObjectMapper extends PartialDataObjectMapper {
+ Set<Type> typesToConsiderAsProxies;
+
+ public FilteringPartialDataObjectMapper(Collection<Type> typesToConsiderAsProxies){
+ this.typesToConsiderAsProxies = new HashSet<Type>(typesToConsiderAsProxies);
+ }
+
+ @Override
+ public boolean isProxy(PartialDataObject partialDataObject) {
+ if (typesToConsiderAsProxies.contains(partialDataObject.getType())){
+ return true;
+ }
+ return super.isProxy(partialDataObject);
+ }
+
+
+}
diff --git a/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/mapper/JavaBeanMapper.java b/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/mapper/JavaBeanMapper.java
new file mode 100644
index 0000000000..08fe60f7de
--- /dev/null
+++ b/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/mapper/JavaBeanMapper.java
@@ -0,0 +1,125 @@
+package com.agfa.hap.sdo.mapper;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Iterator;
+
+import com.agfa.hap.sdo.DataMapper;
+import com.agfa.hap.sdo.Property;
+import com.agfa.hap.sdo.SnapshotDefinition;
+import com.agfa.hap.sdo.Type;
+
+/**
+ * DataMapper that uses Java Bean conventions to access data on java classes.
+ * This mapper assumes that for each {@link Property} there are appropriately named
+ * accessors defined on the corresponding class.
+ * Each {@link Type} is mapped to the java class with the same name. It is also possible
+ * to register a class as corresponding to a type.
+ * <p/>
+ * This DataMapper ensures that opposite properties are properly filled in. As such, objects will
+ * not be added multiple times to a many-valued property if that property has an opposite property.
+ * @author AMOCZ
+ */
+public class JavaBeanMapper implements DataMapper<Object> {
+
+ public JavaBeanMapper(TypeMapper typeMapper) {
+ this.typeMapper = typeMapper;
+ }
+
+ public Iterator<?> getObjects(Object object, Property property) {
+ PropertyAccessor propertyAccessor = typeMapper.property(object.getClass(), property);
+ if (propertyAccessor == null){
+ throw new RuntimeException("no property accessor for sdo property " + property);
+ }
+ Collection<?> value = (Collection<?>) propertyAccessor.getValue(object, property, this);
+ if (value == null){
+ return Collections.emptyList().iterator();
+ }
+ return value.iterator();
+ }
+
+ public Object getProperty(Object object, Property property) {
+ PropertyAccessor propertyAccessor = typeMapper.property(object.getClass(), property);
+ if (propertyAccessor == null){
+ throw new RuntimeException("no property accessor for sdo property " + property);
+ }
+ return propertyAccessor.getValue(object, property, this);
+ }
+
+ public void setProperty(Object object, Property property, Object value) {
+ PropertyAccessor propertyAccessor = typeMapper.property(object.getClass(), property);
+ if (propertyAccessor == null){
+ throw new RuntimeException("no property accessor for sdo property " + property);
+ }
+ propertyAccessor.setValue(object, property, value, this);
+ if (property.getOpposite() != null && value != null) {
+ setOpposite(object, property, value);
+ }
+ }
+
+ protected void setOpposite(Object object, Property property, Object value) {
+ typeMapper.property(value.getClass(), property.getOpposite()).setValue(value, property.getOpposite(), object, this);
+ }
+
+ public void setUnavailable(Object object, Property property) {
+ }
+
+ public Type getType(Object object) {
+ return typeMapper.getCorrespondingType(object.getClass());
+ }
+
+ public Type getCorrespondingType(Class clazz) {
+ return typeMapper.getCorrespondingType(clazz);
+ }
+
+ public TypeMapper getTypeMapper() {
+ return typeMapper;
+ }
+
+ public Object create(Type type) {
+ Constructor<?> constructor = typeMapper.getConstructor(type);
+ if (constructor == null){
+ return null;
+ }
+ try {
+ return constructor.newInstance((Object[]) null);
+ } catch (InstantiationException e) {
+ throw new IllegalArgumentException("Unable to create new instance of bean class corresponding to " + type.getName(), e);
+ } catch (IllegalAccessException e) {
+ throw new IllegalArgumentException("Unable to create new instance of bean class corresponding to " + type.getName(), e);
+ } catch (InvocationTargetException e) {
+ throw new IllegalArgumentException("Unable to create new instance of bean class corresponding to " + type.getName(), e);
+ }
+ }
+
+ public Object newProxy(Type type, Object identity) {
+ return null;
+ }
+
+ public boolean isProxy(Object instance) {
+ return false;
+ }
+
+
+ public Collection<Object> getProperties(Collection<Object> objects, Property bulkProperty, SnapshotDefinition def) {
+ Iterator<Object> it = objects.iterator();
+ if (!it.hasNext()) {
+ return Collections.emptyList();
+ }
+ return (Collection<Object>) typeMapper.property(it.next().getClass(), bulkProperty).getValues(objects, bulkProperty, def, this);
+ }
+
+ public boolean isBulkProperty(Class clazz, Property property) {
+ PropertyAccessor propertyAccessor = typeMapper.property(clazz, property);
+ if (propertyAccessor == null){
+ throw new RuntimeException("no property accessor for sdo property " + property);
+ }
+ return propertyAccessor.isBulkAccessor();
+ }
+
+
+ private final TypeMapper typeMapper;
+
+}
diff --git a/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/mapper/ManyValuedBeanPropertyAccessor.java b/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/mapper/ManyValuedBeanPropertyAccessor.java
new file mode 100644
index 0000000000..1f35cd26b1
--- /dev/null
+++ b/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/mapper/ManyValuedBeanPropertyAccessor.java
@@ -0,0 +1,25 @@
+package com.agfa.hap.sdo.mapper;
+
+import java.util.Collection;
+
+import com.agfa.hap.sdo.DataMapper;
+import com.agfa.hap.sdo.Property;
+
+public class ManyValuedBeanPropertyAccessor extends BeanPropertyAccessor {
+
+ /**
+ * Sets the value of this property for the specified Object.
+ * @throws IllegalArgumentException
+ */
+ public void setValue(Object instance, Property property, Object newValue, DataMapper dataMapper) {
+ Collection<Object> coll = (Collection<Object>) super.getValue(instance, property, dataMapper);
+ if (property.getOpposite() == null || !coll.contains(newValue)) {
+ coll.add(newValue);
+ }
+ }
+
+ public void initialize(Object instance, Collection<Object> value) {
+ super.setValue(instance, null, value, null);
+ }
+
+}
diff --git a/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/mapper/ManyValuedNullPropertyAccessor.java b/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/mapper/ManyValuedNullPropertyAccessor.java
new file mode 100644
index 0000000000..1c5b28d5e1
--- /dev/null
+++ b/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/mapper/ManyValuedNullPropertyAccessor.java
@@ -0,0 +1,23 @@
+package com.agfa.hap.sdo.mapper;
+
+import java.util.Collection;
+
+import com.agfa.hap.sdo.DataMapper;
+import com.agfa.hap.sdo.Property;
+
+public class ManyValuedNullPropertyAccessor extends ManyValuedBeanPropertyAccessor{
+
+ @Override
+ public Object getValue(Object instance, Property property, DataMapper dataMapper) {
+ return null;
+ }
+
+ @Override
+ public void initialize(Object instance, Collection<Object> value) {
+ }
+
+ @Override
+ public void setValue(Object instance, Property property, Object newValue, DataMapper dataMapper) {
+ }
+
+}
diff --git a/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/mapper/NullPropertyAccessor.java b/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/mapper/NullPropertyAccessor.java
new file mode 100644
index 0000000000..3d593fded2
--- /dev/null
+++ b/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/mapper/NullPropertyAccessor.java
@@ -0,0 +1,17 @@
+package com.agfa.hap.sdo.mapper;
+
+import com.agfa.hap.sdo.DataMapper;
+import com.agfa.hap.sdo.Property;
+
+/**
+ * {@link PropertyAccessor} that allows returns null.
+ * @author AMOCZ
+ */
+public class NullPropertyAccessor extends AbstractPropertyAccessor {
+
+ public Object getValue(Object instance, Property property,
+ DataMapper dataMapper) {
+ return null;
+ }
+
+}
diff --git a/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/mapper/PartialDataObjectMapper.java b/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/mapper/PartialDataObjectMapper.java
new file mode 100644
index 0000000000..156723b5c0
--- /dev/null
+++ b/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/mapper/PartialDataObjectMapper.java
@@ -0,0 +1,77 @@
+package com.agfa.hap.sdo.mapper;
+
+import java.util.Collection;
+import java.util.Iterator;
+
+import sun.reflect.generics.reflectiveObjects.NotImplementedException;
+
+import com.agfa.hap.sdo.DataMapper;
+import com.agfa.hap.sdo.PartialDataFactory;
+import com.agfa.hap.sdo.PartialDataObject;
+import com.agfa.hap.sdo.Property;
+import com.agfa.hap.sdo.SnapshotDefinition;
+import com.agfa.hap.sdo.Type;
+
+/**
+ * Default implementation that only support object that are instances
+ * of {@link PartialDataObject}.
+ * @author AMOCZ
+ */
+public class PartialDataObjectMapper implements DataMapper<PartialDataObject> {
+
+ @SuppressWarnings("unchecked")
+ public Iterator<PartialDataObject> getObjects(PartialDataObject object, Property property) {
+ return object.getList(property).iterator();
+ }
+
+ public Object getProperty(PartialDataObject object, Property property) {
+ return object.get(property);
+ }
+
+ public Type getType(PartialDataObject object) {
+ return object.getType();
+ }
+
+ public Type getCorrespondingType(Class clazz) {
+ throw new IllegalArgumentException("No sdo type for class " + clazz.getName());
+ }
+
+ public void setProperty(PartialDataObject object, Property property, Object value) {
+ if (property.isMany()) {
+ if (property.getOpposite() != null) {
+ ((PartialDataObject) value).set(property.getOpposite(), object);
+ } else {
+ object.getList(property).add(value);
+ }
+ } else {
+ object.set(property, value);
+ }
+ }
+
+ public void setUnavailable(PartialDataObject object, Property property) {
+ object.setUnavailable(property);
+ }
+
+ public PartialDataObject create(Type type) {
+ return PartialDataFactory.INSTANCE.create(type);
+ }
+
+ public PartialDataObject newProxy(Type type, Object identity) {
+ return PartialDataFactory.INSTANCE.createProxy(type, identity);
+ }
+
+ public boolean isProxy(PartialDataObject object) {
+ return object.isProxy();
+ }
+
+ public Collection<PartialDataObject> getProperties(Collection<PartialDataObject> object, Property bulkProperty, SnapshotDefinition def) {
+ throw new NotImplementedException();
+ }
+
+ public boolean isBulkProperty(Class clazz, Property property) {
+ return false;
+ }
+
+
+
+}
diff --git a/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/mapper/PropertyAccessor.java b/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/mapper/PropertyAccessor.java
new file mode 100644
index 0000000000..26f12c10cf
--- /dev/null
+++ b/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/mapper/PropertyAccessor.java
@@ -0,0 +1,26 @@
+package com.agfa.hap.sdo.mapper;
+
+import java.util.Collection;
+
+import com.agfa.hap.sdo.DataMapper;
+import com.agfa.hap.sdo.Property;
+import com.agfa.hap.sdo.SnapshotDefinition;
+
+/**
+ * Interface that allows property values to be accessed in a generic way from any instance.
+ * Typically propertyaccessors will be registered in a sdo-propertyaccessors.properties file which is read by ExtendablePropertyAccessorBuilder
+ *
+ * To allow for efficient retrieval, some properties are always accessed in bulk. A typical
+ * example is a property for which a query needs to be made.
+ *
+ * @author AMOCZ
+ *
+ */
+public interface PropertyAccessor {
+
+ Object getValue(Object instance, Property property, DataMapper dataMapper);
+ void setValue(Object instance, Property property, Object value, DataMapper dataMapper);
+
+ boolean isBulkAccessor();
+ Collection<?> getValues(Collection<?> instances, Property property, SnapshotDefinition def, DataMapper dataMapper);
+}
diff --git a/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/mapper/PropertyAccessorBuilder.java b/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/mapper/PropertyAccessorBuilder.java
new file mode 100644
index 0000000000..428768ecc1
--- /dev/null
+++ b/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/mapper/PropertyAccessorBuilder.java
@@ -0,0 +1,53 @@
+package com.agfa.hap.sdo.mapper;
+
+import com.agfa.hap.sdo.Property;
+import com.agfa.hap.sdo.Type;
+
+/**
+ * Factory class to build a map containing all {@link PropertyAccessor} instances for a given class.
+ * @author AMOCZ
+ */
+public abstract class PropertyAccessorBuilder {
+
+ public PropertyAccessorBuilder() {
+ this(null);
+ }
+
+ public PropertyAccessorBuilder(PropertyAccessorBuilder next) {
+ this.next = next;
+ }
+
+ public PropertyAccessor[] buildMap(Class cls, Type type) {
+ if (!accepts(cls, type)) {
+ return next == null ? null : next.buildMap(cls, type);
+ }
+ PropertyAccessor[] result = new PropertyAccessor[type.getProperties().size()];
+ for (Property prop : type.getProperties()) {
+ PropertyAccessor property = createPropertyAccessor(cls, prop);
+ result[prop.getIndex()] = property;
+ }
+ return result;
+ }
+
+ /**
+ * @return If this propertyAccessorBuilder can create propertyAccessors for this class/type
+ * combination.
+ */
+ protected abstract boolean accepts(Class cls, Type type);
+
+ protected abstract PropertyAccessor createPropertyAccessor(Class cls, Property property);
+
+ protected PropertyAccessorBuilder next;
+
+ public PropertyAccessor createPropertyAccessorOrDelegate(Class cls, Property property){
+ if (this.accepts(cls, property.getContainingType())){
+ return this.createPropertyAccessor(cls, property);
+ } else {
+ if (next == null){
+ return null;
+ }
+ return next.createPropertyAccessor(cls, property);
+ }
+ }
+
+}
diff --git a/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/mapper/TruePropertyAccessor.java b/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/mapper/TruePropertyAccessor.java
new file mode 100644
index 0000000000..ad555078e5
--- /dev/null
+++ b/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/mapper/TruePropertyAccessor.java
@@ -0,0 +1,13 @@
+package com.agfa.hap.sdo.mapper;
+
+import com.agfa.hap.sdo.DataMapper;
+import com.agfa.hap.sdo.Property;
+
+public class TruePropertyAccessor extends AbstractPropertyAccessor {
+
+ public Object getValue(Object instance, Property property,
+ DataMapper dataMapper) {
+ return true;
+ }
+
+}
diff --git a/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/mapper/TypeMapper.java b/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/mapper/TypeMapper.java
new file mode 100644
index 0000000000..3340fa4cb1
--- /dev/null
+++ b/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/mapper/TypeMapper.java
@@ -0,0 +1,81 @@
+package com.agfa.hap.sdo.mapper;
+
+import java.lang.reflect.Constructor;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import com.agfa.hap.sdo.Property;
+import com.agfa.hap.sdo.Type;
+import com.agfa.hap.sdo.helper.TypeHelper;
+
+/**
+ * Maps classes onto sdo Type instances.
+ * <P/>
+ * This implementation only on classes that are registered
+ * ({@see {@link #register(Class, String, String)}).
+ *
+ * <P />
+ * This class is thread safe and can be concurrently accessed by multiple threads.
+ * @author AMOCZ
+ */
+public class TypeMapper {
+
+ public TypeMapper() {
+ this(new BeanPropertyAccessorBuilder());
+ }
+
+ public TypeMapper(PropertyAccessorBuilder builder) {
+ this.propertyAccessorBuilder = builder;
+ }
+
+ /**
+ * Locates a property accessor
+ */
+ public PropertyAccessor property(Class cls, Property property) {
+ PropertyAccessor[] properties = buildMap(cls);
+ PropertyAccessor propertyAccessor = properties[property.getIndex()];
+ if (propertyAccessor == null) {
+ throw new IllegalArgumentException("Can't access property " + property.getName() + " on " + cls.getName() + ".");
+ }
+ return propertyAccessor;
+ }
+
+ private PropertyAccessor[] buildMap(Class cls) {
+ String clsName = cls.getName();
+ PropertyAccessor[] props = keyedByPropertyNameCache.get(clsName);
+ if (props == null) {
+ // we don't care too much if it is computed more than once under some race conditions
+ props = propertyAccessorBuilder.buildMap(cls, getCorrespondingType(cls));
+ keyedByPropertyNameCache.put(clsName, props);
+ }
+ return props;
+ }
+
+ public Type getCorrespondingType(Class clazz) {
+ String[] exc = exceptions.get(clazz);
+ if (exc != null) {
+ return TypeHelper.INSTANCE.getType(exc[0], exc[1]);
+ }
+ return null;
+ }
+
+ public void register(Class<?> clazz, String uri, String typeName) {
+ exceptions.put(clazz, new String[] { uri, typeName });
+ try {
+ factories.put(typeName, clazz.getConstructor((Class[]) null));
+ } catch (SecurityException e) {
+ } catch (NoSuchMethodException e) {
+ }
+ }
+
+ public Constructor<?> getConstructor(Type type) {
+ return factories.get(type.getName());
+ }
+
+ private final Map<String, Constructor<?>> factories = new ConcurrentHashMap<String, Constructor<?>>();
+ private final Map<Class<?>, String[]> exceptions = new ConcurrentHashMap<Class<?>, String[]>();
+ private final Map<String, PropertyAccessor[]> keyedByPropertyNameCache =
+ new ConcurrentHashMap<String, PropertyAccessor[]>();
+ protected final PropertyAccessorBuilder propertyAccessorBuilder;
+
+}
diff --git a/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/model/CatalogIdentity.java b/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/model/CatalogIdentity.java
new file mode 100644
index 0000000000..3eb9b4d2ad
--- /dev/null
+++ b/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/model/CatalogIdentity.java
@@ -0,0 +1,51 @@
+package com.agfa.hap.sdo.model;
+
+import java.util.StringTokenizer;
+
+public class CatalogIdentity extends Identity {
+
+ private static final long serialVersionUID = -1631933616618962179L;
+ private static final String SEPARATOR = "!";
+
+ public CatalogIdentity(String databaseId, Long alid, String catalogName, Long dbUID, String lid) {
+ super(databaseId, new Object[]{alid, catalogName, dbUID, lid});
+ }
+
+ public Long getAlid(){
+ return (Long)((Object[])getPrimaryKey())[0];
+ }
+
+ public String getCatalogName(){
+ return (String)((Object[])getPrimaryKey())[1];
+ }
+
+ public Long getDbUID(){
+ return (Long)((Object[])getPrimaryKey())[2];
+ }
+
+ public String getLid(){
+ return (String)((Object[])getPrimaryKey())[3];
+ }
+
+ public static CatalogIdentity fromString(String str) {
+ StringTokenizer tokenizer = new StringTokenizer(str, SEPARATOR);
+ String databaseId = tokenizer.nextToken();
+ String alid = tokenizer.nextToken();
+ String catalogName = tokenizer.nextToken();
+ String dbUID = tokenizer.nextToken();
+ String lid = tokenizer.nextToken();
+ return new CatalogIdentity(databaseId,
+ "null".equals(alid) ? null : Long.valueOf(alid),
+ catalogName,
+ "null".equals(dbUID) ? null : Long.valueOf(dbUID), lid);
+ }
+
+ public static String toString(CatalogIdentity instance) {
+ return instance.getDatabaseId()
+ + SEPARATOR + instance.getAlid()
+ + SEPARATOR + instance.getCatalogName()
+ + SEPARATOR + instance.getDbUID()
+ + SEPARATOR + instance.getLid();
+ }
+
+}
diff --git a/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/model/Identity.java b/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/model/Identity.java
new file mode 100644
index 0000000000..5b1c4bd26c
--- /dev/null
+++ b/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/model/Identity.java
@@ -0,0 +1,60 @@
+package com.agfa.hap.sdo.model;
+
+import java.io.Serializable;
+
+public abstract class Identity implements Serializable {
+
+ private final Serializable primaryKey;
+ private final String databaseId;
+
+ // temporary until we finally decide on how to do the databaseId
+ public Identity(Serializable primaryKey) {
+ this(null, primaryKey);
+ }
+
+ public Identity(String databaseId, Serializable primaryKey) {
+ this.databaseId = databaseId;
+ this.primaryKey = primaryKey;
+ }
+
+ public String getDatabaseId() {
+ return databaseId;
+ }
+
+ public Serializable getPrimaryKey() {
+ return primaryKey;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == null) {
+ return false;
+ }
+ if (other.getClass() != this.getClass()) {
+ return false;
+ }
+ Identity otherIdentity = (Identity) other;
+ return isEquals(primaryKey, otherIdentity.primaryKey) && isEquals(databaseId, otherIdentity.databaseId);
+ }
+
+ @Override
+ public int hashCode() {
+ return primaryKey.hashCode();
+ }
+
+ @Override
+ public String toString() {
+ if (databaseId == null) {
+ return primaryKey.toString();
+ }
+ return databaseId + "/" + primaryKey.toString();
+ }
+
+ private static boolean isEquals(Object one, Object two) {
+ if (one == null) {
+ return two == null;
+ }
+ return one.equals(two);
+ }
+
+}
diff --git a/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/model/LongIdentity.java b/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/model/LongIdentity.java
new file mode 100644
index 0000000000..0d552b7e54
--- /dev/null
+++ b/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/model/LongIdentity.java
@@ -0,0 +1,24 @@
+package com.agfa.hap.sdo.model;
+
+
+public class LongIdentity extends Identity {
+
+ public LongIdentity(String databaseId, Long primaryKey) {
+ super(databaseId, primaryKey);
+ }
+
+ @Override
+ public Long getPrimaryKey() {
+ return (Long) super.getPrimaryKey();
+ }
+
+ public static LongIdentity fromString(String str) {
+ int lastSlashIndex = str.lastIndexOf('/');
+ String databaseId = null;
+ if (lastSlashIndex >= 0) {
+ databaseId = str.substring(0, lastSlashIndex);
+ str = str.substring(lastSlashIndex+1, str.length());
+ }
+ return new LongIdentity(databaseId, Long.parseLong(str));
+ }
+}
diff --git a/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/model/ObjectReference.java b/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/model/ObjectReference.java
new file mode 100644
index 0000000000..66eca80469
--- /dev/null
+++ b/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/model/ObjectReference.java
@@ -0,0 +1,105 @@
+package com.agfa.hap.sdo.model;
+
+import java.io.IOException;
+import java.io.Serializable;
+import java.net.URI;
+import java.net.URISyntaxException;
+
+import com.agfa.hap.sdo.PartialDataFactory;
+import com.agfa.hap.sdo.PartialDataObject;
+
+import commonj.sdo.Type;
+import commonj.sdo.helper.TypeHelper;
+
+/**
+ * Encapsulate a reference to a server-side object. Can be converted to and from a {@link URI}.
+ *
+ * @author AMOCZ
+ */
+public class ObjectReference implements Serializable {
+
+ private static final long serialVersionUID = 8626498796265240806L;
+
+ private transient Type type;
+ private transient Identity id;
+
+ public ObjectReference(String uri) {
+ try {
+ this.initialize(new URI(uri));
+ } catch (URISyntaxException e) {
+ throw new IllegalArgumentException("Invalid uri " + uri);
+ }
+ }
+
+ public ObjectReference(URI uri) {
+ initialize(uri);
+ }
+
+ private void initialize(URI uri) {
+ String frag = uri.getFragment();
+ this.id = LongIdentity.fromString(frag);
+ String ssp = uri.getSchemeSpecificPart();
+ int index = ssp.indexOf('/');
+ this.type = TypeHelper.INSTANCE.getType(ssp.substring(0, index), ssp.substring(index+1));
+ }
+
+ public ObjectReference(Type type, Identity id) {
+ if (id == null){
+ throw new IllegalArgumentException("objectreference should have an id");
+ }
+ this.type = type;
+ this.id = id;
+ }
+
+ public Identity getId() {
+ return id;
+ }
+
+ public Type getType() {
+ return type;
+ }
+
+ public URI asURI() {
+ try {
+ return new URI("ris", type.getURI() + "/" + type.getName(), id.toString());
+ } catch (URISyntaxException e) {
+ throw new IllegalArgumentException("Couldn't create URI", e);
+ }
+ }
+
+ private void writeObject(java.io.ObjectOutputStream out) throws IOException {
+ out.writeUTF(type.getURI());
+ out.writeUTF(type.getName());
+ out.writeObject(id);
+ }
+
+ private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException {
+ String uri = in.readUTF();
+ String name = in.readUTF();
+ type = TypeHelper.INSTANCE.getType(uri, name);
+ id = (Identity) in.readObject();
+ }
+
+ public PartialDataObject createProxy() {
+ return PartialDataFactory.INSTANCE.createProxy(type, id);
+ }
+
+ public String toString() {
+ return type.getURI() + "/" + type.getName() + "#" + id;
+ }
+
+ public boolean equals(Object other) {
+ if (other == null) {
+ return false;
+ }
+ if (other.getClass() != this.getClass()) {
+ return false;
+ }
+ ObjectReference otherRef = (ObjectReference) other;
+ return otherRef.id.equals(id) && otherRef.type.equals(type);
+ }
+
+ public int hashCode() {
+ return id.hashCode();
+ }
+}
diff --git a/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/model/StringIdentity.java b/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/model/StringIdentity.java
new file mode 100644
index 0000000000..27a5be4951
--- /dev/null
+++ b/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/model/StringIdentity.java
@@ -0,0 +1,14 @@
+package com.agfa.hap.sdo.model;
+
+public class StringIdentity extends Identity {
+
+ public StringIdentity(String databaseId, String id){
+ super(databaseId, id);
+ }
+
+ @Override
+ public String getPrimaryKey() {
+ return (String) super.getPrimaryKey();
+ }
+
+}
diff --git a/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/osgi/BundleClassHolder.java b/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/osgi/BundleClassHolder.java
new file mode 100644
index 0000000000..7f89908b05
--- /dev/null
+++ b/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/osgi/BundleClassHolder.java
@@ -0,0 +1,28 @@
+package com.agfa.hap.sdo.osgi;
+
+import org.osgi.framework.Bundle;
+
+import com.agfa.hap.sdo.util.ClassForNameClassHolder;
+
+public class BundleClassHolder extends ClassForNameClassHolder {
+
+ final Bundle bundle;
+
+ public BundleClassHolder(Bundle bundle, String name) {
+ super(name);
+ this.bundle = bundle;
+ }
+
+ @Override
+ public Class resolveClass() {
+ try {
+ return bundle.loadClass(getClassName());
+ } catch (ClassNotFoundException e) {
+ throw new IllegalArgumentException("Could not find class " + getClassName() + " in bundle " + bundle.getBundleId(), e);
+ }
+ }
+
+
+
+
+}
diff --git a/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/osgi/OsgiBasedImplementationClassRepository.java b/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/osgi/OsgiBasedImplementationClassRepository.java
new file mode 100644
index 0000000000..6ec6583d6d
--- /dev/null
+++ b/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/osgi/OsgiBasedImplementationClassRepository.java
@@ -0,0 +1,84 @@
+package com.agfa.hap.sdo.osgi;
+
+import java.io.IOException;
+import java.net.URL;
+import java.util.Enumeration;
+
+import org.eclipse.core.runtime.IExtension;
+import org.eclipse.core.runtime.IExtensionDelta;
+import org.eclipse.core.runtime.IExtensionPoint;
+import org.eclipse.core.runtime.IRegistryChangeEvent;
+import org.eclipse.core.runtime.IRegistryChangeListener;
+import org.eclipse.core.runtime.Platform;
+import org.osgi.framework.Bundle;
+
+import com.agfa.hap.sdo.implementation.ImplementationClassRepository;
+import com.agfa.hap.sdo.util.ClassHolder;
+
+/**
+ * Registers all implementation classes for the bundles that extend the
+ * com.agfa.hap.sdo.implementationclasses extension point.
+ *
+ * @author AMOCZ
+ */
+public class OsgiBasedImplementationClassRepository extends ImplementationClassRepository {
+
+ private static final String IMPLEMENTATIONCLASSES_EXTENSION = "com.agfa.hap.sdo.implementationclasses";
+
+ @Override
+ protected void initialize() {
+ processExtensions();
+ Platform.getExtensionRegistry().addRegistryChangeListener(new IRegistryChangeListener() {
+
+ public void registryChanged(IRegistryChangeEvent event) {
+ IExtensionDelta[] deltas = event.getExtensionDeltas("com.agfa.hap.sdo", IMPLEMENTATIONCLASSES_EXTENSION);
+ for (IExtensionDelta delta : deltas) {
+ if (delta.getKind() == IExtensionDelta.ADDED) {
+ initializeImplementationClasses(delta.getExtension());
+ }
+ }
+ }
+
+ }, "com.agfa.hap.sdo");
+ }
+
+ protected void processExtensions() {
+ IExtensionPoint exp = Platform.getExtensionRegistry().getExtensionPoint(IMPLEMENTATIONCLASSES_EXTENSION);
+ for (IExtension extension : exp.getExtensions()) {
+ initializeImplementationClasses(extension);
+ }
+ }
+
+ protected void initializeImplementationClasses(IExtension extension) {
+ final Bundle bundle = Platform.getBundle(extension.getContributor().getName());
+ try {
+ URL url = bundle.getEntry(IMPLEMENTATIONCLASS_RESOURCEFILE);
+ if (url != null) {
+ // this works outside of eclipse, with a jar build by maven
+ addImplementationClasses(bundle, url);
+ return;
+ }
+ Enumeration<URL> urls = bundle.getResources(IMPLEMENTATIONCLASS_RESOURCEFILE);
+ if (urls == null) {
+ return;
+ }
+ while (urls.hasMoreElements()){
+ addImplementationClasses(bundle, urls.nextElement());
+ }
+ } catch (IOException e) {
+ getLogger().warn("Could not read implementation classes.", e);
+ }
+ }
+
+ protected void addImplementationClasses(final Bundle bundle, URL url) {
+ addImplementationClasses(url, new ClassHolderFactory() {
+
+ public ClassHolder createClassHolder(String className) {
+ return new BundleClassHolder(bundle, className);
+ }
+
+ });
+ }
+
+
+}
diff --git a/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/osgi/SdoActivator.java b/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/osgi/SdoActivator.java
new file mode 100644
index 0000000000..67440b942e
--- /dev/null
+++ b/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/osgi/SdoActivator.java
@@ -0,0 +1,90 @@
+package com.agfa.hap.sdo.osgi;
+
+import org.eclipse.core.runtime.IExtension;
+import org.eclipse.core.runtime.IExtensionPoint;
+import org.eclipse.core.runtime.Platform;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceReference;
+import org.osgi.framework.ServiceRegistration;
+
+import com.agfa.hap.sdo.impl.SdoImplementationFactory;
+import com.agfa.hap.sdo.implementation.ImplementationClassRepository;
+
+public class SdoActivator implements BundleActivator {
+
+ private static SdoActivator instance;
+
+ public static SdoActivator getInstance() {
+ return instance;
+ }
+
+ private BundleContext context;
+
+ public void start(BundleContext context) throws Exception {
+ instance = this;
+ this.context = context;
+ findHelperProvider();
+ initializeImplementationClassesRepository();
+ }
+
+ protected void findHelperProvider() {
+ boolean firstTime = true;
+ IExtensionPoint exp = Platform.getExtensionRegistry().getExtensionPoint("com.agfa.hap.sdo.helperprovider");
+ if (exp == null) {
+ return;
+ }
+ for (IExtension extension : exp.getExtensions()) {
+ if (!firstTime) {
+ throw new IllegalArgumentException("There should be only one extension for com.agfa.hap.sdo.helperprovider!");
+ }
+ firstTime = false;
+ Bundle bundle = Platform.getBundle(extension.getContributor().getName());
+ String impName = extension.getConfigurationElements()[0].getAttribute("class");
+ try {
+ SdoImplementationFactory.setImplementationClass(bundle.loadClass(impName));
+ } catch (ClassNotFoundException e) {
+ throw new IllegalArgumentException("Can't find implementation class repository implementation: " + impName, e);
+ }
+ }
+ }
+
+ protected void initializeImplementationClassesRepository() {
+ ImplementationClassRepository.setInstance(new OsgiBasedImplementationClassRepository());
+ }
+
+ public BundleContext getContext() {
+ return context;
+ }
+
+ /**
+ * Empty default implementation
+ */
+ public void stop(BundleContext context) throws Exception {
+ }
+
+ /**
+ * returns an implementation of the given interface
+ *
+ * @param <T>
+ * @param interfaceClass
+ * @return
+ */
+ @SuppressWarnings("unchecked")
+ public <T> T getService(Class<T> interfaceClass) {
+ ServiceReference sr = context.getServiceReference(interfaceClass
+ .getName());
+ if (sr == null) {
+ return null;
+ }
+
+ T service = (T) context.getService(sr);
+ return service;
+ }
+
+ public <T> ServiceRegistration registerService(Class<T> clazz, T service) {
+ ServiceRegistration sr = context.registerService(clazz.getName(), service, null);
+ return sr;
+ }
+}
diff --git a/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/util/ClassForNameClassHolder.java b/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/util/ClassForNameClassHolder.java
new file mode 100644
index 0000000000..1048b3dc69
--- /dev/null
+++ b/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/util/ClassForNameClassHolder.java
@@ -0,0 +1,24 @@
+package com.agfa.hap.sdo.util;
+
+public class ClassForNameClassHolder implements ClassHolder {
+
+ private final String name;
+
+ public ClassForNameClassHolder(final String name) {
+ super();
+ this.name = name;
+ }
+
+ public Class resolveClass() {
+ try {
+ return Class.forName(name);
+ } catch (ClassNotFoundException e) {
+ throw new IllegalArgumentException("Could not resolve class " + name, e);
+ }
+ }
+
+ public String getClassName() {
+ return name;
+ }
+
+}
diff --git a/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/util/ClassHolder.java b/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/util/ClassHolder.java
new file mode 100644
index 0000000000..24d8a1c580
--- /dev/null
+++ b/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/util/ClassHolder.java
@@ -0,0 +1,14 @@
+package com.agfa.hap.sdo.util;
+
+/**
+ * Object that is able to produce a class when asked. The goal of this class is to
+ * delay classloading as long as possible.
+ * @author AMOCZ
+ */
+public interface ClassHolder {
+
+ Class resolveClass();
+
+ String getClassName();
+
+}
diff --git a/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/util/ClassLoaderBasedClassHolder.java b/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/util/ClassLoaderBasedClassHolder.java
new file mode 100644
index 0000000000..62c72f5f55
--- /dev/null
+++ b/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/util/ClassLoaderBasedClassHolder.java
@@ -0,0 +1,24 @@
+package com.agfa.hap.sdo.util;
+
+public class ClassLoaderBasedClassHolder extends ClassForNameClassHolder {
+
+ private final ClassLoader classLoader;
+
+ public ClassLoaderBasedClassHolder(final String name, final ClassLoader classLoader) {
+ super(name);
+ this.classLoader = classLoader;
+ }
+
+ @Override
+ public Class resolveClass() {
+ try {
+ return classLoader.loadClass(getClassName());
+ } catch (ClassNotFoundException e) {
+ throw new IllegalArgumentException("Could not resolve class " + getClassName(), e);
+ }
+ }
+
+
+
+
+}
diff --git a/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/util/ResultSet.java b/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/util/ResultSet.java
new file mode 100644
index 0000000000..df491c13de
--- /dev/null
+++ b/sandbox/kgoodson/agfasdo/com.agfa.hap.sdo/src/main/java/com/agfa/hap/sdo/util/ResultSet.java
@@ -0,0 +1,133 @@
+package com.agfa.hap.sdo.util;
+
+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 com.agfa.hap.sdo.PartialDataObject;
+
+import commonj.sdo.DataObject;
+import commonj.sdo.Property;
+import commonj.sdo.Type;
+
+/**
+ * This class converts a list of dataobjects into a ResultSet.
+ * In case of many-valued properties, appropriate cartesian products are made.
+ *
+ * Example: Suppose for a list of serviceRequests we want a ResultSet for the
+ * properties requestedProcedures.id, patient.name. In case we have three service
+ * requests (sr?) each with a number of requested procedures (reqProc?), we get
+ * the following "result set".
+ *
+ * sr1 pat1.name reqProc1.id
+ * sr1 pat1.name reqProc2.id
+ * sr2 pat2.name reqProc3.id
+ * sr2 pat2.name reqProc4.id
+ * sr3 null reqProc5.id
+ *
+ * The order in which the rows are returned is not defined.
+ *
+ * This class is useful to use a snapshot as input for a Jasper Reports data source.
+ * @author AMOCZ
+ */
+public class ResultSet {
+
+ private final static String ROOT = "";
+
+ private final List<Map<String, Object>> data;
+
+ private final Type rootType;
+
+ public ResultSet(Collection<? extends DataObject> data, List<String> propertyPaths, Type rootType) {
+ this.data = new ArrayList<Map<String,Object>>();
+ this.rootType = rootType;
+ addData(data);
+ for (String propertyPath : propertyPaths) {
+ addProperty(propertyPath);
+ }
+ }
+
+ public ResultSet(Collection<? extends DataObject> data, String selectClause, Type rootType) {
+ this.data = new ArrayList<Map<String,Object>>();
+ this.rootType = rootType;
+ addData(data);
+ addProperties(selectClause);
+ }
+
+ protected void addProperties(String selectClause) {
+ String[] props = selectClause.split("\\s*,\\s*");
+ for (String prop : props) {
+ addProperty(prop);
+ }
+ }
+
+ public int getSize() {
+ return data.size();
+ }
+
+ public Object getValue(int row, String propertyPath) {
+ return data.get(row).get(propertyPath);
+ }
+
+ protected void addData(Collection<? extends DataObject> dos) {
+ for (DataObject d:dos) {
+ Map<String, Object> map = new HashMap<String, Object>();
+ map.put(ROOT, d);
+ data.add(map);
+ }
+ }
+
+ protected void addProperty(String propertyPath) {
+ String rootPath = ROOT;
+ Type type = rootType;
+ while (propertyPath.length() > 0) {
+ int index = propertyPath.indexOf('.');
+ String propertyName = propertyPath;
+ if (index >= 0) {
+ propertyName = propertyPath.substring(0, index);
+ propertyPath = propertyPath.substring(index+1);
+ } else {
+ propertyPath = "";
+ }
+ Property property = type.getProperty(propertyName);
+ rootPath = addProperty(rootPath, property);
+ type = property.getType();
+ }
+ }
+
+ protected String addProperty(String rootPath, Property property) {
+ String propertyPath = rootPath.length() == 0 ? property.getName() : rootPath + "." + property.getName();
+ int length = data.size();
+ for (int i = 0 ; i < length; i++) {
+ Map<String, Object> values = data.get(i);
+ if (i == 0 && values.containsKey(propertyPath)) {
+ return propertyPath;
+ }
+ PartialDataObject value = (PartialDataObject) values.get(rootPath);
+ if (value == null) {
+ continue;
+ }
+ if (property.isMany()) {
+ boolean firstTime = true;
+ Iterator<PartialDataObject> it = value.getList(property).iterator();
+ while (it.hasNext()) {
+ if (firstTime) {
+ values.put(propertyPath, it.next());
+ firstTime = false;
+ } else {
+ Map<String, Object> newValues = new HashMap<String, Object>(values);
+ newValues.put(propertyPath, it.next());
+ data.add(newValues);
+ }
+ }
+ } else {
+ values.put(propertyPath, value.get(property));
+ }
+ }
+ return propertyPath;
+ }
+
+}