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 2014/07/11 12:56:56 UTC

[44/61] [partial] ISIS-831: digression: moving progmodel/facets into metamodel/facets

http://git-wip-us.apache.org/repos/asf/isis/blob/7227418b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/collections/modify/CollectionAddToFacetViaAccessor.java
----------------------------------------------------------------------
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/collections/modify/CollectionAddToFacetViaAccessor.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/collections/modify/CollectionAddToFacetViaAccessor.java
new file mode 100644
index 0000000..467cf16
--- /dev/null
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/collections/modify/CollectionAddToFacetViaAccessor.java
@@ -0,0 +1,98 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+
+package org.apache.isis.core.metamodel.facets.collections.modify;
+
+import java.lang.reflect.Method;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
+import org.apache.isis.applib.DomainObjectContainer;
+import org.apache.isis.core.metamodel.adapter.ObjectAdapter;
+import org.apache.isis.core.metamodel.adapter.ObjectDirtier;
+import org.apache.isis.core.metamodel.adapter.util.AdapterInvokeUtils;
+import org.apache.isis.core.metamodel.adapter.util.AdapterUtils;
+import org.apache.isis.core.metamodel.facetapi.FacetHolder;
+import org.apache.isis.core.metamodel.facets.ImperativeFacet;
+import org.apache.isis.core.metamodel.facets.collections.modify.CollectionAddToFacetAbstract;
+
+public class CollectionAddToFacetViaAccessor extends CollectionAddToFacetAbstract implements ImperativeFacet {
+
+    private final Method method;
+    private final ObjectDirtier objectDirtier;
+
+    public CollectionAddToFacetViaAccessor(final Method method, final FacetHolder holder, final ObjectDirtier objectDirtier) {
+        super(holder);
+        this.method = method;
+        this.objectDirtier = objectDirtier;
+    }
+
+    /**
+     * Returns a singleton list of the {@link Method} provided in the
+     * constructor.
+     */
+    @Override
+    public List<Method> getMethods() {
+        return Collections.singletonList(method);
+    }
+
+    @Override
+    public Intent getIntent(final Method method) {
+        return Intent.MODIFY_COLLECTION_ADD;
+    }
+
+    @Override
+    public boolean impliesResolve() {
+        return true;
+    }
+
+    /**
+     * Bytecode cannot automatically call
+     * {@link DomainObjectContainer#objectChanged(Object)} because cannot
+     * distinguish whether interacting with accessor to read it or to modify its
+     * contents.
+     */
+    @Override
+    public boolean impliesObjectChanged() {
+        return false;
+    }
+
+    @Override
+    public void add(final ObjectAdapter owningAdapter, final ObjectAdapter elementAdapter) {
+        @SuppressWarnings("unchecked")
+        final Collection<? super Object> collection = (Collection<? super Object>) AdapterInvokeUtils.invoke(method, owningAdapter);
+        final Object elementPojo = AdapterUtils.unwrap(elementAdapter);
+        collection.add(elementPojo);
+        getObjectDirtier().objectChanged(owningAdapter);
+    }
+
+    @Override
+    protected String toStringValues() {
+        return "method=" + method;
+    }
+
+    // /////////////////////////////////////////////////////
+    // Dependencies
+    // /////////////////////////////////////////////////////
+
+    protected ObjectDirtier getObjectDirtier() {
+        return objectDirtier;
+    }
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/7227418b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/collections/modify/CollectionAddToFacetViaMethod.java
----------------------------------------------------------------------
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/collections/modify/CollectionAddToFacetViaMethod.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/collections/modify/CollectionAddToFacetViaMethod.java
new file mode 100644
index 0000000..98609ed
--- /dev/null
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/collections/modify/CollectionAddToFacetViaMethod.java
@@ -0,0 +1,75 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+
+package org.apache.isis.core.metamodel.facets.collections.modify;
+
+import java.lang.reflect.Method;
+import java.util.Collections;
+import java.util.List;
+
+import org.apache.isis.core.metamodel.adapter.ObjectAdapter;
+import org.apache.isis.core.metamodel.adapter.util.AdapterInvokeUtils;
+import org.apache.isis.core.metamodel.facetapi.FacetHolder;
+import org.apache.isis.core.metamodel.facets.ImperativeFacet;
+import org.apache.isis.core.metamodel.facets.collections.modify.CollectionAddToFacetAbstract;
+
+public class CollectionAddToFacetViaMethod extends CollectionAddToFacetAbstract implements ImperativeFacet {
+
+    private final Method method;
+
+    public CollectionAddToFacetViaMethod(final Method method, final FacetHolder holder) {
+        super(holder);
+        this.method = method;
+    }
+
+    /**
+     * Returns a singleton list of the {@link Method} provided in the
+     * constructor.
+     */
+    @Override
+    public List<Method> getMethods() {
+        return Collections.singletonList(method);
+    }
+
+    @Override
+    public Intent getIntent(final Method method) {
+        return Intent.MODIFY_COLLECTION_ADD;
+    }
+
+    @Override
+    public boolean impliesResolve() {
+        return true;
+    }
+
+    @Override
+    public boolean impliesObjectChanged() {
+        return true;
+    }
+
+    @Override
+    public void add(final ObjectAdapter owningAdapter, final ObjectAdapter elementAdapter) {
+        AdapterInvokeUtils.invoke(method, owningAdapter, elementAdapter);
+    }
+
+    @Override
+    protected String toStringValues() {
+        return "method=" + method;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/7227418b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/collections/modify/CollectionAddToRemoveFromAndValidateFacetFactory.java
----------------------------------------------------------------------
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/collections/modify/CollectionAddToRemoveFromAndValidateFacetFactory.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/collections/modify/CollectionAddToRemoveFromAndValidateFacetFactory.java
new file mode 100644
index 0000000..5184e9d
--- /dev/null
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/collections/modify/CollectionAddToRemoveFromAndValidateFacetFactory.java
@@ -0,0 +1,183 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+
+package org.apache.isis.core.metamodel.facets.collections.modify;
+
+import java.lang.reflect.Method;
+
+import org.apache.isis.core.commons.lang.StringExtensions;
+import org.apache.isis.core.metamodel.adapter.ObjectDirtier;
+import org.apache.isis.core.metamodel.adapter.ObjectDirtierAware;
+import org.apache.isis.core.metamodel.exceptions.MetaModelException;
+import org.apache.isis.core.metamodel.facetapi.FacetHolder;
+import org.apache.isis.core.metamodel.facetapi.FacetUtil;
+import org.apache.isis.core.metamodel.facetapi.FeatureType;
+import org.apache.isis.core.metamodel.facets.FacetFactory;
+import org.apache.isis.core.metamodel.methodutils.MethodScope;
+import org.apache.isis.core.metamodel.facets.MethodFinderUtils;
+import org.apache.isis.core.metamodel.facets.MethodPrefixBasedFacetFactoryAbstract;
+import org.apache.isis.core.metamodel.facets.MethodPrefixConstants;
+import org.apache.isis.core.metamodel.facets.collections.validate.CollectionValidateAddToFacetViaMethod;
+import org.apache.isis.core.metamodel.facets.collections.validate.CollectionValidateRemoveFromFacetViaMethod;
+
+/**
+ * TODO: should probably split out into two {@link FacetFactory}s, one for
+ * <tt>addTo()</tt>/<tt>removeFrom()</tt> and one for <tt>validateAddTo()</tt>/
+ * <tt>validateRemoveFrom()</tt>.
+ */
+public class CollectionAddToRemoveFromAndValidateFacetFactory extends MethodPrefixBasedFacetFactoryAbstract implements ObjectDirtierAware {
+
+    private static final String[] PREFIXES = {};
+
+    private ObjectDirtier objectDirtier;
+
+    public CollectionAddToRemoveFromAndValidateFacetFactory() {
+        super(FeatureType.COLLECTIONS_ONLY, OrphanValidation.VALIDATE, PREFIXES);
+    }
+
+    @Override
+    public void process(final ProcessMethodContext processMethodContext) {
+
+        final Class<?> collectionType = attachAddToFacetAndRemoveFromFacet(processMethodContext);
+        attachValidateAddToAndRemoveFromFacetIfMethodsFound(processMethodContext, collectionType);
+    }
+
+    private Class<?> attachAddToFacetAndRemoveFromFacet(final ProcessMethodContext processMethodContext) {
+
+        final Method accessorMethod = processMethodContext.getMethod();
+        final String capitalizedName = StringExtensions.asJavaBaseName(accessorMethod.getName());
+
+        final Class<?> cls = processMethodContext.getCls();
+
+        // add
+        final Method addToMethod = MethodFinderUtils.findMethod(cls, MethodScope.OBJECT, MethodPrefixConstants.ADD_TO_PREFIX + capitalizedName, void.class);
+        processMethodContext.removeMethod(addToMethod);
+
+        // remove
+        final Method removeFromMethod = MethodFinderUtils.findMethod(cls, MethodScope.OBJECT, MethodPrefixConstants.REMOVE_FROM_PREFIX + capitalizedName, void.class);
+        processMethodContext.removeMethod(removeFromMethod);
+
+        // add facets
+        final FacetHolder collection = processMethodContext.getFacetHolder();
+        FacetUtil.addFacet(createAddToFacet(addToMethod, accessorMethod, collection));
+        FacetUtil.addFacet(createRemoveFromFacet(removeFromMethod, accessorMethod, collection));
+
+        // infer typ
+        final Class<?> addToType = ((addToMethod == null || addToMethod.getParameterTypes().length != 1) ? null : addToMethod.getParameterTypes()[0]);
+        final Class<?> removeFromType = ((removeFromMethod == null || removeFromMethod.getParameterTypes().length != 1) ? null : removeFromMethod.getParameterTypes()[0]);
+
+        return inferTypeOfIfPossible(accessorMethod, addToType, removeFromType, collection);
+    }
+
+    /**
+     * TODO need to distinguish between Java collections, arrays and other
+     * collections!
+     */
+    private CollectionAddToFacet createAddToFacet(final Method addToMethodIfAny, final Method accessorMethod, final FacetHolder holder) {
+        if (addToMethodIfAny != null) {
+            return new CollectionAddToFacetViaMethod(addToMethodIfAny, holder);
+        } else {
+            return new CollectionAddToFacetViaAccessor(accessorMethod, holder, getObjectDirtier());
+        }
+    }
+
+    /**
+     * TODO need to distinguish between Java collections, arrays and other
+     * collections!
+     */
+    private CollectionRemoveFromFacet createRemoveFromFacet(final Method removeFromMethodIfAny, final Method accessorMethod, final FacetHolder holder) {
+        if (removeFromMethodIfAny != null) {
+            return new CollectionRemoveFromFacetViaMethod(removeFromMethodIfAny, holder);
+        } else {
+            return new CollectionRemoveFromFacetViaAccessor(accessorMethod, holder, getObjectDirtier());
+        }
+    }
+
+    private Class<?> inferTypeOfIfPossible(final Method getMethod, final Class<?> addType, final Class<?> removeType, final FacetHolder collection) {
+
+        if (addType != null && removeType != null && addType != removeType) {
+            throw new MetaModelException("The addTo/removeFrom methods for " + getMethod.getDeclaringClass() + " must " + "both deal with same type of object: " + addType + "; " + removeType);
+        }
+
+        final Class<?> type = addType != null ? addType : removeType;
+        if (type != null) {
+            FacetUtil.addFacet(new TypeOfFacetInferredFromSupportingMethods(type, collection, getSpecificationLoader()));
+        }
+        return type;
+    }
+
+    private void attachValidateAddToAndRemoveFromFacetIfMethodsFound(final ProcessMethodContext processMethodContext, final Class<?> collectionType) {
+        attachValidateAddToFacetIfValidateAddToMethodIsFound(processMethodContext, collectionType);
+        attachValidateRemoveFacetIfValidateRemoveFromMethodIsFound(processMethodContext, collectionType);
+    }
+
+    private void attachValidateAddToFacetIfValidateAddToMethodIsFound(final ProcessMethodContext processMethodContext, final Class<?> collectionType) {
+
+        final Method getMethod = processMethodContext.getMethod();
+        final String capitalizedName = StringExtensions.asJavaBaseName(getMethod.getName());
+
+        final Class<?> cls = processMethodContext.getCls();
+        final Class<?>[] paramTypes = MethodFinderUtils.paramTypesOrNull(collectionType);
+        Method validateAddToMethod = MethodFinderUtils.findMethod(cls, MethodScope.OBJECT, MethodPrefixConstants.VALIDATE_ADD_TO_PREFIX + capitalizedName, String.class, paramTypes);
+        if (validateAddToMethod == null) {
+            validateAddToMethod = MethodFinderUtils.findMethod(cls, MethodScope.OBJECT, MethodPrefixConstants.VALIDATE_ADD_TO_PREFIX_2 + capitalizedName, String.class, MethodFinderUtils.paramTypesOrNull(collectionType));
+        }
+        if (validateAddToMethod == null) {
+            return;
+        }
+        processMethodContext.removeMethod(validateAddToMethod);
+
+        final FacetHolder collection = processMethodContext.getFacetHolder();
+        FacetUtil.addFacet(new CollectionValidateAddToFacetViaMethod(validateAddToMethod, collection));
+    }
+
+    private void attachValidateRemoveFacetIfValidateRemoveFromMethodIsFound(final ProcessMethodContext processMethodContext, final Class<?> collectionType) {
+
+        final Method getMethod = processMethodContext.getMethod();
+        final String capitalizedName = StringExtensions.asJavaBaseName(getMethod.getName());
+
+        final Class<?> cls = processMethodContext.getCls();
+        final Class<?>[] paramTypes = MethodFinderUtils.paramTypesOrNull(collectionType);
+        Method validateRemoveFromMethod = MethodFinderUtils.findMethod(cls, MethodScope.OBJECT, MethodPrefixConstants.VALIDATE_REMOVE_FROM_PREFIX + capitalizedName, String.class, paramTypes);
+        if (validateRemoveFromMethod == null) {
+            validateRemoveFromMethod = MethodFinderUtils.findMethod(cls, MethodScope.OBJECT, MethodPrefixConstants.VALIDATE_REMOVE_FROM_PREFIX_2 + capitalizedName, String.class, MethodFinderUtils.paramTypesOrNull(collectionType));
+        }
+        if (validateRemoveFromMethod == null) {
+            return;
+        }
+        processMethodContext.removeMethod(validateRemoveFromMethod);
+
+        final FacetHolder collection = processMethodContext.getFacetHolder();
+        FacetUtil.addFacet(new CollectionValidateRemoveFromFacetViaMethod(validateRemoveFromMethod, collection));
+    }
+
+    // ///////////////////////////////////////////////////////
+    // Dependencies (injected)
+    // ///////////////////////////////////////////////////////
+
+    protected ObjectDirtier getObjectDirtier() {
+        return objectDirtier;
+    }
+
+    @Override
+    public void setObjectDirtier(final ObjectDirtier objectDirtier) {
+        this.objectDirtier = objectDirtier;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/7227418b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/collections/modify/CollectionFacet.java
----------------------------------------------------------------------
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/collections/modify/CollectionFacet.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/collections/modify/CollectionFacet.java
index 519913f..0ca3f72 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/collections/modify/CollectionFacet.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/collections/modify/CollectionFacet.java
@@ -19,12 +19,14 @@
 
 package org.apache.isis.core.metamodel.facets.collections.modify;
 
+import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Iterator;
+import java.util.List;
 
 import org.apache.isis.core.metamodel.adapter.ObjectAdapter;
 import org.apache.isis.core.metamodel.facetapi.Facet;
-import org.apache.isis.core.metamodel.facets.typeof.TypeOfFacet;
+import org.apache.isis.core.metamodel.facets.actcoll.typeof.TypeOfFacet;
 import org.apache.isis.core.metamodel.spec.ObjectSpecification;
 
 /**
@@ -63,4 +65,27 @@ public interface CollectionFacet extends Facet {
      */
     TypeOfFacet getTypeOfFacet();
 
+
+    public static class Utils {
+
+        public static CollectionFacet getCollectionFacetFromSpec(final ObjectAdapter objectRepresentingCollection) {
+            final ObjectSpecification collectionSpec = objectRepresentingCollection.getSpecification();
+            return collectionSpec.getFacet(CollectionFacet.class);
+        }
+
+        public static int size(final ObjectAdapter collection) {
+            final CollectionFacet facet = getCollectionFacetFromSpec(collection);
+            return facet.size(collection);
+        }
+
+        public static List<ObjectAdapter> convertToAdapterList(final ObjectAdapter collection) {
+            final CollectionFacet facet = getCollectionFacetFromSpec(collection);
+            final List<ObjectAdapter> adapters = new ArrayList<ObjectAdapter>();
+            for (final ObjectAdapter adapter : facet.iterable(collection)) {
+                adapters.add(adapter);
+            }
+            return adapters;
+        }
+
+    }
 }

http://git-wip-us.apache.org/repos/asf/isis/blob/7227418b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/collections/modify/CollectionFacetUtils.java
----------------------------------------------------------------------
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/collections/modify/CollectionFacetUtils.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/collections/modify/CollectionFacetUtils.java
index 7ce17f9..fed2206 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/collections/modify/CollectionFacetUtils.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/collections/modify/CollectionFacetUtils.java
@@ -19,55 +19,28 @@
 
 package org.apache.isis.core.metamodel.facets.collections.modify;
 
-import java.util.ArrayList;
 import java.util.List;
-
 import org.apache.isis.core.metamodel.adapter.ObjectAdapter;
-import org.apache.isis.core.metamodel.spec.ObjectSpecification;
 
+@Deprecated
 public final class CollectionFacetUtils {
+
     private CollectionFacetUtils() {
     }
 
+    @Deprecated
     public static CollectionFacet getCollectionFacetFromSpec(final ObjectAdapter objectRepresentingCollection) {
-        final ObjectSpecification collectionSpec = objectRepresentingCollection.getSpecification();
-        return collectionSpec.getFacet(CollectionFacet.class);
+        return CollectionFacet.Utils.getCollectionFacetFromSpec(objectRepresentingCollection);
     }
 
+    @Deprecated
     public static int size(final ObjectAdapter collection) {
-        final CollectionFacet facet = getCollectionFacetFromSpec(collection);
-        return facet.size(collection);
-    }
-
-    public static ObjectAdapter firstElement(final ObjectAdapter collection) {
-        final CollectionFacet facet = getCollectionFacetFromSpec(collection);
-        return facet.firstElement(collection);
+        return CollectionFacet.Utils.size(collection);
     }
 
-    /**
-     * @deprecated - use instead {@link #convertToList(ObjectAdapter)}.
-     */
     @Deprecated
-    public static Object[] convertToArray(final ObjectAdapter collection) {
-        return convertToList(collection).toArray();
-    }
-
-    public static List<Object> convertToList(final ObjectAdapter collection) {
-        final CollectionFacet facet = getCollectionFacetFromSpec(collection);
-        final List<Object> objects = new ArrayList<Object>();
-        for (final ObjectAdapter adapter : facet.iterable(collection)) {
-            objects.add(adapter.getObject());
-        }
-        return objects;
-    }
-
     public static List<ObjectAdapter> convertToAdapterList(final ObjectAdapter collection) {
-        final CollectionFacet facet = getCollectionFacetFromSpec(collection);
-        final List<ObjectAdapter> adapters = new ArrayList<ObjectAdapter>();
-        for (final ObjectAdapter adapter : facet.iterable(collection)) {
-            adapters.add(adapter);
-        }
-        return adapters;
+        return CollectionFacet.Utils.convertToAdapterList(collection);
     }
 
 }

http://git-wip-us.apache.org/repos/asf/isis/blob/7227418b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/collections/modify/CollectionRemoveFromFacetViaAccessor.java
----------------------------------------------------------------------
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/collections/modify/CollectionRemoveFromFacetViaAccessor.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/collections/modify/CollectionRemoveFromFacetViaAccessor.java
new file mode 100644
index 0000000..72ba192
--- /dev/null
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/collections/modify/CollectionRemoveFromFacetViaAccessor.java
@@ -0,0 +1,99 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+
+package org.apache.isis.core.metamodel.facets.collections.modify;
+
+import java.lang.reflect.Method;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
+import org.apache.isis.applib.DomainObjectContainer;
+import org.apache.isis.core.metamodel.adapter.ObjectAdapter;
+import org.apache.isis.core.metamodel.adapter.ObjectDirtier;
+import org.apache.isis.core.metamodel.adapter.util.AdapterInvokeUtils;
+import org.apache.isis.core.metamodel.adapter.util.AdapterUtils;
+import org.apache.isis.core.metamodel.facetapi.FacetHolder;
+import org.apache.isis.core.metamodel.facets.ImperativeFacet;
+import org.apache.isis.core.metamodel.facets.ImperativeFacet.Intent;
+import org.apache.isis.core.metamodel.facets.collections.modify.CollectionRemoveFromFacetAbstract;
+
+public class CollectionRemoveFromFacetViaAccessor extends CollectionRemoveFromFacetAbstract implements ImperativeFacet {
+
+    private final Method method;
+    private final ObjectDirtier objectDirtier;
+
+    public CollectionRemoveFromFacetViaAccessor(final Method method, final FacetHolder holder, final ObjectDirtier objectDirtier) {
+        super(holder);
+        this.method = method;
+        this.objectDirtier = objectDirtier;
+    }
+
+    /**
+     * Returns a singleton list of the {@link Method} provided in the
+     * constructor.
+     */
+    @Override
+    public List<Method> getMethods() {
+        return Collections.singletonList(method);
+    }
+
+    @Override
+    public Intent getIntent(final Method method) {
+        return Intent.MODIFY_COLLECTION_REMOVE;
+    }
+
+    @Override
+    public boolean impliesResolve() {
+        return true;
+    }
+
+    /**
+     * Bytecode cannot automatically call
+     * {@link DomainObjectContainer#objectChanged(Object)} because cannot
+     * distinguish whether interacting with accessor to read it or to modify its
+     * contents.
+     */
+    @Override
+    public boolean impliesObjectChanged() {
+        return false;
+    }
+
+    @Override
+    public void remove(final ObjectAdapter owningAdapter, final ObjectAdapter elementAdapter) {
+        @SuppressWarnings("unchecked")
+        final Collection<? super Object> collection = (Collection<? super Object>) AdapterInvokeUtils.invoke(method, owningAdapter);
+        collection.remove(AdapterUtils.unwrap(elementAdapter));
+        getObjectDirtier().objectChanged(owningAdapter);
+    }
+
+    @Override
+    protected String toStringValues() {
+        return "method=" + method;
+    }
+
+    // /////////////////////////////////////////////////////////
+    // Dependencies (from constructor)
+    // /////////////////////////////////////////////////////////
+
+    protected ObjectDirtier getObjectDirtier() {
+        return objectDirtier;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/7227418b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/collections/modify/CollectionRemoveFromFacetViaMethod.java
----------------------------------------------------------------------
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/collections/modify/CollectionRemoveFromFacetViaMethod.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/collections/modify/CollectionRemoveFromFacetViaMethod.java
new file mode 100644
index 0000000..23dfe30
--- /dev/null
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/collections/modify/CollectionRemoveFromFacetViaMethod.java
@@ -0,0 +1,76 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+
+package org.apache.isis.core.metamodel.facets.collections.modify;
+
+import java.lang.reflect.Method;
+import java.util.Collections;
+import java.util.List;
+
+import org.apache.isis.core.metamodel.adapter.ObjectAdapter;
+import org.apache.isis.core.metamodel.adapter.util.AdapterInvokeUtils;
+import org.apache.isis.core.metamodel.facetapi.FacetHolder;
+import org.apache.isis.core.metamodel.facets.ImperativeFacet;
+import org.apache.isis.core.metamodel.facets.ImperativeFacet.Intent;
+import org.apache.isis.core.metamodel.facets.collections.modify.CollectionRemoveFromFacetAbstract;
+
+public class CollectionRemoveFromFacetViaMethod extends CollectionRemoveFromFacetAbstract implements ImperativeFacet {
+
+    private final Method method;
+
+    public CollectionRemoveFromFacetViaMethod(final Method method, final FacetHolder holder) {
+        super(holder);
+        this.method = method;
+    }
+
+    /**
+     * Returns a singleton list of the {@link Method} provided in the
+     * constructor.
+     */
+    @Override
+    public List<Method> getMethods() {
+        return Collections.singletonList(method);
+    }
+
+    @Override
+    public Intent getIntent(final Method method) {
+        return Intent.MODIFY_COLLECTION_REMOVE;
+    }
+
+    @Override
+    public boolean impliesResolve() {
+        return true;
+    }
+
+    @Override
+    public boolean impliesObjectChanged() {
+        return true;
+    }
+
+    @Override
+    public void remove(final ObjectAdapter owningAdapter, final ObjectAdapter elementAdapter) {
+        AdapterInvokeUtils.invoke(method, owningAdapter, elementAdapter);
+    }
+
+    @Override
+    protected String toStringValues() {
+        return "method=" + method;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/7227418b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/collections/modify/TypeOfFacetInferredFromSupportingMethods.java
----------------------------------------------------------------------
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/collections/modify/TypeOfFacetInferredFromSupportingMethods.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/collections/modify/TypeOfFacetInferredFromSupportingMethods.java
new file mode 100644
index 0000000..f91c59e
--- /dev/null
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/collections/modify/TypeOfFacetInferredFromSupportingMethods.java
@@ -0,0 +1,32 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+
+package org.apache.isis.core.metamodel.facets.collections.modify;
+
+import org.apache.isis.core.metamodel.facetapi.FacetHolder;
+import org.apache.isis.core.metamodel.facets.actcoll.typeof.TypeOfFacetAbstract;
+import org.apache.isis.core.metamodel.spec.SpecificationLoader;
+
+public class TypeOfFacetInferredFromSupportingMethods extends TypeOfFacetAbstract {
+
+    public TypeOfFacetInferredFromSupportingMethods(final Class<?> type, final FacetHolder holder, final SpecificationLoader specificationLookup) {
+        super(type, holder, specificationLookup);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/7227418b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/collections/notpersisted/annotation/NotPersistedFacetOnCollectionAnnotation.java
----------------------------------------------------------------------
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/collections/notpersisted/annotation/NotPersistedFacetOnCollectionAnnotation.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/collections/notpersisted/annotation/NotPersistedFacetOnCollectionAnnotation.java
new file mode 100644
index 0000000..fa7110c
--- /dev/null
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/collections/notpersisted/annotation/NotPersistedFacetOnCollectionAnnotation.java
@@ -0,0 +1,31 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+
+package org.apache.isis.core.metamodel.facets.collections.notpersisted.annotation;
+
+import org.apache.isis.core.metamodel.facetapi.FacetHolder;
+import org.apache.isis.core.metamodel.facets.propcoll.notpersisted.NotPersistedFacetAbstract;
+
+public class NotPersistedFacetOnCollectionAnnotation extends NotPersistedFacetAbstract {
+
+    public NotPersistedFacetOnCollectionAnnotation(final FacetHolder holder) {
+        super(holder);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/7227418b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/collections/notpersisted/annotation/NotPersistedFacetOnCollectionAnnotationFactory.java
----------------------------------------------------------------------
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/collections/notpersisted/annotation/NotPersistedFacetOnCollectionAnnotationFactory.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/collections/notpersisted/annotation/NotPersistedFacetOnCollectionAnnotationFactory.java
new file mode 100644
index 0000000..3f9fd75
--- /dev/null
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/collections/notpersisted/annotation/NotPersistedFacetOnCollectionAnnotationFactory.java
@@ -0,0 +1,46 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+
+package org.apache.isis.core.metamodel.facets.collections.notpersisted.annotation;
+
+import org.apache.isis.applib.annotation.NotPersisted;
+import org.apache.isis.core.metamodel.facetapi.FacetHolder;
+import org.apache.isis.core.metamodel.facetapi.FacetUtil;
+import org.apache.isis.core.metamodel.facetapi.FeatureType;
+import org.apache.isis.core.metamodel.facets.Annotations;
+import org.apache.isis.core.metamodel.facets.FacetFactoryAbstract;
+import org.apache.isis.core.metamodel.facets.propcoll.notpersisted.NotPersistedFacet;
+
+public class NotPersistedFacetOnCollectionAnnotationFactory extends FacetFactoryAbstract {
+
+    public NotPersistedFacetOnCollectionAnnotationFactory() {
+        super(FeatureType.COLLECTIONS_ONLY);
+    }
+
+    @Override
+    public void process(final ProcessMethodContext processMethodContext) {
+        final NotPersisted annotation = Annotations.getAnnotation(processMethodContext.getMethod(), NotPersisted.class);
+        FacetUtil.addFacet(create(annotation, processMethodContext.getFacetHolder()));
+    }
+
+    private NotPersistedFacet create(final NotPersisted annotation, final FacetHolder holder) {
+        return annotation == null ? null : new NotPersistedFacetOnCollectionAnnotation(holder);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/7227418b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/collections/paged/annotation/PagedFacetOnCollectionFactory.java
----------------------------------------------------------------------
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/collections/paged/annotation/PagedFacetOnCollectionFactory.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/collections/paged/annotation/PagedFacetOnCollectionFactory.java
new file mode 100644
index 0000000..ec53b89
--- /dev/null
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/collections/paged/annotation/PagedFacetOnCollectionFactory.java
@@ -0,0 +1,86 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+
+package org.apache.isis.core.metamodel.facets.collections.paged.annotation;
+
+import java.util.Properties;
+
+import org.apache.isis.applib.annotation.Paged;
+import org.apache.isis.core.commons.config.IsisConfiguration;
+import org.apache.isis.core.commons.config.IsisConfigurationAware;
+import org.apache.isis.core.metamodel.facetapi.FacetUtil;
+import org.apache.isis.core.metamodel.facetapi.FeatureType;
+import org.apache.isis.core.metamodel.facets.Annotations;
+import org.apache.isis.core.metamodel.facets.ContributeeMemberFacetFactory;
+import org.apache.isis.core.metamodel.facets.FacetFactoryAbstract;
+import org.apache.isis.core.metamodel.facets.objcoll.paged.PagedFacetAnnotation;
+import org.apache.isis.core.metamodel.facets.objcoll.paged.PagedFacetProperties;
+import org.apache.isis.core.metamodel.facets.object.paged.PagedFacet;
+
+public class PagedFacetOnCollectionFactory extends FacetFactoryAbstract
+        implements ContributeeMemberFacetFactory, IsisConfigurationAware {
+
+    private IsisConfiguration configuration;
+
+    public PagedFacetOnCollectionFactory() {
+        super(FeatureType.COLLECTIONS_ONLY);
+    }
+
+    @Override
+    public void process(final ProcessMethodContext processMethodContext) {
+        
+        PagedFacet pagedFacet = createFromMetadataPropertiesIfPossible(processMethodContext);
+        if(pagedFacet == null) {
+            pagedFacet = createFromPagedAnnotationIfPossible(processMethodContext);
+        }
+        // no-op if null
+        FacetUtil.addFacet(pagedFacet);
+    }
+
+    @Override
+    public void process(ProcessContributeeMemberContext processMemberContext) {
+        final PagedFacet pagedFacet = createFromMetadataPropertiesIfPossible(processMemberContext);
+        // no-op if null
+        FacetUtil.addFacet(pagedFacet);
+    }
+
+    private PagedFacet createFromMetadataPropertiesIfPossible(final ProcessContextWithMetadataProperties<?> processMethodContext) {
+        final Properties properties = processMethodContext.metadataProperties("paged");
+        return properties != null ? new PagedFacetProperties(properties, processMethodContext.getFacetHolder()) : null;
+    }
+
+    private PagedFacet createFromPagedAnnotationIfPossible(final ProcessMethodContext processMethodContext) {
+        final Paged annotation = Annotations.getAnnotation(processMethodContext.getMethod(), Paged.class);
+        return annotation != null ? new PagedFacetAnnotation(processMethodContext.getFacetHolder(), annotation.value()) : null;
+    }
+
+
+    // //////////////////////////////////////
+
+    public IsisConfiguration getConfiguration() {
+        return configuration;
+    }
+
+    @Override
+    public void setConfiguration(IsisConfiguration configuration) {
+        this.configuration = configuration;
+    }
+
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/7227418b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/collections/parented/ParentedFacetSinceCollection.java
----------------------------------------------------------------------
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/collections/parented/ParentedFacetSinceCollection.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/collections/parented/ParentedFacetSinceCollection.java
new file mode 100644
index 0000000..385db0f
--- /dev/null
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/collections/parented/ParentedFacetSinceCollection.java
@@ -0,0 +1,31 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+
+package org.apache.isis.core.metamodel.facets.collections.parented;
+
+import org.apache.isis.core.metamodel.facetapi.FacetHolder;
+import org.apache.isis.core.metamodel.facets.object.parented.ParentedFacetAbstract;
+
+public class ParentedFacetSinceCollection extends ParentedFacetAbstract {
+
+    public ParentedFacetSinceCollection(final FacetHolder holder) {
+        super(holder);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/7227418b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/collections/parented/ParentedFacetSinceCollectionFactory.java
----------------------------------------------------------------------
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/collections/parented/ParentedFacetSinceCollectionFactory.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/collections/parented/ParentedFacetSinceCollectionFactory.java
new file mode 100644
index 0000000..2097222
--- /dev/null
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/collections/parented/ParentedFacetSinceCollectionFactory.java
@@ -0,0 +1,53 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+
+package org.apache.isis.core.metamodel.facets.collections.parented;
+
+import org.apache.isis.core.metamodel.facetapi.FacetUtil;
+import org.apache.isis.core.metamodel.facetapi.FeatureType;
+import org.apache.isis.core.metamodel.facets.FacetFactoryAbstract;
+import org.apache.isis.core.metamodel.facets.collections.modify.CollectionFacet;
+import org.apache.isis.core.metamodel.facets.object.parented.ParentedFacet;
+import org.apache.isis.core.metamodel.progmodel.ProgrammingModel;
+import org.apache.isis.core.metamodel.specloader.collectiontyperegistry.CollectionTypeRegistry;
+import org.apache.isis.core.metamodel.facets.collections.collection.CollectionFacetFactory;
+
+/**
+ * All {@link CollectionTypeRegistry collection types} are intrinsically
+ * {@link ParentedFacet parented}.
+ * 
+ * <p> 
+ * Must be registered in the {@link ProgrammingModel} after
+ * {@link CollectionFacetFactory}.
+ */
+public class ParentedFacetSinceCollectionFactory extends FacetFactoryAbstract {
+
+    public ParentedFacetSinceCollectionFactory() {
+        super(FeatureType.OBJECTS_ONLY);
+    }
+
+    @Override
+    public void process(final ProcessClassContext processClassContaxt) {
+        if (!processClassContaxt.getFacetHolder().containsFacet(CollectionFacet.class)) {
+            return;
+        }
+        FacetUtil.addFacet(new ParentedFacetSinceCollection(processClassContaxt.getFacetHolder()));
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/7227418b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/collections/sortedby/annotation/SortedByFacetAnnotation.java
----------------------------------------------------------------------
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/collections/sortedby/annotation/SortedByFacetAnnotation.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/collections/sortedby/annotation/SortedByFacetAnnotation.java
new file mode 100644
index 0000000..14647ca
--- /dev/null
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/collections/sortedby/annotation/SortedByFacetAnnotation.java
@@ -0,0 +1,33 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+
+package org.apache.isis.core.metamodel.facets.collections.sortedby.annotation;
+
+import java.util.Comparator;
+
+import org.apache.isis.core.metamodel.facetapi.FacetHolder;
+import org.apache.isis.core.metamodel.facets.collections.sortedby.SortedByFacetAbstract;
+
+public class SortedByFacetAnnotation extends SortedByFacetAbstract {
+
+    public SortedByFacetAnnotation(final FacetHolder holder, Class<? extends Comparator<?>> value) {
+        super(value, holder);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/7227418b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/collections/sortedby/annotation/SortedByFacetAnnotationFactory.java
----------------------------------------------------------------------
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/collections/sortedby/annotation/SortedByFacetAnnotationFactory.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/collections/sortedby/annotation/SortedByFacetAnnotationFactory.java
new file mode 100644
index 0000000..c8b1ad5
--- /dev/null
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/collections/sortedby/annotation/SortedByFacetAnnotationFactory.java
@@ -0,0 +1,94 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+
+package org.apache.isis.core.metamodel.facets.collections.sortedby.annotation;
+
+import java.util.Comparator;
+import java.util.List;
+
+import org.apache.isis.applib.annotation.SortedBy;
+import org.apache.isis.core.commons.config.IsisConfiguration;
+import org.apache.isis.core.metamodel.facetapi.FacetHolder;
+import org.apache.isis.core.metamodel.facetapi.FacetUtil;
+import org.apache.isis.core.metamodel.facetapi.FeatureType;
+import org.apache.isis.core.metamodel.facetapi.MetaModelValidatorRefiner;
+import org.apache.isis.core.metamodel.facets.Annotations;
+import org.apache.isis.core.metamodel.facets.FacetFactoryAbstract;
+import org.apache.isis.core.metamodel.facets.collections.sortedby.SortedByFacet;
+import org.apache.isis.core.metamodel.spec.ObjectSpecification;
+import org.apache.isis.core.metamodel.spec.feature.Contributed;
+import org.apache.isis.core.metamodel.spec.feature.OneToManyAssociation;
+import org.apache.isis.core.metamodel.specloader.validator.MetaModelValidatorComposite;
+import org.apache.isis.core.metamodel.specloader.validator.MetaModelValidatorVisiting;
+import org.apache.isis.core.metamodel.specloader.validator.MetaModelValidatorVisiting.Visitor;
+import org.apache.isis.core.metamodel.specloader.validator.ValidationFailures;
+
+/**
+ * There is no check that the value is a {@link Comparator}; instead this is done through 
+ * the {@link #refineMetaModelValidator(MetaModelValidatorComposite, IsisConfiguration)}.
+ */
+public class SortedByFacetAnnotationFactory extends FacetFactoryAbstract implements MetaModelValidatorRefiner {
+
+    public SortedByFacetAnnotationFactory() {
+        super(FeatureType.COLLECTIONS_ONLY);
+    }
+
+    @Override
+    public void process(final ProcessMethodContext processMethodContext) {
+        final SortedBy renderAnnotation = Annotations.getAnnotation(processMethodContext.getMethod(), SortedBy.class);
+        FacetUtil.addFacet(create(renderAnnotation, processMethodContext.getFacetHolder()));
+    }
+
+    private SortedByFacet create(final SortedBy annotation, final FacetHolder holder) {
+        if (annotation == null) {
+            return null;
+        } 
+        final Class<?> annotationValue = annotation.value();
+        @SuppressWarnings({ "rawtypes", "unchecked" })
+        final Class<? extends Comparator<?>> comparatorType = (Class)annotationValue;
+        return new SortedByFacetAnnotation(holder, comparatorType);
+    }
+
+    @Override
+    public void refineMetaModelValidator(MetaModelValidatorComposite metaModelValidator, IsisConfiguration configuration) {
+        metaModelValidator.add(new MetaModelValidatorVisiting(newValidatorVisitor()));
+    }
+
+    protected Visitor newValidatorVisitor() {
+        return new MetaModelValidatorVisiting.Visitor() {
+
+            @Override
+            public boolean visit(ObjectSpecification objectSpec, ValidationFailures validationFailures) {
+                List<OneToManyAssociation> objectCollections = objectSpec.getCollections(Contributed.EXCLUDED);
+                for (OneToManyAssociation objectCollection : objectCollections) {
+                    final SortedByFacet facet = objectCollection.getFacet(SortedByFacet.class);
+                    if(facet != null) {
+                        final Class<? extends Comparator<?>> cls = facet.value();
+                        if(!Comparator.class.isAssignableFrom(cls)) {
+                            validationFailures.add("%s#%s is annotated with @SortedBy, but the class specified '%s' is not a Comparator", objectSpec.getIdentifier().getClassName(), objectCollection.getId(), facet.value().getName());
+                        }
+                    }
+                }
+                return true;
+            }
+        };
+    }
+
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/7227418b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/collections/typeof/annotation/TypeOfFacetOnCollectionAnnotation.java
----------------------------------------------------------------------
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/collections/typeof/annotation/TypeOfFacetOnCollectionAnnotation.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/collections/typeof/annotation/TypeOfFacetOnCollectionAnnotation.java
new file mode 100644
index 0000000..83e8477
--- /dev/null
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/collections/typeof/annotation/TypeOfFacetOnCollectionAnnotation.java
@@ -0,0 +1,32 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+
+package org.apache.isis.core.metamodel.facets.collections.typeof.annotation;
+
+import org.apache.isis.core.metamodel.facetapi.FacetHolder;
+import org.apache.isis.core.metamodel.facets.actcoll.typeof.TypeOfFacetAbstract;
+import org.apache.isis.core.metamodel.spec.SpecificationLoader;
+
+public class TypeOfFacetOnCollectionAnnotation extends TypeOfFacetAbstract {
+
+    public TypeOfFacetOnCollectionAnnotation(final Class<?> type, final FacetHolder holder, final SpecificationLoader specificationLookup) {
+        super(type, holder, specificationLookup);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/7227418b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/collections/typeof/annotation/TypeOfFacetOnCollectionAnnotationFactory.java
----------------------------------------------------------------------
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/collections/typeof/annotation/TypeOfFacetOnCollectionAnnotationFactory.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/collections/typeof/annotation/TypeOfFacetOnCollectionAnnotationFactory.java
new file mode 100644
index 0000000..6bcfb18
--- /dev/null
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/collections/typeof/annotation/TypeOfFacetOnCollectionAnnotationFactory.java
@@ -0,0 +1,97 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+
+package org.apache.isis.core.metamodel.facets.collections.typeof.annotation;
+
+import java.lang.reflect.Method;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.lang.reflect.TypeVariable;
+
+import org.apache.isis.applib.annotation.TypeOf;
+import org.apache.isis.core.metamodel.facetapi.FacetUtil;
+import org.apache.isis.core.metamodel.facetapi.FeatureType;
+import org.apache.isis.core.metamodel.facets.Annotations;
+import org.apache.isis.core.metamodel.facets.FacetFactoryAbstract;
+import org.apache.isis.core.metamodel.facets.actcoll.typeof.TypeOfFacetInferredFromArray;
+import org.apache.isis.core.metamodel.facets.actcoll.typeof.TypeOfFacetInferredFromGenerics;
+import org.apache.isis.core.metamodel.specloader.collectiontyperegistry.CollectionTypeRegistry;
+import org.apache.isis.core.metamodel.specloader.collectiontyperegistry.CollectionTypeRegistryAware;
+
+public class TypeOfFacetOnCollectionAnnotationFactory extends FacetFactoryAbstract implements CollectionTypeRegistryAware {
+    private CollectionTypeRegistry collectionTypeRegistry;
+
+    public TypeOfFacetOnCollectionAnnotationFactory() {
+        super(FeatureType.COLLECTIONS_ONLY);
+    }
+
+    @Override
+    public void process(final ProcessMethodContext processMethodContext) {
+
+        final Class<?> methodReturnType = processMethodContext.getMethod().getReturnType();
+        if (!collectionTypeRegistry.isCollectionType(methodReturnType) && !collectionTypeRegistry.isArrayType(methodReturnType)) {
+            return;
+        }
+
+        final Class<?> returnType = processMethodContext.getMethod().getReturnType();
+        if (returnType.isArray()) {
+            final Class<?> componentType = returnType.getComponentType();
+            FacetUtil.addFacet(new TypeOfFacetInferredFromArray(componentType, processMethodContext.getFacetHolder(), getSpecificationLoader()));
+            return;
+        }
+
+        Method method = processMethodContext.getMethod();
+        final TypeOf annotation = Annotations.getAnnotation(method, TypeOf.class);
+        if (annotation != null) {
+            FacetUtil.addFacet(new TypeOfFacetOnCollectionAnnotation(annotation.value(), processMethodContext.getFacetHolder(), getSpecificationLoader()));
+            return;
+        }
+
+        final Type type = processMethodContext.getMethod().getGenericReturnType();
+        if (!(type instanceof ParameterizedType)) {
+            return;
+        }
+
+        final ParameterizedType parameterizedType = (ParameterizedType) type;
+        final Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
+        if (actualTypeArguments.length == 0) {
+            return;
+        }
+
+        final Object actualTypeArgument = actualTypeArguments[0];
+        if (actualTypeArgument instanceof Class) {
+            final Class<?> actualType = (Class<?>) actualTypeArgument;
+            FacetUtil.addFacet(new TypeOfFacetInferredFromGenerics(actualType, processMethodContext.getFacetHolder(), getSpecificationLoader()));
+            return;
+        }
+
+        if (actualTypeArgument instanceof TypeVariable) {
+
+            // TODO: what to do here?
+            return;
+        }
+
+    }
+
+    @Override
+    public void setCollectionTypeRegistry(final CollectionTypeRegistry collectionTypeRegistry) {
+        this.collectionTypeRegistry = collectionTypeRegistry;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/7227418b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/collections/validate/CollectionValidateAddToFacet.java
----------------------------------------------------------------------
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/collections/validate/CollectionValidateAddToFacet.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/collections/validate/CollectionValidateAddToFacet.java
new file mode 100644
index 0000000..6c9f1ed
--- /dev/null
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/collections/validate/CollectionValidateAddToFacet.java
@@ -0,0 +1,40 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+
+package org.apache.isis.core.metamodel.facets.collections.validate;
+
+import org.apache.isis.core.metamodel.adapter.ObjectAdapter;
+import org.apache.isis.core.metamodel.facetapi.Facet;
+import org.apache.isis.core.metamodel.interactions.ValidatingInteractionAdvisor;
+
+/**
+ * Validate that an object can be added to a collection.
+ * 
+ * <p>
+ * In the standard Apache Isis Programming Model, corresponds to invoking the
+ * <tt>validateAddToXxx</tt> support method for a collection.
+ * 
+ */
+public interface CollectionValidateAddToFacet extends Facet, ValidatingInteractionAdvisor {
+
+    /**
+     * Reason the object cannot be added, or <tt>null</tt> if okay.
+     */
+    public String invalidReason(ObjectAdapter target, ObjectAdapter proposedArgument);
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/7227418b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/collections/validate/CollectionValidateAddToFacetAbstract.java
----------------------------------------------------------------------
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/collections/validate/CollectionValidateAddToFacetAbstract.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/collections/validate/CollectionValidateAddToFacetAbstract.java
new file mode 100644
index 0000000..439c2ea
--- /dev/null
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/collections/validate/CollectionValidateAddToFacetAbstract.java
@@ -0,0 +1,48 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+
+package org.apache.isis.core.metamodel.facets.collections.validate;
+
+import org.apache.isis.applib.events.ValidityEvent;
+import org.apache.isis.core.metamodel.facetapi.Facet;
+import org.apache.isis.core.metamodel.facetapi.FacetAbstract;
+import org.apache.isis.core.metamodel.facetapi.FacetHolder;
+import org.apache.isis.core.metamodel.interactions.CollectionAddToContext;
+import org.apache.isis.core.metamodel.interactions.ValidityContext;
+
+public abstract class CollectionValidateAddToFacetAbstract extends FacetAbstract implements CollectionValidateAddToFacet {
+
+    public static Class<? extends Facet> type() {
+        return CollectionValidateAddToFacet.class;
+    }
+
+    public CollectionValidateAddToFacetAbstract(final FacetHolder holder) {
+        super(type(), holder, Derivation.NOT_DERIVED);
+    }
+
+    @Override
+    public String invalidates(final ValidityContext<? extends ValidityEvent> context) {
+        if (!(context instanceof CollectionAddToContext)) {
+            return null;
+        }
+        final CollectionAddToContext collectionAddToContext = (CollectionAddToContext) context;
+        return invalidReason(context.getTarget(), collectionAddToContext.getProposed());
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/7227418b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/collections/validate/CollectionValidateAddToFacetViaMethod.java
----------------------------------------------------------------------
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/collections/validate/CollectionValidateAddToFacetViaMethod.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/collections/validate/CollectionValidateAddToFacetViaMethod.java
new file mode 100644
index 0000000..ea35429
--- /dev/null
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/collections/validate/CollectionValidateAddToFacetViaMethod.java
@@ -0,0 +1,74 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+
+package org.apache.isis.core.metamodel.facets.collections.validate;
+
+import java.lang.reflect.Method;
+import java.util.Collections;
+import java.util.List;
+
+import org.apache.isis.core.metamodel.adapter.ObjectAdapter;
+import org.apache.isis.core.metamodel.adapter.util.AdapterInvokeUtils;
+import org.apache.isis.core.metamodel.facetapi.FacetHolder;
+import org.apache.isis.core.metamodel.facets.ImperativeFacet;
+
+public class CollectionValidateAddToFacetViaMethod extends CollectionValidateAddToFacetAbstract implements ImperativeFacet {
+
+    private final Method method;
+
+    public CollectionValidateAddToFacetViaMethod(final Method method, final FacetHolder holder) {
+        super(holder);
+        this.method = method;
+    }
+
+    /**
+     * Returns a singleton list of the {@link Method} provided in the
+     * constructor.
+     */
+    @Override
+    public List<Method> getMethods() {
+        return Collections.singletonList(method);
+    }
+
+    @Override
+    public Intent getIntent(final Method method) {
+        return Intent.CHECK_IF_VALID;
+    }
+
+    @Override
+    public boolean impliesResolve() {
+        return true;
+    }
+
+    @Override
+    public boolean impliesObjectChanged() {
+        return false;
+    }
+
+    @Override
+    public String invalidReason(final ObjectAdapter owningAdapter, final ObjectAdapter proposedAdapter) {
+        return (String) AdapterInvokeUtils.invoke(method, owningAdapter, proposedAdapter);
+    }
+
+    @Override
+    protected String toStringValues() {
+        return "method=" + method;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/7227418b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/collections/validate/CollectionValidateRemoveFromFacet.java
----------------------------------------------------------------------
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/collections/validate/CollectionValidateRemoveFromFacet.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/collections/validate/CollectionValidateRemoveFromFacet.java
new file mode 100644
index 0000000..1f87423
--- /dev/null
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/collections/validate/CollectionValidateRemoveFromFacet.java
@@ -0,0 +1,39 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+
+package org.apache.isis.core.metamodel.facets.collections.validate;
+
+import org.apache.isis.core.metamodel.adapter.ObjectAdapter;
+import org.apache.isis.core.metamodel.facetapi.Facet;
+import org.apache.isis.core.metamodel.interactions.ValidatingInteractionAdvisor;
+
+/**
+ * Validate that an object can be removed to a collection.
+ * 
+ * <p>
+ * In the standard Apache Isis Programming Model, corresponds to invoking the
+ * <tt>validateRemoveFromXxx</tt> support method for a collection.
+ */
+public interface CollectionValidateRemoveFromFacet extends Facet, ValidatingInteractionAdvisor {
+
+    /**
+     * Reason the object cannot be removed, or <tt>null</tt> if okay.
+     */
+    public String invalidReason(ObjectAdapter inObject, ObjectAdapter value);
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/7227418b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/collections/validate/CollectionValidateRemoveFromFacetAbstract.java
----------------------------------------------------------------------
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/collections/validate/CollectionValidateRemoveFromFacetAbstract.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/collections/validate/CollectionValidateRemoveFromFacetAbstract.java
new file mode 100644
index 0000000..5666d4f
--- /dev/null
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/collections/validate/CollectionValidateRemoveFromFacetAbstract.java
@@ -0,0 +1,47 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+
+package org.apache.isis.core.metamodel.facets.collections.validate;
+
+import org.apache.isis.applib.events.ValidityEvent;
+import org.apache.isis.core.metamodel.facetapi.Facet;
+import org.apache.isis.core.metamodel.facetapi.FacetAbstract;
+import org.apache.isis.core.metamodel.facetapi.FacetHolder;
+import org.apache.isis.core.metamodel.interactions.CollectionRemoveFromContext;
+import org.apache.isis.core.metamodel.interactions.ValidityContext;
+
+public abstract class CollectionValidateRemoveFromFacetAbstract extends FacetAbstract implements CollectionValidateRemoveFromFacet {
+
+    public static Class<? extends Facet> type() {
+        return CollectionValidateRemoveFromFacet.class;
+    }
+
+    public CollectionValidateRemoveFromFacetAbstract(final FacetHolder holder) {
+        super(type(), holder, Derivation.NOT_DERIVED);
+    }
+
+    @Override
+    public String invalidates(final ValidityContext<? extends ValidityEvent> context) {
+        if (!(context instanceof CollectionRemoveFromContext)) {
+            return null;
+        }
+        final CollectionRemoveFromContext collectionRemoveFromContext = (CollectionRemoveFromContext) context;
+        return invalidReason(context.getTarget(), collectionRemoveFromContext.getProposed());
+    }
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/7227418b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/collections/validate/CollectionValidateRemoveFromFacetViaMethod.java
----------------------------------------------------------------------
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/collections/validate/CollectionValidateRemoveFromFacetViaMethod.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/collections/validate/CollectionValidateRemoveFromFacetViaMethod.java
new file mode 100644
index 0000000..ab94b8f
--- /dev/null
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/collections/validate/CollectionValidateRemoveFromFacetViaMethod.java
@@ -0,0 +1,74 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+
+package org.apache.isis.core.metamodel.facets.collections.validate;
+
+import java.lang.reflect.Method;
+import java.util.Collections;
+import java.util.List;
+
+import org.apache.isis.core.metamodel.adapter.ObjectAdapter;
+import org.apache.isis.core.metamodel.adapter.util.AdapterInvokeUtils;
+import org.apache.isis.core.metamodel.facetapi.FacetHolder;
+import org.apache.isis.core.metamodel.facets.ImperativeFacet;
+
+public class CollectionValidateRemoveFromFacetViaMethod extends CollectionValidateRemoveFromFacetAbstract implements ImperativeFacet {
+
+    private final Method method;
+
+    public CollectionValidateRemoveFromFacetViaMethod(final Method method, final FacetHolder holder) {
+        super(holder);
+        this.method = method;
+    }
+
+    /**
+     * Returns a singleton list of the {@link Method} provided in the
+     * constructor.
+     */
+    @Override
+    public List<Method> getMethods() {
+        return Collections.singletonList(method);
+    }
+
+    @Override
+    public Intent getIntent(final Method method) {
+        return Intent.CHECK_IF_VALID;
+    }
+
+    @Override
+    public boolean impliesResolve() {
+        return true;
+    }
+
+    @Override
+    public boolean impliesObjectChanged() {
+        return false;
+    }
+
+    @Override
+    public String invalidReason(final ObjectAdapter owningAdapter, final ObjectAdapter proposedAdapter) {
+        return (String) AdapterInvokeUtils.invoke(method, owningAdapter, proposedAdapter);
+    }
+
+    @Override
+    protected String toStringValues() {
+        return "method=" + method;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/7227418b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/describedas/DescribedAsFacet.java
----------------------------------------------------------------------
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/describedas/DescribedAsFacet.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/describedas/DescribedAsFacet.java
deleted file mode 100644
index a40f08c..0000000
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/describedas/DescribedAsFacet.java
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- *  Licensed to the Apache Software Foundation (ASF) under one
- *  or more contributor license agreements.  See the NOTICE file
- *  distributed with this work for additional information
- *  regarding copyright ownership.  The ASF licenses this file
- *  to you under the Apache License, Version 2.0 (the
- *  "License"); you may not use this file except in compliance
- *  with the License.  You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing,
- *  software distributed under the License is distributed on an
- *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- *  KIND, either express or implied.  See the License for the
- *  specific language governing permissions and limitations
- *  under the License.
- */
-
-package org.apache.isis.core.metamodel.facets.describedas;
-
-import org.apache.isis.core.metamodel.facets.SingleStringValueFacet;
-
-/**
- * Describes a class, a property, collection, an action or an action parameter.
- * 
- * <p>
- * In the standard Apache Isis Programming Model, corresponds to annotating the
- * member with <tt>@DescribedAs</tt>.
- */
-public interface DescribedAsFacet extends SingleStringValueFacet {
-
-}

http://git-wip-us.apache.org/repos/asf/isis/blob/7227418b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/describedas/DescribedAsFacetAbstract.java
----------------------------------------------------------------------
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/describedas/DescribedAsFacetAbstract.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/describedas/DescribedAsFacetAbstract.java
deleted file mode 100644
index 6080846..0000000
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/describedas/DescribedAsFacetAbstract.java
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- *  Licensed to the Apache Software Foundation (ASF) under one
- *  or more contributor license agreements.  See the NOTICE file
- *  distributed with this work for additional information
- *  regarding copyright ownership.  The ASF licenses this file
- *  to you under the Apache License, Version 2.0 (the
- *  "License"); you may not use this file except in compliance
- *  with the License.  You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing,
- *  software distributed under the License is distributed on an
- *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- *  KIND, either express or implied.  See the License for the
- *  specific language governing permissions and limitations
- *  under the License.
- */
-
-package org.apache.isis.core.metamodel.facets.describedas;
-
-import org.apache.isis.core.metamodel.facetapi.Facet;
-import org.apache.isis.core.metamodel.facetapi.FacetHolder;
-import org.apache.isis.core.metamodel.facets.SingleStringValueFacetAbstract;
-
-public abstract class DescribedAsFacetAbstract extends SingleStringValueFacetAbstract implements DescribedAsFacet {
-
-    public static Class<? extends Facet> type() {
-        return DescribedAsFacet.class;
-    }
-
-    public DescribedAsFacetAbstract(final String value, final FacetHolder holder) {
-        super(type(), holder, value);
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/isis/blob/7227418b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/ebc/EqualByContentFacet.java
----------------------------------------------------------------------
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/ebc/EqualByContentFacet.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/ebc/EqualByContentFacet.java
deleted file mode 100644
index 8708cc7..0000000
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/ebc/EqualByContentFacet.java
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- *  Licensed to the Apache Software Foundation (ASF) under one
- *  or more contributor license agreements.  See the NOTICE file
- *  distributed with this work for additional information
- *  regarding copyright ownership.  The ASF licenses this file
- *  to you under the Apache License, Version 2.0 (the
- *  "License"); you may not use this file except in compliance
- *  with the License.  You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing,
- *  software distributed under the License is distributed on an
- *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- *  KIND, either express or implied.  See the License for the
- *  specific language governing permissions and limitations
- *  under the License.
- */
-
-package org.apache.isis.core.metamodel.facets.ebc;
-
-import org.apache.isis.core.metamodel.facets.MarkerFacet;
-import org.apache.isis.core.metamodel.facets.object.immutable.ImmutableFacet;
-import org.apache.isis.core.metamodel.facets.object.value.ValueFacet;
-
-/**
- * Indicates that the instances of this class are equal-by-content.
- * 
- * <p>
- * In the standard Apache Isis Programming Model, typically corresponds to
- * applying the <tt>@EqualByContent</tt> annotation at the class level.
- * 
- * @see ImmutableFacet
- * @see ValueFacet
- */
-public interface EqualByContentFacet extends MarkerFacet {
-
-}

http://git-wip-us.apache.org/repos/asf/isis/blob/7227418b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/ebc/EqualByContentFacetAbstract.java
----------------------------------------------------------------------
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/ebc/EqualByContentFacetAbstract.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/ebc/EqualByContentFacetAbstract.java
deleted file mode 100644
index 5422c4d..0000000
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/ebc/EqualByContentFacetAbstract.java
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- *  Licensed to the Apache Software Foundation (ASF) under one
- *  or more contributor license agreements.  See the NOTICE file
- *  distributed with this work for additional information
- *  regarding copyright ownership.  The ASF licenses this file
- *  to you under the Apache License, Version 2.0 (the
- *  "License"); you may not use this file except in compliance
- *  with the License.  You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing,
- *  software distributed under the License is distributed on an
- *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- *  KIND, either express or implied.  See the License for the
- *  specific language governing permissions and limitations
- *  under the License.
- */
-
-package org.apache.isis.core.metamodel.facets.ebc;
-
-import org.apache.isis.core.metamodel.facetapi.Facet;
-import org.apache.isis.core.metamodel.facetapi.FacetHolder;
-import org.apache.isis.core.metamodel.facets.MarkerFacetAbstract;
-
-public abstract class EqualByContentFacetAbstract extends MarkerFacetAbstract implements EqualByContentFacet {
-
-    public static Class<? extends Facet> type() {
-        return EqualByContentFacet.class;
-    }
-
-    public EqualByContentFacetAbstract(final FacetHolder holder) {
-        super(type(), holder);
-    }
-
-}