You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@isis.apache.org by da...@apache.org on 2011/07/13 07:46:14 UTC

svn commit: r1145867 - in /incubator/isis/trunk/framework: core/metamodel/src/main/java/org/apache/isis/core/metamodel/spec/feature/ core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/ core/metamodel/src/main/java/org/apache/isis/co...

Author: danhaywood
Date: Wed Jul 13 05:46:13 2011
New Revision: 1145867

URL: http://svn.apache.org/viewvc?rev=1145867&view=rev
Log:
json viewer: implemented basic versions of the remaining object resource implementations (not tested yet); introduced concept of CollectionSemantics (provided by CollectionTypeRegistry) in order to verify put/post against set/list

Modified:
    incubator/isis/trunk/framework/core/metamodel/src/main/java/org/apache/isis/core/metamodel/spec/feature/ObjectMemberContext.java
    incubator/isis/trunk/framework/core/metamodel/src/main/java/org/apache/isis/core/metamodel/spec/feature/OneToManyFeature.java
    incubator/isis/trunk/framework/core/metamodel/src/main/java/org/apache/isis/core/metamodel/spec/feature/OneToOneAssociation.java
    incubator/isis/trunk/framework/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/ObjectReflectorDefault.java
    incubator/isis/trunk/framework/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/collectiontyperegistry/CollectionTypeRegistry.java
    incubator/isis/trunk/framework/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/collectiontyperegistry/CollectionTypeRegistryDefault.java
    incubator/isis/trunk/framework/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/specimpl/ObjectMemberAbstract.java
    incubator/isis/trunk/framework/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/specimpl/OneToManyAssociationImpl.java
    incubator/isis/trunk/framework/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/specimpl/OneToOneAssociationImpl.java
    incubator/isis/trunk/framework/core/metamodel/src/test/java/org/apache/isis/core/metamodel/specloader/specimpl/ObjectAssociationAbstractTest.java
    incubator/isis/trunk/framework/runtimes/dflt/runtime/src/test/java/org/apache/isis/runtimes/dflt/runtime/system/ObjectActionImplTest.java
    incubator/isis/trunk/framework/runtimes/dflt/runtime/src/test/java/org/apache/isis/runtimes/dflt/runtime/system/ObjectMemberAbstractTest.java
    incubator/isis/trunk/framework/runtimes/dflt/runtime/src/test/java/org/apache/isis/runtimes/dflt/runtime/system/OneToManyAssociationImplTest.java
    incubator/isis/trunk/framework/viewer/json/applib/src/main/java/org/apache/isis/viewer/json/applib/resources/DomainObjectResource.java
    incubator/isis/trunk/framework/viewer/json/viewer/src/main/java/org/apache/isis/viewer/json/viewer/resources/objects/DomainObjectResourceImpl.java

Modified: incubator/isis/trunk/framework/core/metamodel/src/main/java/org/apache/isis/core/metamodel/spec/feature/ObjectMemberContext.java
URL: http://svn.apache.org/viewvc/incubator/isis/trunk/framework/core/metamodel/src/main/java/org/apache/isis/core/metamodel/spec/feature/ObjectMemberContext.java?rev=1145867&r1=1145866&r2=1145867&view=diff
==============================================================================
--- incubator/isis/trunk/framework/core/metamodel/src/main/java/org/apache/isis/core/metamodel/spec/feature/ObjectMemberContext.java (original)
+++ incubator/isis/trunk/framework/core/metamodel/src/main/java/org/apache/isis/core/metamodel/spec/feature/ObjectMemberContext.java Wed Jul 13 05:46:13 2011
@@ -20,6 +20,7 @@ import org.apache.isis.core.commons.auth
 import org.apache.isis.core.metamodel.adapter.QuerySubmitter;
 import org.apache.isis.core.metamodel.adapter.map.AdapterMap;
 import org.apache.isis.core.metamodel.spec.SpecificationLookup;
+import org.apache.isis.core.metamodel.specloader.collectiontyperegistry.CollectionTypeRegistry;
 
 public class ObjectMemberContext {
 
@@ -27,14 +28,20 @@ public class ObjectMemberContext {
     private final SpecificationLookup specificationLookup;
     private final AdapterMap adapterManager;
     private final QuerySubmitter querySubmitter;
+    private final CollectionTypeRegistry collectionTypeRegistry;
 
-    public ObjectMemberContext(final AuthenticationSessionProvider authenticationSessionProvider,
-        final SpecificationLookup specificationLookup, final AdapterMap adapterManager,
-        final QuerySubmitter querySubmitter) {
+    public ObjectMemberContext(
+        final AuthenticationSessionProvider authenticationSessionProvider,
+        final SpecificationLookup specificationLookup, 
+        final AdapterMap adapterManager,
+        final QuerySubmitter querySubmitter, 
+        final CollectionTypeRegistry collectionTypeRegistry) {
+        
         this.authenticationSessionProvider = authenticationSessionProvider;
         this.specificationLookup = specificationLookup;
         this.adapterManager = adapterManager;
         this.querySubmitter = querySubmitter;
+        this.collectionTypeRegistry = collectionTypeRegistry;
     }
 
     public AuthenticationSessionProvider getAuthenticationSessionProvider() {
@@ -52,4 +59,8 @@ public class ObjectMemberContext {
     public QuerySubmitter getQuerySubmitter() {
         return querySubmitter;
     }
+    
+    public CollectionTypeRegistry getCollectionTypeRegistry() {
+        return collectionTypeRegistry;
+    }
 }
\ No newline at end of file

Modified: incubator/isis/trunk/framework/core/metamodel/src/main/java/org/apache/isis/core/metamodel/spec/feature/OneToManyFeature.java
URL: http://svn.apache.org/viewvc/incubator/isis/trunk/framework/core/metamodel/src/main/java/org/apache/isis/core/metamodel/spec/feature/OneToManyFeature.java?rev=1145867&r1=1145866&r2=1145867&view=diff
==============================================================================
--- incubator/isis/trunk/framework/core/metamodel/src/main/java/org/apache/isis/core/metamodel/spec/feature/OneToManyFeature.java (original)
+++ incubator/isis/trunk/framework/core/metamodel/src/main/java/org/apache/isis/core/metamodel/spec/feature/OneToManyFeature.java Wed Jul 13 05:46:13 2011
@@ -19,6 +19,10 @@
 
 package org.apache.isis.core.metamodel.spec.feature;
 
+import java.util.Collection;
+import java.util.Queue;
+
+
 /**
  * Base interface for {@link OneToManyAssociation} only.
  * 
@@ -27,9 +31,31 @@ package org.apache.isis.core.metamodel.s
  * the base interface for a <tt>OneToManyActionParameter</tt>.
  * 
  * <p>
- * Is also the route upto the {@link ObjectFeature} superinterface.
+ * Is also the route up to the {@link ObjectFeature} superinterface.
  * 
  */
 public interface OneToManyFeature extends ObjectFeature {
 
+    public enum CollectionSemantics {
+        LIST,
+        ARRAY,
+        SET,
+        OTHER;
+
+        /**
+         * The corresponding class is not a subclass of {@link Collection}.
+         */
+        public boolean isArray() { return this == ARRAY; } 
+        public boolean isList() { return this == LIST; }
+        public boolean isSet() { return this == SET; }
+        /**
+         * For example, {@link Queue}, or some other 3rdparty implementation of {@link Collection}.
+         */
+        public boolean isOther() { return this == OTHER; }
+        public boolean isListOrArray() {
+            return isList() || isArray();
+        }
+    }
+
+    CollectionSemantics getCollectionSemantics();
 }

Modified: incubator/isis/trunk/framework/core/metamodel/src/main/java/org/apache/isis/core/metamodel/spec/feature/OneToOneAssociation.java
URL: http://svn.apache.org/viewvc/incubator/isis/trunk/framework/core/metamodel/src/main/java/org/apache/isis/core/metamodel/spec/feature/OneToOneAssociation.java?rev=1145867&r1=1145866&r2=1145867&view=diff
==============================================================================
--- incubator/isis/trunk/framework/core/metamodel/src/main/java/org/apache/isis/core/metamodel/spec/feature/OneToOneAssociation.java (original)
+++ incubator/isis/trunk/framework/core/metamodel/src/main/java/org/apache/isis/core/metamodel/spec/feature/OneToOneAssociation.java Wed Jul 13 05:46:13 2011
@@ -69,8 +69,10 @@ public interface OneToOneAssociation ext
      * back-links or bidirectional association). To initialise a recreated object to this logical state the
      * <code>initAssociation</code> method should be used on each of the objects.
      * 
+     * @deprecated - see {@link #set(ObjectAdapter, ObjectAdapter)}
      * @see #initAssociation(ObjectAdapter, ObjectAdapter)
      */
+    @Deprecated
     void setAssociation(ObjectAdapter inObject, ObjectAdapter associate);
 
     /**
@@ -78,7 +80,9 @@ public interface OneToOneAssociation ext
      * back-link.
      * 
      * @see #setAssociation(ObjectAdapter, ObjectAdapter)
+     * @deprecated - see {@link #set(ObjectAdapter, ObjectAdapter)}
      */
+    @Deprecated
     void clearAssociation(ObjectAdapter inObject);
 
 }

Modified: incubator/isis/trunk/framework/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/ObjectReflectorDefault.java
URL: http://svn.apache.org/viewvc/incubator/isis/trunk/framework/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/ObjectReflectorDefault.java?rev=1145867&r1=1145866&r2=1145867&view=diff
==============================================================================
--- incubator/isis/trunk/framework/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/ObjectReflectorDefault.java (original)
+++ incubator/isis/trunk/framework/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/ObjectReflectorDefault.java Wed Jul 13 05:46:13 2011
@@ -395,7 +395,7 @@ public class ObjectReflectorDefault impl
             final AdapterMap adapterMap = getRuntimeContext().getAdapterMap();
             final ObjectMemberContext objectMemberContext =
                 new ObjectMemberContext(authenticationSessionProvider, specificationLookup, adapterMap,
-                    getRuntimeContext().getQuerySubmitter());
+                    getRuntimeContext().getQuerySubmitter(), collectionTypeRegistry);
             final IntrospectionContext introspectionContext =
                 new IntrospectionContext(getClassSubstitutor(), getMemberLayoutArranger());
             final DependencyInjector dependencyInjector = getRuntimeContext().getDependencyInjector();

Modified: incubator/isis/trunk/framework/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/collectiontyperegistry/CollectionTypeRegistry.java
URL: http://svn.apache.org/viewvc/incubator/isis/trunk/framework/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/collectiontyperegistry/CollectionTypeRegistry.java?rev=1145867&r1=1145866&r2=1145867&view=diff
==============================================================================
--- incubator/isis/trunk/framework/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/collectiontyperegistry/CollectionTypeRegistry.java (original)
+++ incubator/isis/trunk/framework/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/collectiontyperegistry/CollectionTypeRegistry.java Wed Jul 13 05:46:13 2011
@@ -21,6 +21,7 @@ package org.apache.isis.core.metamodel.s
 
 import org.apache.isis.core.commons.components.ApplicationScopedComponent;
 import org.apache.isis.core.commons.components.Injectable;
+import org.apache.isis.core.metamodel.spec.feature.OneToManyFeature.CollectionSemantics;
 
 /**
  * TODO: plan is to allow new collection types to be installed dynamically, allowing the domain programmer to declare
@@ -39,4 +40,6 @@ public interface CollectionTypeRegistry 
 
     public Class<?>[] getCollectionType();
 
+    public CollectionSemantics semanticsOf(Class<?> cls);
+
 }

Modified: incubator/isis/trunk/framework/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/collectiontyperegistry/CollectionTypeRegistryDefault.java
URL: http://svn.apache.org/viewvc/incubator/isis/trunk/framework/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/collectiontyperegistry/CollectionTypeRegistryDefault.java?rev=1145867&r1=1145866&r2=1145867&view=diff
==============================================================================
--- incubator/isis/trunk/framework/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/collectiontyperegistry/CollectionTypeRegistryDefault.java (original)
+++ incubator/isis/trunk/framework/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/collectiontyperegistry/CollectionTypeRegistryDefault.java Wed Jul 13 05:46:13 2011
@@ -22,6 +22,9 @@ package org.apache.isis.core.metamodel.s
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.List;
+import java.util.Set;
+
+import org.apache.isis.core.metamodel.spec.feature.OneToManyFeature.CollectionSemantics;
 
 public class CollectionTypeRegistryDefault extends CollectionTypeRegistryAbstract {
 
@@ -61,4 +64,18 @@ public class CollectionTypeRegistryDefau
         return collectionTypesAsArray;
     }
 
+    public CollectionSemantics semanticsOf(Class<?> underlyingClass) {
+        if(!Collection.class.isAssignableFrom(underlyingClass)) {
+            return CollectionSemantics.ARRAY;
+        }
+        if(List.class.isAssignableFrom(underlyingClass)) {
+           return CollectionSemantics.LIST; 
+        }
+        if(Set.class.isAssignableFrom(underlyingClass)) {
+            return CollectionSemantics.SET; 
+        }
+        return CollectionSemantics.OTHER;
+    }
+
+
 }

Modified: incubator/isis/trunk/framework/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/specimpl/ObjectMemberAbstract.java
URL: http://svn.apache.org/viewvc/incubator/isis/trunk/framework/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/specimpl/ObjectMemberAbstract.java?rev=1145867&r1=1145866&r2=1145867&view=diff
==============================================================================
--- incubator/isis/trunk/framework/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/specimpl/ObjectMemberAbstract.java (original)
+++ incubator/isis/trunk/framework/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/specimpl/ObjectMemberAbstract.java Wed Jul 13 05:46:13 2011
@@ -49,6 +49,7 @@ import org.apache.isis.core.metamodel.sp
 import org.apache.isis.core.metamodel.spec.SpecificationLookup;
 import org.apache.isis.core.metamodel.spec.feature.ObjectMember;
 import org.apache.isis.core.metamodel.spec.feature.ObjectMemberContext;
+import org.apache.isis.core.metamodel.specloader.collectiontyperegistry.CollectionTypeRegistry;
 
 public abstract class ObjectMemberAbstract implements ObjectMember {
 
@@ -65,9 +66,10 @@ public abstract class ObjectMemberAbstra
     private final SpecificationLookup specificationLookup;
     private final AdapterMap adapterMap;
     private final QuerySubmitter querySubmitter;
+    private final CollectionTypeRegistry collectionTypeRegistry;
 
     protected ObjectMemberAbstract(final FacetedMethod facetedMethod, final FeatureType featureType,
-        final ObjectMemberContext objectMembercontext) {
+        final ObjectMemberContext objectMemberContext) {
         final String id = facetedMethod.getIdentifier().getMemberName();
         if (id == null) {
             throw new IllegalArgumentException("Name must always be set");
@@ -77,10 +79,11 @@ public abstract class ObjectMemberAbstra
         this.id = id;
         this.defaultName = NameUtils.naturalName(this.id);
 
-        this.authenticationSessionProvider = objectMembercontext.getAuthenticationSessionProvider();
-        this.specificationLookup = objectMembercontext.getSpecificationLookup();
-        this.adapterMap = objectMembercontext.getAdapterManager();
-        this.querySubmitter = objectMembercontext.getQuerySubmitter();
+        this.authenticationSessionProvider = objectMemberContext.getAuthenticationSessionProvider();
+        this.specificationLookup = objectMemberContext.getSpecificationLookup();
+        this.adapterMap = objectMemberContext.getAdapterManager();
+        this.querySubmitter = objectMemberContext.getQuerySubmitter();
+        this.collectionTypeRegistry = objectMemberContext.getCollectionTypeRegistry();
     }
 
     // /////////////////////////////////////////////////////////////
@@ -302,4 +305,7 @@ public abstract class ObjectMemberAbstra
         return querySubmitter;
     }
 
+    public CollectionTypeRegistry getCollectionTypeRegistry() {
+        return collectionTypeRegistry;
+    }
 }

Modified: incubator/isis/trunk/framework/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/specimpl/OneToManyAssociationImpl.java
URL: http://svn.apache.org/viewvc/incubator/isis/trunk/framework/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/specimpl/OneToManyAssociationImpl.java?rev=1145867&r1=1145866&r2=1145867&view=diff
==============================================================================
--- incubator/isis/trunk/framework/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/specimpl/OneToManyAssociationImpl.java (original)
+++ incubator/isis/trunk/framework/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/specimpl/OneToManyAssociationImpl.java Wed Jul 13 05:46:13 2011
@@ -54,6 +54,12 @@ public class OneToManyAssociationImpl ex
             facetedMethod.getType()), objectMemberContext);
     }
 
+    @Override
+    public CollectionSemantics getCollectionSemantics() {
+        Class<?> underlyingClass = getSpecification().getCorrespondingClass();
+        return getCollectionTypeRegistry().semanticsOf(underlyingClass);
+    }
+
     // /////////////////////////////////////////////////////////////
     // Hidden (or visible)
     // /////////////////////////////////////////////////////////////
@@ -263,4 +269,5 @@ public class OneToManyAssociationImpl ex
         return str.toString();
     }
 
+
 }

Modified: incubator/isis/trunk/framework/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/specimpl/OneToOneAssociationImpl.java
URL: http://svn.apache.org/viewvc/incubator/isis/trunk/framework/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/specimpl/OneToOneAssociationImpl.java?rev=1145867&r1=1145866&r2=1145867&view=diff
==============================================================================
--- incubator/isis/trunk/framework/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/specimpl/OneToOneAssociationImpl.java (original)
+++ incubator/isis/trunk/framework/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/specimpl/OneToOneAssociationImpl.java Wed Jul 13 05:46:13 2011
@@ -159,28 +159,47 @@ public class OneToOneAssociationImpl ext
     @Override
     public void set(final ObjectAdapter ownerAdapter, final ObjectAdapter newReferencedAdapter) {
         if (newReferencedAdapter != null) {
-            setAssociation(ownerAdapter, newReferencedAdapter);
+            setValue(ownerAdapter, newReferencedAdapter);
         } else {
-            clearAssociation(ownerAdapter);
+            clearValue(ownerAdapter);
         }
     }
 
+    /**
+     * @see #set(ObjectAdapter, ObjectAdapter)
+     */
+    @Deprecated
     @Override
     public void setAssociation(final ObjectAdapter ownerAdapter, final ObjectAdapter newReferencedAdapter) {
-        final PropertySetterFacet setterFacet = getFacet(PropertySetterFacet.class);
-        if (setterFacet != null) {
-            if (ownerAdapter.isPersistent() && newReferencedAdapter != null && newReferencedAdapter.isTransient()
-                && !newReferencedAdapter.getSpecification().isAggregated()) {
-                // TODO: move to facet ?
-                throw new IsisException("can't set a reference to a transient object from a persistent one: "
-                    + newReferencedAdapter.titleString() + " (transient)");
-            }
-            setterFacet.setProperty(ownerAdapter, newReferencedAdapter);
-        }
+        setValue(ownerAdapter, newReferencedAdapter);
     }
 
+    private void setValue(final ObjectAdapter ownerAdapter, final ObjectAdapter newReferencedAdapter) {
+        final PropertySetterFacet setterFacet = getFacet(PropertySetterFacet.class);
+        if (setterFacet == null) {
+            return;
+        } 
+        if (ownerAdapter.isPersistent() && 
+                newReferencedAdapter != null && 
+                newReferencedAdapter.isTransient() && 
+                !newReferencedAdapter.getSpecification().isAggregated()) {
+            // TODO: move to facet ?
+            throw new IsisException("can't set a reference to a transient object from a persistent one: "
+                + newReferencedAdapter.titleString() + " (transient)");
+        }
+        setterFacet.setProperty(ownerAdapter, newReferencedAdapter);
+    }
+
+    /**
+     * @see #set(ObjectAdapter, ObjectAdapter)
+     */
+    @Deprecated
     @Override
     public void clearAssociation(final ObjectAdapter ownerAdapter) {
+        clearValue(ownerAdapter);
+    }
+
+    private void clearValue(final ObjectAdapter ownerAdapter) {
         final PropertyClearFacet facet = getFacet(PropertyClearFacet.class);
         facet.clearProperty(ownerAdapter);
     }

Modified: incubator/isis/trunk/framework/core/metamodel/src/test/java/org/apache/isis/core/metamodel/specloader/specimpl/ObjectAssociationAbstractTest.java
URL: http://svn.apache.org/viewvc/incubator/isis/trunk/framework/core/metamodel/src/test/java/org/apache/isis/core/metamodel/specloader/specimpl/ObjectAssociationAbstractTest.java?rev=1145867&r1=1145866&r2=1145867&view=diff
==============================================================================
--- incubator/isis/trunk/framework/core/metamodel/src/test/java/org/apache/isis/core/metamodel/specloader/specimpl/ObjectAssociationAbstractTest.java (original)
+++ incubator/isis/trunk/framework/core/metamodel/src/test/java/org/apache/isis/core/metamodel/specloader/specimpl/ObjectAssociationAbstractTest.java Wed Jul 13 05:46:13 2011
@@ -71,7 +71,7 @@ public class ObjectAssociationAbstractTe
         facetedMethod = FacetedMethod.createProperty(Customer.class, "firstName");
         objectAssociation =
             new ObjectAssociationAbstract(facetedMethod, FeatureType.PROPERTY, new TestProxySpecification("test"),
-                new ObjectMemberContext(null, null, null, null)) {
+                new ObjectMemberContext(null, null, null, null, null)) {
 
                 @Override
                 public ObjectAdapter get(final ObjectAdapter fromObject) {

Modified: incubator/isis/trunk/framework/runtimes/dflt/runtime/src/test/java/org/apache/isis/runtimes/dflt/runtime/system/ObjectActionImplTest.java
URL: http://svn.apache.org/viewvc/incubator/isis/trunk/framework/runtimes/dflt/runtime/src/test/java/org/apache/isis/runtimes/dflt/runtime/system/ObjectActionImplTest.java?rev=1145867&r1=1145866&r2=1145867&view=diff
==============================================================================
--- incubator/isis/trunk/framework/runtimes/dflt/runtime/src/test/java/org/apache/isis/runtimes/dflt/runtime/system/ObjectActionImplTest.java (original)
+++ incubator/isis/trunk/framework/runtimes/dflt/runtime/src/test/java/org/apache/isis/runtimes/dflt/runtime/system/ObjectActionImplTest.java Wed Jul 13 05:46:13 2011
@@ -35,6 +35,7 @@ import org.apache.isis.core.metamodel.fa
 import org.apache.isis.core.metamodel.spec.ObjectSpecification;
 import org.apache.isis.core.metamodel.spec.SpecificationLookup;
 import org.apache.isis.core.metamodel.spec.feature.ObjectMemberContext;
+import org.apache.isis.core.metamodel.specloader.collectiontyperegistry.CollectionTypeRegistry;
 import org.apache.isis.core.metamodel.specloader.specimpl.ObjectActionImpl;
 import org.apache.isis.runtimes.dflt.runtime.testsystem.ProxyJunit3TestCase;
 import org.apache.isis.runtimes.dflt.runtime.testsystem.TestProxyAdapter;
@@ -68,6 +69,7 @@ public class ObjectActionImplTest extend
     private AdapterMap mockAdapterManager;
     private ServicesProvider mockServicesProvider;
     private QuerySubmitter mockQuerySubmitter;
+    private CollectionTypeRegistry mockCollectionTypeRegistry;
 
     @Override
     @Before
@@ -80,6 +82,7 @@ public class ObjectActionImplTest extend
         mockAdapterManager = mockery.mock(AdapterMap.class);
         mockServicesProvider = mockery.mock(ServicesProvider.class);
         mockQuerySubmitter = mockery.mock(QuerySubmitter.class);
+        mockCollectionTypeRegistry = mockery.mock(CollectionTypeRegistry.class);
 
         mockery.checking(new Expectations() {
             {
@@ -90,7 +93,7 @@ public class ObjectActionImplTest extend
 
         action =
             new ObjectActionImpl(mockFacetedMethod, new ObjectMemberContext(mockAuthenticationSessionProvider,
-                mockSpecificationLookup, mockAdapterManager, mockQuerySubmitter), mockServicesProvider);
+                mockSpecificationLookup, mockAdapterManager, mockQuerySubmitter, mockCollectionTypeRegistry), mockServicesProvider);
     }
 
     @Test

Modified: incubator/isis/trunk/framework/runtimes/dflt/runtime/src/test/java/org/apache/isis/runtimes/dflt/runtime/system/ObjectMemberAbstractTest.java
URL: http://svn.apache.org/viewvc/incubator/isis/trunk/framework/runtimes/dflt/runtime/src/test/java/org/apache/isis/runtimes/dflt/runtime/system/ObjectMemberAbstractTest.java?rev=1145867&r1=1145866&r2=1145867&view=diff
==============================================================================
--- incubator/isis/trunk/framework/runtimes/dflt/runtime/src/test/java/org/apache/isis/runtimes/dflt/runtime/system/ObjectMemberAbstractTest.java (original)
+++ incubator/isis/trunk/framework/runtimes/dflt/runtime/src/test/java/org/apache/isis/runtimes/dflt/runtime/system/ObjectMemberAbstractTest.java Wed Jul 13 05:46:13 2011
@@ -204,7 +204,7 @@ class ObjectMemberAbstractImpl extends O
 
     protected ObjectMemberAbstractImpl(final String id) {
         super(FacetedMethod.createProperty(Customer.class, "firstName"), FeatureType.PROPERTY, new ObjectMemberContext(
-            null, null, null, null));
+            null, null, null, null, null));
     }
 
     @Override

Modified: incubator/isis/trunk/framework/runtimes/dflt/runtime/src/test/java/org/apache/isis/runtimes/dflt/runtime/system/OneToManyAssociationImplTest.java
URL: http://svn.apache.org/viewvc/incubator/isis/trunk/framework/runtimes/dflt/runtime/src/test/java/org/apache/isis/runtimes/dflt/runtime/system/OneToManyAssociationImplTest.java?rev=1145867&r1=1145866&r2=1145867&view=diff
==============================================================================
--- incubator/isis/trunk/framework/runtimes/dflt/runtime/src/test/java/org/apache/isis/runtimes/dflt/runtime/system/OneToManyAssociationImplTest.java (original)
+++ incubator/isis/trunk/framework/runtimes/dflt/runtime/src/test/java/org/apache/isis/runtimes/dflt/runtime/system/OneToManyAssociationImplTest.java Wed Jul 13 05:46:13 2011
@@ -36,6 +36,7 @@ import org.apache.isis.core.metamodel.sp
 import org.apache.isis.core.metamodel.spec.SpecificationLookup;
 import org.apache.isis.core.metamodel.spec.feature.ObjectMemberContext;
 import org.apache.isis.core.metamodel.spec.feature.OneToManyAssociation;
+import org.apache.isis.core.metamodel.specloader.collectiontyperegistry.CollectionTypeRegistry;
 import org.apache.isis.core.metamodel.specloader.specimpl.OneToManyAssociationImpl;
 import org.jmock.Expectations;
 import org.jmock.Mockery;
@@ -78,6 +79,7 @@ public class OneToManyAssociationImplTes
 
     private NamedFacet mockNamedFacet;
     private CollectionAddToFacet mockCollectionAddToFacet;
+    private CollectionTypeRegistry mockCollectionTypeRegistry;
 
     @Before
     public void setUp() {
@@ -89,6 +91,7 @@ public class OneToManyAssociationImplTes
         mockSpecificationLookup = context.mock(SpecificationLookup.class);
         mockAdapterManager = context.mock(AdapterMap.class);
         mockQuerySubmitter = context.mock(QuerySubmitter.class);
+        mockCollectionTypeRegistry = context.mock(CollectionTypeRegistry.class);
         mockPeer = context.mock(FacetedMethod.class);
 
         mockNamedFacet = context.mock(NamedFacet.class);
@@ -99,7 +102,7 @@ public class OneToManyAssociationImplTes
         allowingSpecLoaderToReturnSpecs();
         association =
             new OneToManyAssociationImpl(mockPeer, new ObjectMemberContext(mockAuthenticationSessionProvider,
-                mockSpecificationLookup, mockAdapterManager, mockQuerySubmitter));
+                mockSpecificationLookup, mockAdapterManager, mockQuerySubmitter, mockCollectionTypeRegistry));
     }
 
     private void allowingSpecLoaderToReturnSpecs() {

Modified: incubator/isis/trunk/framework/viewer/json/applib/src/main/java/org/apache/isis/viewer/json/applib/resources/DomainObjectResource.java
URL: http://svn.apache.org/viewvc/incubator/isis/trunk/framework/viewer/json/applib/src/main/java/org/apache/isis/viewer/json/applib/resources/DomainObjectResource.java?rev=1145867&r1=1145866&r2=1145867&view=diff
==============================================================================
--- incubator/isis/trunk/framework/viewer/json/applib/src/main/java/org/apache/isis/viewer/json/applib/resources/DomainObjectResource.java (original)
+++ incubator/isis/trunk/framework/viewer/json/applib/src/main/java/org/apache/isis/viewer/json/applib/resources/DomainObjectResource.java Wed Jul 13 05:46:13 2011
@@ -22,7 +22,6 @@ import java.io.InputStream;
 import java.util.List;
 
 import javax.ws.rs.DELETE;
-import javax.ws.rs.FormParam;
 import javax.ws.rs.GET;
 import javax.ws.rs.POST;
 import javax.ws.rs.PUT;
@@ -31,6 +30,7 @@ import javax.ws.rs.PathParam;
 import javax.ws.rs.Produces;
 import javax.ws.rs.QueryParam;
 import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
 
 // under /objects
 public interface DomainObjectResource {
@@ -72,42 +72,41 @@ public interface DomainObjectResource {
     @PUT
     @Path("/{oid}/properties{propertyId}")
     @Produces({ MediaType.APPLICATION_JSON })
-    public Object modifyProperty(
+    public Response modifyProperty(
         @PathParam("oid") final String oidStr,
         @PathParam("propertyId") final String propertyId, 
-        @FormParam("proposedValue") final String proposedValue);
+        final InputStream body);
 
     @PUT
     @Path("/{oid}/collections/{collectionId}")
     @Produces({ MediaType.APPLICATION_JSON })
-    public String addToSet(
+    public Response addToSet(
         @PathParam("oid") final String oidStr,
         @PathParam("collectionId") final String collectionId,
-        @FormParam("proposedValue") final String proposedValueOidStr);
+        final InputStream body);
 
     @DELETE
     @Path("/{oid}/properties/{propertyId}")
     @Produces({ MediaType.APPLICATION_JSON })
-    public String clearProperty(
+    public Response clearProperty(
         @PathParam("oid") final String oidStr, 
         @PathParam("propertyId") final String propertyId);
 
     @DELETE
     @Path("/{oid}/collections/{collectionId}")
     @Produces({ MediaType.APPLICATION_JSON })
-    public String removeFromCollection(
+    public Response removeFromCollection(
         @PathParam("oid") final String oidStr,
         @PathParam("collectionId") final String collectionId,
-        @FormParam("proposedValue") final String proposedValueOidStr);
+        final InputStream body);
 
     @POST
     @Path("/{oid}/collections/{collectionId}")
     @Produces({ MediaType.APPLICATION_JSON })
-    public String addToList(
+    public Response addToList(
         @PathParam("oid") final String oidStr,
         @PathParam("collectionId") final String collectionId,
-        @FormParam("proposedValue") final String proposedValueOidStr);
-
+        final InputStream body);
 
     @POST
     @Path("/{oid}/actions/{actionId}/invoke")
@@ -116,7 +115,4 @@ public interface DomainObjectResource {
         @PathParam("oid") final String oidStr, 
         @PathParam("actionId") final String actionId,
         final InputStream body);
-
-
-    
 }
\ No newline at end of file

Modified: incubator/isis/trunk/framework/viewer/json/viewer/src/main/java/org/apache/isis/viewer/json/viewer/resources/objects/DomainObjectResourceImpl.java
URL: http://svn.apache.org/viewvc/incubator/isis/trunk/framework/viewer/json/viewer/src/main/java/org/apache/isis/viewer/json/viewer/resources/objects/DomainObjectResourceImpl.java?rev=1145867&r1=1145866&r2=1145867&view=diff
==============================================================================
--- incubator/isis/trunk/framework/viewer/json/viewer/src/main/java/org/apache/isis/viewer/json/viewer/resources/objects/DomainObjectResourceImpl.java (original)
+++ incubator/isis/trunk/framework/viewer/json/viewer/src/main/java/org/apache/isis/viewer/json/viewer/resources/objects/DomainObjectResourceImpl.java Wed Jul 13 05:46:13 2011
@@ -25,7 +25,6 @@ import java.util.List;
 import java.util.Map;
 
 import javax.ws.rs.DELETE;
-import javax.ws.rs.FormParam;
 import javax.ws.rs.GET;
 import javax.ws.rs.POST;
 import javax.ws.rs.PUT;
@@ -35,10 +34,10 @@ import javax.ws.rs.Produces;
 import javax.ws.rs.QueryParam;
 import javax.ws.rs.WebApplicationException;
 import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
 
 import org.apache.isis.core.metamodel.adapter.ObjectAdapter;
 import org.apache.isis.core.metamodel.consent.Consent;
-import org.apache.isis.core.metamodel.consent.InteractionInvocationMethod;
 import org.apache.isis.core.metamodel.facets.collections.modify.CollectionFacet;
 import org.apache.isis.core.metamodel.facets.object.encodeable.EncodableFacet;
 import org.apache.isis.core.metamodel.facets.object.value.ValueFacet;
@@ -50,7 +49,6 @@ import org.apache.isis.core.metamodel.sp
 import org.apache.isis.core.metamodel.spec.feature.ObjectMember;
 import org.apache.isis.core.metamodel.spec.feature.OneToManyAssociation;
 import org.apache.isis.core.metamodel.spec.feature.OneToOneAssociation;
-import org.apache.isis.core.progmodel.facets.properties.modify.PropertyModifyFacetFactory;
 import org.apache.isis.viewer.json.applib.resources.DomainObjectResource;
 import org.apache.isis.viewer.json.viewer.resources.ResourceAbstract;
 import org.apache.isis.viewer.json.viewer.util.UrlDecoderUtils;
@@ -62,18 +60,17 @@ import com.google.common.collect.Lists;
 import com.google.common.io.ByteStreams;
 
 @Path("/objects")
-public class DomainObjectResourceImpl extends ResourceAbstract implements DomainObjectResource {
+public class DomainObjectResourceImpl extends ResourceAbstract implements
+        DomainObjectResource {
 
-
-	@GET
+    @GET
     @Path("/{oid}")
     @Produces({ MediaType.APPLICATION_JSON })
-    public String object(
-    		@PathParam("oid") final String oidStr) {
+    public String object(@PathParam("oid") final String oidStr) {
 
-    	final ObjectAdapter objectAdapter = getObjectAdapter(oidStr);
-
-    	final DomainObjectRepBuilder builder = DomainObjectRepBuilder.newBuilder(getResourceContext().repContext(), objectAdapter);
+        final ObjectAdapter objectAdapter = getObjectAdapter(oidStr);
+        final DomainObjectRepBuilder builder = DomainObjectRepBuilder
+                .newBuilder(getResourceContext().repContext(), objectAdapter);
         return jsonRepresentionFrom(builder);
     }
 
@@ -83,11 +80,13 @@ public class DomainObjectResourceImpl ex
     public String propertyDetails(
         @PathParam("oid") final String oidStr,
         @PathParam("propertyId") final String propertyId) {
-    	
+
         final ObjectAdapter objectAdapter = getObjectAdapter(oidStr);
-        final OneToOneAssociation property = getPropertyThatIsVisibleAndUsable(objectAdapter, propertyId, Intent.ACCESS);
-        
-        final PropertyRepBuilder builder = PropertyRepBuilder.newBuilder(getResourceContext().repContext(), objectAdapter, property);
+        final OneToOneAssociation property = getPropertyThatIsVisibleAndUsable(
+                objectAdapter, propertyId, Intent.ACCESS);
+
+        final PropertyRepBuilder builder = PropertyRepBuilder.newBuilder(
+                getResourceContext().repContext(), objectAdapter, property);
         return jsonRepresentionFrom(builder);
     }
 
@@ -96,12 +95,14 @@ public class DomainObjectResourceImpl ex
     @Produces({ MediaType.APPLICATION_JSON })
     public String accessCollection(
         @PathParam("oid") final String oidStr,
-        @PathParam("collectionId") final String collectionId){
-    	
+        @PathParam("collectionId") final String collectionId) {
+
         final ObjectAdapter objectAdapter = getObjectAdapter(oidStr);
-        final OneToManyAssociation collection = getCollectionThatIsVisibleAndUsable(objectAdapter, collectionId, Intent.ACCESS);
-        
-        final CollectionRepBuilder builder = CollectionRepBuilder.newBuilder(getResourceContext().repContext(), objectAdapter, collection);
+        final OneToManyAssociation collection = getCollectionThatIsVisibleAndUsable(
+                objectAdapter, collectionId, Intent.ACCESS);
+
+        final CollectionRepBuilder builder = CollectionRepBuilder.newBuilder(
+                getResourceContext().repContext(), objectAdapter, collection);
         return jsonRepresentionFrom(builder);
     }
 
@@ -109,13 +110,15 @@ public class DomainObjectResourceImpl ex
     @Path("/{oid}/actions/{actionId}")
     @Produces({ MediaType.APPLICATION_JSON })
     public String actionPrompt(
-        @PathParam("oid") final String oidStr, 
+        @PathParam("oid") final String oidStr,
         @PathParam("actionId") final String actionId) {
 
         final ObjectAdapter objectAdapter = getObjectAdapter(oidStr);
-        final ObjectAction action = getObjectActionThatIsVisibleAndUsable(objectAdapter, actionId, Intent.ACCESS);
-        
-        ActionRepBuilder builder = ActionRepBuilder.newBuilder(getResourceContext().repContext(), objectAdapter, action);
+        final ObjectAction action = getObjectActionThatIsVisibleAndUsable(
+                objectAdapter, actionId, Intent.ACCESS);
+
+        ActionRepBuilder builder = ActionRepBuilder.newBuilder(
+                getResourceContext().repContext(), objectAdapter, action);
         return jsonRepresentionFrom(builder);
     }
 
@@ -123,92 +126,97 @@ public class DomainObjectResourceImpl ex
     @Path("/{oid}/actions/{actionId}/invoke")
     @Produces({ MediaType.APPLICATION_JSON })
     public Object invokeActionIdempotent(
-        @PathParam("oid") final String oidStr, 
-        @PathParam("actionId") final String actionId, 
+        @PathParam("oid") final String oidStr,
+        @PathParam("actionId") final String actionId,
         @QueryParam("arg") final List<String> arguments) {
 
-    	final ObjectAdapter objectAdapter = getObjectAdapter(oidStr);
-    	final ObjectAction action = getObjectActionThatIsVisibleAndUsable(objectAdapter, actionId, Intent.ACCESS);
-    	
-    	if(!isIdempotent(action)) {
-            throw new WebApplicationException(responseOfMethodNotAllowed(
-                     "Method not allowed; action '" + action.getId() + "' is not idempotent"));
-    	}
-    	int numParameters = action.getParameterCount();
-		int numArguments = arguments.size();
-		if(numArguments != numParameters) {
-            throw new WebApplicationException(responseOfBadRequest(
-                     "Action '" + action.getId() + "' has " + numParameters + " parameters but received " + numArguments + " arguments"));
-    	}
-    	
-    	final List<ObjectAdapter> parameters = argumentAdaptersFor(action, arguments);
-		return invokeActionUsingAdapters(action, objectAdapter, parameters);
+        final ObjectAdapter objectAdapter = getObjectAdapter(oidStr);
+        final ObjectAction action = getObjectActionThatIsVisibleAndUsable(
+                objectAdapter, actionId, Intent.ACCESS);
+
+        if (!isIdempotent(action)) {
+            throw new WebApplicationException(
+                    responseOfMethodNotAllowed("Method not allowed; action '"
+                            + action.getId() + "' is not idempotent"));
+        }
+        int numParameters = action.getParameterCount();
+        int numArguments = arguments.size();
+        if (numArguments != numParameters) {
+            throw new WebApplicationException(
+                    responseOfBadRequest("Action '" + action.getId() + "' has "
+                            + numParameters + " parameters but received "
+                            + numArguments + " arguments"));
+        }
+
+        final List<ObjectAdapter> parameters = argumentAdaptersFor(action,
+                arguments);
+        return invokeActionUsingAdapters(action, objectAdapter, parameters);
     }
 
     private boolean isIdempotent(final ObjectAction action) {
-    	// TODO: determine whether action is idempotent
-		return true;
-	}
-
-	private List<ObjectAdapter> argumentAdaptersFor(ObjectAction action, List<String> arguments) {
-		List<ObjectActionParameter> parameters = action.getParameters();
-    	List<ObjectAdapter> argumentAdapters = Lists.newArrayList(); 
-    	for(int i=0; i<parameters.size(); i++) {
-    		ObjectActionParameter parameter = parameters.get(i);
-    		ObjectSpecification paramSpc = parameter.getSpecification();
-    		String argument = arguments.get(i);
-			argumentAdapters.add(objectAdapterFor(paramSpc, argument));
-    	}
-    	
-		return argumentAdapters;
-	}
-
-	/**
-	 * Similar to {@link #objectAdapterFor(ObjectSpecification, Object)}, however the object
-	 * being interpreted is a String holding URL encoded JSON (rather than having already been
-	 * parsed into a List/Map representation).
-	 */
-	private ObjectAdapter objectAdapterFor(ObjectSpecification spec, String urlEncodedJson) {
+        // TODO: determine whether action is idempotent
+        return true;
+    }
+
+    private List<ObjectAdapter> argumentAdaptersFor(ObjectAction action,
+        List<String> arguments) {
+        List<ObjectActionParameter> parameters = action.getParameters();
+        List<ObjectAdapter> argumentAdapters = Lists.newArrayList();
+        for (int i = 0; i < parameters.size(); i++) {
+            ObjectActionParameter parameter = parameters.get(i);
+            ObjectSpecification paramSpc = parameter.getSpecification();
+            String argument = arguments.get(i);
+            argumentAdapters.add(objectAdapterFor(paramSpc, argument));
+        }
+
+        return argumentAdapters;
+    }
+
+    /**
+     * Similar to {@link #objectAdapterFor(ObjectSpecification, Object)},
+     * however the object being interpreted is a String holding URL encoded JSON
+     * (rather than having already been parsed into a List/Map representation).
+     */
+    private ObjectAdapter objectAdapterFor(ObjectSpecification spec,
+        String urlEncodedJson) {
         final String json = UrlDecoderUtils.urlDecode(urlEncodedJson);
-        if(spec.containsFacet(EncodableFacet.class)) {
-        	EncodableFacet encodableFacet = spec.getFacet(EncodableFacet.class);
-        	return encodableFacet.fromEncodedString(json);
+        if (spec.containsFacet(EncodableFacet.class)) {
+            EncodableFacet encodableFacet = spec.getFacet(EncodableFacet.class);
+            return encodableFacet.fromEncodedString(json);
         } else {
-			@SuppressWarnings("unchecked")
-			Map<String,Object> representation = objectMapper.convertValue(json, LinkedHashMap.class);
+            @SuppressWarnings("unchecked")
+            Map<String, Object> representation = objectMapper.convertValue(
+                    json, LinkedHashMap.class);
             return objectAdapterFor(spec, representation);
         }
-	}
+    }
+
+    // /////////////////////////////////////////////////////////////////
+    // put
+    // /////////////////////////////////////////////////////////////////
 
-	
-	///////////////////////////////////////////////////////////////////
-	// put
-	///////////////////////////////////////////////////////////////////
-	
-	@PUT
+    @PUT
     @Path("/{oid}/properties/{propertyId}")
     @Produces({ MediaType.APPLICATION_JSON })
-    public Object modifyProperty(
-        @PathParam("oid") final String oidStr,
-        @PathParam("propertyId") final String propertyId, 
-        @FormParam("arg") final String proposedValue) {
+    public Response modifyProperty(@PathParam("oid") final String oidStr,
+        @PathParam("propertyId") final String propertyId,
+        final InputStream body) {
 
-		// TODO: replace @FormParam with body inputstream
-		
         final ObjectAdapter objectAdapter = getObjectAdapter(oidStr);
-        final OneToOneAssociation property = getPropertyThatIsVisibleAndUsable(objectAdapter, propertyId, Intent.MUTATE);
-        
-        ObjectSpecification objectSpec = property.getSpecification();
-        
-		ObjectAdapter proposedValueAdapter = objectAdapterFor(objectSpec, proposedValue);
-		
-		Consent consent = property.isAssociationValid(objectAdapter, proposedValueAdapter);
-		if(consent.isVetoed()) {
-			throw new WebApplicationException(responseOfPreconditionFailed(consent.getReason()));
-		}
+        final OneToOneAssociation property = getPropertyThatIsVisibleAndUsable(
+                objectAdapter, propertyId, Intent.MUTATE);
+
+        ObjectSpecification propertySpec = property.getSpecification();
 
-		PropertySetterFacet setterFacet = property.getFacet(PropertySetterFacet.class);
-		setterFacet.setProperty(objectAdapter, proposedValueAdapter);
+        ObjectAdapter argAdapter = parseBody(propertySpec, body);
+
+        Consent consent = property.isAssociationValid(objectAdapter, argAdapter);
+        if (consent.isVetoed()) {
+            throw new WebApplicationException(
+                    responseOfPreconditionFailed(consent.getReason()));
+        }
+
+        property.set(objectAdapter, argAdapter);
 
         return responseOfOk();
     }
@@ -216,236 +224,344 @@ public class DomainObjectResourceImpl ex
     @PUT
     @Path("/{oid}/collections/{collectionId}")
     @Produces({ MediaType.APPLICATION_JSON })
-    public String addToSet(
+    public Response addToSet(
         @PathParam("oid") final String oidStr,
         @PathParam("collectionId") final String collectionId,
-        @FormParam("arg") final String proposedValueOidStr){
-    	
+        final InputStream body) {
+
         final ObjectAdapter objectAdapter = getObjectAdapter(oidStr);
-        final OneToManyAssociation collection = getCollectionThatIsVisibleAndUsable(objectAdapter, collectionId, Intent.MUTATE);
+        final OneToManyAssociation collection = getCollectionThatIsVisibleAndUsable(
+                objectAdapter, collectionId, Intent.MUTATE);
+
+        if (!collection.getCollectionSemantics().isSet()) {
+            throw new WebApplicationException(responseOfBadRequest("Collection '" + collectionId + "' does not have set semantics"));
+        }
+
+        ObjectSpecification collectionSpec = collection.getSpecification();
+        ObjectAdapter argAdapter = parseBody(collectionSpec, body);
+
+        Consent consent = collection.isValidToAdd(objectAdapter, argAdapter);
+        if (consent.isVetoed()) {
+            throw new WebApplicationException(responseOfPreconditionFailed(consent.getReason()));
+        }
+
+        collection.addElement(objectAdapter, argAdapter);
         
-        return null;
+        return responseOfOk();
     }
 
-	///////////////////////////////////////////////////////////////////
-	// delete
-	///////////////////////////////////////////////////////////////////
+    // /////////////////////////////////////////////////////////////////
+    // delete
+    // /////////////////////////////////////////////////////////////////
 
     @DELETE
     @Path("/{oid}/properties/{propertyId}")
     @Produces({ MediaType.APPLICATION_JSON })
-    public String clearProperty(
-        @PathParam("oid") final String oidStr, 
-        @PathParam("propertyId") final String propertyId){
-    	
+    public Response clearProperty(
+        @PathParam("oid") final String oidStr,
+        @PathParam("propertyId") final String propertyId) {
+
         final ObjectAdapter objectAdapter = getObjectAdapter(oidStr);
-        final OneToOneAssociation property = getPropertyThatIsVisibleAndUsable(objectAdapter, propertyId, Intent.MUTATE);
-        
-        return null;
+        final OneToOneAssociation property = getPropertyThatIsVisibleAndUsable(
+                objectAdapter, propertyId, Intent.MUTATE);
+
+        Consent consent = property.isAssociationValid(objectAdapter, null);
+        if (consent.isVetoed()) {
+            throw new WebApplicationException(
+                    responseOfPreconditionFailed(consent.getReason()));
+        }
+
+        property.set(objectAdapter, null);
+
+        return responseOfOk();
     }
 
     @DELETE
     @Path("/{oid}/collections/{collectionId}")
     @Produces({ MediaType.APPLICATION_JSON })
-    public String removeFromCollection(
+    public Response removeFromCollection(
         @PathParam("oid") final String oidStr,
         @PathParam("collectionId") final String collectionId,
-        @FormParam("arg") final String proposedValueOidStr){
+        final InputStream body) {
+
+        final ObjectAdapter objectAdapter = getObjectAdapter(oidStr);
+        final OneToManyAssociation collection = getCollectionThatIsVisibleAndUsable(
+                objectAdapter, collectionId, Intent.MUTATE);
+
+        ObjectSpecification collectionSpec = collection.getSpecification();
+        ObjectAdapter argAdapter = parseBody(collectionSpec, body);
 
-    	final ObjectAdapter objectAdapter = getObjectAdapter(oidStr);
-    	final OneToManyAssociation collection = getCollectionThatIsVisibleAndUsable(objectAdapter, collectionId, Intent.MUTATE);
+        Consent consent = collection.isValidToRemove(objectAdapter, argAdapter);
+        if (consent.isVetoed()) {
+            throw new WebApplicationException(responseOfPreconditionFailed(consent.getReason()));
+        }
+
+        collection.removeElement(objectAdapter, argAdapter);
         
-        return null;
+        return responseOfOk();
     }
 
-    
-	///////////////////////////////////////////////////////////////////
-	// post
-	///////////////////////////////////////////////////////////////////
+    // /////////////////////////////////////////////////////////////////
+    // post
+    // /////////////////////////////////////////////////////////////////
 
     @POST
     @Path("/{oid}/collections/{collectionId}")
     @Produces({ MediaType.APPLICATION_JSON })
-    public String addToList(
+    public Response addToList(
         @PathParam("oid") final String oidStr,
         @PathParam("collectionId") final String collectionId,
-        @FormParam("arg") final String proposedValueOidStr){
+        final InputStream body) {
 
         final ObjectAdapter objectAdapter = getObjectAdapter(oidStr);
-        final OneToManyAssociation collection = getCollectionThatIsVisibleAndUsable(objectAdapter, collectionId, Intent.MUTATE);
-        
-        return null;
-	}
+        final OneToManyAssociation collection = getCollectionThatIsVisibleAndUsable(
+                objectAdapter, collectionId, Intent.MUTATE);
+
+        if (!collection.getCollectionSemantics().isListOrArray()) {
+            throw new WebApplicationException(responseOfBadRequest("Collection '" + collectionId + "' does not have list or array semantics"));
+        }
+
+        ObjectSpecification collectionSpec = collection.getSpecification();
+        ObjectAdapter argAdapter = parseBody(collectionSpec, body);
+
+        Consent consent = collection.isValidToAdd(objectAdapter, argAdapter);
+        if (consent.isVetoed()) {
+            throw new WebApplicationException(responseOfPreconditionFailed(consent.getReason()));
+        }
 
+        collection.addElement(objectAdapter, argAdapter);
+        
+        return responseOfOk();
+    }
 
     @POST
     @Path("/{oid}/actions/{actionId}/invoke")
     @Produces({ MediaType.APPLICATION_JSON })
     public Object invokeAction(
-        @PathParam("oid") final String oidStr, 
+        @PathParam("oid") final String oidStr,
         @PathParam("actionId") final String actionId,
-        final InputStream body){
+        final InputStream body) {
 
         final ObjectAdapter objectAdapter = getObjectAdapter(oidStr);
-        final ObjectAction action = getObjectActionThatIsVisibleAndUsable(objectAdapter, actionId, Intent.MUTATE);
+        final ObjectAction action = getObjectActionThatIsVisibleAndUsable(
+                objectAdapter, actionId, Intent.MUTATE);
+
+        List<ObjectAdapter> argumentAdapters = parseBody(action, body);
+        return invokeActionUsingAdapters(action, objectAdapter,
+                argumentAdapters);
+    }
+
+    private List<ObjectAdapter> parseBody(final ObjectAction action,
+        final InputStream body) {
+        List<ObjectAdapter> argAdapters = Lists.newArrayList();
+        List<?> arguments = parseBody(body);
+
+        int numParameters = action.getParameterCount();
+        int numArguments = arguments.size();
+        if (numArguments != numParameters) {
+            throw new WebApplicationException(
+                    responseOfBadRequest("Action '" + action.getId()
+                            + "' has " + numParameters
+                            + " parameters but received " + numArguments
+                            + " arguments in body"));
+        }
+
+        for (int i = 0; i < numParameters; i++) {
+            ObjectAdapter argAdapter = objectAdapterFor(action, arguments, i);
+            argAdapters.add(argAdapter);
+        }
+        return argAdapters;
+
+    }
+
+    private ObjectAdapter objectAdapterFor(final ObjectAction action,
+        List<?> arguments, int i) {
+        List<ObjectActionParameter> parameters = action.getParameters();
+
+        ObjectSpecification paramSpec = parameters.get(i).getSpecification();
+        Object arg = arguments.get(i);
+
+        ObjectAdapter objectAdapter = objectAdapterFor(action, i, paramSpec, arg);
+        return objectAdapter;
+    }
+
+    private ObjectAdapter objectAdapterFor(final ObjectAction action, int i,
+        ObjectSpecification paramSpec, Object arg) {
+        try {
+            return objectAdapterFor(paramSpec, arg);
+        } catch (ExpectedStringRepresentingValueException e) {
+            throw new WebApplicationException(
+                    responseOfBadRequest("Action '"
+                            + action.getId()
+                            + "', argument "
+                            + i
+                            + " should be a URL encoded string representing a value of type "
+                            + resourceFor(paramSpec)));
+        } catch (ExpectedMapRepresentingReferenceException e) {
+            throw new WebApplicationException(
+                    responseOfBadRequest("Action '"
+                            + action.getId()
+                            + "', argument "
+                            + i
+                            + " should be a map representing a link to reference of type "
+                            + resourceFor(paramSpec)));
+        }
+    }
 
-		List<ObjectAdapter> argumentAdapters = parseBody(action, body);
-		return invokeActionUsingAdapters(action, objectAdapter, argumentAdapters);
+    // ///////////////////////////////////////////////////////////////////
+    // helpers
+    // ///////////////////////////////////////////////////////////////////
+
+    private Object invokeActionUsingAdapters(final ObjectAction action,
+        final ObjectAdapter objectAdapter,
+        final List<ObjectAdapter> argAdapters) {
+
+        List<ObjectActionParameter> parameters = action.getParameters();
+        for (int i = 0; i < parameters.size(); i++) {
+            ObjectActionParameter parameter = parameters.get(i);
+            ObjectAdapter paramAdapter = argAdapters.get(i);
+            if (paramAdapter.getSpecification().containsFacet(ValueFacet.class)) {
+                Object arg = paramAdapter.getObject();
+                String reasonNotValid = parameter.isValid(objectAdapter, arg);
+                if (reasonNotValid != null) {
+                    throw new WebApplicationException(
+                            responseOfPreconditionFailed(reasonNotValid));
+                }
+            }
+        }
+        ObjectAdapter[] argArray = argAdapters.toArray(new ObjectAdapter[0]);
+        Consent consent = action.isProposedArgumentSetValid(objectAdapter,
+                argArray);
+        if (consent.isVetoed()) {
+            throw new WebApplicationException(
+                    responseOfPreconditionFailed(consent.getReason()));
+        }
+
+        final ObjectAdapter returnedAdapter = action.execute(objectAdapter,
+                argArray);
+        if (returnedAdapter == null) {
+            return responseOfOk();
+        }
+        final CollectionFacet facet = returnedAdapter.getSpecification()
+                .getFacet(CollectionFacet.class);
+        if (facet != null) {
+            final Collection<ObjectAdapter> collectionAdapters = facet
+                    .collection(returnedAdapter);
+            return jsonRepresentationOf(collectionAdapters);
+        } else {
+            return jsonRepresentationOf(returnedAdapter);
+        }
+    }
+
+    private ObjectAdapter parseBody(ObjectSpecification objectSpec,
+        final InputStream body) {
+        List<?> arguments = parseBody(body);
+        if (arguments.size() != 1) {
+            throw new WebApplicationException(
+                    responseOfBadRequest("Body should contain 1 argument representing a value of type '" + resourceFor(objectSpec) + "'"));
+        }
+
+        ObjectAdapter proposedValueAdapter = objectAdapterFor(objectSpec, arguments.get(0));
+        return proposedValueAdapter;
+    }
+
+    private List<?> parseBody(final InputStream body) {
+        try {
+            byte[] byteArray = ByteStreams.toByteArray(body);
+            String bodyAsString = new String(byteArray, Charsets.UTF_8);
+
+            List<?> arguments = objectMapper.readValue(bodyAsString, ArrayList.class);
+            return arguments;
+        } catch (JsonParseException e) {
+            throw new WebApplicationException(e,
+                    responseOfBadRequest("could not parse body"));
+        } catch (JsonMappingException e) {
+            throw new WebApplicationException(
+                    e,
+                    responseOfBadRequest("could not map body to a Map structure"));
+        } catch (IOException e) {
+            throw new WebApplicationException(e,
+                    responseOfBadRequest("could not read body"));
+        }
     }
 
-	private List<ObjectAdapter> parseBody(final ObjectAction action, final InputStream body) {
-		List<ObjectAdapter> argAdapters = Lists.newArrayList();
-		try {
-			byte[] byteArray = ByteStreams.toByteArray(body);
-			String bodyAsString = new String(byteArray, Charsets.UTF_8);
-			
-			List<?> arguments = objectMapper.readValue(bodyAsString, ArrayList.class);
-			
-	    	int numParameters = action.getParameterCount();
-			int numArguments = arguments.size();
-			if(numArguments != numParameters) {
-	            throw new WebApplicationException(responseOfBadRequest(
-	                     "Action '" + action.getId() + "' has " + numParameters + " parameters but received " + numArguments + " arguments"));
-	    	}
-
-			for(int i=0; i<numParameters; i++) {
-				ObjectAdapter argAdapter = toObjectAdapter(action, arguments, i);
-				argAdapters.add(argAdapter);
-			}
-	        return argAdapters;
-
-		} catch (JsonParseException e) {
-			throw new WebApplicationException(e, responseOfBadRequest("could not parse body"));
-		} catch (JsonMappingException e) {
-			throw new WebApplicationException(e, responseOfBadRequest("could not map body to a Map structure"));
-		} catch (IOException e) {
-			throw new WebApplicationException(e, responseOfBadRequest("could not read body"));
-		}
-	}
-
-	private ObjectAdapter toObjectAdapter(final ObjectAction action, List<?> arguments, int i) {
-		List<ObjectActionParameter> parameters = action.getParameters();
-
-		ObjectSpecification paramSpec = parameters.get(i).getSpecification();
-		Object arg = arguments.get(i);
-		
-		ObjectAdapter objectAdapter = toObjectAdapter(action, i, paramSpec, arg);
-		return objectAdapter;
-	}
-
-	private ObjectAdapter toObjectAdapter(final ObjectAction action, int i, ObjectSpecification paramSpec, Object arg) {
-		try {
-			return objectAdapterFor(paramSpec, arg);
-		} catch (ExpectedStringRepresentingValueException e) {
-			throw new WebApplicationException(responseOfBadRequest("Action '" + action.getId() + "', argument " + i + " should be a URL encoded string representing a value of type " + resourceFor(paramSpec)));
-		} catch (ExpectedMapRepresentingReferenceException e) {
-			throw new WebApplicationException(responseOfBadRequest("Action '" + action.getId() + "', argument " + i + " should be a map representing a link to reference of type " + resourceFor(paramSpec)));
-		}
-	}
-
-
-
-	/////////////////////////////////////////////////////////////////////
-	// helpers
-	/////////////////////////////////////////////////////////////////////
-
-	private Object invokeActionUsingAdapters(final ObjectAction action,
-			final ObjectAdapter objectAdapter,
-			final List<ObjectAdapter> argAdapters) {
-		
-		List<ObjectActionParameter> parameters = action.getParameters();
-		for(int i=0; i<parameters.size(); i++) {
-			ObjectActionParameter parameter = parameters.get(i);
-			ObjectAdapter paramAdapter = argAdapters.get(i);
-			if(paramAdapter.getSpecification().containsFacet(ValueFacet.class)) {
-				Object arg = paramAdapter.getObject();
-				String reasonNotValid = parameter.isValid(objectAdapter, arg);
-				if(reasonNotValid != null) {
-					throw new WebApplicationException(responseOfPreconditionFailed(reasonNotValid));
-				}
-			}
-		}
-		ObjectAdapter[] argArray = argAdapters.toArray(new ObjectAdapter[0]);
-		Consent consent = action.isProposedArgumentSetValid(objectAdapter, argArray);
-		if(consent.isVetoed()) {
-			throw new WebApplicationException(responseOfPreconditionFailed(consent.getReason()));
-		}
-		
-		final ObjectAdapter returnedAdapter = action.execute(objectAdapter, argArray);
-		if(returnedAdapter == null) {
-	        return responseOfOk();
-		}
-		final CollectionFacet facet = returnedAdapter.getSpecification().getFacet(CollectionFacet.class);
-		if(facet != null) {
-			final Collection<ObjectAdapter> collectionAdapters = facet.collection(returnedAdapter);
-			return jsonRepresentationOf(collectionAdapters);
-		} else {
-			return jsonRepresentationOf(returnedAdapter);
-		}
-	}
-
-
-	private static String resourceFor(ObjectSpecification paramSpec) {
-		// TODO: should return a string in the form http://localhost:8080/types/xxx
-		return paramSpec.getFullIdentifier();
-	}
-
-	private enum Intent {
-		ACCESS,
-		MUTATE;
-
-		public boolean isMutate() {
-			return this == MUTATE;
-		}
-	}
-
-	private OneToOneAssociation getPropertyThatIsVisibleAndUsable(
-			final ObjectAdapter objectAdapter, final String propertyId, final Intent intent) {
-		ObjectAssociation association = objectAdapter.getSpecification().getAssociation(propertyId);
-        if(association == null || !association.isOneToOneAssociation()) { 
+    private static String resourceFor(ObjectSpecification objectSpec) {
+        // TODO: should return a string in the form
+        // http://localhost:8080/types/xxx
+        return objectSpec.getFullIdentifier();
+    }
+
+    private enum Intent {
+        ACCESS, MUTATE;
+
+        public boolean isMutate() {
+            return this == MUTATE;
+        }
+    }
+
+    private OneToOneAssociation getPropertyThatIsVisibleAndUsable(
+        final ObjectAdapter objectAdapter, final String propertyId,
+        final Intent intent) {
+        ObjectAssociation association = objectAdapter.getSpecification()
+                .getAssociation(propertyId);
+        if (association == null || !association.isOneToOneAssociation()) {
             throwNotFoundException(propertyId, MemberType.PROPERTY);
         }
         OneToOneAssociation property = (OneToOneAssociation) association;
-        return ensureVisibleAndUsableForIntent(objectAdapter, property, MemberType.PROPERTY, intent);
-	}
+        return ensureVisibleAndUsableForIntent(objectAdapter, property,
+                MemberType.PROPERTY, intent);
+    }
 
-	private OneToManyAssociation getCollectionThatIsVisibleAndUsable(
-			final ObjectAdapter objectAdapter, final String collectionId, final Intent intent) {
-		ObjectAssociation association = objectAdapter.getSpecification().getAssociation(collectionId);
-        if(association == null || !association.isOneToManyAssociation()) {
+    private OneToManyAssociation getCollectionThatIsVisibleAndUsable(
+        final ObjectAdapter objectAdapter, 
+        final String collectionId,
+        final Intent intent) {
+        
+        ObjectAssociation association = objectAdapter.getSpecification()
+                .getAssociation(collectionId);
+        if (association == null || !association.isOneToManyAssociation()) {
             throwNotFoundException(collectionId, MemberType.COLLECTION);
         }
         OneToManyAssociation collection = (OneToManyAssociation) association;
-        return ensureVisibleAndUsableForIntent(objectAdapter, collection, MemberType.COLLECTION, intent);
-	}
+        return ensureVisibleAndUsableForIntent(objectAdapter, collection,
+                MemberType.COLLECTION, intent);
+    }
+
+    private ObjectAction getObjectActionThatIsVisibleAndUsable(
+        final ObjectAdapter objectAdapter,
+        final String actionId,
+        Intent intent) {
+        
+        ObjectAction action = objectAdapter.getSpecification().getObjectAction(actionId);
+        return ensureVisibleAndUsableForIntent(objectAdapter, action, MemberType.ACTION, intent);
+    }
 
-	private ObjectAction getObjectActionThatIsVisibleAndUsable(final ObjectAdapter objectAdapter,
-			final String actionId, Intent intent) {
-		ObjectAction action = objectAdapter.getSpecification().getObjectAction(actionId);
-		return ensureVisibleAndUsableForIntent(objectAdapter, action, MemberType.ACTION, intent);
-	}
-
-	public <T extends ObjectMember> T ensureVisibleAndUsableForIntent(
-			final ObjectAdapter objectAdapter, T objectMember, MemberType memberType, Intent intent) {
-		String memberId = objectMember.getId();
-        if(objectMember.isVisible(getSession(), objectAdapter).isVetoed()) {
+    public <T extends ObjectMember> T ensureVisibleAndUsableForIntent(
+        final ObjectAdapter objectAdapter, T objectMember,
+        MemberType memberType, Intent intent) {
+        String memberId = objectMember.getId();
+        if (objectMember.isVisible(getSession(), objectAdapter).isVetoed()) {
             throwNotFoundException(memberId, memberType);
         }
-		if(intent.isMutate()) {
-	        Consent usable = objectMember.isUsable(getSession(), objectAdapter);
-			if(usable.isVetoed()) {
-				String memberTypeStr = memberType.name().toLowerCase();
-				throw new WebApplicationException(responseOfPreconditionFailed(
-				        memberTypeStr + " is not usable: '" + memberId + "' (" + usable.getReason() + ")"));
-			}
+        if (intent.isMutate()) {
+            Consent usable = objectMember.isUsable(getSession(), objectAdapter);
+            if (usable.isVetoed()) {
+                String memberTypeStr = memberType.name().toLowerCase();
+                throw new WebApplicationException(
+                        responseOfPreconditionFailed(memberTypeStr
+                                + " is not usable: '" + memberId + "' ("
+                                + usable.getReason() + ")"));
+            }
         }
         return objectMember;
-	}
+    }
 
-    private static void throwNotFoundException(final String memberId, MemberType memberType) {
+    private static void throwNotFoundException(final String memberId,
+        MemberType memberType) {
         String memberTypeStr = memberType.name().toLowerCase();
-        throw new WebApplicationException(responseOfNotFound(
-                memberTypeStr + " '" + memberId + "' either does not exist or is not visible"));
+        throw new WebApplicationException(
+                responseOfNotFound(memberTypeStr + " '" + memberId
+                        + "' either does not exist or is not visible"));
     }
 
 }