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:10 UTC

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

http://git-wip-us.apache.org/repos/asf/isis/blob/e4735c72/framework/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/object/plural/staticmethod/PluralFacetViaMethod.java
----------------------------------------------------------------------
diff --git a/framework/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/object/plural/staticmethod/PluralFacetViaMethod.java b/framework/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/object/plural/staticmethod/PluralFacetViaMethod.java
new file mode 100644
index 0000000..a0ea907
--- /dev/null
+++ b/framework/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/object/plural/staticmethod/PluralFacetViaMethod.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.object.plural.staticmethod;
+
+import org.apache.isis.core.metamodel.facetapi.FacetHolder;
+import org.apache.isis.core.metamodel.facets.object.plural.PluralFacetAbstract;
+
+public class PluralFacetViaMethod extends PluralFacetAbstract {
+
+    public PluralFacetViaMethod(final String value, final FacetHolder holder) {
+        super(value, holder);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/e4735c72/framework/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/object/plural/staticmethod/PluralMethodFacetFactory.java
----------------------------------------------------------------------
diff --git a/framework/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/object/plural/staticmethod/PluralMethodFacetFactory.java b/framework/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/object/plural/staticmethod/PluralMethodFacetFactory.java
new file mode 100644
index 0000000..52760a5
--- /dev/null
+++ b/framework/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/object/plural/staticmethod/PluralMethodFacetFactory.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.object.plural.staticmethod;
+
+import java.lang.reflect.Method;
+
+import org.apache.isis.core.metamodel.adapter.util.InvokeUtils;
+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.methodutils.MethodScope;
+import org.apache.isis.core.progmodel.facets.MethodFinderUtils;
+import org.apache.isis.core.progmodel.facets.MethodPrefixBasedFacetFactoryAbstract;
+
+public class PluralMethodFacetFactory extends MethodPrefixBasedFacetFactoryAbstract {
+
+    private static final String PLURAL_NAME = "pluralName";
+
+    private static final String[] PREFIXES = { PLURAL_NAME, };
+
+    public PluralMethodFacetFactory() {
+        super(FeatureType.OBJECTS_ONLY, OrphanValidation.VALIDATE, PREFIXES);
+    }
+
+    @Override
+    public void process(final ProcessClassContext processClassContext) {
+        final Class<?> type = processClassContext.getCls();
+        final FacetHolder facetHolder = processClassContext.getFacetHolder();
+
+        final Method method = MethodFinderUtils.findMethod(type, MethodScope.CLASS, PLURAL_NAME, String.class, NO_PARAMETERS_TYPES);
+        if (method != null) {
+            final String name = (String) InvokeUtils.invokeStatic(method);
+            processClassContext.removeMethod(method);
+            FacetUtil.addFacet(new PluralFacetViaMethod(name, facetHolder));
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/e4735c72/framework/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/object/regex/RegExFacet.java
----------------------------------------------------------------------
diff --git a/framework/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/object/regex/RegExFacet.java b/framework/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/object/regex/RegExFacet.java
new file mode 100644
index 0000000..544ebaa
--- /dev/null
+++ b/framework/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/object/regex/RegExFacet.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.progmodel.facets.object.regex;
+
+import org.apache.isis.core.metamodel.facets.MultipleValueFacet;
+import org.apache.isis.core.metamodel.interactions.ValidatingInteractionAdvisor;
+import org.apache.isis.core.progmodel.facets.object.mask.MaskFacet;
+
+/**
+ * Whether the (string) property or a parameter must correspond to a specific
+ * regular expression.
+ * 
+ * <p>
+ * In the standard Apache Isis Programming Model, corresponds to the
+ * <tt>@RegEx</tt> annotation.
+ * 
+ * @see MaskFacet
+ */
+public interface RegExFacet extends MultipleValueFacet, ValidatingInteractionAdvisor {
+
+    public String validation();
+
+    public String format();
+
+    public boolean caseSensitive();
+
+    public boolean doesNotMatch(String proposed);
+
+    public String format(String text);
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/e4735c72/framework/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/object/regex/RegExFacetAbstract.java
----------------------------------------------------------------------
diff --git a/framework/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/object/regex/RegExFacetAbstract.java b/framework/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/object/regex/RegExFacetAbstract.java
new file mode 100644
index 0000000..a3407f9
--- /dev/null
+++ b/framework/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/object/regex/RegExFacetAbstract.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.object.regex;
+
+import org.apache.isis.applib.events.ValidityEvent;
+import org.apache.isis.core.metamodel.adapter.ObjectAdapter;
+import org.apache.isis.core.metamodel.facetapi.Facet;
+import org.apache.isis.core.metamodel.facetapi.FacetHolder;
+import org.apache.isis.core.metamodel.facets.MultipleValueFacetAbstract;
+import org.apache.isis.core.metamodel.interactions.ProposedHolder;
+import org.apache.isis.core.metamodel.interactions.ValidityContext;
+
+public abstract class RegExFacetAbstract extends MultipleValueFacetAbstract implements RegExFacet {
+
+    public static Class<? extends Facet> type() {
+        return RegExFacet.class;
+    }
+
+    private final String validation;
+    private final String format;
+    private final boolean caseSensitive;
+
+    public RegExFacetAbstract(final String validation, final String format, final boolean caseSensitive, final FacetHolder holder) {
+        super(type(), holder);
+        this.validation = validation;
+        this.format = format;
+        this.caseSensitive = caseSensitive;
+    }
+
+    @Override
+    public String validation() {
+        return validation;
+    }
+
+    @Override
+    public String format() {
+        return format;
+    }
+
+    @Override
+    public boolean caseSensitive() {
+        return caseSensitive;
+    }
+
+    // //////////////////////////////////////////////////////////
+
+    @Override
+    public String invalidates(final ValidityContext<? extends ValidityEvent> context) {
+        if (!(context instanceof ProposedHolder)) {
+            return null;
+        }
+        final ProposedHolder proposedHolder = (ProposedHolder) context;
+        final ObjectAdapter proposedArgument = proposedHolder.getProposed();
+        if (proposedArgument == null) {
+            return null;
+        }
+        final String titleString = proposedArgument.titleString();
+        if (!doesNotMatch(titleString)) {
+            return null;
+        }
+        return "Doesn't match pattern";
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/e4735c72/framework/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/object/regex/TitleFacetFormattedByRegex.java
----------------------------------------------------------------------
diff --git a/framework/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/object/regex/TitleFacetFormattedByRegex.java b/framework/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/object/regex/TitleFacetFormattedByRegex.java
new file mode 100644
index 0000000..3b4ebbf
--- /dev/null
+++ b/framework/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/object/regex/TitleFacetFormattedByRegex.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.progmodel.facets.object.regex;
+
+import org.apache.isis.applib.profiles.Localization;
+import org.apache.isis.core.metamodel.adapter.ObjectAdapter;
+import org.apache.isis.core.metamodel.facets.object.title.TitleFacetAbstract;
+
+public class TitleFacetFormattedByRegex extends TitleFacetAbstract {
+
+    private final RegExFacet regexFacet;
+
+    public TitleFacetFormattedByRegex(final RegExFacet regexFacet) {
+        super(regexFacet.getFacetHolder());
+        this.regexFacet = regexFacet;
+    }
+
+    @Override
+    public String title(final ObjectAdapter object, final Localization localization) {
+        return regexFacet.format(object.titleString());
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/e4735c72/framework/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/object/regex/annotation/RegExFacetAnnotationForType.java
----------------------------------------------------------------------
diff --git a/framework/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/object/regex/annotation/RegExFacetAnnotationForType.java b/framework/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/object/regex/annotation/RegExFacetAnnotationForType.java
new file mode 100644
index 0000000..87d770f
--- /dev/null
+++ b/framework/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/object/regex/annotation/RegExFacetAnnotationForType.java
@@ -0,0 +1,59 @@
+/*
+ *  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.object.regex.annotation;
+
+import java.util.regex.Pattern;
+
+import org.apache.isis.core.metamodel.facetapi.FacetHolder;
+import org.apache.isis.core.progmodel.facets.object.regex.RegExFacetAbstract;
+
+public class RegExFacetAnnotationForType extends RegExFacetAbstract {
+
+    private final Pattern pattern;
+
+    public RegExFacetAnnotationForType(final String validation, final String format, final boolean caseSensitive, final FacetHolder holder) {
+        super(validation, format, caseSensitive, holder);
+        pattern = Pattern.compile(validation(), patternFlags());
+    }
+
+    @Override
+    public String format(final String text) {
+        if (text == null) {
+            return "<not a string>";
+        }
+        if (format() == null || format().length() == 0) {
+            return text;
+        }
+        return pattern.matcher(text).replaceAll(format());
+    }
+
+    @Override
+    public boolean doesNotMatch(final String text) {
+        if (text == null) {
+            return true;
+        }
+        return !pattern.matcher(text).matches();
+    }
+
+    private int patternFlags() {
+        return !caseSensitive() ? Pattern.CASE_INSENSITIVE : 0;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/e4735c72/framework/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/object/regex/annotation/RegExFacetAnnotationForTypeFacetFactory.java
----------------------------------------------------------------------
diff --git a/framework/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/object/regex/annotation/RegExFacetAnnotationForTypeFacetFactory.java b/framework/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/object/regex/annotation/RegExFacetAnnotationForTypeFacetFactory.java
new file mode 100644
index 0000000..a7fb5fd
--- /dev/null
+++ b/framework/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/object/regex/annotation/RegExFacetAnnotationForTypeFacetFactory.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.progmodel.facets.object.regex.annotation;
+
+import org.apache.isis.applib.annotation.RegEx;
+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.progmodel.facets.object.regex.RegExFacet;
+
+public class RegExFacetAnnotationForTypeFacetFactory extends FacetFactoryAbstract {
+
+    public RegExFacetAnnotationForTypeFacetFactory() {
+        super(FeatureType.OBJECTS_ONLY);
+    }
+
+    @Override
+    public void process(final ProcessClassContext processClassContaxt) {
+        final RegEx annotation = Annotations.getAnnotation(processClassContaxt.getCls(), RegEx.class);
+        FacetUtil.addFacet(createRegexFacet(annotation, processClassContaxt.getFacetHolder()));
+    }
+
+    private RegExFacet createRegexFacet(final RegEx annotation, final FacetHolder holder) {
+        if (annotation == null) {
+            return null;
+        }
+
+        final String validationExpression = annotation.validation();
+        final boolean caseSensitive = annotation.caseSensitive();
+        final String formatExpression = annotation.format();
+
+        return new RegExFacetAnnotationForType(validationExpression, formatExpression, caseSensitive, holder);
+    }
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/e4735c72/framework/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/object/title/TitleFacetUsingParser.java
----------------------------------------------------------------------
diff --git a/framework/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/object/title/TitleFacetUsingParser.java b/framework/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/object/title/TitleFacetUsingParser.java
new file mode 100644
index 0000000..6f1b4de
--- /dev/null
+++ b/framework/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/object/title/TitleFacetUsingParser.java
@@ -0,0 +1,83 @@
+/*
+ *  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.object.title;
+
+import org.apache.isis.applib.adapters.Parser;
+import org.apache.isis.applib.profiles.Localization;
+import org.apache.isis.core.metamodel.adapter.ObjectAdapter;
+import org.apache.isis.core.metamodel.facetapi.FacetAbstract;
+import org.apache.isis.core.metamodel.facetapi.FacetHolder;
+import org.apache.isis.core.metamodel.facets.object.title.TitleFacet;
+import org.apache.isis.core.metamodel.runtimecontext.ServicesInjector;
+
+public class TitleFacetUsingParser extends FacetAbstract implements TitleFacet {
+
+    private final Parser parser;
+    private final ServicesInjector dependencyInjector;
+
+    public TitleFacetUsingParser(final Parser parser, final FacetHolder holder, final ServicesInjector dependencyInjector) {
+        super(TitleFacet.class, holder, Derivation.NOT_DERIVED);
+        this.parser = parser;
+        this.dependencyInjector = dependencyInjector;
+    }
+
+    @Override
+    protected String toStringValues() {
+        getDependencyInjector().injectServicesInto(parser);
+        return parser.toString();
+    }
+
+    @Override
+    public String title(final ObjectAdapter adapter, final Localization localization) {
+        if (adapter == null) {
+            return null;
+        }
+        final Object object = adapter.getObject();
+        if (object == null) {
+            return null;
+        }
+        getDependencyInjector().injectServicesInto(parser);
+        return parser.displayTitleOf(object, localization);
+    }
+
+    public String title(final ObjectAdapter adapter, final String usingMask) {
+        if (adapter == null) {
+            return null;
+        }
+        final Object object = adapter.getObject();
+        if (object == null) {
+            return null;
+        }
+        getDependencyInjector().injectServicesInto(parser);
+        return parser.displayTitleOf(object, usingMask);
+    }
+
+    // //////////////////////////////////////////////////////
+    // Dependencies (from constructor)
+    // //////////////////////////////////////////////////////
+
+    /**
+     * @return the dependencyInjector
+     */
+    public ServicesInjector getDependencyInjector() {
+        return dependencyInjector;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/e4735c72/framework/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/object/title/TitleFacetViaTitleMethod.java
----------------------------------------------------------------------
diff --git a/framework/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/object/title/TitleFacetViaTitleMethod.java b/framework/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/object/title/TitleFacetViaTitleMethod.java
new file mode 100644
index 0000000..7f3a7f5
--- /dev/null
+++ b/framework/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/object/title/TitleFacetViaTitleMethod.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.progmodel.facets.object.title;
+
+import java.lang.reflect.Method;
+import java.util.Collections;
+import java.util.List;
+
+import org.apache.log4j.Logger;
+
+import org.apache.isis.applib.profiles.Localization;
+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.object.title.TitleFacetAbstract;
+
+public class TitleFacetViaTitleMethod extends TitleFacetAbstract implements ImperativeFacet {
+
+    private static final Logger LOG = Logger.getLogger(TitleFacetViaTitleMethod.class);
+    private final Method method;
+
+    public TitleFacetViaTitleMethod(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 boolean impliesResolve() {
+        return true;
+    }
+
+    @Override
+    public boolean impliesObjectChanged() {
+        return false;
+    }
+
+    @Override
+    public String title(final ObjectAdapter owningAdapter, final Localization localization) {
+        try {
+            return (String) AdapterInvokeUtils.invoke(method, owningAdapter);
+        } catch (final RuntimeException ex) {
+            LOG.warn("title failure", ex);
+            return "Failed Title";
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/e4735c72/framework/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/object/title/TitleFacetViaToStringMethod.java
----------------------------------------------------------------------
diff --git a/framework/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/object/title/TitleFacetViaToStringMethod.java b/framework/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/object/title/TitleFacetViaToStringMethod.java
new file mode 100644
index 0000000..e73c281
--- /dev/null
+++ b/framework/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/object/title/TitleFacetViaToStringMethod.java
@@ -0,0 +1,69 @@
+/*
+ *  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.object.title;
+
+import java.lang.reflect.Method;
+import java.util.Collections;
+import java.util.List;
+
+import org.apache.isis.applib.profiles.Localization;
+import org.apache.isis.core.metamodel.adapter.ObjectAdapter;
+import org.apache.isis.core.metamodel.facetapi.FacetHolder;
+import org.apache.isis.core.metamodel.facets.ImperativeFacet;
+import org.apache.isis.core.metamodel.facets.object.title.TitleFacetAbstract;
+
+public class TitleFacetViaToStringMethod extends TitleFacetAbstract implements ImperativeFacet {
+
+    private final Method method;
+
+    public TitleFacetViaToStringMethod(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 boolean impliesResolve() {
+        return true;
+    }
+
+    @Override
+    public boolean impliesObjectChanged() {
+        return false;
+    }
+
+    @Override
+    public String title(final ObjectAdapter object, final Localization localization) {
+        return object.getObject().toString();
+    }
+
+    @Override
+    public boolean isNoop() {
+        return true;
+    }
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/e4735c72/framework/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/object/title/TitleMethodFacetFactory.java
----------------------------------------------------------------------
diff --git a/framework/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/object/title/TitleMethodFacetFactory.java b/framework/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/object/title/TitleMethodFacetFactory.java
new file mode 100644
index 0000000..b2a2a64
--- /dev/null
+++ b/framework/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/object/title/TitleMethodFacetFactory.java
@@ -0,0 +1,83 @@
+/*
+ *  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.object.title;
+
+import java.lang.reflect.Method;
+
+import org.apache.isis.core.commons.lang.JavaClassUtils;
+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.object.title.TitleFacet;
+import org.apache.isis.core.metamodel.methodutils.MethodScope;
+import org.apache.isis.core.progmodel.facets.MethodFinderUtils;
+import org.apache.isis.core.progmodel.facets.MethodPrefixBasedFacetFactoryAbstract;
+import org.apache.isis.core.progmodel.facets.fallback.FallbackFacetFactory;
+
+public class TitleMethodFacetFactory extends MethodPrefixBasedFacetFactoryAbstract {
+
+    private static final String TO_STRING = "toString";
+    private static final String TITLE = "title";
+
+    private static final String[] PREFIXES = { TO_STRING, TITLE, };
+
+    public TitleMethodFacetFactory() {
+        super(FeatureType.OBJECTS_ONLY, OrphanValidation.VALIDATE, PREFIXES);
+    }
+
+    /**
+     * If no title or toString can be used then will use Facets provided by
+     * {@link FallbackFacetFactory} instead.
+     */
+    @Override
+    public void process(final ProcessClassContext processClassContext) {
+        final Class<?> cls = processClassContext.getCls();
+        final FacetHolder facetHolder = processClassContext.getFacetHolder();
+
+        // may have a facet by virtue of @Title, say.
+        final TitleFacet existingTitleFacet = facetHolder.getFacet(TitleFacet.class);
+        if(existingTitleFacet != null && !existingTitleFacet.isNoop()) {
+            return;
+        }
+        
+        Method method = MethodFinderUtils.findMethod(cls, MethodScope.OBJECT, TITLE, String.class, null);
+        if (method != null) {
+            processClassContext.removeMethod(method);
+            FacetUtil.addFacet(new TitleFacetViaTitleMethod(method, facetHolder));
+            return;
+        }
+        
+
+        try {
+            method = MethodFinderUtils.findMethod(cls, MethodScope.OBJECT, TO_STRING, String.class, null);
+            if (method == null) {
+                return;
+            }
+            if (JavaClassUtils.isJavaClass(method.getDeclaringClass())) {
+                return;
+            }
+            processClassContext.removeMethod(method);
+            FacetUtil.addFacet(new TitleFacetViaToStringMethod(method, facetHolder));
+
+        } catch (final Exception e) {
+            return;
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/e4735c72/framework/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/object/title/annotation/HiddenFacetInTablesInferredFromTitleAnnotation.java
----------------------------------------------------------------------
diff --git a/framework/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/object/title/annotation/HiddenFacetInTablesInferredFromTitleAnnotation.java b/framework/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/object/title/annotation/HiddenFacetInTablesInferredFromTitleAnnotation.java
new file mode 100644
index 0000000..cb944c3
--- /dev/null
+++ b/framework/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/object/title/annotation/HiddenFacetInTablesInferredFromTitleAnnotation.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.object.title.annotation;
+
+import org.apache.isis.applib.annotation.When;
+import org.apache.isis.applib.annotation.Where;
+import org.apache.isis.core.metamodel.facetapi.FacetHolder;
+import org.apache.isis.core.progmodel.facets.members.hidden.HiddenFacetImpl;
+
+public class HiddenFacetInTablesInferredFromTitleAnnotation extends HiddenFacetImpl {
+
+    public HiddenFacetInTablesInferredFromTitleAnnotation(FacetHolder holder) {
+        super(When.ALWAYS, Where.ALL_TABLES, holder);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/e4735c72/framework/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/object/title/annotation/TitleAnnotationFacetFactory.java
----------------------------------------------------------------------
diff --git a/framework/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/object/title/annotation/TitleAnnotationFacetFactory.java b/framework/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/object/title/annotation/TitleAnnotationFacetFactory.java
new file mode 100644
index 0000000..2f53d7a
--- /dev/null
+++ b/framework/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/object/title/annotation/TitleAnnotationFacetFactory.java
@@ -0,0 +1,174 @@
+/*
+ *  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.object.title.annotation;
+
+import java.lang.reflect.Method;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+
+import com.google.common.base.Splitter;
+import com.google.common.collect.Lists;
+
+import org.apache.isis.applib.annotation.Disabled;
+import org.apache.isis.applib.annotation.Title;
+import org.apache.isis.core.metamodel.adapter.LocalizationProvider;
+import org.apache.isis.core.metamodel.adapter.LocalizationProviderAware;
+import org.apache.isis.core.metamodel.adapter.mgr.AdapterManager;
+import org.apache.isis.core.metamodel.adapter.mgr.AdapterManagerAware;
+import org.apache.isis.core.metamodel.facetapi.Facet;
+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.FacetedMethod;
+import org.apache.isis.core.metamodel.facets.hide.HiddenFacet;
+import org.apache.isis.core.metamodel.methodutils.MethodScope;
+import org.apache.isis.core.progmodel.facets.MethodFinderUtils;
+import org.apache.isis.core.progmodel.facets.fallback.FallbackFacetFactory;
+import org.apache.isis.core.progmodel.facets.members.disabled.DisabledFacet;
+import org.apache.isis.core.progmodel.facets.members.disabled.annotation.DisabledFacetAnnotation;
+import org.apache.isis.core.progmodel.facets.object.title.annotation.TitleFacetViaTitleAnnotation.TitleComponent;
+
+public class TitleAnnotationFacetFactory extends FacetFactoryAbstract implements AdapterManagerAware, LocalizationProviderAware {
+
+    private AdapterManager adapterManager;
+    private LocalizationProvider localizationProvider;
+
+    public TitleAnnotationFacetFactory() {
+        super(FeatureType.OBJECTS_AND_PROPERTIES);
+    }
+
+    /**
+     * If no method tagged with {@link Title} annotation then will use Facets
+     * provided by {@link FallbackFacetFactory} instead.
+     */
+    @Override
+    public void process(final ProcessClassContext processClassContext) {
+        final Class<?> cls = processClassContext.getCls();
+        final FacetHolder facetHolder = processClassContext.getFacetHolder();
+
+        final List<Method> methods = MethodFinderUtils.findMethodsWithAnnotation(cls, MethodScope.OBJECT, Title.class);
+
+        Collections.sort(methods, new Comparator<Method>() {
+            Comparator<String> comparator = new SequenceComparator();
+
+            @Override
+            public int compare(final Method o1, final Method o2) {
+                final Title a1 = o1.getAnnotation(Title.class);
+                final Title a2 = o2.getAnnotation(Title.class);
+                return comparator.compare(a1.sequence(), a2.sequence());
+            }
+        });
+        if (methods.isEmpty()) {
+            return;
+        }
+        final List<TitleComponent> titleComponents = Lists.transform(methods, TitleComponent.FROM_METHOD);
+        FacetUtil.addFacet(new TitleFacetViaTitleAnnotation(titleComponents, facetHolder, adapterManager, localizationProvider));
+    }
+
+    /**
+     * Any property annotated with <tt>Title</tt> is hidden by default in tables.
+     */
+    @Override
+    public void process(final ProcessMethodContext processMethodContext) {
+        final FacetedMethod facetHolder = processMethodContext.getFacetHolder();
+        if(facetHolder.containsFacet(HiddenFacet.class)) {
+            // don't overwrite any facet already installed
+            return;
+        }
+        // otherwise, install hidden facet if this property annotated with @Title
+        final Title annotation = Annotations.getAnnotation(processMethodContext.getMethod(), Title.class);
+        FacetUtil.addFacet(create(annotation, facetHolder));
+    }
+
+    private Facet create(final Title annotation, final FacetHolder holder) {
+        return annotation != null ? new HiddenFacetInTablesInferredFromTitleAnnotation(holder) : null;
+    }
+
+
+
+
+    static class SequenceComparator implements Comparator<String> {
+
+        @Override
+        public int compare(final String sequence1, final String sequence2) {
+
+            final List<String> components1 = componentsFor(sequence1);
+            final List<String> components2 = componentsFor(sequence2);
+
+            final int size1 = components1.size();
+            final int size2 = components2.size();
+
+            if (size1 == 0 && size2 == 0) {
+                return 0;
+            }
+
+            // continue to loop until we run out of components.
+            int n = 0;
+            while (true) {
+                final int length = n + 1;
+                // check if run out of components in either side
+                if (size1 < length && size2 >= length) {
+                    return -1; // o1 before o2
+                }
+                if (size2 < length && size1 >= length) {
+                    return +1; // o2 before o1
+                }
+                if (size1 < length && size2 < length) {
+                    // run out of components
+                    return 0;
+                }
+                // we have this component on each side
+                int componentCompare = 0;
+                try {
+                    final Integer c1 = Integer.valueOf(components1.get(n));
+                    final Integer c2 = Integer.valueOf(components2.get(n));
+                    componentCompare = c1.compareTo(c2);
+                } catch (final NumberFormatException nfe) {
+                    // not integers compare as strings
+                    componentCompare = components1.get(n).compareTo(components2.get(n));
+                }
+
+                if (componentCompare != 0) {
+                    return componentCompare;
+                }
+                // this component is the same; lets look at the next
+                n++;
+            }
+        }
+
+        private static List<String> componentsFor(final String sequence) {
+            return Lists.newArrayList(Splitter.on('.').split(sequence));
+        }
+
+    }
+
+    @Override
+    public void setLocalizationProvider(final LocalizationProvider localizationProvider) {
+        this.localizationProvider = localizationProvider;
+    }
+
+    @Override
+    public void setAdapterManager(final AdapterManager adapterMap) {
+        this.adapterManager = adapterMap;
+    }
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/e4735c72/framework/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/object/title/annotation/TitleFacetViaTitleAnnotation.java
----------------------------------------------------------------------
diff --git a/framework/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/object/title/annotation/TitleFacetViaTitleAnnotation.java b/framework/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/object/title/annotation/TitleFacetViaTitleAnnotation.java
new file mode 100644
index 0000000..384fc47
--- /dev/null
+++ b/framework/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/object/title/annotation/TitleFacetViaTitleAnnotation.java
@@ -0,0 +1,149 @@
+/*
+ *  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.object.title.annotation;
+
+import java.lang.reflect.Method;
+import java.util.List;
+
+import com.google.common.base.Function;
+import com.google.common.base.Strings;
+
+import org.apache.log4j.Logger;
+
+import org.apache.isis.applib.annotation.Title;
+import org.apache.isis.applib.profiles.Localization;
+import org.apache.isis.core.metamodel.adapter.LocalizationProvider;
+import org.apache.isis.core.metamodel.adapter.ObjectAdapter;
+import org.apache.isis.core.metamodel.adapter.mgr.AdapterManager;
+import org.apache.isis.core.metamodel.adapter.util.AdapterInvokeUtils;
+import org.apache.isis.core.metamodel.facetapi.FacetHolder;
+import org.apache.isis.core.metamodel.facets.object.title.TitleFacet;
+import org.apache.isis.core.metamodel.facets.object.title.TitleFacetAbstract;
+import org.apache.isis.core.metamodel.spec.ObjectSpecification;
+
+public class TitleFacetViaTitleAnnotation extends TitleFacetAbstract {
+
+    private static final Logger LOG = Logger.getLogger(TitleFacetViaTitleAnnotation.class);
+    private final List<TitleComponent> components;
+    private final AdapterManager adapterManager;
+    private final LocalizationProvider localizationProvider;
+
+    public static class TitleComponent {
+        public static final Function<? super Method, ? extends TitleComponent> FROM_METHOD = new Function<Method, TitleComponent>() {
+
+            @Override
+            public TitleComponent apply(final Method input) {
+                return TitleComponent.of(input);
+            }
+        };
+
+        private final String prepend;
+        private final String append;
+        private final Method method;
+        private final int abbreviateTo;
+
+        private TitleComponent(final String prepend, final String append, final Method method, final int abbreviateTo) {
+            super();
+            this.prepend = prepend;
+            this.append = append;
+            this.method = method;
+            this.abbreviateTo = abbreviateTo;
+        }
+
+        public String getPrepend() {
+            return prepend;
+        }
+
+        public String getAppend() {
+            return append;
+        }
+
+        public Method getMethod() {
+            return method;
+        }
+
+        public static TitleComponent of(final Method method) {
+            final Title annotation = method.getAnnotation(Title.class);
+            final String prepend = annotation != null ? annotation.prepend() : " ";
+            final String append = annotation != null ? annotation.append() : "";
+            final int abbreviateTo = annotation != null ? annotation.abbreviatedTo() : Integer.MAX_VALUE;
+            return new TitleComponent(prepend, append, method, abbreviateTo);
+        }
+    }
+
+    public TitleFacetViaTitleAnnotation(final List<TitleComponent> components, final FacetHolder holder, final AdapterManager adapterManager, final LocalizationProvider localizationProvider) {
+        super(holder);
+        this.components = components;
+        this.adapterManager = adapterManager;
+        this.localizationProvider = localizationProvider;
+    }
+
+    @Override
+    public String title(final ObjectAdapter owningAdapter, final Localization localization) {
+        final StringBuilder stringBuilder = new StringBuilder();
+
+        try {
+            for (final TitleComponent component : this.components) {
+                String title = null;
+                final Object titlePart = AdapterInvokeUtils.invoke(component.getMethod(), owningAdapter);
+                if (titlePart != null) {
+                    // use either titleFacet...
+                    title = titleOf(titlePart);
+                    if (Strings.isNullOrEmpty(title)) {
+                        // or the toString() otherwise
+                        title = titlePart.toString().trim();
+                    }
+                }
+                if (Strings.isNullOrEmpty(title)) {
+                    continue;
+                }
+                stringBuilder.append(component.getPrepend());
+                stringBuilder.append(abbreviated(title, component.abbreviateTo));
+                stringBuilder.append(component.getAppend());
+            }
+
+            return stringBuilder.toString().trim();
+        } catch (final RuntimeException ex) {
+            LOG.warn("Title failure", ex);
+            return "Failed Title";
+        }
+    }
+
+    private String titleOf(final Object domainObject) {
+        final ObjectAdapter adapter = adapterManager.adapterFor(domainObject);
+        if (adapter == null) {
+            return null;
+        }
+        final ObjectSpecification returnSpec = adapter.getSpecification();
+        if (!returnSpec.containsFacet(TitleFacet.class)) {
+            return null;
+        }
+        return returnSpec.getTitle(adapter, localizationProvider.getLocalization());
+    }
+
+    public List<TitleComponent> getComponents() {
+        return components;
+    }
+
+    private static String abbreviated(final String str, final int maxLength) {
+        return str.length() < maxLength ? str : str.substring(0, maxLength - 3) + "...";
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/e4735c72/framework/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/object/typicallen/annotation/TypicalLengthAnnotationOnTypeFacetFactory.java
----------------------------------------------------------------------
diff --git a/framework/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/object/typicallen/annotation/TypicalLengthAnnotationOnTypeFacetFactory.java b/framework/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/object/typicallen/annotation/TypicalLengthAnnotationOnTypeFacetFactory.java
new file mode 100644
index 0000000..5962fce
--- /dev/null
+++ b/framework/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/object/typicallen/annotation/TypicalLengthAnnotationOnTypeFacetFactory.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.progmodel.facets.object.typicallen.annotation;
+
+import org.apache.isis.applib.annotation.TypicalLength;
+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.typicallen.TypicalLengthFacet;
+
+public class TypicalLengthAnnotationOnTypeFacetFactory extends FacetFactoryAbstract {
+
+    public TypicalLengthAnnotationOnTypeFacetFactory() {
+        super(FeatureType.OBJECTS_ONLY);
+    }
+
+    @Override
+    public void process(final ProcessClassContext processClassContaxt) {
+        final TypicalLength annotation = Annotations.getAnnotation(processClassContaxt.getCls(), TypicalLength.class);
+        FacetUtil.addFacet(create(annotation, processClassContaxt.getFacetHolder()));
+    }
+
+    private TypicalLengthFacet create(final TypicalLength annotation, final FacetHolder holder) {
+        return annotation != null ? new TypicalLengthFacetAnnotationOnType(annotation.value(), holder) : null;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/e4735c72/framework/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/object/typicallen/annotation/TypicalLengthFacetAnnotationOnType.java
----------------------------------------------------------------------
diff --git a/framework/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/object/typicallen/annotation/TypicalLengthFacetAnnotationOnType.java b/framework/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/object/typicallen/annotation/TypicalLengthFacetAnnotationOnType.java
new file mode 100644
index 0000000..bb70330
--- /dev/null
+++ b/framework/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/object/typicallen/annotation/TypicalLengthFacetAnnotationOnType.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.progmodel.facets.object.typicallen.annotation;
+
+import org.apache.isis.core.metamodel.facetapi.FacetHolder;
+import org.apache.isis.core.metamodel.facets.typicallen.TypicalLengthFacetAbstract;
+
+public class TypicalLengthFacetAnnotationOnType extends TypicalLengthFacetAbstract {
+
+    private final int value;
+
+    public TypicalLengthFacetAnnotationOnType(final int value, final FacetHolder holder) {
+        super(holder, false);
+        this.value = value;
+    }
+
+    @Override
+    public int value() {
+        return value;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/e4735c72/framework/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/object/validate/ValidateObjectFacet.java
----------------------------------------------------------------------
diff --git a/framework/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/object/validate/ValidateObjectFacet.java b/framework/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/object/validate/ValidateObjectFacet.java
new file mode 100644
index 0000000..ab70505
--- /dev/null
+++ b/framework/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/object/validate/ValidateObjectFacet.java
@@ -0,0 +1,55 @@
+/*
+ *  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.object.validate;
+
+import org.apache.isis.core.metamodel.adapter.ObjectAdapter;
+import org.apache.isis.core.metamodel.facetapi.Facet;
+import org.apache.isis.core.metamodel.facets.object.callbacks.PersistingCallbackFacet;
+import org.apache.isis.core.metamodel.facets.object.callbacks.UpdatingCallbackFacet;
+import org.apache.isis.core.metamodel.interactions.ValidatingInteractionAdvisor;
+
+/**
+ * Mechanism for determining whether this object is in a valid state, for
+ * example so that it can be persisted or updated.
+ * 
+ * <p>
+ * Even though all the properties of an object may themselves be valid, there
+ * could be inter-property dependencies which are invalid. For example
+ * <tt>fromDate</tt> > <tt>toDate</tt> would probably represent an invalid
+ * state.
+ * 
+ * <p>
+ * In the standard Apache Isis Programming Model, typically corresponds to the
+ * <tt>validate</tt> method.
+ * 
+ * @see PersistingCallbackFacet
+ * @see UpdatingCallbackFacet
+ */
+public interface ValidateObjectFacet extends Facet, ValidatingInteractionAdvisor {
+
+    /**
+     * The reason the object is invalid.
+     * 
+     * <p>
+     * . If the object is actually valid, should return <tt>null</tt>.
+     */
+    public String invalidReason(ObjectAdapter adapter);
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/e4735c72/framework/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/object/validate/ValidateObjectFacetAbstract.java
----------------------------------------------------------------------
diff --git a/framework/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/object/validate/ValidateObjectFacetAbstract.java b/framework/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/object/validate/ValidateObjectFacetAbstract.java
new file mode 100644
index 0000000..8ddad6e
--- /dev/null
+++ b/framework/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/object/validate/ValidateObjectFacetAbstract.java
@@ -0,0 +1,49 @@
+/*
+ *  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.object.validate;
+
+import org.apache.isis.applib.events.ValidityEvent;
+import org.apache.isis.core.metamodel.adapter.ObjectAdapter;
+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.ObjectValidityContext;
+import org.apache.isis.core.metamodel.interactions.ValidityContext;
+
+public abstract class ValidateObjectFacetAbstract extends FacetAbstract implements ValidateObjectFacet {
+
+    public static Class<? extends Facet> type() {
+        return ValidateObjectFacet.class;
+    }
+
+    public ValidateObjectFacetAbstract(final FacetHolder holder) {
+        super(type(), holder, Derivation.NOT_DERIVED);
+    }
+
+    @Override
+    public String invalidates(final ValidityContext<? extends ValidityEvent> vc) {
+        if (!(vc instanceof ObjectValidityContext)) {
+            return null;
+        }
+        final ObjectAdapter toValidate = vc.getTarget();
+        return toValidate != null ? invalidReason(toValidate) : null;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/e4735c72/framework/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/object/validate/method/ValidateObjectFacetViaValidateMethod.java
----------------------------------------------------------------------
diff --git a/framework/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/object/validate/method/ValidateObjectFacetViaValidateMethod.java b/framework/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/object/validate/method/ValidateObjectFacetViaValidateMethod.java
new file mode 100644
index 0000000..65d7dbf
--- /dev/null
+++ b/framework/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/object/validate/method/ValidateObjectFacetViaValidateMethod.java
@@ -0,0 +1,66 @@
+/*
+ *  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.object.validate.method;
+
+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.progmodel.facets.object.validate.ValidateObjectFacetAbstract;
+
+public class ValidateObjectFacetViaValidateMethod extends ValidateObjectFacetAbstract implements ImperativeFacet {
+
+    private final Method method;
+
+    public ValidateObjectFacetViaValidateMethod(final Method method, final FacetHolder holder) {
+        super(holder);
+        this.method = method;
+    }
+
+    @Override
+    public List<Method> getMethods() {
+        return Collections.singletonList(method);
+    }
+
+    @Override
+    public boolean impliesResolve() {
+        return true;
+    }
+
+    @Override
+    public boolean impliesObjectChanged() {
+        return false;
+    }
+
+    @Override
+    public String invalidReason(final ObjectAdapter owningAdapter) {
+        return (String) AdapterInvokeUtils.invoke(method, owningAdapter);
+    }
+
+    @Override
+    protected String toStringValues() {
+        return "method=" + method;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/e4735c72/framework/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/object/validate/method/ValidateObjectViaValidateMethodFacetFactory.java
----------------------------------------------------------------------
diff --git a/framework/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/object/validate/method/ValidateObjectViaValidateMethodFacetFactory.java b/framework/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/object/validate/method/ValidateObjectViaValidateMethodFacetFactory.java
new file mode 100644
index 0000000..d0bc271
--- /dev/null
+++ b/framework/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/object/validate/method/ValidateObjectViaValidateMethodFacetFactory.java
@@ -0,0 +1,52 @@
+/*
+ *  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.object.validate.method;
+
+import java.lang.reflect.Method;
+
+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.methodutils.MethodScope;
+import org.apache.isis.core.progmodel.facets.MethodFinderUtils;
+import org.apache.isis.core.progmodel.facets.MethodPrefixBasedFacetFactoryAbstract;
+
+public class ValidateObjectViaValidateMethodFacetFactory extends MethodPrefixBasedFacetFactoryAbstract {
+
+    private static final String VALIDATE_PREFIX = "validate";
+
+    private static final String[] PREFIXES = { VALIDATE_PREFIX, };
+
+    public ValidateObjectViaValidateMethodFacetFactory() {
+        super(FeatureType.OBJECTS_ONLY, OrphanValidation.VALIDATE, PREFIXES);
+    }
+
+    @Override
+    public void process(final ProcessClassContext processClassContext) {
+        final Class<?> cls = processClassContext.getCls();
+        final FacetHolder facetHolder = processClassContext.getFacetHolder();
+
+        final Method method = MethodFinderUtils.findMethod(cls, MethodScope.OBJECT, VALIDATE_PREFIX, String.class, NO_PARAMETERS_TYPES);
+        if (method != null) {
+            FacetUtil.addFacet(new ValidateObjectFacetViaValidateMethod(method, facetHolder));
+            processClassContext.removeMethod(method);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/e4735c72/framework/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/object/validperspec/MustSatisfySpecificationOnTypeFacet.java
----------------------------------------------------------------------
diff --git a/framework/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/object/validperspec/MustSatisfySpecificationOnTypeFacet.java b/framework/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/object/validperspec/MustSatisfySpecificationOnTypeFacet.java
new file mode 100644
index 0000000..6cf748a
--- /dev/null
+++ b/framework/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/object/validperspec/MustSatisfySpecificationOnTypeFacet.java
@@ -0,0 +1,63 @@
+/*
+ *  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.object.validperspec;
+
+import java.util.List;
+
+import org.apache.isis.applib.events.ValidityEvent;
+import org.apache.isis.applib.spec.Specification;
+import org.apache.isis.applib.util.ReasonBuffer;
+import org.apache.isis.core.metamodel.adapter.ObjectAdapter;
+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.ProposedHolder;
+import org.apache.isis.core.metamodel.interactions.ValidatingInteractionAdvisor;
+import org.apache.isis.core.metamodel.interactions.ValidityContext;
+
+public class MustSatisfySpecificationOnTypeFacet extends FacetAbstract implements ValidatingInteractionAdvisor {
+
+    public static Class<? extends Facet> type() {
+        return MustSatisfySpecificationOnTypeFacet.class;
+    }
+
+    private final List<Specification> specifications;
+
+    public MustSatisfySpecificationOnTypeFacet(final List<Specification> specifications, final FacetHolder holder) {
+        super(type(), holder, Derivation.NOT_DERIVED);
+        this.specifications = specifications;
+    }
+
+    @Override
+    public String invalidates(final ValidityContext<? extends ValidityEvent> validityContext) {
+        if (!(validityContext instanceof ProposedHolder)) {
+            return null;
+        }
+        final ProposedHolder proposedHolder = (ProposedHolder) validityContext;
+        final ObjectAdapter targetNO = proposedHolder.getProposed();
+        final Object targetObject = targetNO.getObject();
+        final ReasonBuffer buf = new ReasonBuffer();
+        for (final Specification specification : specifications) {
+            buf.append(specification.satisfies(targetObject));
+        }
+        return buf.getReason();
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/e4735c72/framework/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/object/validperspec/MustSatisfySpecificationOnTypeFacetFactory.java
----------------------------------------------------------------------
diff --git a/framework/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/object/validperspec/MustSatisfySpecificationOnTypeFacetFactory.java b/framework/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/object/validperspec/MustSatisfySpecificationOnTypeFacetFactory.java
new file mode 100644
index 0000000..33f4e02
--- /dev/null
+++ b/framework/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/object/validperspec/MustSatisfySpecificationOnTypeFacetFactory.java
@@ -0,0 +1,77 @@
+/*
+ *  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.object.validperspec;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.isis.applib.annotation.MustSatisfy;
+import org.apache.isis.applib.spec.Specification;
+import org.apache.isis.core.metamodel.facetapi.Facet;
+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;
+
+public class MustSatisfySpecificationOnTypeFacetFactory extends FacetFactoryAbstract {
+
+    public MustSatisfySpecificationOnTypeFacetFactory() {
+        super(FeatureType.OBJECTS_ONLY);
+    }
+
+    @Override
+    public void process(final ProcessClassContext processClassContaxt) {
+        FacetUtil.addFacet(create(processClassContaxt.getCls(), processClassContaxt.getFacetHolder()));
+    }
+
+    private Facet create(final Class<?> clazz, final FacetHolder holder) {
+        return create(Annotations.getAnnotation(clazz, MustSatisfy.class), holder);
+    }
+
+    private static Facet create(final MustSatisfy annotation, final FacetHolder holder) {
+        if (annotation == null) {
+            return null;
+        }
+        final Class<?>[] values = annotation.value();
+        final List<Specification> specifications = new ArrayList<Specification>();
+        for (final Class<?> value : values) {
+            final Specification specification = newSpecificationElseNull(value);
+            if (specification != null) {
+                specifications.add(specification);
+            }
+        }
+        return specifications.size() > 0 ? new MustSatisfySpecificationOnTypeFacet(specifications, holder) : null;
+    }
+
+    private static Specification newSpecificationElseNull(final Class<?> value) {
+        if (!(Specification.class.isAssignableFrom(value))) {
+            return null;
+        }
+        try {
+            return (Specification) value.newInstance();
+        } catch (final InstantiationException e) {
+            return null;
+        } catch (final IllegalAccessException e) {
+            return null;
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/e4735c72/framework/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/object/validprops/ObjectValidPropertiesFacet.java
----------------------------------------------------------------------
diff --git a/framework/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/object/validprops/ObjectValidPropertiesFacet.java b/framework/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/object/validprops/ObjectValidPropertiesFacet.java
new file mode 100644
index 0000000..de25a65
--- /dev/null
+++ b/framework/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/object/validprops/ObjectValidPropertiesFacet.java
@@ -0,0 +1,42 @@
+/*
+ *  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.object.validprops;
+
+import org.apache.isis.core.metamodel.facetapi.Facet;
+import org.apache.isis.core.metamodel.facets.mandatory.MandatoryFacet;
+import org.apache.isis.core.metamodel.interactions.ObjectValidityContext;
+import org.apache.isis.core.metamodel.interactions.ValidatingInteractionAdvisor;
+
+/**
+ * Object-level {@link ValidatingInteractionAdvisor validator} that ensures that
+ * all {@link MandatoryFacet mandatory} properties are entered prior to
+ * persisting the object.
+ */
+public interface ObjectValidPropertiesFacet extends Facet, ValidatingInteractionAdvisor {
+
+    /**
+     * The reason the object is invalid.
+     * 
+     * <p>
+     * . If the object is actually valid, should return <tt>null</tt>.
+     */
+    public String invalidReason(ObjectValidityContext context);
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/e4735c72/framework/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/object/validprops/ObjectValidPropertiesFacetAbstract.java
----------------------------------------------------------------------
diff --git a/framework/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/object/validprops/ObjectValidPropertiesFacetAbstract.java b/framework/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/object/validprops/ObjectValidPropertiesFacetAbstract.java
new file mode 100644
index 0000000..fb4d44c
--- /dev/null
+++ b/framework/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/object/validprops/ObjectValidPropertiesFacetAbstract.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.progmodel.facets.object.validprops;
+
+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.ObjectValidityContext;
+import org.apache.isis.core.metamodel.interactions.ValidityContext;
+
+public abstract class ObjectValidPropertiesFacetAbstract extends FacetAbstract implements ObjectValidPropertiesFacet {
+
+    public static Class<? extends Facet> type() {
+        return ObjectValidPropertiesFacet.class;
+    }
+
+    public ObjectValidPropertiesFacetAbstract(final FacetHolder holder) {
+        super(type(), holder, Derivation.NOT_DERIVED);
+    }
+
+    @Override
+    public String invalidates(final ValidityContext<? extends ValidityEvent> ic) {
+        if (!(ic instanceof ObjectValidityContext)) {
+            return null;
+        }
+        final ObjectValidityContext validityContext = (ObjectValidityContext) ic;
+        return invalidReason(validityContext);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/e4735c72/framework/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/object/validprops/ObjectValidPropertiesFacetFactory.java
----------------------------------------------------------------------
diff --git a/framework/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/object/validprops/ObjectValidPropertiesFacetFactory.java b/framework/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/object/validprops/ObjectValidPropertiesFacetFactory.java
new file mode 100644
index 0000000..1c950b5
--- /dev/null
+++ b/framework/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/object/validprops/ObjectValidPropertiesFacetFactory.java
@@ -0,0 +1,36 @@
+/*
+ *  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.object.validprops;
+
+import org.apache.isis.core.metamodel.facetapi.FacetUtil;
+import org.apache.isis.core.metamodel.facetapi.FeatureType;
+import org.apache.isis.core.metamodel.facets.FacetFactoryAbstract;
+
+public class ObjectValidPropertiesFacetFactory extends FacetFactoryAbstract {
+
+    public ObjectValidPropertiesFacetFactory() {
+        super(FeatureType.OBJECTS_ONLY);
+    }
+
+    @Override
+    public void process(final ProcessClassContext processClassContaxt) {
+        FacetUtil.addFacet(new ObjectValidPropertiesFacetImpl(processClassContaxt.getFacetHolder()));
+    }
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/e4735c72/framework/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/object/validprops/ObjectValidPropertiesFacetImpl.java
----------------------------------------------------------------------
diff --git a/framework/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/object/validprops/ObjectValidPropertiesFacetImpl.java b/framework/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/object/validprops/ObjectValidPropertiesFacetImpl.java
new file mode 100644
index 0000000..16ba9d6
--- /dev/null
+++ b/framework/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/object/validprops/ObjectValidPropertiesFacetImpl.java
@@ -0,0 +1,71 @@
+/*
+ *  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.object.validprops;
+
+import org.apache.isis.applib.annotation.Where;
+import org.apache.isis.core.metamodel.adapter.ObjectAdapter;
+import org.apache.isis.core.metamodel.facetapi.FacetHolder;
+import org.apache.isis.core.metamodel.interactions.ObjectValidityContext;
+import org.apache.isis.core.metamodel.spec.feature.ObjectAssociation;
+import org.apache.isis.core.metamodel.spec.feature.ObjectAssociationFilters;
+import org.apache.isis.core.metamodel.spec.feature.OneToOneAssociation;
+
+public class ObjectValidPropertiesFacetImpl extends ObjectValidPropertiesFacetAbstract {
+
+    // REVIEW: should provide this rendering context, rather than hardcoding.
+    // the net effect currently is that class members annotated with 
+    // @Hidden(where=Where.ANYWHERE) or @Disabled(where=Where.ANYWHERE) will indeed
+    // be hidden/disabled, but will be visible/enabled (perhaps incorrectly) 
+    // for any other value for Where
+    private final Where where = Where.ANYWHERE;
+
+    public ObjectValidPropertiesFacetImpl(final FacetHolder holder) {
+        super(holder);
+    }
+
+    @Override
+    public String invalidReason(final ObjectValidityContext context) {
+        final StringBuilder buf = new StringBuilder();
+        final ObjectAdapter adapter = context.getTarget();
+        for (final ObjectAssociation property : adapter.getSpecification().getAssociations(ObjectAssociationFilters.PROPERTIES)) {
+            // ignore hidden properties
+            if (property.isVisible(context.getSession(), adapter, where).isVetoed()) {
+                continue;
+            }
+            // ignore disabled properties
+            if (property.isUsable(context.getSession(), adapter, where).isVetoed()) {
+                continue;
+            }
+            final OneToOneAssociation otoa = (OneToOneAssociation) property;
+            final ObjectAdapter value = otoa.get(adapter);
+            if (otoa.isAssociationValid(adapter, value).isVetoed()) {
+                if (buf.length() > 0) {
+                    buf.append(", ");
+                }
+                buf.append(property.getName());
+            }
+        }
+        if (buf.length() > 0) {
+            return "Invalid properties: " + buf.toString();
+        }
+        return null;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/e4735c72/framework/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/object/value/EqualByContentFacetViaValueSemantics.java
----------------------------------------------------------------------
diff --git a/framework/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/object/value/EqualByContentFacetViaValueSemantics.java b/framework/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/object/value/EqualByContentFacetViaValueSemantics.java
new file mode 100644
index 0000000..f82afc8
--- /dev/null
+++ b/framework/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/object/value/EqualByContentFacetViaValueSemantics.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.object.value;
+
+import org.apache.isis.core.metamodel.facetapi.FacetHolder;
+import org.apache.isis.core.metamodel.facets.ebc.EqualByContentFacetAbstract;
+
+public class EqualByContentFacetViaValueSemantics extends EqualByContentFacetAbstract {
+
+    public EqualByContentFacetViaValueSemantics(final FacetHolder holder) {
+        super(holder);
+    }
+
+}