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

[16/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/commons/xml/ContentWriter.java
----------------------------------------------------------------------
diff --git a/framework/core/metamodel/src/main/java/org/apache/isis/core/commons/xml/ContentWriter.java b/framework/core/metamodel/src/main/java/org/apache/isis/core/commons/xml/ContentWriter.java
new file mode 100644
index 0000000..3a452d2
--- /dev/null
+++ b/framework/core/metamodel/src/main/java/org/apache/isis/core/commons/xml/ContentWriter.java
@@ -0,0 +1,29 @@
+/*
+ *  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.commons.xml;
+
+import java.io.IOException;
+import java.io.Writer;
+
+public interface ContentWriter {
+
+    void write(Writer writer) throws IOException;
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/e4735c72/framework/core/metamodel/src/main/java/org/apache/isis/core/commons/xml/XmlFile.java
----------------------------------------------------------------------
diff --git a/framework/core/metamodel/src/main/java/org/apache/isis/core/commons/xml/XmlFile.java b/framework/core/metamodel/src/main/java/org/apache/isis/core/commons/xml/XmlFile.java
new file mode 100644
index 0000000..30d6379
--- /dev/null
+++ b/framework/core/metamodel/src/main/java/org/apache/isis/core/commons/xml/XmlFile.java
@@ -0,0 +1,170 @@
+/*
+ *  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.commons.xml;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.FilenameFilter;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.OutputStreamWriter;
+
+import org.xml.sax.ContentHandler;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+import org.xml.sax.SAXParseException;
+import org.xml.sax.XMLReader;
+import org.xml.sax.helpers.XMLReaderFactory;
+
+import org.apache.isis.core.commons.exceptions.IsisException;
+import org.apache.isis.core.commons.lang.ToString;
+
+public class XmlFile {
+    private static final String[] ESCAPE_STRING = { "&", "<", ">", """, "'" };
+    private static final String[] SPECIAL_CHARACTERS = { "&", "<", ">", "\"", "'" };
+
+    public static String getValueWithSpecialsEscaped(final String s) {
+        String result = s;
+        for (int i = 0; i < SPECIAL_CHARACTERS.length; i++) {
+            final String special = SPECIAL_CHARACTERS[i];
+            int pos = -1;
+            while (true) {
+                pos = result.indexOf(special, pos + 1);
+                if (pos < 0) {
+                    break;
+                }
+                result = result.substring(0, pos) + ESCAPE_STRING[i] + result.substring(pos + special.length());
+            }
+        }
+        return result;
+    }
+
+    private final String charset;
+    private final File directory;
+
+    public XmlFile(final String charset, final String directory) {
+        this.directory = new File(directory);
+        createDirectoryIfRequired();
+        this.charset = charset;
+    }
+
+    private void createDirectoryIfRequired() {
+        if (this.directory.exists()) {
+            return;
+        }
+        this.directory.mkdirs();
+    }
+
+    private void createDirectoryIfRequired(File file) {
+        if(file.exists()) {
+            return;
+        }
+        file.getParentFile().mkdirs();
+    }
+
+    public File getDirectory() {
+        return directory;
+    }
+
+    private File file(final String fileName) {
+        return new File(directory, fileName + ".xml");
+    }
+
+    public void writeXml(final String name, final ContentWriter writer) {
+        OutputStreamWriter pw;
+
+        try {
+            final File file = file(name);
+            createDirectoryIfRequired(file);
+            pw = new OutputStreamWriter(new FileOutputStream(file), charset);
+            pw.write("<?xml version=\"1.0\" encoding=\"" + charset + "\" ?>\n");
+            pw.write("\n");
+            writer.write(pw);
+            pw.write("\n");
+            pw.close();
+        } catch (final IOException e) {
+            throw new IsisException("Problems writing data files", e);
+        }
+    }
+
+
+    public boolean parse(final ContentHandler handler, final String fileName) {
+        XMLReader parser;
+
+        try {
+            parser = XMLReaderFactory.createXMLReader();
+        } catch (final SAXException e) {
+            try {
+                parser = XMLReaderFactory.createXMLReader("org.apache.xerces.parsers.SAXParser");
+            } catch (final SAXException e2) {
+                try {
+                    parser = XMLReaderFactory.createXMLReader("org.apache.crimson.parser.XMLReaderImpl");
+                } catch (final SAXException failed) {
+                    throw new IsisException("Couldn't locate a SAX parser");
+                }
+            }
+        }
+
+        final File file = file(fileName);
+        try {
+            parser.setContentHandler(handler);
+            final FileInputStream fis = new FileInputStream(file);
+            final InputStreamReader isr = new InputStreamReader(fis, charset);
+            final InputSource is = new InputSource(isr);
+            parser.parse(is);
+
+            return true;
+        } catch (final FileNotFoundException e) {
+            return false;
+        } catch (final IOException e) {
+            throw new IsisException("Error reading XML file", e);
+        } catch (final SAXParseException e) {
+            throw new IsisException("Error while parsing: " + e.getMessage() + " in " + file + ")", e);
+        } catch (final SAXException e) {
+            throw new IsisException("Error in file " + file + " ", e);
+        }
+    }
+
+    public void delete(final String fileName) {
+        file(fileName).delete();
+    }
+
+    /**
+     * The XML store is deemed to be initialised if the directory used to store
+     * the data has no xml files in it.
+     */
+    public boolean isFixturesInstalled() {
+        final String[] list = directory.list(new FilenameFilter() {
+            @Override
+            public boolean accept(final File dir, final String name) {
+                return name.toLowerCase().endsWith(".xml");
+            }
+        });
+        return list.length > 0;
+    }
+    
+    @Override
+    public String toString() {
+        return new ToString(this).append("directory", this.directory).toString();
+    }
+    
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/e4735c72/framework/core/metamodel/src/main/java/org/apache/isis/core/commons/xml/package-info.java
----------------------------------------------------------------------
diff --git a/framework/core/metamodel/src/main/java/org/apache/isis/core/commons/xml/package-info.java b/framework/core/metamodel/src/main/java/org/apache/isis/core/commons/xml/package-info.java
new file mode 100644
index 0000000..9ec3da4
--- /dev/null
+++ b/framework/core/metamodel/src/main/java/org/apache/isis/core/commons/xml/package-info.java
@@ -0,0 +1,28 @@
+/*
+ *  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.
+ */
+
+/**
+ * Provides helper classes for writing alternative implementations
+ * that just persist to a proprietary XML format.
+ *
+ * <p>
+ * Specifically, these classes are used by the <i>XmlObjectStore</i>
+ * and the <i>XmlProfileStore</i>.
+ */
+package org.apache.isis.core.commons.xml;
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/isis/blob/e4735c72/framework/core/metamodel/src/main/java/org/apache/isis/core/progmodel/app/IsisMetaModel.java
----------------------------------------------------------------------
diff --git a/framework/core/metamodel/src/main/java/org/apache/isis/core/progmodel/app/IsisMetaModel.java b/framework/core/metamodel/src/main/java/org/apache/isis/core/progmodel/app/IsisMetaModel.java
new file mode 100644
index 0000000..2ccaa4c
--- /dev/null
+++ b/framework/core/metamodel/src/main/java/org/apache/isis/core/progmodel/app/IsisMetaModel.java
@@ -0,0 +1,393 @@
+/*
+ *  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.app;
+
+import static org.apache.isis.core.commons.ensure.Ensure.ensureThatArg;
+import static org.apache.isis.core.commons.ensure.Ensure.ensureThatState;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.notNullValue;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+import java.util.TreeSet;
+
+import com.google.common.collect.Lists;
+
+import org.apache.isis.applib.DomainObjectContainer;
+import org.apache.isis.core.commons.components.ApplicationScopedComponent;
+import org.apache.isis.core.commons.config.IsisConfiguration;
+import org.apache.isis.core.commons.config.IsisConfigurationDefault;
+import org.apache.isis.core.metamodel.facetdecorator.FacetDecorator;
+import org.apache.isis.core.metamodel.layout.MemberLayoutArranger;
+import org.apache.isis.core.metamodel.progmodel.ProgrammingModel;
+import org.apache.isis.core.metamodel.runtimecontext.RuntimeContext;
+import org.apache.isis.core.metamodel.runtimecontext.ServicesInjector;
+import org.apache.isis.core.metamodel.services.ServicesInjectorSpi;
+import org.apache.isis.core.metamodel.services.container.DomainObjectContainerDefault;
+import org.apache.isis.core.metamodel.spec.ObjectSpecification;
+import org.apache.isis.core.metamodel.spec.SpecificationLoaderSpi;
+import org.apache.isis.core.metamodel.specloader.ObjectReflectorDefault;
+import org.apache.isis.core.metamodel.specloader.classsubstitutor.ClassSubstitutor;
+import org.apache.isis.core.metamodel.specloader.classsubstitutor.ClassSubstitutorAbstract;
+import org.apache.isis.core.metamodel.specloader.collectiontyperegistry.CollectionTypeRegistry;
+import org.apache.isis.core.metamodel.specloader.collectiontyperegistry.CollectionTypeRegistryDefault;
+import org.apache.isis.core.metamodel.specloader.traverser.SpecificationTraverser;
+import org.apache.isis.core.metamodel.specloader.traverser.SpecificationTraverserDefault;
+import org.apache.isis.core.metamodel.specloader.validator.MetaModelValidator;
+import org.apache.isis.core.metamodel.specloader.validator.ValidationFailures;
+import org.apache.isis.core.progmodel.layout.dflt.MemberLayoutArrangerDefault;
+import org.apache.isis.core.progmodel.metamodelvalidator.dflt.MetaModelValidatorDefault;
+
+/**
+ * Facade for the entire Isis metamodel and supporting components.
+ * 
+ * <p>
+ * Currently this is <i>not</i> used by Isis proper, but is available for use by integration tests.
+ * The intention is to factor it into <tt>IsisSystem</tt>.
+ */
+public class IsisMetaModel implements ApplicationScopedComponent {
+
+    private static enum State {
+        NOT_INITIALIZED, INITIALIZED, SHUTDOWN;
+    }
+
+    private final List<Object> services = Lists.newArrayList();
+
+    private State state = State.NOT_INITIALIZED;
+
+    private ObjectReflectorDefault reflector;
+    private RuntimeContext runtimeContext;
+
+    private IsisConfiguration configuration;
+    private ClassSubstitutor classSubstitutor;
+    private CollectionTypeRegistry collectionTypeRegistry;
+    private ProgrammingModel programmingModel;
+    private SpecificationTraverser specificationTraverser;
+    private MemberLayoutArranger memberLayoutArranger;
+    private Set<FacetDecorator> facetDecorators;
+    private MetaModelValidator metaModelValidator;
+
+    private DomainObjectContainer container;
+
+    private ValidationFailures validationFailures;
+
+    
+    public static class Builder {
+        private final RuntimeContext runtimeContext;
+        private final ProgrammingModel programmingModel;
+        private DomainObjectContainer container = new DomainObjectContainerDefault();
+        private List<Object> services = Lists.newArrayList();
+        
+        private Builder(RuntimeContext embeddedContext, ProgrammingModel programmingModel) {
+            this.runtimeContext = embeddedContext;
+            this.programmingModel = programmingModel;
+        }
+        
+        public Builder with(DomainObjectContainer container) {
+            this.container = container;
+            return this;
+        }
+        
+        public Builder withServices(Object... services) {
+            return withServices(Arrays.asList(services));
+        }
+        
+        public Builder withServices(List<Object> services) {
+            this.services = services;
+            return this;
+        }
+        
+        public IsisMetaModel build() {
+            final IsisMetaModel isisMetaModel = new IsisMetaModel(runtimeContext, programmingModel, services);
+            if(container != null) {
+                isisMetaModel.setContainer(container);
+            }
+            return isisMetaModel;
+        }
+    }
+    
+    public static Builder builder(RuntimeContext runtimeContext, ProgrammingModel programmingModel) {
+        return new Builder(runtimeContext, programmingModel);
+    }
+
+    public IsisMetaModel(final RuntimeContext runtimeContext, ProgrammingModel programmingModel, final List<Object> services) {
+        this(runtimeContext, programmingModel, services.toArray());
+    }
+    
+    public IsisMetaModel(final RuntimeContext runtimeContext, ProgrammingModel programmingModel, final Object... services) {
+        this.runtimeContext = runtimeContext;
+
+        setContainer(new DomainObjectContainerDefault());
+        this.services.addAll(Arrays.asList(services));
+        setConfiguration(new IsisConfigurationDefault());
+        setClassSubstitutor(new ClassSubstitutorAbstract() {});
+        setCollectionTypeRegistry(new CollectionTypeRegistryDefault());
+        setSpecificationTraverser(new SpecificationTraverserDefault());
+        setMemberLayoutArranger(new MemberLayoutArrangerDefault());
+        setFacetDecorators(new TreeSet<FacetDecorator>());
+        setProgrammingModelFacets(programmingModel);
+
+        setMetaModelValidator(new MetaModelValidatorDefault());
+    }
+
+    private void setContainer(DomainObjectContainer container) {
+        this.container = container;
+    }
+
+    /**
+     * The list of classes representing services, as specified in the
+     * {@link #IsisMetaModel(EmbeddedContext, Class...) constructor}.
+     * 
+     * <p>
+     * To obtain the instantiated services, use the
+     * {@link ServicesInjectorSpi#getRegisteredServices()} (available from
+     * {@link #getServicesInjector()}).
+     */
+    public List<Object> getServices() {
+        return Collections.unmodifiableList(services);
+    }
+
+    // ///////////////////////////////////////////////////////
+    // init, shutdown
+    // ///////////////////////////////////////////////////////
+
+    @Override
+    public void init() {
+        ensureNotInitialized();
+        reflector = new ObjectReflectorDefault(configuration, classSubstitutor, collectionTypeRegistry, specificationTraverser, memberLayoutArranger, programmingModel, facetDecorators, metaModelValidator);
+
+        runtimeContext.injectInto(container);
+        runtimeContext.setContainer(container);
+        runtimeContext.injectInto(reflector);
+        reflector.injectInto(runtimeContext);
+
+        validationFailures = reflector.initAndValidate();
+        runtimeContext.init();
+
+        for (final Object service : services) {
+            final ObjectSpecification serviceSpec = reflector.loadSpecification(service.getClass());
+            serviceSpec.markAsService();
+        }
+        state = State.INITIALIZED;
+    }
+    
+    public ValidationFailures getValidationFailures() {
+        return validationFailures;
+    }
+
+    @Override
+    public void shutdown() {
+        ensureInitialized();
+        state = State.SHUTDOWN;
+    }
+
+    // ///////////////////////////////////////////////////////
+    // SpecificationLoader
+    // ///////////////////////////////////////////////////////
+
+    /**
+     * Available once {@link #init() initialized}.
+     */
+    public SpecificationLoaderSpi getSpecificationLoader() {
+        return reflector;
+    }
+
+    // ///////////////////////////////////////////////////////
+    // DomainObjectContainer
+    // ///////////////////////////////////////////////////////
+
+    /**
+     * Available once {@link #init() initialized}.
+     */
+    public DomainObjectContainer getDomainObjectContainer() {
+        ensureInitialized();
+        return container;
+    }
+
+    // ///////////////////////////////////////////////////////
+    // DependencyInjector
+    // ///////////////////////////////////////////////////////
+
+    /**
+     * Available once {@link #init() initialized}.
+     */
+    public ServicesInjector getDependencyInjector() {
+        ensureInitialized();
+        return runtimeContext.getDependencyInjector();
+    }
+
+    // ///////////////////////////////////////////////////////
+    // Override defaults
+    // ///////////////////////////////////////////////////////
+
+    /**
+     * The {@link IsisConfiguration} in force, either defaulted or specified
+     * {@link #setConfiguration(IsisConfiguration) explicitly.}
+     */
+    public IsisConfiguration getConfiguration() {
+        return configuration;
+    }
+
+    /**
+     * Optionally specify the {@link IsisConfiguration}.
+     * 
+     * <p>
+     * Call prior to {@link #init()}.
+     */
+    public void setConfiguration(final IsisConfiguration configuration) {
+        ensureNotInitialized();
+        ensureThatArg(configuration, is(notNullValue()));
+        this.configuration = configuration;
+    }
+
+    /**
+     * The {@link ClassSubstitutor} in force, either defaulted or specified
+     * {@link #setClassSubstitutor(ClassSubstitutor) explicitly}.
+     */
+    public ClassSubstitutor getClassSubstitutor() {
+        return classSubstitutor;
+    }
+
+    /**
+     * Optionally specify the {@link ClassSubstitutor}.
+     * 
+     * <p>
+     * Call prior to {@link #init()}.
+     */
+    public void setClassSubstitutor(final ClassSubstitutor classSubstitutor) {
+        ensureNotInitialized();
+        ensureThatArg(classSubstitutor, is(notNullValue()));
+        this.classSubstitutor = classSubstitutor;
+    }
+
+    /**
+     * The {@link CollectionTypeRegistry} in force, either defaulted or
+     * specified {@link #setCollectionTypeRegistry(CollectionTypeRegistry)
+     * explicitly.}
+     */
+    public CollectionTypeRegistry getCollectionTypeRegistry() {
+        return collectionTypeRegistry;
+    }
+
+    /**
+     * Optionally specify the {@link CollectionTypeRegistry}.
+     * 
+     * <p>
+     * Call prior to {@link #init()}.
+     */
+    public void setCollectionTypeRegistry(final CollectionTypeRegistry collectionTypeRegistry) {
+        ensureNotInitialized();
+        ensureThatArg(collectionTypeRegistry, is(notNullValue()));
+        this.collectionTypeRegistry = collectionTypeRegistry;
+    }
+
+    /**
+     * The {@link SpecificationTraverser} in force, either defaulted or
+     * specified {@link #setSpecificationTraverser(SpecificationTraverser)
+     * explicitly}.
+     */
+    public SpecificationTraverser getSpecificationTraverser() {
+        return specificationTraverser;
+    }
+
+    /**
+     * Optionally specify the {@link SpecificationTraverser}.
+     */
+    public void setSpecificationTraverser(final SpecificationTraverser specificationTraverser) {
+        this.specificationTraverser = specificationTraverser;
+    }
+
+    /**
+     * Optionally specify the {@link MemberLayoutArranger}.
+     */
+    public void setMemberLayoutArranger(final MemberLayoutArranger memberLayoutArranger) {
+        this.memberLayoutArranger = memberLayoutArranger;
+    }
+
+    /**
+     * The {@link ProgrammingModel} in force, either defaulted or specified
+     * {@link #setProgrammingModelFacets(ProgrammingModel) explicitly}.
+     */
+    public ProgrammingModel getProgrammingModelFacets() {
+        return programmingModel;
+    }
+
+    /**
+     * Optionally specify the {@link ProgrammingModel}.
+     * 
+     * <p>
+     * Call prior to {@link #init()}.
+     */
+    public void setProgrammingModelFacets(final ProgrammingModel programmingModel) {
+        ensureNotInitialized();
+        ensureThatArg(programmingModel, is(notNullValue()));
+        this.programmingModel = programmingModel;
+    }
+
+    /**
+     * The {@link FacetDecorator}s in force, either defaulted or specified
+     * {@link #setFacetDecorators(Set) explicitly}.
+     */
+    public Set<FacetDecorator> getFacetDecorators() {
+        return Collections.unmodifiableSet(facetDecorators);
+    }
+
+    /**
+     * Optionally specify the {@link FacetDecorator}s.
+     * 
+     * <p>
+     * Call prior to {@link #init()}.
+     */
+    public void setFacetDecorators(final Set<FacetDecorator> facetDecorators) {
+        ensureNotInitialized();
+        ensureThatArg(facetDecorators, is(notNullValue()));
+        this.facetDecorators = facetDecorators;
+    }
+
+    /**
+     * The {@link MetaModelValidator} in force, either defaulted or specified
+     * {@link #setMetaModelValidator(MetaModelValidator) explicitly}.
+     */
+    public MetaModelValidator getMetaModelValidator() {
+        return metaModelValidator;
+    }
+
+    /**
+     * Optionally specify the {@link MetaModelValidator}.
+     */
+    public void setMetaModelValidator(final MetaModelValidator metaModelValidator) {
+        this.metaModelValidator = metaModelValidator;
+    }
+
+    // ///////////////////////////////////////////////////////
+    // State management
+    // ///////////////////////////////////////////////////////
+
+    private State ensureNotInitialized() {
+        return ensureThatState(state, is(State.NOT_INITIALIZED));
+    }
+
+    private State ensureInitialized() {
+        return ensureThatState(state, is(State.INITIALIZED));
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/e4735c72/framework/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facetdecorators/help/HelpFacetDecorator.java
----------------------------------------------------------------------
diff --git a/framework/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facetdecorators/help/HelpFacetDecorator.java b/framework/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facetdecorators/help/HelpFacetDecorator.java
new file mode 100644
index 0000000..272242d
--- /dev/null
+++ b/framework/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facetdecorators/help/HelpFacetDecorator.java
@@ -0,0 +1,26 @@
+/*
+ *  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.facetdecorators.help;
+
+import org.apache.isis.core.metamodel.facetdecorator.FacetDecorator;
+
+public interface HelpFacetDecorator extends FacetDecorator {
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/e4735c72/framework/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facetdecorators/help/HelpFacetDecoratorUsingHelpManager.java
----------------------------------------------------------------------
diff --git a/framework/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facetdecorators/help/HelpFacetDecoratorUsingHelpManager.java b/framework/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facetdecorators/help/HelpFacetDecoratorUsingHelpManager.java
new file mode 100644
index 0000000..a799ecb
--- /dev/null
+++ b/framework/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facetdecorators/help/HelpFacetDecoratorUsingHelpManager.java
@@ -0,0 +1,67 @@
+/*
+ *  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.facetdecorators.help;
+
+import org.apache.isis.applib.Identifier;
+import org.apache.isis.core.metamodel.facetapi.Facet;
+import org.apache.isis.core.metamodel.facetapi.FacetHolder;
+import org.apache.isis.core.metamodel.facetapi.IdentifiedHolder;
+import org.apache.isis.core.metamodel.facetdecorator.FacetDecoratorAbstract;
+import org.apache.isis.core.metamodel.facets.help.HelpFacet;
+
+public class HelpFacetDecoratorUsingHelpManager extends FacetDecoratorAbstract implements HelpFacetDecorator {
+    private final HelpManager helpManager;
+
+    public HelpFacetDecoratorUsingHelpManager(final HelpManager manager) {
+        helpManager = manager;
+    }
+
+    @Override
+    public Facet decorate(final Facet facet, final FacetHolder facetHolder) {
+        if (facet.facetType() != HelpFacet.class) {
+            return facet;
+        }
+
+        if (!(facetHolder instanceof IdentifiedHolder)) {
+            return null;
+        }
+
+        final IdentifiedHolder identifiedHolder = (IdentifiedHolder) facetHolder;
+        return decorateWithHelpFacet(facet, identifiedHolder);
+    }
+
+    private Facet decorateWithHelpFacet(final Facet facet, final IdentifiedHolder identifiedHolder) {
+        final Identifier identifier = identifiedHolder.getIdentifier();
+
+        final String helpText = helpManager.getHelpText(identifier);
+        if (helpText != null) {
+            final HelpFacetLookedUpViaHelpManager decoratingFacet = new HelpFacetLookedUpViaHelpManager(helpText, facet.getFacetHolder());
+            identifiedHolder.addFacet(decoratingFacet);
+            return decoratingFacet;
+        }
+        return null;
+    }
+
+    @Override
+    @SuppressWarnings("unchecked")
+    public Class<? extends Facet>[] getFacetTypes() {
+        return new Class[] { HelpFacet.class };
+    }
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/e4735c72/framework/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facetdecorators/help/HelpFacetLookedUpViaHelpManager.java
----------------------------------------------------------------------
diff --git a/framework/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facetdecorators/help/HelpFacetLookedUpViaHelpManager.java b/framework/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facetdecorators/help/HelpFacetLookedUpViaHelpManager.java
new file mode 100644
index 0000000..8d6127b
--- /dev/null
+++ b/framework/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facetdecorators/help/HelpFacetLookedUpViaHelpManager.java
@@ -0,0 +1,34 @@
+/*
+ *  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.facetdecorators.help;
+
+import org.apache.isis.core.metamodel.facetapi.FacetHolder;
+import org.apache.isis.core.metamodel.facets.help.HelpFacetAbstract;
+
+/**
+ * Looked up via the {@link HelpManager}.
+ */
+public class HelpFacetLookedUpViaHelpManager extends HelpFacetAbstract {
+
+    public HelpFacetLookedUpViaHelpManager(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/facetdecorators/help/HelpManager.java
----------------------------------------------------------------------
diff --git a/framework/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facetdecorators/help/HelpManager.java b/framework/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facetdecorators/help/HelpManager.java
new file mode 100644
index 0000000..78d699e
--- /dev/null
+++ b/framework/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facetdecorators/help/HelpManager.java
@@ -0,0 +1,34 @@
+/*
+ *  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.facetdecorators.help;
+
+import org.apache.isis.applib.Identifier;
+import org.apache.isis.core.commons.components.ApplicationScopedComponent;
+
+public interface HelpManager extends ApplicationScopedComponent {
+
+    /**
+     * Returns help text for the specified {@link Identifier}.
+     * 
+     * <p>
+     * If no help text is available then an empty String should be returned.
+     */
+    String getHelpText(Identifier identifier);
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/e4735c72/framework/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facetdecorators/help/HelpManagerAbstract.java
----------------------------------------------------------------------
diff --git a/framework/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facetdecorators/help/HelpManagerAbstract.java b/framework/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facetdecorators/help/HelpManagerAbstract.java
new file mode 100644
index 0000000..8c19fd4
--- /dev/null
+++ b/framework/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facetdecorators/help/HelpManagerAbstract.java
@@ -0,0 +1,47 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+
+package org.apache.isis.core.progmodel.facetdecorators.help;
+
+import org.apache.isis.applib.Identifier;
+
+/**
+ * Default implementation that does nothing.
+ * 
+ */
+public abstract class HelpManagerAbstract implements HelpManager {
+
+    /**
+     * Does nothing.
+     */
+    @Override
+    public void init() {
+    }
+
+    /**
+     * Does nothing.
+     */
+    @Override
+    public void shutdown() {
+    }
+
+    @Override
+    public abstract String getHelpText(final Identifier identifier);
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/e4735c72/framework/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facetdecorators/help/file/HelpFacetDecoratorUsingFilesInstaller.java
----------------------------------------------------------------------
diff --git a/framework/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facetdecorators/help/file/HelpFacetDecoratorUsingFilesInstaller.java b/framework/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facetdecorators/help/file/HelpFacetDecoratorUsingFilesInstaller.java
new file mode 100644
index 0000000..1b300ea
--- /dev/null
+++ b/framework/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facetdecorators/help/file/HelpFacetDecoratorUsingFilesInstaller.java
@@ -0,0 +1,47 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+
+package org.apache.isis.core.progmodel.facetdecorators.help.file;
+
+import java.util.Arrays;
+import java.util.List;
+
+import org.apache.isis.core.commons.config.InstallerAbstract;
+import org.apache.isis.core.metamodel.facetdecorator.FacetDecorator;
+import org.apache.isis.core.metamodel.specloader.FacetDecoratorInstaller;
+import org.apache.isis.core.progmodel.facetdecorators.help.HelpFacetDecoratorUsingHelpManager;
+import org.apache.isis.core.progmodel.facetdecorators.help.file.internal.HelpManagerUsingFiles;
+
+public class HelpFacetDecoratorUsingFilesInstaller extends InstallerAbstract implements FacetDecoratorInstaller {
+
+    public HelpFacetDecoratorUsingFilesInstaller() {
+        super(FacetDecoratorInstaller.TYPE, "help-file");
+    }
+
+    @Override
+    public List<FacetDecorator> createDecorators() {
+        final HelpManagerUsingFiles manager = new HelpManagerUsingFiles(getConfiguration());
+        return Arrays.<FacetDecorator> asList(new HelpFacetDecoratorUsingHelpManager(manager));
+    }
+
+    @Override
+    public List<Class<?>> getTypes() {
+        return listOf(List.class);
+    }
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/e4735c72/framework/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facetdecorators/help/file/internal/HelpManagerUsingFiles.java
----------------------------------------------------------------------
diff --git a/framework/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facetdecorators/help/file/internal/HelpManagerUsingFiles.java b/framework/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facetdecorators/help/file/internal/HelpManagerUsingFiles.java
new file mode 100644
index 0000000..d6f9b89
--- /dev/null
+++ b/framework/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facetdecorators/help/file/internal/HelpManagerUsingFiles.java
@@ -0,0 +1,146 @@
+/*
+ *  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.facetdecorators.help.file.internal;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileReader;
+import java.io.IOException;
+
+import org.apache.log4j.Logger;
+
+import org.apache.isis.applib.Identifier;
+import org.apache.isis.core.commons.config.IsisConfiguration;
+import org.apache.isis.core.progmodel.facetdecorators.help.HelpManagerAbstract;
+
+public class HelpManagerUsingFiles extends HelpManagerAbstract {
+
+    private static final Logger LOG = Logger.getLogger(HelpManagerUsingFiles.class);
+
+    /**
+     * The name of the file used unless overridden with
+     * {@link #setFileName(String)}.
+     */
+    public static final String DEFAULT_FILE_NAME = "help.txt";
+    private static final String CLASS_PREFIX = "c:";
+    private static final String NAME_PREFIX = "m:";
+
+    private String fileName = DEFAULT_FILE_NAME;
+
+    @SuppressWarnings("unused")
+    private final IsisConfiguration configuration;
+
+    public HelpManagerUsingFiles(final IsisConfiguration configuration) {
+        this.configuration = configuration;
+    }
+
+    public void setFileName(final String fileName) {
+        this.fileName = fileName;
+    }
+
+    @Override
+    public String getHelpText(final Identifier identifier) {
+        BufferedReader reader = null;
+        try {
+            reader = getReader();
+
+            if (reader == null) {
+                return "No help available (no file found)";
+            }
+
+            final String className = CLASS_PREFIX + identifier.getClassName().toLowerCase();
+            final String name = NAME_PREFIX + identifier.getMemberName().toLowerCase();
+
+            final StringBuffer str = new StringBuffer();
+            String line;
+
+            boolean lookingForClass = true;
+            boolean lookingForName = identifier.getMemberName().length() > 0;
+            /*
+             * Read through each line in file.
+             */
+            while ((line = reader.readLine()) != null) {
+                // Skip comments - lines begining with hash
+                if (line.length() > 0 && line.charAt(0) == '#') {
+                    continue;
+                }
+
+                /*
+                 * Look for class.
+                 */
+                if (line.toLowerCase().equals(className)) {
+                    lookingForClass = false;
+                    continue;
+                }
+
+                if (lookingForClass) {
+                    continue;
+                } else if (line.toLowerCase().startsWith(CLASS_PREFIX)) {
+                    break;
+                }
+
+                /*
+                 * Look for field/method.
+                 */
+                if (line.toLowerCase().equals(name)) {
+                    lookingForName = false;
+                    continue;
+                }
+
+                if (lookingForName) {
+                    continue;
+                } else if (line.toLowerCase().startsWith(NAME_PREFIX)) {
+                    break;
+                }
+
+                str.append(line);
+                str.append('\n');
+            }
+
+            return str.toString();
+
+        } catch (final FileNotFoundException e) {
+            LOG.error("opening help file", e);
+            return "Failure opening help file: " + e.getMessage();
+        } catch (final IOException e) {
+            LOG.error("reading help file", e);
+            return "Failure reading help file: " + e.getMessage();
+        } finally {
+            if (reader != null) {
+                try {
+                    reader.close();
+                } catch (final IOException ignore) {
+                }
+            }
+        }
+    }
+
+    protected BufferedReader getReader() throws FileNotFoundException {
+        final File file = new File(fileName);
+        if (!file.exists()) {
+            final String message = "No help file found: " + file.getAbsolutePath();
+            LOG.warn(message);
+            return null;
+        }
+
+        return new BufferedReader(new FileReader(file));
+    }
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/e4735c72/framework/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facetdecorators/help/package-info.java
----------------------------------------------------------------------
diff --git a/framework/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facetdecorators/help/package-info.java b/framework/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facetdecorators/help/package-info.java
new file mode 100644
index 0000000..a3f8085
--- /dev/null
+++ b/framework/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facetdecorators/help/package-info.java
@@ -0,0 +1,28 @@
+/*
+ *  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.
+ */
+
+/**
+ * Help API, utilizing the {@link org.apache.isis.core.metamodel.facetdecorator.FacetDecorator} mechanism..
+ * 
+ * <p>
+ * At the time of writing - unlike
+ * authorisation - there is no help-proxy implementation.  Therefore for client/server deployments
+ * any help content must be deployed locally.
+ */
+package org.apache.isis.core.progmodel.facetdecorators.help;
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/isis/blob/e4735c72/framework/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facetdecorators/i18n/I18nFacetDecorator.java
----------------------------------------------------------------------
diff --git a/framework/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facetdecorators/i18n/I18nFacetDecorator.java b/framework/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facetdecorators/i18n/I18nFacetDecorator.java
new file mode 100644
index 0000000..1d40793
--- /dev/null
+++ b/framework/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facetdecorators/i18n/I18nFacetDecorator.java
@@ -0,0 +1,101 @@
+/*
+ *  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.facetdecorators.i18n;
+
+import org.apache.isis.applib.Identifier;
+import org.apache.isis.core.metamodel.facetapi.Facet;
+import org.apache.isis.core.metamodel.facetapi.FacetHolder;
+import org.apache.isis.core.metamodel.facetapi.IdentifiedHolder;
+import org.apache.isis.core.metamodel.facetdecorator.FacetDecoratorAbstract;
+import org.apache.isis.core.metamodel.facets.describedas.DescribedAsFacet;
+import org.apache.isis.core.metamodel.facets.help.HelpFacet;
+import org.apache.isis.core.metamodel.facets.named.NamedFacet;
+import org.apache.isis.core.progmodel.facetdecorators.i18n.internal.DescribedAsFacetWrapI18n;
+import org.apache.isis.core.progmodel.facetdecorators.i18n.internal.HelpFacetWrapI18n;
+import org.apache.isis.core.progmodel.facetdecorators.i18n.internal.NamedFacetWrapI18n;
+
+public class I18nFacetDecorator extends FacetDecoratorAbstract {
+    private final I18nManager i18nManager;
+
+    public I18nFacetDecorator(final I18nManager manager) {
+        i18nManager = manager;
+    }
+
+    @Override
+    public Facet decorate(final Facet facet, final FacetHolder facetHolder) {
+        if (!(facetHolder instanceof IdentifiedHolder)) {
+            return null;
+        }
+
+        final IdentifiedHolder identifiedHolder = (IdentifiedHolder) facetHolder;
+
+        final Class<?> facetType = facet.facetType();
+        if (facetType == NamedFacet.class) {
+            return decorateWithNamedFacet(facet, identifiedHolder);
+        }
+        if (facetType == DescribedAsFacet.class) {
+            return decorateWithDescribedAsFacet(facet, identifiedHolder);
+        }
+        if (facetType == HelpFacet.class) {
+            return decorateWithHelpFacet(facet, identifiedHolder);
+        }
+        return null;
+    }
+
+    private Facet decorateWithNamedFacet(final Facet facet, final IdentifiedHolder identifiedHolder) {
+
+        final Identifier identifier = identifiedHolder.getIdentifier();
+        final String i18nName = i18nManager.getName(identifier);
+        if (i18nName == null) {
+            return null;
+        }
+        final NamedFacetWrapI18n decoratingFacet = new NamedFacetWrapI18n(i18nName, facet.getFacetHolder());
+        identifiedHolder.addFacet(decoratingFacet);
+        return decoratingFacet;
+    }
+
+    private Facet decorateWithDescribedAsFacet(final Facet facet, final IdentifiedHolder identifiedHolder) {
+        final Identifier identifier = identifiedHolder.getIdentifier();
+        final String i18nDescription = i18nManager.getDescription(identifier);
+        if (i18nDescription == null) {
+            return null;
+        }
+        final DescribedAsFacetWrapI18n decoratingFacet = new DescribedAsFacetWrapI18n(i18nDescription, facet.getFacetHolder());
+        identifiedHolder.addFacet(decoratingFacet);
+        return decoratingFacet;
+    }
+
+    private Facet decorateWithHelpFacet(final Facet facet, final IdentifiedHolder identifiedHolder) {
+        final Identifier identifier = identifiedHolder.getIdentifier();
+        final String i18nHelp = i18nManager.getHelp(identifier);
+        if (i18nHelp == null) {
+            return null;
+        }
+        final HelpFacetWrapI18n decoratingFacet = new HelpFacetWrapI18n(i18nHelp, facet.getFacetHolder());
+        identifiedHolder.addFacet(decoratingFacet);
+        return decoratingFacet;
+    }
+
+    @SuppressWarnings("unchecked")
+    @Override
+    public Class<? extends Facet>[] getFacetTypes() {
+        return new Class[] { NamedFacet.class, DescribedAsFacet.class, HelpFacet.class };
+    }
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/e4735c72/framework/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facetdecorators/i18n/I18nManager.java
----------------------------------------------------------------------
diff --git a/framework/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facetdecorators/i18n/I18nManager.java b/framework/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facetdecorators/i18n/I18nManager.java
new file mode 100644
index 0000000..059f7d0
--- /dev/null
+++ b/framework/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facetdecorators/i18n/I18nManager.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.facetdecorators.i18n;
+
+import java.util.List;
+
+import org.apache.isis.applib.Identifier;
+import org.apache.isis.core.commons.components.ApplicationScopedComponent;
+
+/**
+ * Authorises the user in the current session view and use members of an object.
+ * 
+ * <p>
+ * TODO: allow description and help to be found for parameters
+ */
+public interface I18nManager extends ApplicationScopedComponent {
+
+    /**
+     * Get the localized name for the specified identified action/property.
+     * 
+     * <p>
+     * Returns null if no name available.
+     */
+    String getName(Identifier identifier);
+
+    /**
+     * Get the localized description for the specified identified
+     * action/property.
+     * 
+     * <p>
+     * Returns null if no description available.
+     */
+    String getDescription(Identifier identifier);
+
+    /**
+     * Get the localized help text for the specified identified action/property.
+     * 
+     * <p>
+     * Returns null if no help text available.
+     */
+    String getHelp(Identifier identifier);
+
+    /**
+     * Get the localized parameter names for the specified identified
+     * action/property.
+     * 
+     * <p>
+     * Returns null if no parameters are available. Otherwise returns an array
+     * of String objects the size of the number of parameters, where each
+     * element is the localised name for the corresponding parameter, or is null
+     * if no parameter name is available.
+     */
+    List<String> getParameterNames(Identifier identifier);
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/e4735c72/framework/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facetdecorators/i18n/internal/DescribedAsFacetWrapI18n.java
----------------------------------------------------------------------
diff --git a/framework/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facetdecorators/i18n/internal/DescribedAsFacetWrapI18n.java b/framework/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facetdecorators/i18n/internal/DescribedAsFacetWrapI18n.java
new file mode 100644
index 0000000..9fc4fa6
--- /dev/null
+++ b/framework/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facetdecorators/i18n/internal/DescribedAsFacetWrapI18n.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.facetdecorators.i18n.internal;
+
+import org.apache.isis.core.metamodel.facetapi.FacetHolder;
+import org.apache.isis.core.metamodel.facets.describedas.DescribedAsFacetAbstract;
+
+public class DescribedAsFacetWrapI18n extends DescribedAsFacetAbstract {
+
+    public DescribedAsFacetWrapI18n(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/facetdecorators/i18n/internal/HelpFacetWrapI18n.java
----------------------------------------------------------------------
diff --git a/framework/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facetdecorators/i18n/internal/HelpFacetWrapI18n.java b/framework/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facetdecorators/i18n/internal/HelpFacetWrapI18n.java
new file mode 100644
index 0000000..d570f13
--- /dev/null
+++ b/framework/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facetdecorators/i18n/internal/HelpFacetWrapI18n.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.facetdecorators.i18n.internal;
+
+import org.apache.isis.core.metamodel.facetapi.FacetHolder;
+import org.apache.isis.core.metamodel.facets.help.HelpFacetAbstract;
+
+public class HelpFacetWrapI18n extends HelpFacetAbstract {
+
+    public HelpFacetWrapI18n(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/facetdecorators/i18n/internal/NamedFacetWrapI18n.java
----------------------------------------------------------------------
diff --git a/framework/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facetdecorators/i18n/internal/NamedFacetWrapI18n.java b/framework/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facetdecorators/i18n/internal/NamedFacetWrapI18n.java
new file mode 100644
index 0000000..a382fd2
--- /dev/null
+++ b/framework/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facetdecorators/i18n/internal/NamedFacetWrapI18n.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.facetdecorators.i18n.internal;
+
+import org.apache.isis.core.metamodel.facetapi.FacetHolder;
+import org.apache.isis.core.metamodel.facets.named.NamedFacetAbstract;
+
+public class NamedFacetWrapI18n extends NamedFacetAbstract {
+
+    public NamedFacetWrapI18n(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/facetdecorators/i18n/resourcebundle/I18nDecoratorUsingResourceBundleInstaller.java
----------------------------------------------------------------------
diff --git a/framework/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facetdecorators/i18n/resourcebundle/I18nDecoratorUsingResourceBundleInstaller.java b/framework/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facetdecorators/i18n/resourcebundle/I18nDecoratorUsingResourceBundleInstaller.java
new file mode 100644
index 0000000..78c91f9
--- /dev/null
+++ b/framework/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facetdecorators/i18n/resourcebundle/I18nDecoratorUsingResourceBundleInstaller.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.facetdecorators.i18n.resourcebundle;
+
+import java.util.Arrays;
+import java.util.List;
+
+import org.apache.isis.core.commons.config.InstallerAbstract;
+import org.apache.isis.core.metamodel.facetdecorator.FacetDecorator;
+import org.apache.isis.core.metamodel.specloader.FacetDecoratorInstaller;
+import org.apache.isis.core.progmodel.facetdecorators.i18n.I18nFacetDecorator;
+import org.apache.isis.core.progmodel.facetdecorators.i18n.resourcebundle.internal.I18nManagerUsingResourceBundle;
+
+public class I18nDecoratorUsingResourceBundleInstaller extends InstallerAbstract implements FacetDecoratorInstaller {
+
+    public I18nDecoratorUsingResourceBundleInstaller() {
+        super(FacetDecoratorInstaller.TYPE, "resource-i18n");
+    }
+
+    @Override
+    public List<FacetDecorator> createDecorators() {
+        final I18nManagerUsingResourceBundle manager = new I18nManagerUsingResourceBundle(getConfiguration());
+        manager.init();
+        return Arrays.<FacetDecorator> asList(new I18nFacetDecorator(manager));
+    }
+
+    @Override
+    public List<Class<?>> getTypes() {
+        return listOf(List.class); // ie, of List<FacetDecorator>
+    }
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/e4735c72/framework/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facetdecorators/i18n/resourcebundle/internal/I18nManagerUsingResourceBundle.java
----------------------------------------------------------------------
diff --git a/framework/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facetdecorators/i18n/resourcebundle/internal/I18nManagerUsingResourceBundle.java b/framework/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facetdecorators/i18n/resourcebundle/internal/I18nManagerUsingResourceBundle.java
new file mode 100644
index 0000000..e0cb922
--- /dev/null
+++ b/framework/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facetdecorators/i18n/resourcebundle/internal/I18nManagerUsingResourceBundle.java
@@ -0,0 +1,182 @@
+/*
+ *  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.facetdecorators.i18n.resourcebundle.internal;
+
+import java.util.List;
+import java.util.MissingResourceException;
+import java.util.ResourceBundle;
+
+import com.google.common.collect.Lists;
+
+import org.apache.log4j.Logger;
+
+import org.apache.isis.applib.Identifier;
+import org.apache.isis.core.commons.config.IsisConfiguration;
+import org.apache.isis.core.commons.lang.StringUtils;
+import org.apache.isis.core.progmodel.facetdecorators.i18n.I18nManager;
+
+/**
+ * REVIEW: why isn't there a type for collections also?
+ */
+public class I18nManagerUsingResourceBundle implements I18nManager {
+
+    private static final Logger LOG = Logger.getLogger(I18nManagerUsingResourceBundle.class);
+
+    private static final String BASE_FILE_NAME = "i18n";
+
+    private static final String MEMBER_TYPE_PROPERTY = "property";
+    private static final String MEMBER_TYPE_COLLECTION = "collection";
+    private static final String MEMBER_TYPE_ACTION = "action";
+    private static final String MEMBER_TYPE_PARAMETER = "parameter";
+
+    private static final String TEXT_TYPE_NAME = "name";
+    private static final String TEXT_TYPE_DESCRIPTION = "description";
+    private static final String TEXT_TYPE_HELP = "help";
+
+    private ResourceBundle bundle;
+
+    @SuppressWarnings("unused")
+    private final IsisConfiguration configuration;
+
+    // //////////////////////////////////////////////////////////////
+    // Contructor, init, shutdown
+    // //////////////////////////////////////////////////////////////
+
+    public I18nManagerUsingResourceBundle(final IsisConfiguration configuration) {
+        this.configuration = configuration;
+    }
+
+    @Override
+    public void init() {
+        try {
+            bundle = ResourceBundle.getBundle(BASE_FILE_NAME);
+        } catch (final MissingResourceException e) {
+            LOG.warn("Missing resource bundle: " + e.getMessage());
+        }
+
+    }
+
+    @Override
+    public void shutdown() {
+    }
+
+    // //////////////////////////////////////////////////////////////
+    // Members
+    // //////////////////////////////////////////////////////////////
+
+    @Override
+    public String getName(final Identifier identifier) {
+        return internalizedTextForClassMember(identifier, TEXT_TYPE_NAME);
+    }
+
+    @Override
+    public String getDescription(final Identifier identifier) {
+        return internalizedTextForClassMember(identifier, TEXT_TYPE_DESCRIPTION);
+    }
+
+    @Override
+    public String getHelp(final Identifier identifier) {
+        return internalizedTextForClassMember(identifier, TEXT_TYPE_HELP);
+    }
+
+    private String internalizedTextForClassMember(final Identifier identifier, final String textType) {
+        if (bundle == null) {
+            return null;
+        }
+        final List<String> key = buildMemberTypeKey(identifier, textType);
+        return lookupTextFromBundle(key);
+    }
+
+    private static List<String> buildMemberTypeKey(final Identifier identifier, final String textType) {
+        final List<String> keys = Lists.newArrayList();
+
+        if (identifier.isPropertyOrCollection()) {
+            keys.add(buildMemberTypeKey(identifier, textType, MEMBER_TYPE_PROPERTY));
+            keys.add(buildMemberTypeKey(identifier, textType, MEMBER_TYPE_COLLECTION));
+        } else {
+            keys.add(buildMemberTypeKey(identifier, textType, MEMBER_TYPE_ACTION));
+        }
+        return keys;
+    }
+
+    private static String buildMemberTypeKey(final Identifier identifier, final String textType, final String memberType) {
+        final StringBuilder sb = new StringBuilder();
+        sb.append(identifier.getClassName()).append(".");
+        sb.append(memberType);
+        final String memberName = identifier.getMemberName();
+        if (!StringUtils.isNullOrEmpty(memberName)) {
+            sb.append(".").append(memberName);
+        }
+        sb.append(".").append(textType);
+        return sb.toString();
+    }
+
+    // //////////////////////////////////////////////////////////////
+    // Parameters
+    // //////////////////////////////////////////////////////////////
+
+    @Override
+    public List<String> getParameterNames(final Identifier identifier) {
+        return internalizedTextForParameter(identifier, TEXT_TYPE_NAME);
+    }
+
+    private List<String> internalizedTextForParameter(final Identifier identifier, final String textType) {
+        if (bundle == null) {
+            return null;
+        }
+        final List<String> internalizedText = Lists.newArrayList();
+        final List<String> memberParameterNames = identifier.getMemberParameterNames();
+        int paramNum = 0;
+        for (@SuppressWarnings("unused")
+        final String dummy : memberParameterNames) {
+            final String key = buildParameterTypeKey(identifier, textType, paramNum);
+            internalizedText.add(lookupTextFromBundle(key));
+            paramNum++;
+        }
+        return internalizedText;
+    }
+
+    private static String buildParameterTypeKey(final Identifier identifier, final String textType, final int paramNum) {
+        return identifier.getClassName() + "." + MEMBER_TYPE_ACTION + "." + identifier.getMemberName() + "." + MEMBER_TYPE_PARAMETER + (paramNum + 1) + "." + textType;
+    }
+
+    // //////////////////////////////////////////////////////////////
+    // Helpers
+    // //////////////////////////////////////////////////////////////
+
+    private String lookupTextFromBundle(final List<String> keys) {
+        for (final String key : keys) {
+            final String text = lookupTextFromBundle(key);
+            if (text != null) {
+                return text;
+            }
+        }
+        return null;
+    }
+
+    private String lookupTextFromBundle(final String key) {
+        try {
+            return bundle.getString(key);
+        } catch (final MissingResourceException 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/CollectionUtils.java
----------------------------------------------------------------------
diff --git a/framework/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/CollectionUtils.java b/framework/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/CollectionUtils.java
new file mode 100644
index 0000000..9a9757b
--- /dev/null
+++ b/framework/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/CollectionUtils.java
@@ -0,0 +1,43 @@
+/*
+ *  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;
+
+import org.apache.isis.core.metamodel.adapter.ObjectAdapter;
+import org.apache.isis.core.metamodel.adapter.mgr.AdapterManager;
+import org.apache.isis.core.metamodel.facets.collections.modify.CollectionFacet;
+import org.apache.isis.core.metamodel.facets.collections.modify.CollectionFacetUtils;
+import org.apache.isis.core.metamodel.spec.ObjectSpecification;
+
+public final class CollectionUtils {
+    private CollectionUtils() {
+    }
+
+    public static Object[] getCollectionAsObjectArray(final Object option, final ObjectSpecification spec, final AdapterManager adapterMap) {
+        final ObjectAdapter collection = adapterMap.adapterFor(option);
+        final CollectionFacet facet = CollectionFacetUtils.getCollectionFacetFromSpec(collection);
+        final Object[] optionArray = new Object[facet.size(collection)];
+        int j = 0;
+        for (final ObjectAdapter nextElement : facet.iterable(collection)) {
+            optionArray[j++] = nextElement.getObject();
+        }
+        return optionArray;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/e4735c72/framework/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/MethodFinderUtils.java
----------------------------------------------------------------------
diff --git a/framework/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/MethodFinderUtils.java b/framework/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/MethodFinderUtils.java
new file mode 100644
index 0000000..f3dcfcc
--- /dev/null
+++ b/framework/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/MethodFinderUtils.java
@@ -0,0 +1,180 @@
+/**
+ *  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;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.isis.core.metamodel.facetapi.MethodRemover;
+import org.apache.isis.core.metamodel.methodutils.MethodScope;
+
+public final class MethodFinderUtils {
+
+    private MethodFinderUtils() {
+    }
+
+    public static Method findMethodWithOrWithoutParameters(final Class<?> type, final MethodScope classMethod, final String name, final Class<?> returnType, final Class<?>[] paramTypes) {
+        Method method = MethodFinderUtils.findMethod(type, classMethod, name, returnType, paramTypes);
+        if (method == null) {
+            method = MethodFinderUtils.findMethod(type, classMethod, name, returnType, MethodPrefixBasedFacetFactoryAbstract.NO_PARAMETERS_TYPES);
+        }
+        return method;
+    }
+
+    /**
+     * Returns a specific public methods that: have the specified prefix; have
+     * the specified return type, or void, if canBeVoid is true; and has the
+     * specified number of parameters. If the returnType is specified as null
+     * then the return type is ignored.
+     * 
+     * @param paramTypes
+     *            the set of parameters the method should have, if null then is
+     *            ignored
+     */
+    public static Method findMethod(final Class<?> type, final MethodScope methodScope, final String name, final Class<?> returnType, final Class<?>[] paramTypes) {
+        Method method;
+        try {
+            method = type.getMethod(name, paramTypes);
+        } catch (final SecurityException e) {
+            return null;
+        } catch (final NoSuchMethodException e) {
+            return null;
+        }
+
+        final int modifiers = method.getModifiers();
+
+        // check for public modifier
+        if (!Modifier.isPublic(modifiers)) {
+            return null;
+        }
+
+        // check for scope modifier
+        if (!methodScope.matchesScopeOf(method)) {
+            return null;
+        }
+
+        // check for name
+        if (!method.getName().equals(name)) {
+            return null;
+        }
+
+        // check for return type
+        if (returnType != null && returnType != method.getReturnType()) {
+            return null;
+        }
+
+        // check params (if required)
+        if (paramTypes != null) {
+            final Class<?>[] parameterTypes = method.getParameterTypes();
+            if (paramTypes.length != parameterTypes.length) {
+                return null;
+            }
+
+            for (int c = 0; c < paramTypes.length; c++) {
+                if ((paramTypes[c] != null) && (paramTypes[c] != parameterTypes[c])) {
+                    return null;
+                }
+            }
+        }
+
+        return method;
+    }
+
+    protected static boolean doesNotMatchScope(final MethodScope methodScope, final Method method) {
+        return methodScope.doesNotMatchScope(method);
+    }
+
+    public static Method findMethod(final Class<?> type, final MethodScope methodScope, final String name, final Class<?> returnType) {
+        try {
+            final Method[] methods = type.getMethods();
+            for (final Method method2 : methods) {
+                final Method method = method2;
+                final int modifiers = method.getModifiers();
+                // check for public modifier
+                if (!Modifier.isPublic(modifiers)) {
+                    continue;
+                }
+
+                // check correct scope (static vs instance)
+                if (!methodScope.matchesScopeOf(method)) {
+                    continue;
+                }
+
+                // check for name
+                if (!method.getName().equals(name)) {
+                    continue;
+                }
+
+                // check for return type
+                if (returnType != null && returnType != method.getReturnType()) {
+                    continue;
+                }
+                return method;
+            }
+        } catch (final SecurityException e) {
+            return null;
+        }
+        return null;
+    }
+
+    public static List<Method> findMethodsWithAnnotation(final Class<?> type, final MethodScope methodScope, final Class<? extends Annotation> annotationClass) {
+
+        final List<Method> methods = new ArrayList<Method>();
+
+        // Validate arguments
+        if ((type == null) || (methodScope == null) || (annotationClass == null)) {
+            throw new IllegalArgumentException("One or more arguments are 'null' valued");
+        }
+
+        // Find methods annotated with the specified annotation
+        for (final Method method : type.getMethods()) {
+            if (!methodScope.matchesScopeOf(method)) {
+                continue;
+            }
+
+            if (method.isAnnotationPresent(annotationClass)) {
+                methods.add(method);
+            }
+        }
+
+        return methods;
+    }
+
+    public static void removeMethod(final MethodRemover methodRemover, final Method method) {
+        if (methodRemover != null && method != null) {
+            methodRemover.removeMethod(method);
+        }
+    }
+
+    public static Class<?>[] paramTypesOrNull(final Class<?> type) {
+        return type == null ? null : new Class[] { type };
+    }
+
+    public static boolean allParametersOfSameType(final Class<?>[] params) {
+        final Class<?> firstParam = params[0];
+        for (int i = 1; i < params.length; i++) {
+            if (params[i] != firstParam) {
+                return false;
+            }
+        }
+        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/MethodPrefixBasedFacetFactoryAbstract.java
----------------------------------------------------------------------
diff --git a/framework/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/MethodPrefixBasedFacetFactoryAbstract.java b/framework/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/MethodPrefixBasedFacetFactoryAbstract.java
new file mode 100644
index 0000000..3d2d73a
--- /dev/null
+++ b/framework/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/MethodPrefixBasedFacetFactoryAbstract.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;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+import org.apache.isis.core.commons.config.IsisConfiguration;
+import org.apache.isis.core.metamodel.facetapi.FeatureType;
+import org.apache.isis.core.metamodel.facets.FacetFactoryAbstract;
+import org.apache.isis.core.metamodel.facets.MethodPrefixBasedFacetFactory;
+import org.apache.isis.core.metamodel.spec.ObjectSpecification;
+import org.apache.isis.core.metamodel.spec.feature.ObjectAction;
+import org.apache.isis.core.metamodel.spec.feature.ObjectActionContainer.Contributed;
+import org.apache.isis.core.metamodel.specloader.validator.MetaModelValidatorComposite;
+import org.apache.isis.core.metamodel.specloader.validator.MetaModelValidatorVisiting;
+import org.apache.isis.core.metamodel.specloader.validator.ValidationFailures;
+
+public abstract class MethodPrefixBasedFacetFactoryAbstract extends FacetFactoryAbstract implements MethodPrefixBasedFacetFactory {
+
+    private final List<String> prefixes;
+
+    protected static final Object[] NO_PARAMETERS = new Object[0];
+    protected static final Class<?>[] NO_PARAMETERS_TYPES = new Class<?>[0];
+
+    private final OrphanValidation orphanValidation;
+
+    protected enum OrphanValidation {
+        VALIDATE,
+        DONT_VALIDATE
+    }
+    
+    public MethodPrefixBasedFacetFactoryAbstract(final List<FeatureType> featureTypes, OrphanValidation orphanValidation, final String... prefixes) {
+        super(featureTypes);
+        this.orphanValidation = orphanValidation;
+        this.prefixes = Collections.unmodifiableList(Arrays.asList(prefixes));
+    }
+
+    @Override
+    public List<String> getPrefixes() {
+        return prefixes;
+    }
+
+    @Override
+    public void refineMetaModelValidator(MetaModelValidatorComposite metaModelValidator, IsisConfiguration configuration) {
+        if(orphanValidation == OrphanValidation.DONT_VALIDATE) {
+            return;
+        }
+        metaModelValidator.add(new MetaModelValidatorVisiting(new MetaModelValidatorVisiting.Visitor() {
+
+            @Override
+            public boolean visit(ObjectSpecification objectSpec, ValidationFailures validationFailures) {
+                List<ObjectAction> objectActions = objectSpec.getObjectActions(Contributed.EXCLUDED);
+                for (ObjectAction objectAction : objectActions) {
+                    for (String prefix : prefixes) {
+                        if (objectAction.getId().startsWith(prefix)) {
+                            validationFailures.add("%s#%s has prefix %s, has probably been orphaned.  If not an orphan, then rename and use @Named annotation", objectSpec.getIdentifier().getClassName(), objectAction.getId(), prefix);
+                        }
+                    }
+                }
+                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/MethodPrefixConstants.java
----------------------------------------------------------------------
diff --git a/framework/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/MethodPrefixConstants.java b/framework/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/MethodPrefixConstants.java
new file mode 100644
index 0000000..1426d3e
--- /dev/null
+++ b/framework/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/MethodPrefixConstants.java
@@ -0,0 +1,68 @@
+/**
+ *  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;
+
+import org.apache.isis.core.commons.lang.StringUtils;
+
+public class MethodPrefixConstants {
+
+    public static final String GET_PREFIX = "get";
+    public static final String IS_PREFIX = "is";
+    public static final String SET_PREFIX = "set";
+    public static final String CLEAR_PREFIX = "clear";
+    public static final String MODIFY_PREFIX = "modify";
+    public static final String ADD_TO_PREFIX = "addTo";
+    public static final String REMOVE_FROM_PREFIX = "removeFrom";
+
+    public static final String NAME_PREFIX = "name";
+    public static final String DESCRIPTION_PREFIX = "description";
+    public static final String OPTIONAL_PREFIX = "optional";
+
+    public static final String DEFAULT_PREFIX = "default";
+    public static final String CHOICES_PREFIX = "choices";
+
+    public static final String HIDE_PREFIX = "hide";
+    public static final String ALWAYS_HIDE_PREFIX = "alwaysHide";
+
+    public static final String DISABLE_PREFIX = "disable";
+    public static final String PROTECT_PREFIX = "protect";
+
+    public static final String VALIDATE_PREFIX = "validate";
+    public static final String VALIDATE_ADD_TO_PREFIX = VALIDATE_PREFIX + ADD_TO_PREFIX;
+    public static final String VALIDATE_ADD_TO_PREFIX_2 = VALIDATE_PREFIX + StringUtils.capitalize(ADD_TO_PREFIX);
+    public static final String VALIDATE_REMOVE_FROM_PREFIX = VALIDATE_PREFIX + REMOVE_FROM_PREFIX;
+    public static final String VALIDATE_REMOVE_FROM_PREFIX_2 = VALIDATE_PREFIX + StringUtils.capitalize(REMOVE_FROM_PREFIX);
+
+    public static final String FIELD_PREFIX = "field";
+    public static final String ACTION_PREFIX = "action";
+
+    public static final String CREATED_PREFIX = "created";
+    public static final String LOADED_PREFIX = "loaded";
+    public static final String LOADING_PREFIX = "loading";
+    public static final String SAVED_PREFIX = "saved";
+    public static final String SAVING_PREFIX = "saving";
+    public static final String PERSISTED_PREFIX = "persisted";
+    public static final String PERSISTING_PREFIX = "persisting";
+    public static final String DELETED_PREFIX = "deleted";
+    public static final String DELETING_PREFIX = "deleting";
+    public static final String REMOVED_PREFIX = "removed";
+    public static final String REMOVING_PREFIX = "removing";
+    public static final String UPDATED_PREFIX = "updated";
+    public static final String UPDATING_PREFIX = "updating";
+    public static final String SINGULAR_NAME = "singularName";
+
+}