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 2012/12/05 00:41:12 UTC

[48/52] [partial] ISIS-188: consolidating isis-core

http://git-wip-us.apache.org/repos/asf/isis/blob/e4735c72/framework/core/metamodel/src/test/java/org/apache/isis/core/progmodel/facets/propcoll/notpersisted/NotPersistedAnnotationFacetFactoryTest.java
----------------------------------------------------------------------
diff --git a/framework/core/metamodel/src/test/java/org/apache/isis/core/progmodel/facets/propcoll/notpersisted/NotPersistedAnnotationFacetFactoryTest.java b/framework/core/metamodel/src/test/java/org/apache/isis/core/progmodel/facets/propcoll/notpersisted/NotPersistedAnnotationFacetFactoryTest.java
new file mode 100644
index 0000000..57406f1
--- /dev/null
+++ b/framework/core/metamodel/src/test/java/org/apache/isis/core/progmodel/facets/propcoll/notpersisted/NotPersistedAnnotationFacetFactoryTest.java
@@ -0,0 +1,81 @@
+/*
+ *  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.progmodel.facets.propcoll.notpersisted;
+
+import java.lang.reflect.Method;
+import java.util.Collection;
+
+import org.apache.isis.applib.annotation.NotPersisted;
+import org.apache.isis.core.metamodel.facetapi.Facet;
+import org.apache.isis.core.metamodel.facets.FacetFactory.ProcessMethodContext;
+import org.apache.isis.core.metamodel.facets.notpersisted.NotPersistedFacet;
+import org.apache.isis.core.progmodel.facets.AbstractFacetFactoryTest;
+import org.apache.isis.core.progmodel.facets.collections.notpersisted.annotation.NotPersistedAnnotationForCollectionFacetFactory;
+import org.apache.isis.core.progmodel.facets.collections.notpersisted.annotation.NotPersistedFacetAnnotationForCollection;
+import org.apache.isis.core.progmodel.facets.properties.notpersisted.annotation.NotPersistedAnnotationForPropertyFacetFactory;
+import org.apache.isis.core.progmodel.facets.properties.notpersisted.annotation.NotPersistedFacetAnnotationForProperty;
+
+public class NotPersistedAnnotationFacetFactoryTest extends AbstractFacetFactoryTest {
+
+    public void testNotPersistedAnnotationPickedUpOnProperty() {
+        final NotPersistedAnnotationForPropertyFacetFactory facetFactory = new NotPersistedAnnotationForPropertyFacetFactory();
+
+        class Customer {
+            @SuppressWarnings("unused")
+            @NotPersisted()
+            public String getFirstName() {
+                return null;
+            }
+        }
+        final Method method = findMethod(Customer.class, "getFirstName");
+
+        facetFactory.process(new ProcessMethodContext(Customer.class, method, methodRemover, facetedMethod));
+
+        final Facet facet = facetedMethod.getFacet(NotPersistedFacet.class);
+        assertNotNull(facet);
+        assertTrue(facet instanceof NotPersistedFacetAnnotationForProperty);
+
+        assertNoMethodsRemoved();
+    }
+
+    public void testNotPersistedAnnotationPickedUpOnCollection() {
+        final NotPersistedAnnotationForCollectionFacetFactory facetFactory = new NotPersistedAnnotationForCollectionFacetFactory();
+
+        class Order {
+        }
+        class Customer {
+            @SuppressWarnings("unused")
+            @NotPersisted()
+            public Collection<Order> getOrders() {
+                return null;
+            }
+        }
+        final Method method = findMethod(Customer.class, "getOrders");
+
+        facetFactory.process(new ProcessMethodContext(Customer.class, method, methodRemover, facetedMethod));
+
+        final Facet facet = facetedMethod.getFacet(NotPersistedFacet.class);
+        assertNotNull(facet);
+        assertTrue(facet instanceof NotPersistedFacetAnnotationForCollection);
+
+        assertNoMethodsRemoved();
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/e4735c72/framework/core/metamodel/src/test/java/org/apache/isis/core/progmodel/facets/properties/PropertyMethodsFacetFactoryTest.java
----------------------------------------------------------------------
diff --git a/framework/core/metamodel/src/test/java/org/apache/isis/core/progmodel/facets/properties/PropertyMethodsFacetFactoryTest.java b/framework/core/metamodel/src/test/java/org/apache/isis/core/progmodel/facets/properties/PropertyMethodsFacetFactoryTest.java
new file mode 100644
index 0000000..823c79b
--- /dev/null
+++ b/framework/core/metamodel/src/test/java/org/apache/isis/core/progmodel/facets/properties/PropertyMethodsFacetFactoryTest.java
@@ -0,0 +1,797 @@
+/*
+ *  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.progmodel.facets.properties;
+
+import java.lang.reflect.Method;
+
+import org.apache.isis.applib.security.UserMemento;
+import org.apache.isis.core.metamodel.facetapi.Facet;
+import org.apache.isis.core.metamodel.facets.FacetFactory.ProcessMethodContext;
+import org.apache.isis.core.metamodel.facets.accessor.PropertyOrCollectionAccessorFacet;
+import org.apache.isis.core.metamodel.facets.describedas.DescribedAsFacet;
+import org.apache.isis.core.metamodel.facets.hide.HiddenFacet;
+import org.apache.isis.core.metamodel.facets.named.NamedFacet;
+import org.apache.isis.core.metamodel.facets.notpersisted.NotPersistedFacet;
+import org.apache.isis.core.metamodel.facets.properties.choices.PropertyChoicesFacet;
+import org.apache.isis.core.metamodel.facets.properties.defaults.PropertyDefaultFacet;
+import org.apache.isis.core.metamodel.facets.properties.modify.PropertyClearFacet;
+import org.apache.isis.core.metamodel.facets.properties.modify.PropertyInitializationFacet;
+import org.apache.isis.core.metamodel.facets.properties.modify.PropertySetterFacet;
+import org.apache.isis.core.progmodel.facets.AbstractFacetFactoryTest;
+import org.apache.isis.core.progmodel.facets.members.describedas.staticmethod.DescribedAsFacetViaDescriptionMethodFacetFactory;
+import org.apache.isis.core.progmodel.facets.members.describedas.staticmethod.DescribedAsFacetViaMethod;
+import org.apache.isis.core.progmodel.facets.members.disabled.DisableForContextFacet;
+import org.apache.isis.core.progmodel.facets.members.disabled.DisableForSessionFacet;
+import org.apache.isis.core.progmodel.facets.members.disabled.DisabledFacet;
+import org.apache.isis.core.progmodel.facets.members.disabled.forsession.DisableForSessionFacetViaMethod;
+import org.apache.isis.core.progmodel.facets.members.disabled.forsession.DisabledFacetViaDisableForSessionMethodFacetFactory;
+import org.apache.isis.core.progmodel.facets.members.disabled.method.DisableForContextFacetViaMethod;
+import org.apache.isis.core.progmodel.facets.members.disabled.method.DisabledFacetViaDisableMethodFacetFactory;
+import org.apache.isis.core.progmodel.facets.members.disabled.staticmethod.DisabledFacetAlwaysEverywhere;
+import org.apache.isis.core.progmodel.facets.members.disabled.staticmethod.DisabledFacetViaProtectMethodFacetFactory;
+import org.apache.isis.core.progmodel.facets.members.hidden.HideForContextFacet;
+import org.apache.isis.core.progmodel.facets.members.hidden.HideForSessionFacet;
+import org.apache.isis.core.progmodel.facets.members.hidden.forsession.HiddenFacetViaHideForSessionMethodFacetFactory;
+import org.apache.isis.core.progmodel.facets.members.hidden.forsession.HideForSessionFacetViaMethod;
+import org.apache.isis.core.progmodel.facets.members.hidden.method.HiddenFacetViaHideMethodFacetFactory;
+import org.apache.isis.core.progmodel.facets.members.hidden.method.HideForContextFacetViaMethod;
+import org.apache.isis.core.progmodel.facets.members.hidden.staticmethod.HiddenFacetAlwaysEverywhere;
+import org.apache.isis.core.progmodel.facets.members.hidden.staticmethod.HiddenFacetViaAlwaysHideMethodFacetFactory;
+import org.apache.isis.core.progmodel.facets.members.named.staticmethod.NamedFacetViaMethod;
+import org.apache.isis.core.progmodel.facets.members.named.staticmethod.NamedFacetViaNameMethodFacetFactory;
+import org.apache.isis.core.progmodel.facets.properties.accessor.PropertyAccessorFacetFactory;
+import org.apache.isis.core.progmodel.facets.properties.accessor.PropertyAccessorFacetViaAccessor;
+import org.apache.isis.core.progmodel.facets.properties.choices.method.PropertyChoicesFacetFactory;
+import org.apache.isis.core.progmodel.facets.properties.choices.method.PropertyChoicesFacetViaMethod;
+import org.apache.isis.core.progmodel.facets.properties.defaults.method.PropertyDefaultFacetFactory;
+import org.apache.isis.core.progmodel.facets.properties.defaults.method.PropertyDefaultFacetViaMethod;
+import org.apache.isis.core.progmodel.facets.properties.derived.inferred.NotPersistableFacetInferred;
+import org.apache.isis.core.progmodel.facets.properties.modify.PropertyClearFacetViaClearMethod;
+import org.apache.isis.core.progmodel.facets.properties.modify.PropertyClearFacetViaSetterMethod;
+import org.apache.isis.core.progmodel.facets.properties.modify.PropertyInitializationFacetViaSetterMethod;
+import org.apache.isis.core.progmodel.facets.properties.modify.PropertyModifyFacetFactory;
+import org.apache.isis.core.progmodel.facets.properties.modify.PropertySetAndClearFacetFactory;
+import org.apache.isis.core.progmodel.facets.properties.modify.PropertySetterFacetViaModifyMethod;
+import org.apache.isis.core.progmodel.facets.properties.modify.PropertySetterFacetViaSetterMethod;
+import org.apache.isis.core.progmodel.facets.properties.validate.PropertyValidateFacet;
+import org.apache.isis.core.progmodel.facets.properties.validate.PropertyValidateFacetFactory;
+import org.apache.isis.core.progmodel.facets.properties.validate.PropertyValidateFacetViaMethod;
+
+public class PropertyMethodsFacetFactoryTest extends AbstractFacetFactoryTest {
+
+    public void testPropertyAccessorFacetIsInstalledAndMethodRemoved() {
+        final PropertyAccessorFacetFactory facetFactory = new PropertyAccessorFacetFactory();
+        facetFactory.setSpecificationLookup(reflector);
+
+        class Customer {
+            @SuppressWarnings("unused")
+            public String getFirstName() {
+                return null;
+            }
+        }
+        final Method propertyAccessorMethod = findMethod(Customer.class, "getFirstName");
+
+        facetFactory.process(new ProcessMethodContext(CustomerStatic.class, propertyAccessorMethod, methodRemover, facetedMethod));
+
+        final Facet facet = facetedMethod.getFacet(PropertyOrCollectionAccessorFacet.class);
+        assertNotNull(facet);
+        assertTrue(facet instanceof PropertyAccessorFacetViaAccessor);
+        final PropertyAccessorFacetViaAccessor propertyAccessorFacetViaAccessor = (PropertyAccessorFacetViaAccessor) facet;
+        assertEquals(propertyAccessorMethod, propertyAccessorFacetViaAccessor.getMethods().get(0));
+
+        assertTrue(methodRemover.getRemovedMethodMethodCalls().contains(propertyAccessorMethod));
+    }
+
+    public void testSetterFacetIsInstalledForSetterMethodAndMethodRemoved() {
+        final PropertySetAndClearFacetFactory facetFactory = new PropertySetAndClearFacetFactory();
+        facetFactory.setSpecificationLookup(reflector);
+
+        class Customer {
+            @SuppressWarnings("unused")
+            public String getFirstName() {
+                return null;
+            }
+
+            @SuppressWarnings("unused")
+            public void setFirstName(final String firstName) {
+            }
+        }
+        final Method propertyAccessorMethod = findMethod(Customer.class, "getFirstName");
+        final Method propertySetterMethod = findMethod(Customer.class, "setFirstName", new Class[] { String.class });
+
+        facetFactory.process(new ProcessMethodContext(Customer.class, propertyAccessorMethod, methodRemover, facetedMethod));
+
+        final Facet facet = facetedMethod.getFacet(PropertySetterFacet.class);
+        assertNotNull(facet);
+        assertTrue(facet instanceof PropertySetterFacetViaSetterMethod);
+        final PropertySetterFacetViaSetterMethod propertySetterFacet = (PropertySetterFacetViaSetterMethod) facet;
+        assertEquals(propertySetterMethod, propertySetterFacet.getMethods().get(0));
+
+        assertTrue(methodRemover.getRemovedMethodMethodCalls().contains(propertySetterMethod));
+    }
+
+    public void testInitializationFacetIsInstalledForSetterMethodAndMethodRemoved() {
+        final PropertySetAndClearFacetFactory facetFactory = new PropertySetAndClearFacetFactory();
+        facetFactory.setSpecificationLookup(reflector);
+
+        class Customer {
+            @SuppressWarnings("unused")
+            public String getFirstName() {
+                return null;
+            }
+
+            @SuppressWarnings("unused")
+            public void setFirstName(final String firstName) {
+            }
+        }
+        final Method propertyAccessorMethod = findMethod(Customer.class, "getFirstName");
+        final Method propertySetterMethod = findMethod(Customer.class, "setFirstName", new Class[] { String.class });
+
+        facetFactory.process(new ProcessMethodContext(Customer.class, propertyAccessorMethod, methodRemover, facetedMethod));
+
+        final Facet facet = facetedMethod.getFacet(PropertyInitializationFacet.class);
+        assertNotNull(facet);
+        assertTrue(facet instanceof PropertyInitializationFacet);
+        final PropertyInitializationFacetViaSetterMethod propertySetterFacet = (PropertyInitializationFacetViaSetterMethod) facet;
+        assertEquals(propertySetterMethod, propertySetterFacet.getMethods().get(0));
+
+        assertTrue(methodRemover.getRemovedMethodMethodCalls().contains(propertySetterMethod));
+    }
+
+    public void testSetterFacetIsInstalledMeansNoDisabledOrDerivedFacetsInstalled() {
+        final PropertySetAndClearFacetFactory facetFactory = new PropertySetAndClearFacetFactory();
+        facetFactory.setSpecificationLookup(reflector);
+
+        class Customer {
+            @SuppressWarnings("unused")
+            public String getFirstName() {
+                return null;
+            }
+
+            @SuppressWarnings("unused")
+            public void setFirstName(final String firstName) {
+            }
+        }
+        final Method propertyAccessorMethod = findMethod(Customer.class, "getFirstName");
+
+        facetFactory.process(new ProcessMethodContext(Customer.class, propertyAccessorMethod, methodRemover, facetedMethod));
+
+        assertNull(facetedMethod.getFacet(NotPersistedFacet.class));
+        assertNull(facetedMethod.getFacet(NotPersistedFacet.class));
+    }
+
+    public void testSetterFacetIsInstalledForModifyMethodAndMethodRemoved() {
+
+        final PropertyModifyFacetFactory facetFactoryForModify = new PropertyModifyFacetFactory();
+        facetFactoryForModify.setSpecificationLookup(reflector);
+        final PropertySetAndClearFacetFactory facetFactoryForSetter = new PropertySetAndClearFacetFactory();
+        facetFactoryForSetter.setSpecificationLookup(reflector);
+
+        class Customer {
+            @SuppressWarnings("unused")
+            public String getFirstName() {
+                return null;
+            }
+
+            @SuppressWarnings("unused")
+            public void modifyFirstName(final String firstName) {
+            }
+        }
+        final Method propertyAccessorMethod = findMethod(Customer.class, "getFirstName");
+        final Method propertyModifyMethod = findMethod(Customer.class, "modifyFirstName", new Class[] { String.class });
+
+        final ProcessMethodContext processMethodContext = new ProcessMethodContext(Customer.class, propertyAccessorMethod, methodRemover, facetedMethod);
+        facetFactoryForModify.process(processMethodContext);
+        facetFactoryForSetter.process(processMethodContext);
+
+        final Facet facet = facetedMethod.getFacet(PropertySetterFacet.class);
+        assertNotNull(facet);
+        assertTrue(facet instanceof PropertySetterFacetViaModifyMethod);
+        final PropertySetterFacetViaModifyMethod propertySetterFacet = (PropertySetterFacetViaModifyMethod) facet;
+        assertEquals(propertyModifyMethod, propertySetterFacet.getMethods().get(0));
+
+        assertTrue(methodRemover.getRemovedMethodMethodCalls().contains(propertyModifyMethod));
+    }
+
+    public void testModifyMethodWithNoSetterStillInstallsDisabledAndDerivedFacets() {
+        final PropertySetAndClearFacetFactory facetFactory = new PropertySetAndClearFacetFactory();
+        facetFactory.setSpecificationLookup(reflector);
+        final PropertyModifyFacetFactory facetFactoryForModify = new PropertyModifyFacetFactory();
+        facetFactoryForModify.setSpecificationLookup(reflector);
+
+        class Customer {
+            @SuppressWarnings("unused")
+            public String getFirstName() {
+                return null;
+            }
+
+            @SuppressWarnings("unused")
+            public void modifyFirstName(final String firstName) {
+            }
+        }
+        final Method propertyAccessorMethod = findMethod(Customer.class, "getFirstName");
+
+        final ProcessMethodContext processMethodContext = new ProcessMethodContext(Customer.class, propertyAccessorMethod, methodRemover, facetedMethod);
+        facetFactory.process(processMethodContext);
+        facetFactoryForModify.process(processMethodContext);
+
+        Facet facet = facetedMethod.getFacet(NotPersistedFacet.class);
+        assertNotNull(facet);
+        assertTrue(facet instanceof NotPersistableFacetInferred);
+
+        facet = facetedMethod.getFacet(DisabledFacet.class);
+        assertNotNull(facet);
+        assertTrue(facet instanceof DisabledFacetAlwaysEverywhere);
+    }
+
+    public void testIfHaveSetterAndModifyFacetThenTheModifyFacetWinsOut() {
+        final PropertySetAndClearFacetFactory facetFactory = new PropertySetAndClearFacetFactory();
+        facetFactory.setSpecificationLookup(reflector);
+        final PropertyModifyFacetFactory facetFactoryForModify = new PropertyModifyFacetFactory();
+        facetFactoryForModify.setSpecificationLookup(reflector);
+
+        class Customer {
+            @SuppressWarnings("unused")
+            public String getFirstName() {
+                return null;
+            }
+
+            @SuppressWarnings("unused")
+            public void setFirstName(final String firstName) {
+            }
+
+            @SuppressWarnings("unused")
+            public void modifyFirstName(final String firstName) {
+            }
+        }
+        final Method propertyAccessorMethod = findMethod(Customer.class, "getFirstName");
+        final Method propertySetterMethod = findMethod(Customer.class, "setFirstName", new Class[] { String.class });
+        final Method propertyModifyMethod = findMethod(Customer.class, "modifyFirstName", new Class[] { String.class });
+
+        final ProcessMethodContext processMethodContext = new ProcessMethodContext(Customer.class, propertyAccessorMethod, methodRemover, facetedMethod);
+        facetFactory.process(processMethodContext);
+        facetFactoryForModify.process(processMethodContext);
+
+        final Facet facet = facetedMethod.getFacet(PropertySetterFacet.class);
+        assertNotNull(facet);
+        assertTrue(facet instanceof PropertySetterFacetViaModifyMethod);
+        final PropertySetterFacetViaModifyMethod propertySetterFacet = (PropertySetterFacetViaModifyMethod) facet;
+        assertEquals(propertyModifyMethod, propertySetterFacet.getMethods().get(0));
+
+        assertTrue(methodRemover.getRemovedMethodMethodCalls().contains(propertySetterMethod));
+        assertTrue(methodRemover.getRemovedMethodMethodCalls().contains(propertyModifyMethod));
+    }
+
+    public void testClearFacet() {
+        final PropertySetAndClearFacetFactory facetFactory = new PropertySetAndClearFacetFactory();
+        facetFactory.setSpecificationLookup(reflector);
+
+        class Customer {
+            @SuppressWarnings("unused")
+            public String getFirstName() {
+                return null;
+            }
+
+            @SuppressWarnings("unused")
+            public void clearFirstName() {
+            }
+        }
+        final Method propertyAccessorMethod = findMethod(Customer.class, "getFirstName");
+        final Method propertyClearMethod = findMethod(Customer.class, "clearFirstName");
+
+        facetFactory.process(new ProcessMethodContext(Customer.class, propertyAccessorMethod, methodRemover, facetedMethod));
+
+        final Facet facet = facetedMethod.getFacet(PropertyClearFacet.class);
+        assertNotNull(facet);
+        assertTrue(facet instanceof PropertyClearFacetViaClearMethod);
+        final PropertyClearFacetViaClearMethod propertyClearFacet = (PropertyClearFacetViaClearMethod) facet;
+        assertEquals(propertyClearMethod, propertyClearFacet.getMethods().get(0));
+
+        assertTrue(methodRemover.getRemovedMethodMethodCalls().contains(propertyClearMethod));
+    }
+
+    public void testClearFacetViaSetterIfNoExplicitClearMethod() {
+        final PropertySetAndClearFacetFactory facetFactory = new PropertySetAndClearFacetFactory();
+        facetFactory.setSpecificationLookup(reflector);
+
+        class Customer {
+            @SuppressWarnings("unused")
+            public String getFirstName() {
+                return null;
+            }
+
+            @SuppressWarnings("unused")
+            public void setFirstName(final String firstName) {
+            }
+        }
+        final Method propertyAccessorMethod = findMethod(Customer.class, "getFirstName");
+        final Method propertySetterMethod = findMethod(Customer.class, "setFirstName", new Class[] { String.class });
+
+        facetFactory.process(new ProcessMethodContext(Customer.class, propertyAccessorMethod, methodRemover, facetedMethod));
+
+        final Facet facet = facetedMethod.getFacet(PropertyClearFacet.class);
+        assertNotNull(facet);
+        assertTrue(facet instanceof PropertyClearFacetViaSetterMethod);
+        final PropertyClearFacetViaSetterMethod propertyClearFacet = (PropertyClearFacetViaSetterMethod) facet;
+        assertEquals(propertySetterMethod, propertyClearFacet.getMethods().get(0));
+    }
+
+    public void testChoicesFacetFoundAndMethodRemoved() {
+        final PropertyChoicesFacetFactory facetFactory = new PropertyChoicesFacetFactory();
+        facetFactory.setSpecificationLookup(reflector);
+
+        class Customer {
+            @SuppressWarnings("unused")
+            public String getFirstName() {
+                return null;
+            }
+
+            @SuppressWarnings("unused")
+            public Object[] choicesFirstName() {
+                return null;
+            }
+        }
+        final Method propertyAccessorMethod = findMethod(Customer.class, "getFirstName");
+        final Method propertyChoicesMethod = findMethod(Customer.class, "choicesFirstName");
+
+        facetFactory.process(new ProcessMethodContext(Customer.class, propertyAccessorMethod, methodRemover, facetedMethod));
+
+        final Facet facet = facetedMethod.getFacet(PropertyChoicesFacet.class);
+        assertNotNull(facet);
+        assertTrue(facet instanceof PropertyChoicesFacetViaMethod);
+        final PropertyChoicesFacetViaMethod propertyChoicesFacet = (PropertyChoicesFacetViaMethod) facet;
+        assertEquals(propertyChoicesMethod, propertyChoicesFacet.getMethods().get(0));
+
+        assertTrue(methodRemover.getRemovedMethodMethodCalls().contains(propertyChoicesMethod));
+    }
+
+    public void testDefaultFacetFoundAndMethodRemoved() {
+        final PropertyDefaultFacetFactory facetFactory = new PropertyDefaultFacetFactory();
+        facetFactory.setSpecificationLookup(reflector);
+
+        class Customer {
+            @SuppressWarnings("unused")
+            public String getFirstName() {
+                return null;
+            }
+
+            @SuppressWarnings("unused")
+            public String defaultFirstName() {
+                return null;
+            }
+        }
+        final Method propertyAccessorMethod = findMethod(Customer.class, "getFirstName");
+        final Method propertyDefaultMethod = findMethod(Customer.class, "defaultFirstName");
+
+        facetFactory.process(new ProcessMethodContext(Customer.class, propertyAccessorMethod, methodRemover, facetedMethod));
+
+        final Facet facet = facetedMethod.getFacet(PropertyDefaultFacet.class);
+        assertNotNull(facet);
+        assertTrue(facet instanceof PropertyDefaultFacetViaMethod);
+        final PropertyDefaultFacetViaMethod propertyDefaultFacet = (PropertyDefaultFacetViaMethod) facet;
+        assertEquals(propertyDefaultMethod, propertyDefaultFacet.getMethods().get(0));
+
+        assertTrue(methodRemover.getRemovedMethodMethodCalls().contains(propertyDefaultMethod));
+    }
+
+    public void testValidateFacetFoundAndMethodRemoved() {
+        final PropertyValidateFacetFactory facetFactory = new PropertyValidateFacetFactory();
+        facetFactory.setSpecificationLookup(reflector);
+
+        class Customer {
+            @SuppressWarnings("unused")
+            public String getFirstName() {
+                return null;
+            }
+
+            @SuppressWarnings("unused")
+            public String validateFirstName(final String firstName) {
+                return null;
+            }
+        }
+        final Method propertyAccessorMethod = findMethod(Customer.class, "getFirstName");
+        final Method propertyValidateMethod = findMethod(Customer.class, "validateFirstName", new Class[] { String.class });
+
+        facetFactory.process(new ProcessMethodContext(Customer.class, propertyAccessorMethod, methodRemover, facetedMethod));
+
+        final Facet facet = facetedMethod.getFacet(PropertyValidateFacet.class);
+        assertNotNull(facet);
+        assertTrue(facet instanceof PropertyValidateFacetViaMethod);
+        final PropertyValidateFacetViaMethod propertyValidateFacet = (PropertyValidateFacetViaMethod) facet;
+        assertEquals(propertyValidateMethod, propertyValidateFacet.getMethods().get(0));
+
+        assertTrue(methodRemover.getRemovedMethodMethodCalls().contains(propertyValidateMethod));
+    }
+
+    public void testDisableFacetFoundAndMethodRemoved() {
+        final DisabledFacetViaDisableMethodFacetFactory facetFactory = new DisabledFacetViaDisableMethodFacetFactory();
+        facetFactory.setSpecificationLookup(reflector);
+
+        class Customer {
+            @SuppressWarnings("unused")
+            public String getFirstName() {
+                return null;
+            }
+
+            @SuppressWarnings("unused")
+            public String disableFirstName() {
+                return "disabled";
+            }
+        }
+        final Method propertyAccessorMethod = findMethod(Customer.class, "getFirstName");
+        final Method propertyDisableMethod = findMethod(Customer.class, "disableFirstName", new Class[] {});
+
+        facetFactory.process(new ProcessMethodContext(Customer.class, propertyAccessorMethod, methodRemover, facetedMethod));
+
+        final Facet facet = facetedMethod.getFacet(DisableForContextFacet.class);
+        assertNotNull(facet);
+        assertTrue(facet instanceof DisableForContextFacetViaMethod);
+        final DisableForContextFacetViaMethod disableForContextFacet = (DisableForContextFacetViaMethod) facet;
+        assertEquals(propertyDisableMethod, disableForContextFacet.getMethods().get(0));
+
+        assertTrue(methodRemover.getRemovedMethodMethodCalls().contains(propertyDisableMethod));
+    }
+
+    public void testDisableFacetNoArgsFoundAndMethodRemoved() {
+        final DisabledFacetViaDisableMethodFacetFactory facetFactory = new DisabledFacetViaDisableMethodFacetFactory();
+        facetFactory.setSpecificationLookup(reflector);
+
+        class Customer {
+            @SuppressWarnings("unused")
+            public String getFirstName() {
+                return null;
+            }
+
+            @SuppressWarnings("unused")
+            public String disableFirstName() {
+                return "disabled";
+            }
+        }
+        final Method propertyAccessorMethod = findMethod(Customer.class, "getFirstName");
+        final Method propertyDisableMethod = findMethod(Customer.class, "disableFirstName");
+
+        facetFactory.process(new ProcessMethodContext(Customer.class, propertyAccessorMethod, methodRemover, facetedMethod));
+
+        final Facet facet = facetedMethod.getFacet(DisableForContextFacet.class);
+        assertNotNull(facet);
+        assertTrue(facet instanceof DisableForContextFacetViaMethod);
+        final DisableForContextFacetViaMethod disableForContextFacet = (DisableForContextFacetViaMethod) facet;
+        assertEquals(propertyDisableMethod, disableForContextFacet.getMethods().get(0));
+
+        assertTrue(methodRemover.getRemovedMethodMethodCalls().contains(propertyDisableMethod));
+    }
+
+    public void testHiddenFacetFoundAndMethodRemoved() {
+        final HiddenFacetViaHideMethodFacetFactory facetFactory = new HiddenFacetViaHideMethodFacetFactory();
+        facetFactory.setSpecificationLookup(reflector);
+
+        class Customer {
+            @SuppressWarnings("unused")
+            public String getFirstName() {
+                return null;
+            }
+
+            @SuppressWarnings("unused")
+            public boolean hideFirstName() {
+                return true;
+            }
+        }
+        final Method propertyAccessorMethod = findMethod(Customer.class, "getFirstName");
+        final Method propertyHideMethod = findMethod(Customer.class, "hideFirstName", new Class[] {});
+
+        facetFactory.process(new ProcessMethodContext(Customer.class, propertyAccessorMethod, methodRemover, facetedMethod));
+
+        final Facet facet = facetedMethod.getFacet(HideForContextFacet.class);
+        assertNotNull(facet);
+        assertTrue(facet instanceof HideForContextFacetViaMethod);
+        final HideForContextFacetViaMethod hideForContextFacet = (HideForContextFacetViaMethod) facet;
+        assertEquals(propertyHideMethod, hideForContextFacet.getMethods().get(0));
+
+        assertTrue(methodRemover.getRemovedMethodMethodCalls().contains(propertyHideMethod));
+    }
+
+    public void testHiddenFacetWithNoArgFoundAndMethodRemoved() {
+        final HiddenFacetViaHideMethodFacetFactory facetFactory = new HiddenFacetViaHideMethodFacetFactory();
+        facetFactory.setSpecificationLookup(reflector);
+
+        class Customer {
+            @SuppressWarnings("unused")
+            public String getFirstName() {
+                return null;
+            }
+
+            @SuppressWarnings("unused")
+            public boolean hideFirstName() {
+                return true;
+            }
+        }
+        final Method propertyAccessorMethod = findMethod(Customer.class, "getFirstName");
+        final Method propertyHideMethod = findMethod(Customer.class, "hideFirstName");
+
+        facetFactory.process(new ProcessMethodContext(Customer.class, propertyAccessorMethod, methodRemover, facetedMethod));
+
+        final Facet facet = facetedMethod.getFacet(HideForContextFacet.class);
+        assertNotNull(facet);
+        assertTrue(facet instanceof HideForContextFacetViaMethod);
+        final HideForContextFacetViaMethod hideForContextFacet = (HideForContextFacetViaMethod) facet;
+        assertEquals(propertyHideMethod, hideForContextFacet.getMethods().get(0));
+
+        assertTrue(methodRemover.getRemovedMethodMethodCalls().contains(propertyHideMethod));
+    }
+
+    public void testPropertyFoundOnSuperclass() {
+        final PropertyAccessorFacetFactory facetFactory = new PropertyAccessorFacetFactory();
+        facetFactory.setSpecificationLookup(reflector);
+
+        class Customer {
+            @SuppressWarnings("unused")
+            public String getFirstName() {
+                return null;
+            }
+        }
+
+        class CustomerEx extends Customer {
+        }
+
+        final Method propertyAccessorMethod = findMethod(Customer.class, "getFirstName");
+
+        facetFactory.process(new ProcessMethodContext(CustomerEx.class, propertyAccessorMethod, methodRemover, facetedMethod));
+
+        final Facet facet = facetedMethod.getFacet(PropertyOrCollectionAccessorFacet.class);
+        assertNotNull(facet);
+        assertTrue(facet instanceof PropertyAccessorFacetViaAccessor);
+        final PropertyAccessorFacetViaAccessor accessorFacet = (PropertyAccessorFacetViaAccessor) facet;
+        assertEquals(propertyAccessorMethod, accessorFacet.getMethods().get(0));
+    }
+
+    public void testPropertyFoundOnSuperclassButHelperMethodFoundOnSubclass() {
+        final PropertyAccessorFacetFactory facetFactory = new PropertyAccessorFacetFactory();
+        facetFactory.setSpecificationLookup(reflector);
+        final HiddenFacetViaHideMethodFacetFactory facetFactoryForHide = new HiddenFacetViaHideMethodFacetFactory();
+        facetFactoryForHide.setSpecificationLookup(reflector);
+        final DisabledFacetViaDisableMethodFacetFactory facetFactoryForDisable = new DisabledFacetViaDisableMethodFacetFactory();
+        facetFactoryForDisable.setSpecificationLookup(reflector);
+
+        class Customer {
+            @SuppressWarnings("unused")
+            public String getFirstName() {
+                return null;
+            }
+        }
+
+        class CustomerEx extends Customer {
+            @SuppressWarnings("unused")
+            public boolean hideFirstName() {
+                return true;
+            }
+
+            @SuppressWarnings("unused")
+            public String disableFirstName() {
+                return "disabled";
+            }
+        }
+
+        final Method propertyAccessorMethod = findMethod(Customer.class, "getFirstName");
+        final Method propertyHideMethod = findMethod(CustomerEx.class, "hideFirstName");
+        final Method propertyDisableMethod = findMethod(CustomerEx.class, "disableFirstName");
+
+        final ProcessMethodContext processMethodContext = new ProcessMethodContext(CustomerEx.class, propertyAccessorMethod, methodRemover, facetedMethod);
+        facetFactory.process(processMethodContext);
+        facetFactoryForHide.process(processMethodContext);
+        facetFactoryForDisable.process(processMethodContext);
+
+        final Facet facet = facetedMethod.getFacet(HideForContextFacet.class);
+        assertNotNull(facet);
+        assertTrue(facet instanceof HideForContextFacetViaMethod);
+        final HideForContextFacetViaMethod hideForContextFacet = (HideForContextFacetViaMethod) facet;
+        assertEquals(propertyHideMethod, hideForContextFacet.getMethods().get(0));
+
+        final Facet facet2 = facetedMethod.getFacet(DisableForContextFacet.class);
+        assertNotNull(facet2);
+        assertTrue(facet2 instanceof DisableForContextFacetViaMethod);
+        final DisableForContextFacetViaMethod disableForContextFacet = (DisableForContextFacetViaMethod) facet2;
+        assertEquals(propertyDisableMethod, disableForContextFacet.getMethods().get(0));
+    }
+
+    public static class CustomerStatic {
+        public String getFirstName() {
+            return null;
+        }
+
+        // required otherwise marked as DisabledFacetAlways
+        public void setFirstName(final String firstName) {
+        }
+
+        public static String nameFirstName() {
+            return "Given name";
+        };
+
+        public static String descriptionFirstName() {
+            return "Some old description";
+        }
+
+        public static boolean alwaysHideFirstName() {
+            return true;
+        }
+
+        public static boolean protectFirstName() {
+            return true;
+        }
+
+        public static boolean hideFirstName(final UserMemento userMemento) {
+            return true;
+        }
+
+        public static String disableFirstName(final UserMemento userMemento) {
+            return "disabled for this user";
+        }
+
+        public String getLastName() {
+            return null;
+        }
+
+        // required otherwise marked as DisabledFacetAlways
+        public void setLastName(final String firstName) {
+        }
+
+        public static boolean alwaysHideLastName() {
+            return false;
+        }
+
+        public static boolean protectLastName() {
+            return false;
+        }
+    }
+
+    public void testInstallsNamedFacetUsingNameMethodAndRemovesMethod() {
+        final NamedFacetViaNameMethodFacetFactory facetFactory = new NamedFacetViaNameMethodFacetFactory();
+        facetFactory.setSpecificationLookup(reflector);
+
+        final Method propertyAccessorMethod = findMethod(CustomerStatic.class, "getFirstName");
+        final Method nameMethod = findMethod(CustomerStatic.class, "nameFirstName");
+
+        facetFactory.process(new ProcessMethodContext(CustomerStatic.class, propertyAccessorMethod, methodRemover, facetedMethod));
+
+        final Facet facet = facetedMethod.getFacet(NamedFacet.class);
+        assertNotNull(facet);
+        assertTrue(facet instanceof NamedFacetViaMethod);
+        final NamedFacetViaMethod namedFacet = (NamedFacetViaMethod) facet;
+        assertEquals("Given name", namedFacet.value());
+
+        assertTrue(methodRemover.getRemovedMethodMethodCalls().contains(nameMethod));
+    }
+
+    public void testInstallsDescribedAsFacetUsingDescriptionAndRemovesMethod() {
+        final DescribedAsFacetViaDescriptionMethodFacetFactory facetFactory = new DescribedAsFacetViaDescriptionMethodFacetFactory();
+        facetFactory.setSpecificationLookup(reflector);
+
+        final Method propertyAccessorMethod = findMethod(CustomerStatic.class, "getFirstName");
+        final Method descriptionMethod = findMethod(CustomerStatic.class, "descriptionFirstName");
+
+        facetFactory.process(new ProcessMethodContext(CustomerStatic.class, propertyAccessorMethod, methodRemover, facetedMethod));
+
+        final Facet facet = facetedMethod.getFacet(DescribedAsFacet.class);
+        assertNotNull(facet);
+        assertTrue(facet instanceof DescribedAsFacetViaMethod);
+        final DescribedAsFacetViaMethod describedAsFacet = (DescribedAsFacetViaMethod) facet;
+        assertEquals("Some old description", describedAsFacet.value());
+
+        assertTrue(methodRemover.getRemovedMethodMethodCalls().contains(descriptionMethod));
+    }
+
+    public void testInstallsHiddenFacetUsingAlwaysHideAndRemovesMethod() {
+        final HiddenFacetViaAlwaysHideMethodFacetFactory facetFactory = new HiddenFacetViaAlwaysHideMethodFacetFactory();
+        facetFactory.setSpecificationLookup(reflector);
+
+        final Method propertyAccessorMethod = findMethod(CustomerStatic.class, "getFirstName");
+        final Method propertyAlwaysHideMethod = findMethod(CustomerStatic.class, "alwaysHideFirstName");
+
+        facetFactory.process(new ProcessMethodContext(CustomerStatic.class, propertyAccessorMethod, methodRemover, facetedMethod));
+
+        final Facet facet = facetedMethod.getFacet(HiddenFacet.class);
+        assertNotNull(facet);
+        assertTrue(facet instanceof HiddenFacetAlwaysEverywhere);
+
+        assertTrue(methodRemover.getRemovedMethodMethodCalls().contains(propertyAlwaysHideMethod));
+    }
+
+    public void testInstallsHiddenFacetUsingAlwaysHideWhenNotAndRemovesMethod() {
+        final HiddenFacetViaAlwaysHideMethodFacetFactory facetFactory = new HiddenFacetViaAlwaysHideMethodFacetFactory();
+        facetFactory.setSpecificationLookup(reflector);
+
+        final Method propertyAccessorMethod = findMethod(CustomerStatic.class, "getLastName");
+        final Method propertyAlwaysHideMethod = findMethod(CustomerStatic.class, "alwaysHideLastName");
+
+        facetFactory.process(new ProcessMethodContext(CustomerStatic.class, propertyAccessorMethod, methodRemover, facetedMethod));
+
+        assertNull(facetedMethod.getFacet(HiddenFacet.class));
+
+        assertTrue(methodRemover.getRemovedMethodMethodCalls().contains(propertyAlwaysHideMethod));
+    }
+
+    public void testInstallsDisabledFacetUsingProtectAndRemovesMethod() {
+        final DisabledFacetViaProtectMethodFacetFactory facetFactory = new DisabledFacetViaProtectMethodFacetFactory();
+        facetFactory.setSpecificationLookup(reflector);
+
+        final Method propertyAccessorMethod = findMethod(CustomerStatic.class, "getFirstName");
+        final Method propertyProtectMethod = findMethod(CustomerStatic.class, "protectFirstName");
+
+        facetFactory.process(new ProcessMethodContext(CustomerStatic.class, propertyAccessorMethod, methodRemover, facetedMethod));
+
+        final Facet facet = facetedMethod.getFacet(DisabledFacet.class);
+        assertNotNull(facet);
+        assertTrue(facet instanceof DisabledFacetAlwaysEverywhere);
+
+        assertTrue(methodRemover.getRemovedMethodMethodCalls().contains(propertyProtectMethod));
+    }
+
+    public void testDoesNotInstallDisabledFacetUsingProtectWhenNotAndRemovesMethod() {
+        final DisabledFacetViaProtectMethodFacetFactory facetFactory = new DisabledFacetViaProtectMethodFacetFactory();
+        facetFactory.setSpecificationLookup(reflector);
+
+        final Method propertyAccessorMethod = findMethod(CustomerStatic.class, "getLastName");
+        final Method propertyProtectMethod = findMethod(CustomerStatic.class, "protectLastName");
+
+        facetFactory.process(new ProcessMethodContext(CustomerStatic.class, propertyAccessorMethod, methodRemover, facetedMethod));
+
+        final Facet facet = facetedMethod.getFacet(DisabledFacet.class);
+        assertNull(facet);
+
+        assertTrue(methodRemover.getRemovedMethodMethodCalls().contains(propertyProtectMethod));
+    }
+
+    public void testInstallsHiddenForSessionFacetAndRemovesMethod() {
+        final HiddenFacetViaHideForSessionMethodFacetFactory facetFactory = new HiddenFacetViaHideForSessionMethodFacetFactory();
+        facetFactory.setSpecificationLookup(reflector);
+
+        final Method propertyAccessorMethod = findMethod(CustomerStatic.class, "getFirstName");
+        final Method hideMethod = findMethod(CustomerStatic.class, "hideFirstName", new Class[] { UserMemento.class });
+
+        facetFactory.process(new ProcessMethodContext(CustomerStatic.class, propertyAccessorMethod, methodRemover, facetedMethod));
+
+        final Facet facet = facetedMethod.getFacet(HideForSessionFacet.class);
+        assertNotNull(facet);
+        assertTrue(facet instanceof HideForSessionFacetViaMethod);
+        final HideForSessionFacetViaMethod hideForSessionFacetViaMethod = (HideForSessionFacetViaMethod) facet;
+        assertEquals(hideMethod, hideForSessionFacetViaMethod.getMethods().get(0));
+
+        assertTrue(methodRemover.getRemovedMethodMethodCalls().contains(hideMethod));
+
+    }
+
+    public void testInstallsDisabledForSessionFacetAndRemovesMethod() {
+        final DisabledFacetViaDisableForSessionMethodFacetFactory facetFactory = new DisabledFacetViaDisableForSessionMethodFacetFactory();
+        facetFactory.setSpecificationLookup(reflector);
+
+        final Method propertyAccessorMethod = findMethod(CustomerStatic.class, "getFirstName");
+        final Method disableMethod = findMethod(CustomerStatic.class, "disableFirstName", new Class[] { UserMemento.class });
+
+        facetFactory.process(new ProcessMethodContext(CustomerStatic.class, propertyAccessorMethod, methodRemover, facetedMethod));
+
+        final Facet facet = facetedMethod.getFacet(DisableForSessionFacet.class);
+        assertNotNull(facet);
+        assertTrue(facet instanceof DisableForSessionFacetViaMethod);
+        final DisableForSessionFacetViaMethod disableForSessionFacetViaMethod = (DisableForSessionFacetViaMethod) facet;
+        assertEquals(disableMethod, disableForSessionFacetViaMethod.getMethods().get(0));
+
+        assertTrue(methodRemover.getRemovedMethodMethodCalls().contains(disableMethod));
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/e4735c72/framework/core/metamodel/src/test/java/org/apache/isis/core/progmodel/facets/propparam/multiline/MultiLineAnnotationFacetFactoryTest.java
----------------------------------------------------------------------
diff --git a/framework/core/metamodel/src/test/java/org/apache/isis/core/progmodel/facets/propparam/multiline/MultiLineAnnotationFacetFactoryTest.java b/framework/core/metamodel/src/test/java/org/apache/isis/core/progmodel/facets/propparam/multiline/MultiLineAnnotationFacetFactoryTest.java
new file mode 100644
index 0000000..bc27c5b
--- /dev/null
+++ b/framework/core/metamodel/src/test/java/org/apache/isis/core/progmodel/facets/propparam/multiline/MultiLineAnnotationFacetFactoryTest.java
@@ -0,0 +1,147 @@
+/*
+ *  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.progmodel.facets.propparam.multiline;
+
+import java.lang.reflect.Method;
+
+import org.apache.isis.applib.annotation.MultiLine;
+import org.apache.isis.core.metamodel.facetapi.Facet;
+import org.apache.isis.core.metamodel.facets.FacetFactory.ProcessClassContext;
+import org.apache.isis.core.metamodel.facets.FacetFactory.ProcessMethodContext;
+import org.apache.isis.core.metamodel.facets.FacetFactory.ProcessParameterContext;
+import org.apache.isis.core.metamodel.facets.multiline.MultiLineFacet;
+import org.apache.isis.core.progmodel.facets.AbstractFacetFactoryTest;
+import org.apache.isis.core.progmodel.facets.object.multiline.annotation.MultiLineAnnotationOnTypeFacetFactory;
+import org.apache.isis.core.progmodel.facets.object.multiline.annotation.MultiLineFacetAnnotationOnType;
+import org.apache.isis.core.progmodel.facets.param.multiline.annotation.MultiLineAnnotationOnParameterFacetFactory;
+import org.apache.isis.core.progmodel.facets.param.multiline.annotation.MultiLineFacetAnnotationOnParameter;
+import org.apache.isis.core.progmodel.facets.properties.multiline.annotation.MultiLineAnnotationOnPropertyFacetFactory;
+import org.apache.isis.core.progmodel.facets.properties.multiline.annotation.MultiLineFacetAnnotationOnProperty;
+
+public class MultiLineAnnotationFacetFactoryTest extends AbstractFacetFactoryTest {
+
+    public void testMultiLineAnnotationPickedUpOnClass() {
+        final MultiLineAnnotationOnTypeFacetFactory facetFactory = new MultiLineAnnotationOnTypeFacetFactory();
+
+        @MultiLine(numberOfLines = 3, preventWrapping = false)
+        class Customer {
+        }
+
+        facetFactory.process(new ProcessClassContext(Customer.class, methodRemover, facetedMethod));
+
+        final Facet facet = facetedMethod.getFacet(MultiLineFacet.class);
+        assertNotNull(facet);
+        assertTrue(facet instanceof MultiLineFacetAnnotationOnType);
+        final MultiLineFacetAnnotationOnType multiLineFacetAnnotation = (MultiLineFacetAnnotationOnType) facet;
+        assertEquals(3, multiLineFacetAnnotation.numberOfLines());
+        assertEquals(false, multiLineFacetAnnotation.preventWrapping());
+    }
+
+    public void testMultiLineAnnotationPickedUpOnProperty() {
+        final MultiLineAnnotationOnPropertyFacetFactory facetFactory = new MultiLineAnnotationOnPropertyFacetFactory();
+
+        class Customer {
+            @SuppressWarnings("unused")
+            @MultiLine(numberOfLines = 12, preventWrapping = true)
+            public String getFirstName() {
+                return null;
+            }
+        }
+        final Method method = findMethod(Customer.class, "getFirstName");
+
+        facetFactory.process(new ProcessMethodContext(Customer.class, method, methodRemover, facetedMethod));
+
+        final Facet facet = facetedMethod.getFacet(MultiLineFacet.class);
+        assertNotNull(facet);
+        assertTrue(facet instanceof MultiLineFacetAnnotationOnProperty);
+        final MultiLineFacetAnnotationOnProperty multiLineFacetAnnotation = (MultiLineFacetAnnotationOnProperty) facet;
+        assertEquals(12, multiLineFacetAnnotation.numberOfLines());
+        assertEquals(true, multiLineFacetAnnotation.preventWrapping());
+    }
+
+    public void testMultiLineAnnotationPickedUpOnActionParameter() {
+        final MultiLineAnnotationOnParameterFacetFactory facetFactory = new MultiLineAnnotationOnParameterFacetFactory();
+
+        class Customer {
+            @SuppressWarnings("unused")
+            public void someAction(@MultiLine(numberOfLines = 8, preventWrapping = false) final String foo) {
+            }
+        }
+        final Method method = findMethod(Customer.class, "someAction", new Class[] { String.class });
+
+        facetFactory.processParams(new ProcessParameterContext(method, 0, facetedMethodParameter));
+
+        final Facet facet = facetedMethodParameter.getFacet(MultiLineFacet.class);
+        assertNotNull(facet);
+        assertTrue(facet instanceof MultiLineFacetAnnotationOnParameter);
+        final MultiLineFacetAnnotationOnParameter multiLineFacetAnnotation = (MultiLineFacetAnnotationOnParameter) facet;
+        assertEquals(8, multiLineFacetAnnotation.numberOfLines());
+        assertEquals(false, multiLineFacetAnnotation.preventWrapping());
+    }
+
+    public void testMultiLineAnnotationDefaults() {
+        final MultiLineAnnotationOnTypeFacetFactory facetFactory = new MultiLineAnnotationOnTypeFacetFactory();
+
+        @MultiLine
+        class Customer {
+        }
+
+        facetFactory.process(new ProcessClassContext(Customer.class, methodRemover, facetedMethod));
+
+        final Facet facet = facetedMethod.getFacet(MultiLineFacet.class);
+        final MultiLineFacetAnnotationOnType multiLineFacetAnnotationOnType = (MultiLineFacetAnnotationOnType) facet;
+        assertEquals(6, multiLineFacetAnnotationOnType.numberOfLines());
+        assertEquals(true, multiLineFacetAnnotationOnType.preventWrapping());
+    }
+
+    public void testMultiLineAnnotationIgnoredForNonStringProperties() {
+        final MultiLineAnnotationOnPropertyFacetFactory facetFactory = new MultiLineAnnotationOnPropertyFacetFactory();
+
+        class Customer {
+            @SuppressWarnings("unused")
+            @MultiLine(numberOfLines = 8, preventWrapping = false)
+            public int getNumberOfOrders() {
+                return 0;
+            }
+        }
+        final Method method = findMethod(Customer.class, "getNumberOfOrders");
+
+        facetFactory.process(new ProcessMethodContext(Customer.class, method, methodRemover, facetedMethod));
+
+        final Facet facet = facetedMethod.getFacet(MultiLineFacet.class);
+        assertNull(facet);
+    }
+
+    public void testMultiLineAnnotationIgnoredForNonStringActionParameters() {
+        final MultiLineAnnotationOnParameterFacetFactory facetFactory = new MultiLineAnnotationOnParameterFacetFactory();
+
+        class Customer {
+            @SuppressWarnings("unused")
+            public void someAction(@MultiLine(numberOfLines = 8, preventWrapping = false) final int foo) {
+            }
+        }
+        final Method method = findMethod(Customer.class, "someAction", new Class[] { int.class });
+
+        facetFactory.processParams(new ProcessParameterContext(method, 0, facetedMethodParameter));
+
+        assertNull(facetedMethod.getFacet(MultiLineFacet.class));
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/e4735c72/framework/core/metamodel/src/test/java/org/apache/isis/core/progmodel/facets/propparam/specification/DomainObjectWithMustSatisfyAnnotations.java
----------------------------------------------------------------------
diff --git a/framework/core/metamodel/src/test/java/org/apache/isis/core/progmodel/facets/propparam/specification/DomainObjectWithMustSatisfyAnnotations.java b/framework/core/metamodel/src/test/java/org/apache/isis/core/progmodel/facets/propparam/specification/DomainObjectWithMustSatisfyAnnotations.java
new file mode 100644
index 0000000..3a0d684
--- /dev/null
+++ b/framework/core/metamodel/src/test/java/org/apache/isis/core/progmodel/facets/propparam/specification/DomainObjectWithMustSatisfyAnnotations.java
@@ -0,0 +1,57 @@
+/*
+ *  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.progmodel.facets.propparam.specification;
+
+import org.apache.isis.applib.AbstractDomainObject;
+import org.apache.isis.applib.annotation.MustSatisfy;
+
+public class DomainObjectWithMustSatisfyAnnotations extends AbstractDomainObject {
+
+    private String firstName;
+
+    @MustSatisfy(SpecificationAlwaysSatisfied.class)
+    public String getFirstName() {
+        resolve(firstName);
+        return firstName;
+    }
+
+    public void setFirstName(final String firstName) {
+        this.firstName = firstName;
+        objectChanged();
+    }
+
+    private String lastName;
+
+    @MustSatisfy(SpecificationRequiresFirstLetterToBeUpperCase.class)
+    public String getLastName() {
+        resolve(lastName);
+        return lastName;
+    }
+
+    public void setLastName(final String lastName) {
+        this.lastName = lastName;
+        objectChanged();
+    }
+
+    public void changeLastName(@MustSatisfy(SpecificationRequiresFirstLetterToBeUpperCase.class) final String lastName) {
+        setLastName(lastName);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/e4735c72/framework/core/metamodel/src/test/java/org/apache/isis/core/progmodel/facets/propparam/specification/DomainObjectWithoutMustSatisfyAnnotations.java
----------------------------------------------------------------------
diff --git a/framework/core/metamodel/src/test/java/org/apache/isis/core/progmodel/facets/propparam/specification/DomainObjectWithoutMustSatisfyAnnotations.java b/framework/core/metamodel/src/test/java/org/apache/isis/core/progmodel/facets/propparam/specification/DomainObjectWithoutMustSatisfyAnnotations.java
new file mode 100644
index 0000000..4ff0045
--- /dev/null
+++ b/framework/core/metamodel/src/test/java/org/apache/isis/core/progmodel/facets/propparam/specification/DomainObjectWithoutMustSatisfyAnnotations.java
@@ -0,0 +1,54 @@
+/*
+ *  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.progmodel.facets.propparam.specification;
+
+import org.apache.isis.applib.AbstractDomainObject;
+
+public class DomainObjectWithoutMustSatisfyAnnotations extends AbstractDomainObject {
+
+    private String firstName;
+
+    public String getFirstName() {
+        resolve(firstName);
+        return firstName;
+    }
+
+    public void setFirstName(final String firstName) {
+        this.firstName = firstName;
+        objectChanged();
+    }
+
+    private String lastName;
+
+    public String getLastName() {
+        resolve(lastName);
+        return lastName;
+    }
+
+    public void setLastName(final String lastName) {
+        this.lastName = lastName;
+        objectChanged();
+    }
+
+    public void changeLastName(final String lastName) {
+        setLastName(lastName);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/e4735c72/framework/core/metamodel/src/test/java/org/apache/isis/core/progmodel/facets/propparam/specification/MustSatisfySpecificationFacetFactoryInstantiationTest.java
----------------------------------------------------------------------
diff --git a/framework/core/metamodel/src/test/java/org/apache/isis/core/progmodel/facets/propparam/specification/MustSatisfySpecificationFacetFactoryInstantiationTest.java b/framework/core/metamodel/src/test/java/org/apache/isis/core/progmodel/facets/propparam/specification/MustSatisfySpecificationFacetFactoryInstantiationTest.java
new file mode 100644
index 0000000..ab5b726
--- /dev/null
+++ b/framework/core/metamodel/src/test/java/org/apache/isis/core/progmodel/facets/propparam/specification/MustSatisfySpecificationFacetFactoryInstantiationTest.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.progmodel.facets.propparam.specification;
+
+import org.junit.Test;
+
+import org.apache.isis.core.progmodel.facets.object.validperspec.MustSatisfySpecificationOnTypeFacetFactory;
+
+public class MustSatisfySpecificationFacetFactoryInstantiationTest {
+
+    @Test
+    public void canInstantiate() {
+        new MustSatisfySpecificationOnTypeFacetFactory();
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/e4735c72/framework/core/metamodel/src/test/java/org/apache/isis/core/progmodel/facets/propparam/specification/MustSatisfySpecificationFacetFactoryProcessParameterTest.java
----------------------------------------------------------------------
diff --git a/framework/core/metamodel/src/test/java/org/apache/isis/core/progmodel/facets/propparam/specification/MustSatisfySpecificationFacetFactoryProcessParameterTest.java b/framework/core/metamodel/src/test/java/org/apache/isis/core/progmodel/facets/propparam/specification/MustSatisfySpecificationFacetFactoryProcessParameterTest.java
new file mode 100644
index 0000000..70a16cc
--- /dev/null
+++ b/framework/core/metamodel/src/test/java/org/apache/isis/core/progmodel/facets/propparam/specification/MustSatisfySpecificationFacetFactoryProcessParameterTest.java
@@ -0,0 +1,89 @@
+/*
+ *  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.progmodel.facets.propparam.specification;
+
+import static org.apache.isis.core.commons.matchers.IsisMatchers.anInstanceOf;
+
+import java.lang.reflect.Method;
+
+import org.jmock.Expectations;
+import org.jmock.Mockery;
+import org.jmock.integration.junit4.JMock;
+import org.jmock.integration.junit4.JUnit4Mockery;
+import org.jmock.lib.legacy.ClassImposteriser;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import org.apache.isis.core.metamodel.facets.FacetFactory.ProcessParameterContext;
+import org.apache.isis.core.metamodel.facets.FacetedMethodParameter;
+import org.apache.isis.core.progmodel.facets.param.validate.perspec.MustSatisfySpecificationOnParameterFacet;
+import org.apache.isis.core.progmodel.facets.param.validate.perspec.MustSatisfySpecificationOnParameterFacetFactory;
+
+@RunWith(JMock.class)
+public class MustSatisfySpecificationFacetFactoryProcessParameterTest {
+
+    private final Mockery mockery = new JUnit4Mockery() {
+        {
+            setImposteriser(ClassImposteriser.INSTANCE);
+        }
+    };
+
+    private FacetedMethodParameter mockFacetedMethodParameter;
+
+    private Class<DomainObjectWithoutMustSatisfyAnnotations> domainObjectClassWithoutAnnotation;
+    private Class<DomainObjectWithMustSatisfyAnnotations> domainObjectClassWithAnnotation;
+    private Method changeLastNameMethodWithout;
+    private Method changeLastNameMethodWith;
+
+    @Before
+    public void setUp() throws Exception {
+        mockFacetedMethodParameter = mockery.mock(FacetedMethodParameter.class);
+        domainObjectClassWithoutAnnotation = DomainObjectWithoutMustSatisfyAnnotations.class;
+        domainObjectClassWithAnnotation = DomainObjectWithMustSatisfyAnnotations.class;
+        changeLastNameMethodWithout = domainObjectClassWithoutAnnotation.getMethod("changeLastName", String.class);
+        changeLastNameMethodWith = domainObjectClassWithAnnotation.getMethod("changeLastName", String.class);
+    }
+
+    @Test
+    public void addsAMustSatisfySpecificationFacetIfAnnotated() {
+        final MustSatisfySpecificationOnParameterFacetFactory facetFactory = new MustSatisfySpecificationOnParameterFacetFactory();
+
+        mockery.checking(new Expectations() {
+            {
+                one(mockFacetedMethodParameter).addFacet(with(anInstanceOf(MustSatisfySpecificationOnParameterFacet.class)));
+            }
+        });
+        facetFactory.processParams(new ProcessParameterContext(changeLastNameMethodWith, 0, mockFacetedMethodParameter));
+    }
+
+    @Test
+    public void doesNotAddsAMustSatisfySpecificationFacetIfNotAnnotated() {
+        final MustSatisfySpecificationOnParameterFacetFactory facetFactory = new MustSatisfySpecificationOnParameterFacetFactory();
+
+        mockery.checking(new Expectations() {
+            {
+                never(mockFacetedMethodParameter).addFacet(with(anInstanceOf(MustSatisfySpecificationOnParameterFacet.class)));
+            }
+        });
+        facetFactory.processParams(new ProcessParameterContext(changeLastNameMethodWithout, 0, mockFacetedMethodParameter));
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/e4735c72/framework/core/metamodel/src/test/java/org/apache/isis/core/progmodel/facets/propparam/specification/MustSatisfySpecificationFacetFactoryProcessPropertyTest.java
----------------------------------------------------------------------
diff --git a/framework/core/metamodel/src/test/java/org/apache/isis/core/progmodel/facets/propparam/specification/MustSatisfySpecificationFacetFactoryProcessPropertyTest.java b/framework/core/metamodel/src/test/java/org/apache/isis/core/progmodel/facets/propparam/specification/MustSatisfySpecificationFacetFactoryProcessPropertyTest.java
new file mode 100644
index 0000000..453a03b
--- /dev/null
+++ b/framework/core/metamodel/src/test/java/org/apache/isis/core/progmodel/facets/propparam/specification/MustSatisfySpecificationFacetFactoryProcessPropertyTest.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.progmodel.facets.propparam.specification;
+
+import static org.apache.isis.core.commons.matchers.IsisMatchers.anInstanceOf;
+
+import java.lang.reflect.Method;
+
+import org.jmock.Expectations;
+import org.jmock.Mockery;
+import org.jmock.integration.junit4.JMock;
+import org.jmock.integration.junit4.JUnit4Mockery;
+import org.jmock.lib.legacy.ClassImposteriser;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import org.apache.isis.core.metamodel.facetapi.MethodRemover;
+import org.apache.isis.core.metamodel.facets.FacetFactory.ProcessMethodContext;
+import org.apache.isis.core.metamodel.facets.FacetedMethod;
+import org.apache.isis.core.progmodel.facets.properties.validate.perspec.MustSatisfySpecificationOnPropertyFacet;
+import org.apache.isis.core.progmodel.facets.properties.validate.perspec.MustSatisfySpecificationOnPropertyFacetFactory;
+
+@RunWith(JMock.class)
+public class MustSatisfySpecificationFacetFactoryProcessPropertyTest {
+
+    private final Mockery mockery = new JUnit4Mockery() {
+        {
+            setImposteriser(ClassImposteriser.INSTANCE);
+        }
+    };
+
+    private MethodRemover mockMethodRemover;
+    private FacetedMethod mockFacetHolder;
+
+    private Class<DomainObjectWithoutMustSatisfyAnnotations> domainObjectClassWithoutAnnotation;
+    private Class<DomainObjectWithMustSatisfyAnnotations> domainObjectClassWithAnnotation;
+    private Method firstNameMethodWithout;
+    private Method firstNameMethodWith;
+
+    @Before
+    public void setUp() throws Exception {
+        mockMethodRemover = mockery.mock(MethodRemover.class);
+        mockFacetHolder = mockery.mock(FacetedMethod.class);
+        domainObjectClassWithoutAnnotation = DomainObjectWithoutMustSatisfyAnnotations.class;
+        domainObjectClassWithAnnotation = DomainObjectWithMustSatisfyAnnotations.class;
+        firstNameMethodWithout = domainObjectClassWithoutAnnotation.getMethod("getFirstName");
+        firstNameMethodWith = domainObjectClassWithAnnotation.getMethod("getFirstName");
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        mockMethodRemover = null;
+        mockFacetHolder = null;
+    }
+
+    @Test
+    public void addsAMustSatisfySpecificationFacetIfAnnotated() {
+        final MustSatisfySpecificationOnPropertyFacetFactory facetFactory = new MustSatisfySpecificationOnPropertyFacetFactory();
+
+        mockery.checking(new Expectations() {
+            {
+                one(mockFacetHolder).addFacet(with(anInstanceOf(MustSatisfySpecificationOnPropertyFacet.class)));
+            }
+        });
+        facetFactory.process(new ProcessMethodContext(domainObjectClassWithAnnotation.getClass(), firstNameMethodWith, mockMethodRemover, mockFacetHolder));
+    }
+
+    @Test
+    public void doesNotAddsAMustSatisfySpecificationFacetIfNotAnnotated() {
+        final MustSatisfySpecificationOnPropertyFacetFactory facetFactory = new MustSatisfySpecificationOnPropertyFacetFactory();
+
+        mockery.checking(new Expectations() {
+            {
+                never(mockFacetHolder).addFacet(with(anInstanceOf(MustSatisfySpecificationOnPropertyFacet.class)));
+            }
+        });
+        facetFactory.process(new ProcessMethodContext(domainObjectClassWithAnnotation.getClass(), firstNameMethodWithout, mockMethodRemover, mockFacetHolder));
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/e4735c72/framework/core/metamodel/src/test/java/org/apache/isis/core/progmodel/facets/propparam/specification/MustSatisfySpecificationValidatingInteractionMoreTest.java
----------------------------------------------------------------------
diff --git a/framework/core/metamodel/src/test/java/org/apache/isis/core/progmodel/facets/propparam/specification/MustSatisfySpecificationValidatingInteractionMoreTest.java b/framework/core/metamodel/src/test/java/org/apache/isis/core/progmodel/facets/propparam/specification/MustSatisfySpecificationValidatingInteractionMoreTest.java
new file mode 100644
index 0000000..13388ff
--- /dev/null
+++ b/framework/core/metamodel/src/test/java/org/apache/isis/core/progmodel/facets/propparam/specification/MustSatisfySpecificationValidatingInteractionMoreTest.java
@@ -0,0 +1,122 @@
+/*
+ *  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.progmodel.facets.propparam.specification;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.not;
+import static org.hamcrest.CoreMatchers.nullValue;
+import static org.junit.Assert.assertThat;
+
+import org.jmock.Expectations;
+import org.jmock.Mockery;
+import org.jmock.integration.junit4.JMock;
+import org.jmock.integration.junit4.JUnit4Mockery;
+import org.jmock.lib.legacy.ClassImposteriser;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import org.apache.isis.core.metamodel.adapter.ObjectAdapter;
+import org.apache.isis.core.metamodel.facetapi.FacetHolder;
+import org.apache.isis.core.metamodel.facetapi.IdentifiedHolder;
+import org.apache.isis.core.metamodel.interactions.PropertyModifyContext;
+import org.apache.isis.core.progmodel.facets.object.validperspec.MustSatisfySpecificationOnTypeFacet;
+
+@RunWith(JMock.class)
+public class MustSatisfySpecificationValidatingInteractionMoreTest {
+
+    private final Mockery mockery = new JUnit4Mockery() {
+        {
+            setImposteriser(ClassImposteriser.INSTANCE);
+        }
+    };
+
+    private MustSatisfySpecificationOnTypeFacet facetForSpecificationFirstLetterUpperCase;
+    private FacetHolder mockHolder;
+
+    private PropertyModifyContext mockContext;
+
+    private ObjectAdapter mockProposedObjectAdapter;
+
+    private SpecificationRequiresFirstLetterToBeUpperCase requiresFirstLetterToBeUpperCase;
+
+    @Before
+    public void setUp() throws Exception {
+        mockHolder = mockery.mock(IdentifiedHolder.class);
+        requiresFirstLetterToBeUpperCase = new SpecificationRequiresFirstLetterToBeUpperCase();
+
+        facetForSpecificationFirstLetterUpperCase = new MustSatisfySpecificationOnTypeFacet(Utils.listOf(requiresFirstLetterToBeUpperCase), mockHolder);
+
+        mockContext = mockery.mock(PropertyModifyContext.class);
+        mockProposedObjectAdapter = mockery.mock(ObjectAdapter.class, "proposed");
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        mockHolder = null;
+        requiresFirstLetterToBeUpperCase = null;
+        mockContext = null;
+    }
+
+    /**
+     * As in:
+     * 
+     * <pre>
+     * [at]ValidatedBy(SpecificationRequiresFirstLetterToBeUpperCase.class)
+     * public void getLastName() { ... }
+     * </pre>
+     * 
+     * @see SpecificationRequiresFirstLetterToBeUpperCase
+     */
+    @Test
+    public void validatesUsingSpecificationIfProposedOkay() {
+        mockery.checking(new Expectations() {
+            {
+                one(mockContext).getProposed();
+                will(returnValue(mockProposedObjectAdapter));
+
+                one(mockProposedObjectAdapter).getObject();
+                will(returnValue("This starts with an upper case letter and so is okay"));
+            }
+        });
+
+        final String reason = facetForSpecificationFirstLetterUpperCase.invalidates(mockContext);
+        assertThat(reason, is(nullValue()));
+    }
+
+    @Test
+    public void invalidatesUsingSpecificationIfProposedNotOkay() {
+        mockery.checking(new Expectations() {
+            {
+                one(mockContext).getProposed();
+                will(returnValue(mockProposedObjectAdapter));
+
+                one(mockProposedObjectAdapter).getObject();
+                will(returnValue("this starts with an lower case letter and so is not okay"));
+            }
+        });
+
+        final String reason = facetForSpecificationFirstLetterUpperCase.invalidates(mockContext);
+        assertThat(reason, is(not(nullValue())));
+        assertThat(reason, is("Must start with upper case"));
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/e4735c72/framework/core/metamodel/src/test/java/org/apache/isis/core/progmodel/facets/propparam/specification/MustSatisfySpecificationValidatingInteractionTest.java
----------------------------------------------------------------------
diff --git a/framework/core/metamodel/src/test/java/org/apache/isis/core/progmodel/facets/propparam/specification/MustSatisfySpecificationValidatingInteractionTest.java b/framework/core/metamodel/src/test/java/org/apache/isis/core/progmodel/facets/propparam/specification/MustSatisfySpecificationValidatingInteractionTest.java
new file mode 100644
index 0000000..b9d9193
--- /dev/null
+++ b/framework/core/metamodel/src/test/java/org/apache/isis/core/progmodel/facets/propparam/specification/MustSatisfySpecificationValidatingInteractionTest.java
@@ -0,0 +1,109 @@
+/*
+ *  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.progmodel.facets.propparam.specification;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.not;
+import static org.hamcrest.CoreMatchers.nullValue;
+import static org.junit.Assert.assertThat;
+
+import org.jmock.Expectations;
+import org.jmock.Mockery;
+import org.jmock.integration.junit4.JMock;
+import org.jmock.integration.junit4.JUnit4Mockery;
+import org.jmock.lib.legacy.ClassImposteriser;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import org.apache.isis.core.metamodel.adapter.ObjectAdapter;
+import org.apache.isis.core.metamodel.facetapi.FacetHolder;
+import org.apache.isis.core.metamodel.facetapi.IdentifiedHolder;
+import org.apache.isis.core.metamodel.interactions.PropertyModifyContext;
+import org.apache.isis.core.progmodel.facets.object.validperspec.MustSatisfySpecificationOnTypeFacet;
+
+@RunWith(JMock.class)
+public class MustSatisfySpecificationValidatingInteractionTest {
+
+    private final Mockery mockery = new JUnit4Mockery() {
+        {
+            setImposteriser(ClassImposteriser.INSTANCE);
+        }
+    };
+
+    private MustSatisfySpecificationOnTypeFacet facetForSpecificationAlwaysSatisfied;
+    private MustSatisfySpecificationOnTypeFacet facetForSpecificationNeverSatisfied;
+    private FacetHolder mockHolder;
+
+    private PropertyModifyContext mockContext;
+
+    private ObjectAdapter mockProposedObjectAdapter;
+    private Object mockProposedObject;
+
+    private SpecificationAlwaysSatisfied specificationAlwaysSatisfied;
+    private SpecificationNeverSatisfied specificationNeverSatisfied;
+
+    @Before
+    public void setUp() throws Exception {
+        mockHolder = mockery.mock(IdentifiedHolder.class);
+        specificationAlwaysSatisfied = new SpecificationAlwaysSatisfied();
+        specificationNeverSatisfied = new SpecificationNeverSatisfied();
+
+        facetForSpecificationAlwaysSatisfied = new MustSatisfySpecificationOnTypeFacet(Utils.listOf(specificationAlwaysSatisfied), mockHolder);
+        facetForSpecificationNeverSatisfied = new MustSatisfySpecificationOnTypeFacet(Utils.listOf(specificationNeverSatisfied), mockHolder);
+
+        mockContext = mockery.mock(PropertyModifyContext.class);
+        mockProposedObjectAdapter = mockery.mock(ObjectAdapter.class, "proposed");
+        mockProposedObject = mockery.mock(Object.class, "proposedObject");
+
+        mockery.checking(new Expectations() {
+            {
+                one(mockContext).getProposed();
+                will(returnValue(mockProposedObjectAdapter));
+
+                one(mockProposedObjectAdapter).getObject();
+                will(returnValue(mockProposedObject));
+            }
+        });
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        mockHolder = null;
+        facetForSpecificationAlwaysSatisfied = null;
+        facetForSpecificationNeverSatisfied = null;
+        mockContext = null;
+    }
+
+    @Test
+    public void validatesWhenSpecificationDoesNotVeto() {
+        final String reason = facetForSpecificationAlwaysSatisfied.invalidates(mockContext);
+        assertThat(reason, is(nullValue()));
+    }
+
+    @Test
+    public void invalidatesWhenSpecificationVetoes() {
+        final String reason = facetForSpecificationNeverSatisfied.invalidates(mockContext);
+        assertThat(reason, is(not(nullValue())));
+        assertThat(reason, is("not satisfied"));
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/e4735c72/framework/core/metamodel/src/test/java/org/apache/isis/core/progmodel/facets/propparam/specification/SpecificationAlwaysSatisfied.java
----------------------------------------------------------------------
diff --git a/framework/core/metamodel/src/test/java/org/apache/isis/core/progmodel/facets/propparam/specification/SpecificationAlwaysSatisfied.java b/framework/core/metamodel/src/test/java/org/apache/isis/core/progmodel/facets/propparam/specification/SpecificationAlwaysSatisfied.java
new file mode 100644
index 0000000..0b1e52e
--- /dev/null
+++ b/framework/core/metamodel/src/test/java/org/apache/isis/core/progmodel/facets/propparam/specification/SpecificationAlwaysSatisfied.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.progmodel.facets.propparam.specification;
+
+import org.apache.isis.applib.spec.Specification;
+
+public class SpecificationAlwaysSatisfied implements Specification {
+
+    @Override
+    public String satisfies(final Object obj) {
+        return null;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/e4735c72/framework/core/metamodel/src/test/java/org/apache/isis/core/progmodel/facets/propparam/specification/SpecificationAndTests.java
----------------------------------------------------------------------
diff --git a/framework/core/metamodel/src/test/java/org/apache/isis/core/progmodel/facets/propparam/specification/SpecificationAndTests.java b/framework/core/metamodel/src/test/java/org/apache/isis/core/progmodel/facets/propparam/specification/SpecificationAndTests.java
new file mode 100644
index 0000000..a639ea1
--- /dev/null
+++ b/framework/core/metamodel/src/test/java/org/apache/isis/core/progmodel/facets/propparam/specification/SpecificationAndTests.java
@@ -0,0 +1,111 @@
+/*
+ *  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.progmodel.facets.propparam.specification;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.not;
+import static org.hamcrest.CoreMatchers.nullValue;
+import static org.junit.Assert.assertThat;
+
+import org.junit.Test;
+
+import org.apache.isis.applib.spec.Specification;
+import org.apache.isis.applib.spec.SpecificationAnd;
+
+public class SpecificationAndTests {
+
+    private final Specification alwaysSatisfied = new SpecificationAlwaysSatisfied();
+    private final Specification neverSatisfied = new SpecificationNeverSatisfied();
+
+    @Test
+    public void satisfiedIfNone() {
+        class MySpecAnd extends SpecificationAnd {
+            public MySpecAnd() {
+            }
+        }
+        ;
+        final Specification mySpecAnd = new MySpecAnd();
+        assertThat(mySpecAnd.satisfies(null), is(nullValue()));
+    }
+
+    @Test
+    public void satisfiedIfOneAndOkay() {
+        class MySpecAnd extends SpecificationAnd {
+            public MySpecAnd() {
+                super(alwaysSatisfied);
+            }
+        }
+        ;
+        final Specification mySpecAnd = new MySpecAnd();
+        assertThat(mySpecAnd.satisfies(null), is(nullValue()));
+    }
+
+    @Test
+    public void notSatisfiedIfOneAndNotOkay() {
+        class MySpecAnd extends SpecificationAnd {
+            public MySpecAnd() {
+                super(neverSatisfied);
+            }
+        }
+        ;
+        final Specification mySpecAnd = new MySpecAnd();
+        assertThat(mySpecAnd.satisfies(null), is(not(nullValue())));
+        assertThat(mySpecAnd.satisfies(null), is("not satisfied"));
+    }
+
+    @Test
+    public void notSatisfiedIfTwoAndOneIsNotOkay() {
+        class MySpecAnd extends SpecificationAnd {
+            public MySpecAnd() {
+                super(alwaysSatisfied, neverSatisfied);
+            }
+        }
+        ;
+        final Specification mySpecAnd = new MySpecAnd();
+        assertThat(mySpecAnd.satisfies(null), is(not(nullValue())));
+        assertThat(mySpecAnd.satisfies(null), is("not satisfied"));
+    }
+
+    @Test
+    public void satisfiedIfTwoAndBothAreOkay() {
+        class MySpecAnd extends SpecificationAnd {
+            public MySpecAnd() {
+                super(alwaysSatisfied, alwaysSatisfied);
+            }
+        }
+        ;
+        final Specification mySpecAnd = new MySpecAnd();
+        assertThat(mySpecAnd.satisfies(null), is(nullValue()));
+    }
+
+    @Test
+    public void notSatisfiedIfTwoAndBothAreNotOkayWithConcatenatedReason() {
+        class MySpecAnd extends SpecificationAnd {
+            public MySpecAnd() {
+                super(neverSatisfied, neverSatisfied);
+            }
+        }
+        ;
+        final Specification mySpecAnd = new MySpecAnd();
+        assertThat(mySpecAnd.satisfies(null), is(not(nullValue())));
+        assertThat(mySpecAnd.satisfies(null), is("not satisfied; not satisfied"));
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/e4735c72/framework/core/metamodel/src/test/java/org/apache/isis/core/progmodel/facets/propparam/specification/SpecificationNeverSatisfied.java
----------------------------------------------------------------------
diff --git a/framework/core/metamodel/src/test/java/org/apache/isis/core/progmodel/facets/propparam/specification/SpecificationNeverSatisfied.java b/framework/core/metamodel/src/test/java/org/apache/isis/core/progmodel/facets/propparam/specification/SpecificationNeverSatisfied.java
new file mode 100644
index 0000000..f229dee
--- /dev/null
+++ b/framework/core/metamodel/src/test/java/org/apache/isis/core/progmodel/facets/propparam/specification/SpecificationNeverSatisfied.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.progmodel.facets.propparam.specification;
+
+import org.apache.isis.applib.spec.Specification;
+
+public class SpecificationNeverSatisfied implements Specification {
+
+    @Override
+    public String satisfies(final Object obj) {
+        return "not satisfied";
+    }
+
+}