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 2013/07/19 12:16:57 UTC

[3/3] git commit: ISIS-468: can suppress collections, can read from property file

ISIS-468: can suppress collections, can read from property file


Project: http://git-wip-us.apache.org/repos/asf/isis/repo
Commit: http://git-wip-us.apache.org/repos/asf/isis/commit/ad8112c4
Tree: http://git-wip-us.apache.org/repos/asf/isis/tree/ad8112c4
Diff: http://git-wip-us.apache.org/repos/asf/isis/diff/ad8112c4

Branch: refs/heads/master
Commit: ad8112c4973d98b4253140deda9684710e7ec128
Parents: ec1646f
Author: Dan Haywood <da...@apache.org>
Authored: Fri Jul 19 11:03:41 2013 +0100
Committer: Dan Haywood <da...@apache.org>
Committed: Fri Jul 19 11:03:41 2013 +0100

----------------------------------------------------------------------
 .../wicket/model/util/ObjectSpecifications.java |  32 ++++--
 .../entity/combined/EntityCombinedPanel.html    |   1 +
 .../entity/combined/EntityCombinedPanel.java    |  35 ++++--
 .../applib/annotation/MemberGroupLayout.java    | 111 +++++++++++++++----
 .../devutils/DeveloperUtilitiesService.java     |   2 +
 .../MemberGroupLayoutColumnSpansTest.java       |  37 +++++++
 .../membergroups/MemberGroupLayoutFacet.java    |   1 +
 .../DeveloperUtilitiesServiceDefault.java       |  35 ++++--
 .../metamodel/spec/SpecificationLoaderSpi.java  |   1 +
 .../feature/ObjectAssociationContainer.java     |   2 +-
 .../specloader/ObjectReflectorDefault.java      |  16 +++
 .../specloader/SpecificationCacheDefault.java   |  18 +++
 .../MemberGroupLayoutFacetAbstract.java         |  16 ++-
 .../MemberGroupLayoutFacetAnnotation.java       |  36 ++++++
 .../MemberGroupLayoutFacetFactory.java          |  95 +++++++++++++++-
 .../MemberGroupLayoutFacetFallback.java         |  38 +++++++
 .../MemberGroupLayoutFacetProperties.java       |  81 ++++++++++++++
 .../membergroups/MemberGroupsFacetAbstract.java |  55 ---------
 .../MemberGroupsFacetAnnotation.java            |  38 +++++++
 .../MemberGroupLayoutFacetAnnotation.java       |  39 -------
 .../MemberGroupLayoutFacetFallback.java         |  39 -------
 .../annotation/MemberGroupsFacetAnnotation.java |  38 -------
 .../progmodel/facets/ProgrammableReflector.java |   6 +
 .../MemberGroupLayoutFacetFactoryTest.java      |  12 +-
 ...LayoutFacetPropertiesTest_asColumnSpans.java |  71 ++++++++++++
 ...upLayoutFacetPropertiesTest_asGroupList.java |  74 +++++++++++++
 .../dom/src/main/java/dom/todo/ToDoItem.java    |   3 +-
 .../dom/src/main/java/dom/todo/ToDoItem.layout  |   5 +
 .../java/app/services/DeveloperUtilities.java   |   5 +-
 .../app/services/ToDoItemsFixturesService.java  |   7 ++
 30 files changed, 711 insertions(+), 238 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/isis/blob/ad8112c4/component/viewer/wicket/model/src/main/java/org/apache/isis/viewer/wicket/model/util/ObjectSpecifications.java
----------------------------------------------------------------------
diff --git a/component/viewer/wicket/model/src/main/java/org/apache/isis/viewer/wicket/model/util/ObjectSpecifications.java b/component/viewer/wicket/model/src/main/java/org/apache/isis/viewer/wicket/model/util/ObjectSpecifications.java
index b2f2cfb..439cf12 100644
--- a/component/viewer/wicket/model/src/main/java/org/apache/isis/viewer/wicket/model/util/ObjectSpecifications.java
+++ b/component/viewer/wicket/model/src/main/java/org/apache/isis/viewer/wicket/model/util/ObjectSpecifications.java
@@ -19,6 +19,7 @@
 
 package org.apache.isis.viewer.wicket.model.util;
 
+import java.util.Collections;
 import java.util.List;
 import java.util.Set;
 
@@ -35,10 +36,8 @@ public final class ObjectSpecifications {
 
     public enum MemberGroupLayoutHint {
         LEFT,
-        MIDDLE;
-        public List<String> getValue(MemberGroupLayoutFacet facet) {
-            return this == LEFT? facet.getLeft(): facet.getMiddle();
-        }
+        MIDDLE,
+        RIGHT;
     }
 
     public static List<String> orderByMemberGroups(ObjectSpecification objSpec, Set<String> groupNamesToOrder, MemberGroupLayoutHint memberGroupLayoutHint) {
@@ -50,16 +49,25 @@ public final class ObjectSpecifications {
             return leftColumnGroupNames;
         }
         
-        if(memberGroupLayoutHint == MemberGroupLayoutHint.LEFT) {
-            // per the requested order, including any groups not mentioned in either list, excluding any groups in the middle column
-            final List<String> groupNamedInRequiredOrder = facet.getLeft();
-            final List<String> order = order(leftColumnGroupNames, groupNamedInRequiredOrder);
+        if(memberGroupLayoutHint == MemberGroupLayoutHint.MIDDLE) {
+            return facet.getColumnSpans().getMiddle()>0? facet.getMiddle(): Collections.<String>emptyList();
+        }
+        if(memberGroupLayoutHint == MemberGroupLayoutHint.RIGHT) {
+            return facet.getColumnSpans().getRight()>0? facet.getRight(): Collections.<String>emptyList();
+        }
+        
+        // else left; per the requested order, including any groups not mentioned in either list, 
+        // but excluding any groups in the middle or right columns
+        final List<String> groupNamedInRequiredOrder = facet.getLeft();
+        final List<String> order = order(leftColumnGroupNames, groupNamedInRequiredOrder);
+        
+        if(facet.getColumnSpans().getMiddle() > 0) {
             order.removeAll(facet.getMiddle());
-            return order;
-        } else {
-            // strictly those listed for the middle column.
-            return facet.getMiddle();
         }
+        if(facet.getColumnSpans().getRight() > 0) {
+            order.removeAll(facet.getRight());
+        }
+        return order;
     }
 
     static List<String> order(final List<String> valuesToOrder, final List<String> valuesInRequiredOrder) {

http://git-wip-us.apache.org/repos/asf/isis/blob/ad8112c4/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/entity/combined/EntityCombinedPanel.html
----------------------------------------------------------------------
diff --git a/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/entity/combined/EntityCombinedPanel.html b/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/entity/combined/EntityCombinedPanel.html
index ff610d1..ca3e476 100644
--- a/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/entity/combined/EntityCombinedPanel.html
+++ b/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/entity/combined/EntityCombinedPanel.html
@@ -27,6 +27,7 @@
         <div class="row-fluid">
 			<div wicket:id="entityPropertiesLeft"/>
             <div wicket:id="entityPropertiesMiddle"/>
+            <div wicket:id="entityPropertiesRight"/>
 			<div wicket:id="entityCollections"/>
         </div>
 	</div>

http://git-wip-us.apache.org/repos/asf/isis/blob/ad8112c4/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/entity/combined/EntityCombinedPanel.java
----------------------------------------------------------------------
diff --git a/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/entity/combined/EntityCombinedPanel.java b/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/entity/combined/EntityCombinedPanel.java
index 2403d5e..2840ce2 100644
--- a/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/entity/combined/EntityCombinedPanel.java
+++ b/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/entity/combined/EntityCombinedPanel.java
@@ -37,6 +37,10 @@ import org.apache.isis.viewer.wicket.ui.util.CssClassAppender;
 public class EntityCombinedPanel extends PanelAbstract<EntityModel> {
 
     private static final long serialVersionUID = 1L;
+    
+    private static final String ID_ENTITY_PROPERTIES_MIDDLE = "entityPropertiesMiddle";
+    private static final String ID_ENTITY_PROPERTIES_LEFT = "entityPropertiesLeft";
+    private static final String ID_ENTITY_PROPERTIES_RIGHT = "entityPropertiesRight";
 
     public EntityCombinedPanel(final String id, final EntityModel entityModel) {
         super(id, entityModel);
@@ -54,20 +58,37 @@ public class EntityCombinedPanel extends PanelAbstract<EntityModel> {
         final ColumnSpans columnSpans = mglFacet.getColumnSpans();
         
         addOrReplace(ComponentType.ENTITY_SUMMARY, model);
-        model.setMemberGroupLayoutHint(MemberGroupLayoutHint.LEFT);
         
-        final Component leftColumn = getComponentFactoryRegistry().addOrReplaceComponent(this, "entityPropertiesLeft", ComponentType.ENTITY_PROPERTIES, model);
+        // left property column
+        model.setMemberGroupLayoutHint(MemberGroupLayoutHint.LEFT);
+        final Component leftColumn = getComponentFactoryRegistry().addOrReplaceComponent(this, ID_ENTITY_PROPERTIES_LEFT, ComponentType.ENTITY_PROPERTIES, model);
         addClassForSpan(leftColumn, columnSpans.getLeft());
         
-        if(!mglFacet.getMiddle().isEmpty()) {
+        // middle property column
+        if(columnSpans.getMiddle() > 0) {
             model.setMemberGroupLayoutHint(MemberGroupLayoutHint.MIDDLE);
-            final Component middleColumn = getComponentFactoryRegistry().addOrReplaceComponent(this, "entityPropertiesMiddle", ComponentType.ENTITY_PROPERTIES, model);
+            final Component middleColumn = getComponentFactoryRegistry().addOrReplaceComponent(this, ID_ENTITY_PROPERTIES_MIDDLE, ComponentType.ENTITY_PROPERTIES, model);
             addClassForSpan(middleColumn, columnSpans.getMiddle());
         } else {
-            permanentlyHide("entityPropertiesMiddle");
+            permanentlyHide(ID_ENTITY_PROPERTIES_MIDDLE);
+        }
+        
+        // right property column
+        if(columnSpans.getRight() > 0) {
+            model.setMemberGroupLayoutHint(MemberGroupLayoutHint.RIGHT);
+            final Component rightColumn = getComponentFactoryRegistry().addOrReplaceComponent(this, ID_ENTITY_PROPERTIES_RIGHT, ComponentType.ENTITY_PROPERTIES, model);
+            addClassForSpan(rightColumn, columnSpans.getRight());
+        } else {
+            permanentlyHide(ID_ENTITY_PROPERTIES_RIGHT);
+        }
+
+        // collections column
+        if(columnSpans.getCollections() > 0) {
+            final Component propertiesColumn = addOrReplace(ComponentType.ENTITY_COLLECTIONS, model);
+            addClassForSpan(propertiesColumn, columnSpans.getCollections());
+        } else {
+            permanentlyHide(ComponentType.ENTITY_COLLECTIONS.toString());
         }
-        final Component rightColumn = addOrReplace(ComponentType.ENTITY_COLLECTIONS, model);
-        addClassForSpan(rightColumn, columnSpans.getRight());
     }
 
     private static void addClassForSpan(final Component component, final int numGridCols) {

http://git-wip-us.apache.org/repos/asf/isis/blob/ad8112c4/core/applib/src/main/java/org/apache/isis/applib/annotation/MemberGroupLayout.java
----------------------------------------------------------------------
diff --git a/core/applib/src/main/java/org/apache/isis/applib/annotation/MemberGroupLayout.java b/core/applib/src/main/java/org/apache/isis/applib/annotation/MemberGroupLayout.java
index 7441e86..60a097e 100644
--- a/core/applib/src/main/java/org/apache/isis/applib/annotation/MemberGroupLayout.java
+++ b/core/applib/src/main/java/org/apache/isis/applib/annotation/MemberGroupLayout.java
@@ -24,6 +24,8 @@ import java.lang.annotation.Inherited;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.lang.annotation.Target;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
 
 /**
  * A successor to {@link MemberGroups}, specifying the (groups of) members in a page,
@@ -47,25 +49,83 @@ import java.lang.annotation.Target;
 @Retention(RetentionPolicy.RUNTIME)
 public @interface MemberGroupLayout {
 
+    /**
+     * The relative widths of the columns of members.
+     * 
+     * <p>
+     * Each value of this enum is in the form <tt>_X_Y_Z_W</tt>.  The 
+     * <tt>X</tt>, <tt>Y</tt> and <tt>Z</tt>
+     * indicate the relative widths of (up to) three property columns,
+     * while <tt>W</tt> indicates the relative width of the collection column.
+     */
     public enum ColumnSpans {
-        _2_0_10(2,0,10),
-        _3_0_9(3,0,9),
-        _4_0_8(4,0,8),
-        _5_0_7(5,0,7),
-        _6_0_6(6,0,6),
-        _3_3_6(3,3,6),
-        _2_3_7(2,3,7),
-        _2_4_6(2,4,6),
-        _4_4_4(4,4,4);
+        // two column, with collections
+        _2_0_0_10,
+        _3_0_0_9,
+        _4_0_0_8,
+        _5_0_0_7,
+        _6_0_0_6,
+        
+        // three column, with collections
+        _2_2_0_8,
+        _2_3_0_7,
+        _2_4_0_6,
+        _2_5_0_5,
+        _2_6_0_4,
+        
+        _3_3_0_6,
+        _3_4_0_5,
+        _3_5_0_4,
+        _3_6_0_3,
+        
+        _4_4_0_4,
+        
+        // two column, suppress collections
+        _2_0_10_0,
+        _3_0_9_0,
+        _4_0_8_0,
+        _5_0_7_0,
+        _6_0_6_0,
+        
+        // three column, suppress collections
+        _2_2_8_0,
+        _2_3_7_0,
+        _2_4_6_0,
+        _2_5_5_0,
+        _2_6_4_0,
+        
+        _3_3_6_0,
+        _3_4_5_0,
+        _3_5_4_0,
+        _3_6_3_0,
         
-        private int left;
-        private int middle;
-        private int right;
+        _4_4_4_0,
+        _4_5_3_0,
+        _4_6_2_0,
+        ;
+        
+        private final int left;
+        private final int middle;
+        private final int right;
+        private final int collections;
+                
+        private ColumnSpans() {
+            final Pattern namePattern = Pattern.compile("^_(\\d+)_(\\d+)_(\\d+)_(\\d+)$");
+            final String name = name();
+            Matcher matcher = namePattern.matcher(name);
+            if(!matcher.matches()) {
+                // call to matches is required; Matcher is a state machine
+                throw new IllegalArgumentException("enum constant's name must match " + namePattern.pattern());
+            } 
+            
+            this.left = parseGroup(matcher, 1);
+            this.middle = parseGroup(matcher, 2);
+            this.right = parseGroup(matcher, 3);
+            this.collections = parseGroup(matcher, 4);
+        }
 
-        private ColumnSpans(int left, int middle, int right) {
-            this.left = left;
-            this.middle = middle;
-            this.right = right;
+        private static int parseGroup(Matcher matcher, final int group) {
+            return Integer.parseInt(matcher.group(group));
         }
 
         public int getLeft() {
@@ -77,16 +137,19 @@ public @interface MemberGroupLayout {
         public int getRight() {
             return right;
         }
+        public int getCollections() {
+            return collections;
+        }
         
     }
 
     /**
-     * Specify the spans of each of the <i>left</i>_<i>middle</i>_<i>right</i> columns.
+     * Specify the spans of each of the columns.
      * 
      * <p>
      * The sum of the spans is always 12.
      */
-    ColumnSpans columnSpans() default ColumnSpans._4_0_8;
+    ColumnSpans columnSpans() default ColumnSpans._4_0_0_8;
 
     /**
      * Order of groups of properties as they appear in the left-most column of a webpage,
@@ -108,9 +171,19 @@ public @interface MemberGroupLayout {
      * 
      * <p>
      * If the value of this attribute is non-empty but the {@link #columnSpans()} specifies a zero size
-     * (eg {@link ColumnSpans#_2_0_10}, then the framework will not boot and will instead indicate 
+     * (eg {@link ColumnSpans#_2_0_0_10}, then the framework will not boot and will instead indicate 
      * a meta-model validation exception. 
      */
     String[] middle() default {};
 
+    /**
+     * As {@link #right()}, but for the right column in a page.
+     * 
+     * <p>
+     * If the value of this attribute is non-empty but the {@link #columnSpans()} specifies a zero size
+     * (eg {@link ColumnSpans#_2_0_0_10}, then the framework will not boot and will instead indicate 
+     * a meta-model validation exception.
+     */
+    String[] right() default {};
+
 }

http://git-wip-us.apache.org/repos/asf/isis/blob/ad8112c4/core/applib/src/main/java/org/apache/isis/applib/services/devutils/DeveloperUtilitiesService.java
----------------------------------------------------------------------
diff --git a/core/applib/src/main/java/org/apache/isis/applib/services/devutils/DeveloperUtilitiesService.java b/core/applib/src/main/java/org/apache/isis/applib/services/devutils/DeveloperUtilitiesService.java
index b7f5edf..7634f23 100644
--- a/core/applib/src/main/java/org/apache/isis/applib/services/devutils/DeveloperUtilitiesService.java
+++ b/core/applib/src/main/java/org/apache/isis/applib/services/devutils/DeveloperUtilitiesService.java
@@ -17,6 +17,7 @@
 package org.apache.isis.applib.services.devutils;
 
 import org.apache.isis.applib.annotation.ActionSemantics;
+import org.apache.isis.applib.annotation.Prototype;
 import org.apache.isis.applib.annotation.ActionSemantics.Of;
 import org.apache.isis.applib.annotation.Named;
 import org.apache.isis.applib.value.Clob;
@@ -24,6 +25,7 @@ import org.apache.isis.applib.value.Clob;
 @Named("Developer Utilities")
 public interface DeveloperUtilitiesService {
 
+    @Prototype
     @ActionSemantics(Of.SAFE)
     public Clob downloadMetaModel();
     

http://git-wip-us.apache.org/repos/asf/isis/blob/ad8112c4/core/applib/src/test/java/org/apache/isis/applib/annotation/MemberGroupLayoutColumnSpansTest.java
----------------------------------------------------------------------
diff --git a/core/applib/src/test/java/org/apache/isis/applib/annotation/MemberGroupLayoutColumnSpansTest.java b/core/applib/src/test/java/org/apache/isis/applib/annotation/MemberGroupLayoutColumnSpansTest.java
new file mode 100644
index 0000000..f079480
--- /dev/null
+++ b/core/applib/src/test/java/org/apache/isis/applib/annotation/MemberGroupLayoutColumnSpansTest.java
@@ -0,0 +1,37 @@
+/*
+ *  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.applib.annotation;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertThat;
+
+import org.junit.Test;
+
+public class MemberGroupLayoutColumnSpansTest {
+
+    @Test
+    public void regex() {
+        assertThat(MemberGroupLayout.ColumnSpans._2_3_0_7.getLeft(), is(2));
+        assertThat(MemberGroupLayout.ColumnSpans._2_3_0_7.getMiddle(), is(3));
+        assertThat(MemberGroupLayout.ColumnSpans._2_3_0_7.getRight(), is(0));
+        assertThat(MemberGroupLayout.ColumnSpans._2_3_0_7.getCollections(), is(7));
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/ad8112c4/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/object/membergroups/MemberGroupLayoutFacet.java
----------------------------------------------------------------------
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/object/membergroups/MemberGroupLayoutFacet.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/object/membergroups/MemberGroupLayoutFacet.java
index 409c1ab..ade0002 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/object/membergroups/MemberGroupLayoutFacet.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/object/membergroups/MemberGroupLayoutFacet.java
@@ -43,4 +43,5 @@ public interface MemberGroupLayoutFacet extends Facet {
 
     public List<String> getLeft();
     public List<String> getMiddle();
+    public List<String> getRight();
 }

http://git-wip-us.apache.org/repos/asf/isis/blob/ad8112c4/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/devutils/DeveloperUtilitiesServiceDefault.java
----------------------------------------------------------------------
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/devutils/DeveloperUtilitiesServiceDefault.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/devutils/DeveloperUtilitiesServiceDefault.java
index 6d403c0..b2f2341 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/devutils/DeveloperUtilitiesServiceDefault.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/devutils/DeveloperUtilitiesServiceDefault.java
@@ -29,12 +29,15 @@ import com.google.common.collect.Lists;
 
 import org.apache.isis.applib.annotation.ActionSemantics;
 import org.apache.isis.applib.annotation.ActionSemantics.Of;
+import org.apache.isis.applib.annotation.MemberOrder;
+import org.apache.isis.applib.annotation.NotInServiceMenu;
 import org.apache.isis.applib.annotation.Programmatic;
+import org.apache.isis.applib.annotation.Prototype;
 import org.apache.isis.applib.services.devutils.DeveloperUtilitiesService;
 import org.apache.isis.applib.value.Clob;
 import org.apache.isis.core.metamodel.spec.ObjectSpecification;
-import org.apache.isis.core.metamodel.spec.SpecificationLoader;
-import org.apache.isis.core.metamodel.spec.SpecificationLoaderAware;
+import org.apache.isis.core.metamodel.spec.SpecificationLoaderSpi;
+import org.apache.isis.core.metamodel.spec.SpecificationLoaderSpiAware;
 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.spec.feature.ObjectAssociation;
@@ -42,7 +45,7 @@ import org.apache.isis.core.metamodel.spec.feature.ObjectAssociationFilters;
 import org.apache.isis.core.metamodel.spec.feature.OneToManyAssociation;
 import org.apache.isis.core.metamodel.spec.feature.OneToOneAssociation;
 
-public class DeveloperUtilitiesServiceDefault implements DeveloperUtilitiesService, SpecificationLoaderAware {
+public class DeveloperUtilitiesServiceDefault implements DeveloperUtilitiesService, SpecificationLoaderSpiAware {
 
     private final MimeType mimeTypeTextCsv;
 
@@ -53,9 +56,10 @@ public class DeveloperUtilitiesServiceDefault implements DeveloperUtilitiesServi
             throw new RuntimeException(e);
         }
     }
-    
+
+    // //////////////////////////////////////
+
     @Override
-    @ActionSemantics(Of.SAFE)
     public Clob downloadMetaModel() {
         
         final Collection<ObjectSpecification> specifications = specificationLoader.allSpecifications();
@@ -121,11 +125,26 @@ public class DeveloperUtilitiesServiceDefault implements DeveloperUtilitiesServi
         return className.startsWith("java") || className.startsWith("org.joda") || className.startsWith("org.apache.isis");
     }
 
-    private SpecificationLoader specificationLoader;
     
-    @Override
+    // //////////////////////////////////////
+
+    
+    @NotInServiceMenu
+    @ActionSemantics(Of.SAFE)
+    @MemberOrder(sequence="99")
+    @Prototype
+    public Object rebuildMetaModel(Object domainObject) {
+        specificationLoader.invalidateCacheFor(domainObject);
+        return domainObject;
+    }
+    
+    // //////////////////////////////////////
+
+    private SpecificationLoaderSpi specificationLoader;
+    
     @Programmatic
-    public void setSpecificationLookup(SpecificationLoader specificationLoader) {
+    @Override
+    public void setSpecificationLoaderSpi(SpecificationLoaderSpi specificationLoader) {
         this.specificationLoader = specificationLoader;
     }
 

http://git-wip-us.apache.org/repos/asf/isis/blob/ad8112c4/core/metamodel/src/main/java/org/apache/isis/core/metamodel/spec/SpecificationLoaderSpi.java
----------------------------------------------------------------------
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/spec/SpecificationLoaderSpi.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/spec/SpecificationLoaderSpi.java
index 2ae7a95..f1dc0d0 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/spec/SpecificationLoaderSpi.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/spec/SpecificationLoaderSpi.java
@@ -40,4 +40,5 @@ public interface SpecificationLoaderSpi extends ApplicationScopedComponent, Debu
      */
     void validateSpecifications(ValidationFailures validationFailures);
 
+    void invalidateCacheFor(Object domainObject);
 }

http://git-wip-us.apache.org/repos/asf/isis/blob/ad8112c4/core/metamodel/src/main/java/org/apache/isis/core/metamodel/spec/feature/ObjectAssociationContainer.java
----------------------------------------------------------------------
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/spec/feature/ObjectAssociationContainer.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/spec/feature/ObjectAssociationContainer.java
index f8622df..a0313d9 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/spec/feature/ObjectAssociationContainer.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/spec/feature/ObjectAssociationContainer.java
@@ -47,7 +47,7 @@ public interface ObjectAssociationContainer {
      * 
      * To get the statically visible fields (where any invisible and
      * unauthorised fields have been removed) use
-     * <tt>ObjectAssociationFilters#STATICALLY_VISIBLE_ASSOCIATIONS</tt>
+     * <tt>ObjectAssociationFilters#staticallyVisible(...)</tt>
      * 
      * @see Filters
      */

http://git-wip-us.apache.org/repos/asf/isis/blob/ad8112c4/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/ObjectReflectorDefault.java
----------------------------------------------------------------------
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/ObjectReflectorDefault.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/ObjectReflectorDefault.java
index 1af1be5..efd9aa8 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/ObjectReflectorDefault.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/ObjectReflectorDefault.java
@@ -334,6 +334,8 @@ public final class ObjectReflectorDefault implements SpecificationLoaderSpi, App
         return newlyDiscoveredClasses;
     }
 
+    
+    
     @Override
     public void shutdown() {
         LOG.info("shutting down " + this);
@@ -344,6 +346,19 @@ public final class ObjectReflectorDefault implements SpecificationLoaderSpi, App
         facetDecoratorSet.shutdown();
     }
 
+
+    @Override
+    public void invalidateCacheFor(Object domainObject) {
+        final Class<? extends Object> cls = domainObject.getClass();
+        final Class<?> substitutedType = getClassSubstitutor().getClass(cls);
+        ObjectSpecification origSpec = getCache().remove(substitutedType.getName());
+        
+        final ObjectSpecification newSpec = loadSpecification(cls);
+        // ObjectSpecId is a value type, so in fact it shouldn't matter whether
+        // the original or the new Spec's SpecId is used.
+        getCache().recache(origSpec.getSpecId(), newSpec);
+    }
+
     
     // /////////////////////////////////////////////////////////////
     // init services, destroy services
@@ -764,4 +779,5 @@ public final class ObjectReflectorDefault implements SpecificationLoaderSpi, App
         }
     }
 
+
 }

http://git-wip-us.apache.org/repos/asf/isis/blob/ad8112c4/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/SpecificationCacheDefault.java
----------------------------------------------------------------------
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/SpecificationCacheDefault.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/SpecificationCacheDefault.java
index 33fcca6..1078520 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/SpecificationCacheDefault.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/SpecificationCacheDefault.java
@@ -41,6 +41,7 @@ class SpecificationCacheDefault {
     public void cache(final String className, final ObjectSpecification spec) {
         specByClassName.put(className, spec);
     }
+    
 
     public void clear() {
         specByClassName.clear();
@@ -65,4 +66,21 @@ class SpecificationCacheDefault {
         this.specById.putAll(specById);
     }
 
+    public ObjectSpecification remove(String typeName) {
+        ObjectSpecification removed = specByClassName.remove(typeName);
+        if(removed != null) {
+            specById.remove(removed.getSpecId());
+        }
+        return removed;
+    }
+
+    /**
+     * 
+     * @param specId
+     * @param spec
+     */
+    public void recache(ObjectSpecId specId, ObjectSpecification spec) {
+        specById.put(specId, spec);
+    }
+
 }

http://git-wip-us.apache.org/repos/asf/isis/blob/ad8112c4/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/object/membergroups/MemberGroupLayoutFacetAbstract.java
----------------------------------------------------------------------
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/object/membergroups/MemberGroupLayoutFacetAbstract.java b/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/object/membergroups/MemberGroupLayoutFacetAbstract.java
index a8c996f..8bb9299 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/object/membergroups/MemberGroupLayoutFacetAbstract.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/object/membergroups/MemberGroupLayoutFacetAbstract.java
@@ -33,25 +33,31 @@ public abstract class MemberGroupLayoutFacetAbstract extends FacetAbstract imple
     private final ColumnSpans columns;
     private final List<String> left;
     private final List<String> middle;
+    private final List<String> right;
 
     public static Class<? extends Facet> type() {
         return MemberGroupLayoutFacet.class;
     }
     
-    protected static List<String> asList(final String[] value) {
+    protected static List<String> asListWithDefaultGroup(final String[] value) {
         return value == null || value.length == 0 
                 ? Arrays.asList(MemberGroupLayoutFacet.DEFAULT_GROUP) 
                 : Arrays.asList(value);
     }
 
+    protected static List<String> asList(final String[] value) {
+        return Arrays.asList(value);
+    }
+    
     public MemberGroupLayoutFacetAbstract(
             final ColumnSpans columns,
-            final List<String> left, final List<String> middle, 
+            final List<String> left, final List<String> middle, final List<String> right, 
             FacetHolder holder) {
         super(type(), holder, Derivation.NOT_DERIVED);
-        this.columns = columns;
+        this.columns = columns != null? columns: ColumnSpans._4_0_0_8;
         this.left = left;
         this.middle = middle;
+        this.right = right;
     }
 
     @Override
@@ -69,4 +75,8 @@ public abstract class MemberGroupLayoutFacetAbstract extends FacetAbstract imple
         return middle;
     }
 
+    @Override
+    public List<String> getRight() {
+        return right;
+    }
 }

http://git-wip-us.apache.org/repos/asf/isis/blob/ad8112c4/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/object/membergroups/MemberGroupLayoutFacetAnnotation.java
----------------------------------------------------------------------
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/object/membergroups/MemberGroupLayoutFacetAnnotation.java b/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/object/membergroups/MemberGroupLayoutFacetAnnotation.java
new file mode 100644
index 0000000..6255264
--- /dev/null
+++ b/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/object/membergroups/MemberGroupLayoutFacetAnnotation.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.membergroups;
+
+import org.apache.isis.applib.annotation.MemberGroupLayout;
+import org.apache.isis.core.metamodel.facetapi.FacetHolder;
+
+public class MemberGroupLayoutFacetAnnotation extends MemberGroupLayoutFacetAbstract {
+
+    public MemberGroupLayoutFacetAnnotation(
+            final MemberGroupLayout memberGroupLayout,
+            FacetHolder holder) {
+        super(memberGroupLayout.columnSpans(), 
+                asListWithDefaultGroup(memberGroupLayout.left()), 
+                asList(memberGroupLayout.middle()), 
+                asList(memberGroupLayout.right()), 
+                holder);
+    }
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/ad8112c4/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/object/membergroups/MemberGroupLayoutFacetFactory.java
----------------------------------------------------------------------
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/object/membergroups/MemberGroupLayoutFacetFactory.java b/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/object/membergroups/MemberGroupLayoutFacetFactory.java
index 5d84d35..de87330 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/object/membergroups/MemberGroupLayoutFacetFactory.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/object/membergroups/MemberGroupLayoutFacetFactory.java
@@ -19,19 +19,36 @@
 
 package org.apache.isis.core.progmodel.facets.object.membergroups;
 
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.util.List;
+import java.util.Properties;
+
+import com.google.common.io.InputSupplier;
+import com.google.common.io.Resources;
+
 import org.apache.isis.applib.annotation.MemberGroupLayout;
+import org.apache.isis.applib.annotation.MemberGroupLayout.ColumnSpans;
 import org.apache.isis.applib.annotation.MemberGroups;
+import org.apache.isis.applib.annotation.Where;
+import org.apache.isis.core.commons.config.IsisConfiguration;
 import org.apache.isis.core.metamodel.facetapi.FacetHolder;
 import org.apache.isis.core.metamodel.facetapi.FacetUtil;
 import org.apache.isis.core.metamodel.facetapi.FeatureType;
+import org.apache.isis.core.metamodel.facetapi.MetaModelValidatorRefiner;
 import org.apache.isis.core.metamodel.facets.Annotations;
 import org.apache.isis.core.metamodel.facets.FacetFactoryAbstract;
 import org.apache.isis.core.metamodel.facets.object.membergroups.MemberGroupLayoutFacet;
-import org.apache.isis.core.progmodel.facets.object.membergroups.annotation.MemberGroupLayoutFacetAnnotation;
-import org.apache.isis.core.progmodel.facets.object.membergroups.annotation.MemberGroupLayoutFacetFallback;
-import org.apache.isis.core.progmodel.facets.object.membergroups.annotation.MemberGroupsFacetAnnotation;
+import org.apache.isis.core.metamodel.spec.ObjectSpecification;
+import org.apache.isis.core.metamodel.spec.feature.ObjectAssociation;
+import org.apache.isis.core.metamodel.spec.feature.ObjectAssociationFilters;
+import org.apache.isis.core.metamodel.specloader.validator.MetaModelValidatorComposite;
+import org.apache.isis.core.metamodel.specloader.validator.MetaModelValidatorVisiting;
+import org.apache.isis.core.metamodel.specloader.validator.MetaModelValidatorVisiting.Visitor;
+import org.apache.isis.core.metamodel.specloader.validator.ValidationFailures;
 
-public class MemberGroupLayoutFacetFactory extends FacetFactoryAbstract {
+public class MemberGroupLayoutFacetFactory extends FacetFactoryAbstract implements MetaModelValidatorRefiner {
 
     public MemberGroupLayoutFacetFactory() {
         super(FeatureType.OBJECTS_ONLY);
@@ -45,16 +62,82 @@ public class MemberGroupLayoutFacetFactory extends FacetFactoryAbstract {
     private MemberGroupLayoutFacet create(final ProcessClassContext processClassContext) {
         final FacetHolder holder = processClassContext.getFacetHolder();
         
-        final MemberGroupLayout mglAnnot = Annotations.getAnnotation(processClassContext.getCls(), MemberGroupLayout.class);
+        final Class<?> cls = processClassContext.getCls();
+
+        // try to find property file (different suffices supported)
+        final Properties properties = propertiesFor(cls, ".memberGroupLayout", ".memberLayout", ".layout");
+        if(properties != null) {
+            return new MemberGroupLayoutFacetProperties(properties, holder);
+        }
+        
+        final MemberGroupLayout mglAnnot = Annotations.getAnnotation(cls, MemberGroupLayout.class);
         if (mglAnnot != null) {
             return new MemberGroupLayoutFacetAnnotation(mglAnnot, holder);
         }
-        final MemberGroups mgAnnot = Annotations.getAnnotation(processClassContext.getCls(), MemberGroups.class);
+        final MemberGroups mgAnnot = Annotations.getAnnotation(cls, MemberGroups.class);
         if (mgAnnot != null) {
             return new MemberGroupsFacetAnnotation(mgAnnot, processClassContext.getFacetHolder());
         }
         return new MemberGroupLayoutFacetFallback(holder); 
     }
 
+    static Properties propertiesFor(final Class<?> cls, final String... suffices) {
+        for (String suffix : suffices) {
+            final Properties properties = propertiesFor(cls, suffix);
+            if(properties != null) {
+                return properties;
+            }
+        }
+        return null;
+    }
+    static Properties propertiesFor(final Class<?> cls, final String suffix) {
+        try {
+            final URL url = Resources.getResource(cls, cls.getSimpleName()+suffix);
+            final InputSupplier<InputStream> inputSupplier = Resources.newInputStreamSupplier(url);
+            final Properties properties = new Properties();
+            properties.load(inputSupplier.getInput());
+            return properties;
+        } catch (Exception e) {
+            return null;
+        }
+    }
+
+    @Override
+    public void refineMetaModelValidator(MetaModelValidatorComposite metaModelValidator, IsisConfiguration configuration) {
+        metaModelValidator.add(new MetaModelValidatorVisiting(newValidatorVisitor()));
+    }
+
+    private Visitor newValidatorVisitor() {
+        return new MetaModelValidatorVisiting.Visitor() {
+
+            @Override
+            public boolean visit(ObjectSpecification objectSpec, ValidationFailures validationFailures) {
+                MemberGroupLayoutFacet facet = objectSpec.getFacet(MemberGroupLayoutFacet.class);
+                ColumnSpans columnSpans = facet.getColumnSpans();
+                final List<String> middle = facet.getMiddle();
+                final List<String> right = facet.getRight();
+                List<ObjectAssociation> objectCollections = objectSpec.getAssociations(ObjectAssociationFilters.staticallyVisible(Where.OBJECT_FORMS));
+
+                if(columnSpans.getMiddle() == 0 && !middle.isEmpty()) {
+                    validationFailures.add("%s MemberGroupLayout: middle (property) column is 0 for ColumnSpans (%s), but groups have been listed (%s).  NB: ColumnSpans may have been defaulted if could not be parsed.", objectSpec.getIdentifier().getClassName(), columnSpans.name(), middle);
+                }
+                if(columnSpans.getMiddle() > 0 && middle.isEmpty()) {
+                    validationFailures.add("%s MemberGroupLayout: middle (property) column is non-zero for ColumnSpans (%s), but no groups have been listed", objectSpec.getIdentifier().getClassName(), columnSpans.name());
+                }
+                
+                if(columnSpans.getRight() == 0 && !right.isEmpty()) {
+                    validationFailures.add("%s MemberGroupLayout: right (property) column is 0 for ColumnSpans (%s), but groups have been listed (%s).  NB: ColumnSpans may have been defaulted if could not be parsed.", objectSpec.getIdentifier().getClassName(), columnSpans.name(), right);
+                }
+                if(columnSpans.getRight() > 0 && right.isEmpty()) {
+                    validationFailures.add("%s MemberGroupLayout: right (property) column is non-zero for ColumnSpans (%s), but no groups have been listed", objectSpec.getIdentifier().getClassName(), columnSpans.name());
+                }
+                
+                if(columnSpans.getCollections() == 0 && !objectCollections.isEmpty()) {
+                    validationFailures.add("%s MemberGroupLayout: collections column is 0 for ColumnSpans (%s), but there are (up to) %d visible collections visible", objectSpec.getIdentifier().getClassName(), columnSpans.name(), objectCollections.size());
+                }
+                return true;
+            }
+        };
+    }
 
 }

http://git-wip-us.apache.org/repos/asf/isis/blob/ad8112c4/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/object/membergroups/MemberGroupLayoutFacetFallback.java
----------------------------------------------------------------------
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/object/membergroups/MemberGroupLayoutFacetFallback.java b/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/object/membergroups/MemberGroupLayoutFacetFallback.java
new file mode 100644
index 0000000..792a0ef
--- /dev/null
+++ b/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/object/membergroups/MemberGroupLayoutFacetFallback.java
@@ -0,0 +1,38 @@
+/*
+ *  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.membergroups;
+
+import java.util.Arrays;
+import java.util.Collections;
+
+import org.apache.isis.core.metamodel.facetapi.FacetHolder;
+import org.apache.isis.core.metamodel.facets.object.membergroups.MemberGroupLayoutFacet;
+
+public class MemberGroupLayoutFacetFallback extends MemberGroupLayoutFacetAbstract {
+
+    public MemberGroupLayoutFacetFallback(final FacetHolder holder) {
+        super(null, 
+                Arrays.asList(MemberGroupLayoutFacet.DEFAULT_GROUP), 
+                Collections.<String>emptyList(), 
+                Collections.<String>emptyList(), 
+                holder);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/ad8112c4/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/object/membergroups/MemberGroupLayoutFacetProperties.java
----------------------------------------------------------------------
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/object/membergroups/MemberGroupLayoutFacetProperties.java b/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/object/membergroups/MemberGroupLayoutFacetProperties.java
new file mode 100644
index 0000000..c0b93b1
--- /dev/null
+++ b/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/object/membergroups/MemberGroupLayoutFacetProperties.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.membergroups;
+
+import java.util.Properties;
+
+import com.google.common.base.Function;
+import com.google.common.base.Predicate;
+import com.google.common.base.Splitter;
+import com.google.common.collect.Iterables;
+
+import org.apache.isis.applib.annotation.MemberGroupLayout.ColumnSpans;
+import org.apache.isis.core.metamodel.facetapi.FacetHolder;
+
+public class MemberGroupLayoutFacetProperties extends MemberGroupLayoutFacetAbstract {
+
+    public MemberGroupLayoutFacetProperties(
+            final Properties properties,
+            FacetHolder holder) {
+        super(asColumnSpans(properties), 
+                asListWithDefaultGroup(asGroupList(properties, "left")), 
+                asList(asGroupList(properties, "middle")), 
+                asList(asGroupList(properties, "right")), 
+                holder);
+    }
+
+    static ColumnSpans asColumnSpans(Properties properties) {
+        String columnSpansStr = properties.getProperty("columnSpans");
+        
+        if(columnSpansStr == null) {
+            return null;
+        }
+        try {
+            return ColumnSpans.valueOf(columnSpansStr.trim());
+        } catch(IllegalArgumentException ex) {
+            return null;
+        }
+    }
+
+    static String[] asGroupList(Properties properties, final String key) {
+        final String property = properties.getProperty(key);
+        if(property == null) {
+            return new String[0];
+        }
+        final Iterable<String> split = Splitter.on(',').split(property);
+        return Iterables.toArray(
+                    Iterables.filter(
+                        Iterables.transform(
+                                split, 
+                                new Function<String,String>(){
+                                    @Override
+                                    public String apply(String input) {
+                                        return input==null?"":input.trim();
+                                    }
+                                }), 
+                        new Predicate<String>() {
+                            @Override
+                            public boolean apply(String input) {
+                                return input!=null && !input.isEmpty();
+                            }
+                        }), 
+                    String.class);
+    }
+}

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

http://git-wip-us.apache.org/repos/asf/isis/blob/ad8112c4/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/object/membergroups/MemberGroupsFacetAnnotation.java
----------------------------------------------------------------------
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/object/membergroups/MemberGroupsFacetAnnotation.java b/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/object/membergroups/MemberGroupsFacetAnnotation.java
new file mode 100644
index 0000000..85df0a2
--- /dev/null
+++ b/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/object/membergroups/MemberGroupsFacetAnnotation.java
@@ -0,0 +1,38 @@
+/*
+ *  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.membergroups;
+
+import java.util.Collections;
+
+import org.apache.isis.applib.annotation.MemberGroupLayout.ColumnSpans;
+import org.apache.isis.applib.annotation.MemberGroups;
+import org.apache.isis.core.metamodel.facetapi.FacetHolder;
+
+public class MemberGroupsFacetAnnotation extends MemberGroupLayoutFacetAbstract {
+
+    public MemberGroupsFacetAnnotation(final MemberGroups annotation, final FacetHolder holder) {
+        super(null, 
+                asListWithDefaultGroup(annotation.value()), 
+                Collections.<String>emptyList(), 
+                Collections.<String>emptyList(), 
+                holder);
+    }
+
+}

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

http://git-wip-us.apache.org/repos/asf/isis/blob/ad8112c4/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/object/membergroups/annotation/MemberGroupLayoutFacetFallback.java
----------------------------------------------------------------------
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/object/membergroups/annotation/MemberGroupLayoutFacetFallback.java b/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/object/membergroups/annotation/MemberGroupLayoutFacetFallback.java
deleted file mode 100644
index 290ca8f..0000000
--- a/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/object/membergroups/annotation/MemberGroupLayoutFacetFallback.java
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- *  Licensed to the Apache Software Foundation (ASF) under one
- *  or more contributor license agreements.  See the NOTICE file
- *  distributed with this work for additional information
- *  regarding copyright ownership.  The ASF licenses this file
- *  to you under the Apache License, Version 2.0 (the
- *  "License"); you may not use this file except in compliance
- *  with the License.  You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing,
- *  software distributed under the License is distributed on an
- *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- *  KIND, either express or implied.  See the License for the
- *  specific language governing permissions and limitations
- *  under the License.
- */
-
-package org.apache.isis.core.progmodel.facets.object.membergroups.annotation;
-
-import java.util.Arrays;
-import java.util.Collections;
-
-import org.apache.isis.applib.annotation.MemberGroupLayout.ColumnSpans;
-import org.apache.isis.core.metamodel.facetapi.FacetHolder;
-import org.apache.isis.core.metamodel.facets.object.membergroups.MemberGroupLayoutFacet;
-import org.apache.isis.core.progmodel.facets.object.membergroups.MemberGroupLayoutFacetAbstract;
-
-public class MemberGroupLayoutFacetFallback extends MemberGroupLayoutFacetAbstract {
-
-    public MemberGroupLayoutFacetFallback(final FacetHolder holder) {
-        super(ColumnSpans._4_0_8, 
-                Arrays.asList(MemberGroupLayoutFacet.DEFAULT_GROUP), 
-                Collections.<String>emptyList(), 
-                holder);
-    }
-
-}

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

http://git-wip-us.apache.org/repos/asf/isis/blob/ad8112c4/core/metamodel/src/test/java/org/apache/isis/core/progmodel/facets/ProgrammableReflector.java
----------------------------------------------------------------------
diff --git a/core/metamodel/src/test/java/org/apache/isis/core/progmodel/facets/ProgrammableReflector.java b/core/metamodel/src/test/java/org/apache/isis/core/progmodel/facets/ProgrammableReflector.java
index 9ee2184..df91a36 100644
--- a/core/metamodel/src/test/java/org/apache/isis/core/progmodel/facets/ProgrammableReflector.java
+++ b/core/metamodel/src/test/java/org/apache/isis/core/progmodel/facets/ProgrammableReflector.java
@@ -83,6 +83,7 @@ public class ProgrammableReflector implements SpecificationLoaderSpi, Applicatio
     public void shutdown() {
     }
 
+
     @Override
     public boolean loaded(final Class<?> cls) {
         throw new NotYetImplementedException();
@@ -146,4 +147,9 @@ public class ProgrammableReflector implements SpecificationLoaderSpi, Applicatio
         throw new NotYetImplementedException();
     }
 
+    @Override
+    public void invalidateCacheFor(Object domainObject) {
+    }
+
+
 }

http://git-wip-us.apache.org/repos/asf/isis/blob/ad8112c4/core/metamodel/src/test/java/org/apache/isis/core/progmodel/facets/object/membergroups/MemberGroupLayoutFacetFactoryTest.java
----------------------------------------------------------------------
diff --git a/core/metamodel/src/test/java/org/apache/isis/core/progmodel/facets/object/membergroups/MemberGroupLayoutFacetFactoryTest.java b/core/metamodel/src/test/java/org/apache/isis/core/progmodel/facets/object/membergroups/MemberGroupLayoutFacetFactoryTest.java
index c770449..b5f7ddb 100644
--- a/core/metamodel/src/test/java/org/apache/isis/core/progmodel/facets/object/membergroups/MemberGroupLayoutFacetFactoryTest.java
+++ b/core/metamodel/src/test/java/org/apache/isis/core/progmodel/facets/object/membergroups/MemberGroupLayoutFacetFactoryTest.java
@@ -29,9 +29,6 @@ import org.apache.isis.core.metamodel.facetapi.Facet;
 import org.apache.isis.core.metamodel.facets.FacetFactory.ProcessClassContext;
 import org.apache.isis.core.metamodel.facets.object.membergroups.MemberGroupLayoutFacet;
 import org.apache.isis.core.progmodel.facets.AbstractFacetFactoryTest;
-import org.apache.isis.core.progmodel.facets.object.membergroups.annotation.MemberGroupLayoutFacetAnnotation;
-import org.apache.isis.core.progmodel.facets.object.membergroups.annotation.MemberGroupLayoutFacetFallback;
-import org.apache.isis.core.progmodel.facets.object.membergroups.annotation.MemberGroupsFacetAnnotation;
 
 public class MemberGroupLayoutFacetFactoryTest extends AbstractFacetFactoryTest {
 
@@ -53,7 +50,8 @@ public class MemberGroupLayoutFacetFactoryTest extends AbstractFacetFactoryTest
     @MemberGroupLayout(
             left={"General","Foo", "Bar"}, 
             middle={"Baz", "Boz"}, 
-            columnSpans=ColumnSpans._2_4_6)
+            right={"Flip", "Flop"}, 
+            columnSpans=ColumnSpans._2_4_6_0)
     public static class ClassWithMemberGroupLayoutAnnotation {
         
     }
@@ -61,7 +59,7 @@ public class MemberGroupLayoutFacetFactoryTest extends AbstractFacetFactoryTest
     @MemberGroupLayout(
             left={"General","Foo", "Bar"}, 
             middle={"Baz", "Boz"}, 
-            columnSpans=ColumnSpans._2_4_6)
+            columnSpans=ColumnSpans._2_4_0_6)
     @MemberGroups({"General","Foo", "Bar"})
     public static class ClassWithMemberGroupLayoutAndMemberGroupsAnnotation {
         
@@ -89,10 +87,12 @@ public class MemberGroupLayoutFacetFactoryTest extends AbstractFacetFactoryTest
         final MemberGroupLayoutFacetAnnotation memberGroupsFacet = (MemberGroupLayoutFacetAnnotation) facet;
         final List<String> leftNames = memberGroupsFacet.getLeft();
         final List<String> middleNames = memberGroupsFacet.getMiddle();
+        final List<String> rightNames = memberGroupsFacet.getRight();
         final ColumnSpans columnSpans = memberGroupsFacet.getColumnSpans();
         assertEquals(Arrays.asList("General", "Foo", "Bar"), leftNames);
         assertEquals(Arrays.asList("Baz", "Boz"), middleNames);
-        assertEquals(ColumnSpans._2_4_6, columnSpans);
+        assertEquals(Arrays.asList("Flip", "Flop"), rightNames);
+        assertEquals(ColumnSpans._2_4_6_0, columnSpans);
 
         assertNoMethodsRemoved();
     }

http://git-wip-us.apache.org/repos/asf/isis/blob/ad8112c4/core/metamodel/src/test/java/org/apache/isis/core/progmodel/facets/object/membergroups/MemberGroupLayoutFacetPropertiesTest_asColumnSpans.java
----------------------------------------------------------------------
diff --git a/core/metamodel/src/test/java/org/apache/isis/core/progmodel/facets/object/membergroups/MemberGroupLayoutFacetPropertiesTest_asColumnSpans.java b/core/metamodel/src/test/java/org/apache/isis/core/progmodel/facets/object/membergroups/MemberGroupLayoutFacetPropertiesTest_asColumnSpans.java
new file mode 100644
index 0000000..758c66c
--- /dev/null
+++ b/core/metamodel/src/test/java/org/apache/isis/core/progmodel/facets/object/membergroups/MemberGroupLayoutFacetPropertiesTest_asColumnSpans.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.membergroups;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.nullValue;
+import static org.junit.Assert.assertThat;
+
+import java.util.Properties;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import org.apache.isis.applib.annotation.MemberGroupLayout.ColumnSpans;
+
+public class MemberGroupLayoutFacetPropertiesTest_asColumnSpans {
+
+    private Properties properties;
+
+    @Before
+    public void setUp() throws Exception {
+        properties = new Properties();
+    }
+    
+    @Test
+    public void empty() throws Exception {
+        assertThat(MemberGroupLayoutFacetProperties.asColumnSpans(properties), is(nullValue()));
+    }
+    
+    @Test
+    public void other() throws Exception {
+        properties.put("foo", "a,b,c");
+        assertThat(MemberGroupLayoutFacetProperties.asColumnSpans(properties), is(nullValue()));
+    }
+
+    @Test
+    public void happyCase() throws Exception {
+        properties.put("columnSpans", "_2_3_0_7");
+        assertThat(MemberGroupLayoutFacetProperties.asColumnSpans(properties), is(ColumnSpans._2_3_0_7));
+    }
+    
+    @Test
+    public void trimmed() throws Exception {
+        properties.put("columnSpans", " _2_3_0_7 ");
+        assertThat(MemberGroupLayoutFacetProperties.asColumnSpans(properties), is(ColumnSpans._2_3_0_7));
+    }
+    
+    @Test
+    public void invalid() throws Exception {
+        properties.put("columnSpans", "_x_x_x_x");
+        assertThat(MemberGroupLayoutFacetProperties.asColumnSpans(properties), is(nullValue()));
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/ad8112c4/core/metamodel/src/test/java/org/apache/isis/core/progmodel/facets/object/membergroups/MemberGroupLayoutFacetPropertiesTest_asGroupList.java
----------------------------------------------------------------------
diff --git a/core/metamodel/src/test/java/org/apache/isis/core/progmodel/facets/object/membergroups/MemberGroupLayoutFacetPropertiesTest_asGroupList.java b/core/metamodel/src/test/java/org/apache/isis/core/progmodel/facets/object/membergroups/MemberGroupLayoutFacetPropertiesTest_asGroupList.java
new file mode 100644
index 0000000..092c1d0
--- /dev/null
+++ b/core/metamodel/src/test/java/org/apache/isis/core/progmodel/facets/object/membergroups/MemberGroupLayoutFacetPropertiesTest_asGroupList.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.membergroups;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertThat;
+
+import java.util.Properties;
+
+import org.junit.Before;
+import org.junit.Test;
+
+public class MemberGroupLayoutFacetPropertiesTest_asGroupList {
+
+    private Properties properties;
+
+    @Before
+    public void setUp() throws Exception {
+        properties = new Properties();
+    }
+    
+    @Test
+    public void empty() throws Exception {
+        assertThat(MemberGroupLayoutFacetProperties.asGroupList(properties, "left"), is(new String[]{}));
+    }
+    
+    @Test
+    public void other() throws Exception {
+        properties.put("foo", "a,b,c");
+        assertThat(MemberGroupLayoutFacetProperties.asGroupList(properties, "left"), is(new String[]{}));
+    }
+
+    @Test
+    public void happyCase() throws Exception {
+        properties.put("left", "a,b,c");
+        assertThat(MemberGroupLayoutFacetProperties.asGroupList(properties, "left"), is(new String[]{"a","b","c"}));
+    }
+    
+    @Test
+    public void single() throws Exception {
+        properties.put("left", "a");
+        assertThat(MemberGroupLayoutFacetProperties.asGroupList(properties, "left"), is(new String[]{"a"}));
+    }
+
+    @Test
+    public void trimmed() throws Exception {
+        properties.put("left", " a, b ,c ");
+        assertThat(MemberGroupLayoutFacetProperties.asGroupList(properties, "left"), is(new String[]{"a","b","c"}));
+    }
+
+    @Test
+    public void filter() throws Exception {
+        properties.put("left", "a,,c");
+        assertThat(MemberGroupLayoutFacetProperties.asGroupList(properties, "left"), is(new String[]{"a","c"}));
+    }
+    
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/ad8112c4/example/application/quickstart_wicket_restful_jdo/dom/src/main/java/dom/todo/ToDoItem.java
----------------------------------------------------------------------
diff --git a/example/application/quickstart_wicket_restful_jdo/dom/src/main/java/dom/todo/ToDoItem.java b/example/application/quickstart_wicket_restful_jdo/dom/src/main/java/dom/todo/ToDoItem.java
index a427400..9df6815 100644
--- a/example/application/quickstart_wicket_restful_jdo/dom/src/main/java/dom/todo/ToDoItem.java
+++ b/example/application/quickstart_wicket_restful_jdo/dom/src/main/java/dom/todo/ToDoItem.java
@@ -94,7 +94,6 @@ import org.apache.isis.applib.value.Blob;
 @Audited
 @PublishedObject(ToDoItemChangedPayloadFactory.class)
 @AutoComplete(repository=ToDoItems.class, action="autoComplete") // default unless overridden by autoCompleteNXxx() method
-@MemberGroupLayout(columnSpans=ColumnSpans._4_4_4, left={"General"}, middle={"Detail"})
 @Bookmarkable
 public class ToDoItem implements Comparable<ToDoItem> /*, Locatable*/ { // GMAP3: uncomment to use https://github.com/danhaywood/isis-wicket-gmap3
 
@@ -447,7 +446,7 @@ public class ToDoItem implements Comparable<ToDoItem> /*, Locatable*/ { // GMAP3
     
     @MemberOrder(sequence = "5")
     @NotPersisted
-    @Render(Type.LAZILY)
+    @Render(Type.EAGERLY)
     @CssClass("x-info")
     public List<ToDoItem> getSimilarItems() {
         return toDoItems.similarTo(this);

http://git-wip-us.apache.org/repos/asf/isis/blob/ad8112c4/example/application/quickstart_wicket_restful_jdo/dom/src/main/java/dom/todo/ToDoItem.layout
----------------------------------------------------------------------
diff --git a/example/application/quickstart_wicket_restful_jdo/dom/src/main/java/dom/todo/ToDoItem.layout b/example/application/quickstart_wicket_restful_jdo/dom/src/main/java/dom/todo/ToDoItem.layout
new file mode 100644
index 0000000..0a0ec85
--- /dev/null
+++ b/example/application/quickstart_wicket_restful_jdo/dom/src/main/java/dom/todo/ToDoItem.layout
@@ -0,0 +1,5 @@
+columnSpans=_2_4_0_6
+#columnSpans=_6_0_6_0 
+left=General 
+middle=Detail
+right=
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/isis/blob/ad8112c4/example/application/quickstart_wicket_restful_jdo/webapp/src/main/java/app/services/DeveloperUtilities.java
----------------------------------------------------------------------
diff --git a/example/application/quickstart_wicket_restful_jdo/webapp/src/main/java/app/services/DeveloperUtilities.java b/example/application/quickstart_wicket_restful_jdo/webapp/src/main/java/app/services/DeveloperUtilities.java
index 4675c4e..8e6fa2a 100644
--- a/example/application/quickstart_wicket_restful_jdo/webapp/src/main/java/app/services/DeveloperUtilities.java
+++ b/example/application/quickstart_wicket_restful_jdo/webapp/src/main/java/app/services/DeveloperUtilities.java
@@ -16,8 +16,6 @@
  */
 package app.services;
 
-import org.apache.isis.applib.annotation.ActionSemantics;
-import org.apache.isis.applib.annotation.ActionSemantics.Of;
 import org.apache.isis.applib.annotation.MemberOrder;
 import org.apache.isis.applib.value.Clob;
 import org.apache.isis.core.metamodel.services.devutils.DeveloperUtilitiesServiceDefault;
@@ -30,8 +28,9 @@ public class DeveloperUtilities extends DeveloperUtilitiesServiceDefault {
      */
     @MemberOrder(name="Fixtures", sequence="90")
     @Override
-    @ActionSemantics(Of.SAFE)
     public Clob downloadMetaModel() {
         return super.downloadMetaModel();
     }
+
 }
+

http://git-wip-us.apache.org/repos/asf/isis/blob/ad8112c4/example/application/quickstart_wicket_restful_jdo/webapp/src/main/java/app/services/ToDoItemsFixturesService.java
----------------------------------------------------------------------
diff --git a/example/application/quickstart_wicket_restful_jdo/webapp/src/main/java/app/services/ToDoItemsFixturesService.java b/example/application/quickstart_wicket_restful_jdo/webapp/src/main/java/app/services/ToDoItemsFixturesService.java
index f665ec3..c24f1db 100644
--- a/example/application/quickstart_wicket_restful_jdo/webapp/src/main/java/app/services/ToDoItemsFixturesService.java
+++ b/example/application/quickstart_wicket_restful_jdo/webapp/src/main/java/app/services/ToDoItemsFixturesService.java
@@ -26,6 +26,7 @@ import fixture.todo.ToDoItemsFixture;
 
 import org.apache.isis.applib.AbstractService;
 import org.apache.isis.applib.annotation.Named;
+import org.apache.isis.applib.annotation.Prototype;
 import org.apache.isis.core.runtime.fixtures.FixturesInstallerDelegate;
 
 /**
@@ -34,11 +35,15 @@ import org.apache.isis.core.runtime.fixtures.FixturesInstallerDelegate;
 @Named("Fixtures")
 public class ToDoItemsFixturesService extends AbstractService {
 
+    @Prototype
     public String install() {
         installFixturesFor(null); // ie current user
         return "Example fixtures installed";
     }
 
+    // //////////////////////////////////////
+
+    @Prototype
     public String installFor(@Named("User") String user) {
         installFixturesFor(user);
         return "Example fixtures installed for " + user;
@@ -50,6 +55,8 @@ public class ToDoItemsFixturesService extends AbstractService {
         return Lists.newArrayList("guest", "dick", "bob", "joe");
     }
 
+    // //////////////////////////////////////
+
     private static void installFixturesFor(String user) {
         final FixturesInstallerDelegate installer = new FixturesInstallerDelegate().withOverride();
         installer.addFixture(new ToDoItemsFixture(user));