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 2016/02/10 12:33:35 UTC

[2/5] isis git commit: ISIS-993: introducing the concept of minimal/normalized/complete grids (still WIP)

ISIS-993: introducing the concept of minimal/normalized/complete grids (still WIP)

Also:
- moved responsibility for determining the available set of gridNormalizerServices to GridService (ensure only one such per grid implementation)
- moved the FC applib classes into the metamodel, so no longer public API.


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

Branch: refs/heads/ISIS-993
Commit: 5c831757d34919ab7b36f84a6e6579a4061a97e3
Parents: f3e8bbf
Author: Dan Haywood <da...@haywood-associates.co.uk>
Authored: Tue Feb 9 08:02:44 2016 +0000
Committer: Dan Haywood <da...@haywood-associates.co.uk>
Committed: Tue Feb 9 08:02:44 2016 +0000

----------------------------------------------------------------------
 .../isis/applib/layout/fixedcols/FCColumn.java  | 161 ---------------
 .../applib/layout/fixedcols/FCColumnOwner.java  |  25 ---
 .../isis/applib/layout/fixedcols/FCGrid.java    | 204 -------------------
 .../isis/applib/layout/fixedcols/FCTab.java     | 159 ---------------
 .../applib/layout/fixedcols/FCTabGroup.java     |  94 ---------
 .../layout/fixedcols/FCTabGroupOwner.java       |  25 ---
 .../applib/layout/fixedcols/package-info.java   |  35 ----
 .../services/layout/GridNormalizerService.java  |  48 +++++
 .../applib/services/layout/GridService.java     |  53 ++++-
 .../layout/Object_downloadLayoutXml.java        |  15 +-
 .../services/layout/Object_viewLayout.java      |   8 +-
 .../facets/object/grid/GridFacetDefault.java    |  29 +--
 .../facets/object/grid/GridFacetFactory.java    |  11 +-
 .../json/LayoutMetadataReaderFromJson.java      |   2 +-
 .../services/grid/GridNormalizerService.java    |  38 ----
 .../grid/GridNormalizerServiceAbstract.java     |  26 ++-
 .../services/grid/GridServiceDefault.java       | 110 +++++++++-
 .../grid/fixedcols/GridNormalizerServiceFC.java |   8 +-
 .../grid/fixedcols/applib/FCColumn.java         | 161 +++++++++++++++
 .../grid/fixedcols/applib/FCColumnOwner.java    |  25 +++
 .../services/grid/fixedcols/applib/FCGrid.java  | 204 +++++++++++++++++++
 .../services/grid/fixedcols/applib/FCTab.java   | 159 +++++++++++++++
 .../grid/fixedcols/applib/FCTabGroup.java       |  94 +++++++++
 .../grid/fixedcols/applib/FCTabGroupOwner.java  |  25 +++
 .../grid/fixedcols/applib/package-info.java     |  44 ++++
 .../services/metamodel/MetadataMenu.java        |   2 +-
 .../metamodel/spec/ObjectSpecifications.java    |   2 +-
 .../metamodel/services/grid/BS3GridTest.java    |   1 +
 .../metamodel/services/grid/FCGridTest.java     |   9 +-
 .../viewer/wicket/model/models/EntityModel.java |   2 +-
 .../links/EntityLinksSelectorPanelFactory.java  |   2 +-
 .../layout/fixedcols/FCGridPanel.java           |   6 +-
 .../components/layout/fixedcols/PropUtil.java   |   2 +-
 .../collections/EntityCollectionsPanel.java     |   2 +-
 .../layout/fixedcols/columns/EntityColumn.java  |   2 +-
 .../propsandcolls/EntityPropsAndCollsForm.java  |   6 +-
 .../tabgrouplist/TabGroupListPanel.java         |   2 +-
 .../layout/fixedcols/tabs/TabGroupPanel.java    |   4 +-
 .../layout/fixedcols/tabs/TabPanel.java         |   2 +-
 39 files changed, 983 insertions(+), 824 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/isis/blob/5c831757/core/applib/src/main/java/org/apache/isis/applib/layout/fixedcols/FCColumn.java
----------------------------------------------------------------------
diff --git a/core/applib/src/main/java/org/apache/isis/applib/layout/fixedcols/FCColumn.java b/core/applib/src/main/java/org/apache/isis/applib/layout/fixedcols/FCColumn.java
deleted file mode 100644
index 79d94b4..0000000
--- a/core/applib/src/main/java/org/apache/isis/applib/layout/fixedcols/FCColumn.java
+++ /dev/null
@@ -1,161 +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.applib.layout.fixedcols;
-
-import java.io.Serializable;
-import java.util.List;
-
-import javax.xml.bind.annotation.XmlAttribute;
-import javax.xml.bind.annotation.XmlElementRef;
-import javax.xml.bind.annotation.XmlTransient;
-import javax.xml.bind.annotation.XmlType;
-
-import com.google.common.collect.Lists;
-
-import org.apache.isis.applib.annotation.MemberGroupLayout;
-import org.apache.isis.applib.layout.common.CollectionLayoutData;
-import org.apache.isis.applib.layout.common.CollectionLayoutDataOwner;
-import org.apache.isis.applib.layout.common.FieldSet;
-import org.apache.isis.applib.layout.common.FieldSetOwner;
-import org.apache.isis.applib.layout.common.Owned;
-import org.apache.isis.applib.layout.common.PropertyLayoutData;
-
-/**
- * The column contains a mixture of {@link FieldSet}s (of {@link PropertyLayoutData properties}) and also
- * {@link CollectionLayoutData collection}s.
- *
- * <p>
- * A column generally is used within a {@link FCTab}; there can be up to three such (left, middle and right).  It is
- * also possible for their to be a column far-left on the top-level {@link FCGrid page}, and another far-right.
- * </p>
- *
- */
-@XmlType(
-        propOrder = {
-                "fieldSets"
-                , "collections"
-        }
-)
-public class FCColumn implements Serializable, FieldSetOwner, CollectionLayoutDataOwner, Owned<FCColumnOwner> {
-
-    private static final long serialVersionUID = 1L;
-
-    public FCColumn() {
-    }
-
-    public FCColumn(final int span) {
-        setSpan(span);
-    }
-
-    private int span = 4;
-
-    @XmlAttribute(required = true)
-    public int getSpan() {
-        return span;
-    }
-
-    public void setSpan(final int span) {
-        this.span = span;
-    }
-
-
-
-    private List<FieldSet> fieldSets = Lists.newArrayList();
-
-    // no wrapper
-    @XmlElementRef(type = FieldSet.class, name = "fieldSet", required = false)
-    public List<FieldSet> getFieldSets() {
-        return fieldSets;
-    }
-
-    public void setFieldSets(final List<FieldSet> fieldSets) {
-        this.fieldSets = fieldSets;
-    }
-
-
-    private List<CollectionLayoutData> collections = Lists.newArrayList();
-
-    // no wrapper
-    @XmlElementRef(type = CollectionLayoutData.class, name = "collection", required = false)
-    public List<CollectionLayoutData> getCollections() {
-        return collections;
-    }
-
-    public void setCollections(final List<CollectionLayoutData> collections) {
-        this.collections = collections;
-    }
-
-
-    private FCColumnOwner owner;
-    /**
-     * Owner.
-     *
-     * <p>
-     *     Set programmatically by framework after reading in from XML.
-     * </p>
-     */
-    @XmlTransient
-    public FCColumnOwner getOwner() {
-        return owner;
-    }
-
-    public void setOwner(final FCColumnOwner owner) {
-        this.owner = owner;
-    }
-
-
-
-
-    private Hint hint;
-
-    @XmlTransient
-    public Hint getHint() {
-        return hint;
-    }
-
-    public void setHint(final Hint hint) {
-        this.hint = hint;
-    }
-
-
-
-    public enum Hint {
-        LEFT,
-        MIDDLE,
-        RIGHT;
-
-        public int from(MemberGroupLayout.ColumnSpans columnSpans) {
-            if(this == LEFT) return columnSpans.getLeft();
-            if(this == MIDDLE) return columnSpans.getMiddle();
-            if(this == RIGHT) return columnSpans.getRight();
-            throw new IllegalStateException();
-        }
-
-        public FCColumn from(final FCTab fcTab) {
-            if(fcTab == null) {
-                return null;
-            }
-            if(this == LEFT) return fcTab.getLeft();
-            if(this == MIDDLE) return fcTab.getMiddle();
-            if(this == RIGHT) return fcTab.getRight();
-            throw new IllegalStateException();
-        }
-
-    }
-}

http://git-wip-us.apache.org/repos/asf/isis/blob/5c831757/core/applib/src/main/java/org/apache/isis/applib/layout/fixedcols/FCColumnOwner.java
----------------------------------------------------------------------
diff --git a/core/applib/src/main/java/org/apache/isis/applib/layout/fixedcols/FCColumnOwner.java b/core/applib/src/main/java/org/apache/isis/applib/layout/fixedcols/FCColumnOwner.java
deleted file mode 100644
index fe2ea76..0000000
--- a/core/applib/src/main/java/org/apache/isis/applib/layout/fixedcols/FCColumnOwner.java
+++ /dev/null
@@ -1,25 +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.applib.layout.fixedcols;
-
-import org.apache.isis.applib.layout.common.Owner;
-
-public interface FCColumnOwner extends Owner {
-
-}

http://git-wip-us.apache.org/repos/asf/isis/blob/5c831757/core/applib/src/main/java/org/apache/isis/applib/layout/fixedcols/FCGrid.java
----------------------------------------------------------------------
diff --git a/core/applib/src/main/java/org/apache/isis/applib/layout/fixedcols/FCGrid.java b/core/applib/src/main/java/org/apache/isis/applib/layout/fixedcols/FCGrid.java
deleted file mode 100644
index 01626dd..0000000
--- a/core/applib/src/main/java/org/apache/isis/applib/layout/fixedcols/FCGrid.java
+++ /dev/null
@@ -1,204 +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.applib.layout.fixedcols;
-
-import java.io.Serializable;
-import java.util.List;
-
-import javax.xml.bind.annotation.XmlElement;
-import javax.xml.bind.annotation.XmlElementRef;
-import javax.xml.bind.annotation.XmlRootElement;
-import javax.xml.bind.annotation.XmlType;
-
-import org.apache.isis.applib.layout.common.ActionLayoutData;
-import org.apache.isis.applib.layout.common.ActionLayoutDataOwner;
-import org.apache.isis.applib.layout.common.CollectionLayoutData;
-import org.apache.isis.applib.layout.common.DomainObjectLayoutData;
-import org.apache.isis.applib.layout.common.FieldSet;
-import org.apache.isis.applib.layout.common.Grid;
-import org.apache.isis.applib.layout.common.GridAbstract;
-import org.apache.isis.applib.layout.common.PropertyLayoutData;
-import org.apache.isis.applib.services.dto.Dto;
-
-/**
- * Top-level page, consisting of an optional {@link FCColumn column} on the far left and another (also optional) on the
- * far right, with the middle consisting of a number of {@link FCTabGroup tabgroup}s, stacked vertically.
- */
-@XmlRootElement(
-        name = "grid"
-)
-@XmlType(
-        name = "grid"
-        , propOrder = {
-                "actions"
-                , "left"
-                , "tabGroups"
-                , "right"
-        }
-)
-public class FCGrid extends GridAbstract implements Dto, ActionLayoutDataOwner, Serializable, FCColumnOwner, FCTabGroupOwner {
-
-    private static final long serialVersionUID = 1L;
-
-    private List<ActionLayoutData> actions;
-
-    // no wrapper
-    @XmlElementRef(type = ActionLayoutData.class, name="action", required = false)
-    public List<ActionLayoutData> getActions() {
-        return actions;
-    }
-
-    public void setActions(List<ActionLayoutData> actionLayoutDatas) {
-        this.actions = actionLayoutDatas;
-    }
-
-
-
-    private FCColumn left;
-
-    @XmlElement(required = false)
-    public FCColumn getLeft() {
-        return left;
-    }
-
-    public void setLeft(final FCColumn left) {
-        this.left = left;
-        left.setHint(FCColumn.Hint.LEFT);
-    }
-
-
-
-    private List<FCTabGroup> tabGroups;
-
-    // no wrapper
-    @XmlElement(name = "tabGroup", required = true)
-    public List<FCTabGroup> getTabGroups() {
-        return tabGroups;
-    }
-
-    public void setTabGroups(List<FCTabGroup> tabGroups) {
-        this.tabGroups = tabGroups;
-    }
-
-
-
-    private FCColumn right;
-
-    @XmlElement(required = false)
-    public FCColumn getRight() {
-        return right;
-    }
-
-    public void setRight(final FCColumn right) {
-        this.right = right;
-        right.setHint(FCColumn.Hint.RIGHT);
-    }
-
-
-
-    interface Visitor extends Grid.Visitor {
-        void visit(final FCGrid fcPage);
-        void visit(final FCTabGroup fcTabGroup);
-        void visit(final FCTab fcTab);
-        void visit(final FCColumn fcColumn);
-    }
-
-    public static class VisitorAdapter extends Grid.VisitorAdapter implements Visitor {
-        @Override
-        public void visit(final FCGrid fcPage) { }
-        @Override
-        public void visit(final FCTabGroup fcTabGroup) { }
-        @Override
-        public void visit(final FCTab fcTab) { }
-        @Override
-        public void visit(final FCColumn fcColumn) { }
-        @Override
-        public void visit(final PropertyLayoutData propertyLayoutData) {}
-        @Override
-        public void visit(final CollectionLayoutData collectionLayoutData) {}
-        @Override
-        public void visit(final ActionLayoutData actionLayoutData) { }
-    }
-
-
-    /**
-     * Visits all elements of the graph.  The {@link Visitor} implementation
-     * can assume that all "owner" references are populated.
-     */
-    public void visit(final Grid.Visitor visitor) {
-        FCGrid.Visitor fcVisitor = asFcVisitor(visitor);
-        fcVisitor.visit(this);
-        traverseActions(this, visitor);
-        traverseColumn(getLeft(), this, visitor);
-        final List<FCTabGroup> tabGroups = getTabGroups();
-        for (final FCTabGroup fcTabGroup : tabGroups) {
-            fcTabGroup.setOwner(this);
-            fcVisitor.visit(fcTabGroup);
-            final List<FCTab> tabs = fcTabGroup.getTabs();
-            for (final FCTab fcTab : tabs) {
-                fcTab.setOwner(fcTabGroup);
-                fcVisitor.visit(fcTab);
-                traverseColumn(fcTab.getLeft(), fcTab, visitor);
-                traverseColumn(fcTab.getMiddle(), fcTab, visitor);
-                traverseColumn(fcTab.getRight(), fcTab, visitor);
-            }
-        }
-        traverseColumn(getRight(), this, visitor);
-    }
-
-    private void traverseColumn(
-            final FCColumn fcColumn,
-            final FCColumnOwner fcColumnOwner,
-            final Grid.Visitor visitor) {
-        if(fcColumn == null) {
-            return;
-        }
-        FCGrid.Visitor fcVisitor = asFcVisitor(visitor);
-        fcColumn.setOwner(fcColumnOwner);
-        fcVisitor.visit(fcColumn);
-        traverseFieldSets(fcColumn, visitor);
-        traverseCollections(fcColumn, visitor);
-    }
-
-    private static Visitor asFcVisitor(final Grid.Visitor visitor) {
-        return visitor instanceof Visitor? (Visitor) visitor : new VisitorAdapter() {
-            @Override public void visit(final DomainObjectLayoutData domainObjectLayoutData) {
-                visitor.visit(domainObjectLayoutData);
-            }
-
-            @Override public void visit(final ActionLayoutData actionLayoutData) {
-                visitor.visit(actionLayoutData);
-            }
-
-            @Override public void visit(final PropertyLayoutData propertyLayoutData) {
-                visitor.visit(propertyLayoutData);
-            }
-
-            @Override public void visit(final CollectionLayoutData collectionLayoutData) {
-                visitor.visit(collectionLayoutData);
-            }
-
-            @Override public void visit(final FieldSet fieldSet) {
-                visitor.visit(fieldSet);
-            }
-        };
-    }
-
-
-}

http://git-wip-us.apache.org/repos/asf/isis/blob/5c831757/core/applib/src/main/java/org/apache/isis/applib/layout/fixedcols/FCTab.java
----------------------------------------------------------------------
diff --git a/core/applib/src/main/java/org/apache/isis/applib/layout/fixedcols/FCTab.java b/core/applib/src/main/java/org/apache/isis/applib/layout/fixedcols/FCTab.java
deleted file mode 100644
index f7b7100..0000000
--- a/core/applib/src/main/java/org/apache/isis/applib/layout/fixedcols/FCTab.java
+++ /dev/null
@@ -1,159 +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.applib.layout.fixedcols;
-
-import java.io.Serializable;
-import java.util.List;
-
-import javax.xml.bind.annotation.XmlAttribute;
-import javax.xml.bind.annotation.XmlElement;
-import javax.xml.bind.annotation.XmlTransient;
-import javax.xml.bind.annotation.XmlType;
-
-import com.google.common.base.Predicate;
-import com.google.common.collect.Lists;
-
-import org.apache.isis.applib.annotation.Programmatic;
-import org.apache.isis.applib.layout.common.CollectionLayoutData;
-import org.apache.isis.applib.layout.common.MemberRegion;
-import org.apache.isis.applib.layout.common.Owned;
-import org.apache.isis.applib.layout.common.FieldSet;
-
-@XmlType(
-        name="tab"
-        , propOrder = {
-                "name"
-                , "left"
-                , "middle"
-                , "right"
-        }
-)
-public class FCTab implements FCColumnOwner, Serializable, Owned<FCTabGroup> {
-
-    private static final long serialVersionUID = 1L;
-
-    private String name;
-
-    @XmlAttribute(required = true)
-    public String getName() {
-        return name;
-    }
-
-    public void setName(String name) {
-        this.name = name;
-    }
-
-
-
-    private FCColumn left = new FCColumn();
-
-    @XmlElement(required = true)
-    public FCColumn getLeft() {
-        return left;
-    }
-
-    public void setLeft(final FCColumn left) {
-        this.left = left;
-        left.setHint(FCColumn.Hint.LEFT);
-    }
-
-
-    private FCColumn middle;
-
-    @XmlElement(required = false)
-    public FCColumn getMiddle() {
-        return middle;
-    }
-
-    public void setMiddle(final FCColumn middle) {
-        this.middle = middle;
-        middle.setHint(FCColumn.Hint.MIDDLE);
-    }
-
-
-    private FCColumn right;
-
-    @XmlElement(required = false)
-    public FCColumn getRight() {
-        return right;
-    }
-
-    public void setRight(final FCColumn right) {
-        this.right = right;
-        right.setHint(FCColumn.Hint.RIGHT);
-    }
-
-
-
-    private FCTabGroup owner;
-    /**
-     * Owner.
-     *
-     * <p>
-     *     Set programmatically by framework after reading in from XML.
-     * </p>
-     */
-    @XmlTransient
-    public FCTabGroup getOwner() {
-        return owner;
-    }
-
-    public void setOwner(final FCTabGroup owner) {
-        this.owner = owner;
-    }
-
-    /**
-     * Aggregates the contents of all collections on this tab.
-     */
-    @Programmatic
-    public List<MemberRegion> getContents() {
-        final List<MemberRegion> contents = Lists.newArrayList();
-        appendContent(contents, getLeft());
-        appendContent(contents, getMiddle());
-        appendContent(contents, getRight());
-        return contents;
-    }
-
-
-
-    private static void appendContent(final List<MemberRegion> contents, final FCColumn FCColumn) {
-        if(FCColumn == null) {
-            return;
-        }
-        final List<FieldSet> fieldSets = FCColumn.getFieldSets();
-        if(fieldSets != null) {
-            contents.addAll(fieldSets);
-        }
-        final List<CollectionLayoutData> collectionLayoutDatas = FCColumn.getCollections();
-        if(collectionLayoutDatas != null) {
-            contents.addAll(collectionLayoutDatas);
-        }
-    }
-
-    public static class Predicates {
-        public static Predicate<FCTab> notEmpty() {
-            return new Predicate<FCTab>() {
-                @Override
-                public boolean apply(final FCTab FCTab) {
-                    return !FCTab.getContents().isEmpty();
-                }
-            };
-        }
-    }
-}

http://git-wip-us.apache.org/repos/asf/isis/blob/5c831757/core/applib/src/main/java/org/apache/isis/applib/layout/fixedcols/FCTabGroup.java
----------------------------------------------------------------------
diff --git a/core/applib/src/main/java/org/apache/isis/applib/layout/fixedcols/FCTabGroup.java b/core/applib/src/main/java/org/apache/isis/applib/layout/fixedcols/FCTabGroup.java
deleted file mode 100644
index 93c88970..0000000
--- a/core/applib/src/main/java/org/apache/isis/applib/layout/fixedcols/FCTabGroup.java
+++ /dev/null
@@ -1,94 +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.applib.layout.fixedcols;
-
-import java.io.Serializable;
-import java.util.List;
-
-import javax.xml.bind.annotation.XmlElement;
-import javax.xml.bind.annotation.XmlTransient;
-import javax.xml.bind.annotation.XmlType;
-
-import com.google.common.base.Predicate;
-import com.google.common.collect.FluentIterable;
-import com.google.common.collect.Lists;
-
-import org.apache.isis.applib.layout.common.Owned;
-
-@XmlType(
-        propOrder = {
-                "tabs"
-        }
-
-)
-public class FCTabGroup implements FCColumnOwner, Serializable, Owned<FCTabGroupOwner> {
-
-    private static final long serialVersionUID = 1L;
-
-    private List<FCTab> tabs = Lists.newArrayList();
-
-    // no wrapper
-    @XmlElement(name = "tab", required = false)
-    public List<FCTab> getTabs() {
-        return tabs;
-    }
-
-    public void setTabs(List<FCTab> tabs) {
-        this.tabs = tabs;
-    }
-
-
-
-    private FCTabGroupOwner owner;
-
-    /**
-     * Owner.
-     *
-     * <p>
-     *     Set programmatically by framework after reading in from XML.
-     * </p>
-     */
-    @XmlTransient
-    public FCTabGroupOwner getOwner() {
-        return owner;
-    }
-
-    public void setOwner(final FCTabGroupOwner owner) {
-        this.owner = owner;
-    }
-
-
-
-
-
-
-    public static class Predicates {
-        public static Predicate<FCTabGroup> notEmpty() {
-            return new Predicate<FCTabGroup>() {
-                @Override
-                public boolean apply(final FCTabGroup tabGroup) {
-                    return FluentIterable
-                            .from(tabGroup.getTabs())
-                            .anyMatch(FCTab.Predicates.notEmpty());
-                }
-            };
-        }
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/isis/blob/5c831757/core/applib/src/main/java/org/apache/isis/applib/layout/fixedcols/FCTabGroupOwner.java
----------------------------------------------------------------------
diff --git a/core/applib/src/main/java/org/apache/isis/applib/layout/fixedcols/FCTabGroupOwner.java b/core/applib/src/main/java/org/apache/isis/applib/layout/fixedcols/FCTabGroupOwner.java
deleted file mode 100644
index 2b37c0f..0000000
--- a/core/applib/src/main/java/org/apache/isis/applib/layout/fixedcols/FCTabGroupOwner.java
+++ /dev/null
@@ -1,25 +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.applib.layout.fixedcols;
-
-import org.apache.isis.applib.layout.common.Owner;
-
-public interface FCTabGroupOwner extends Owner {
-
-}

http://git-wip-us.apache.org/repos/asf/isis/blob/5c831757/core/applib/src/main/java/org/apache/isis/applib/layout/fixedcols/package-info.java
----------------------------------------------------------------------
diff --git a/core/applib/src/main/java/org/apache/isis/applib/layout/fixedcols/package-info.java b/core/applib/src/main/java/org/apache/isis/applib/layout/fixedcols/package-info.java
deleted file mode 100644
index af1fb85..0000000
--- a/core/applib/src/main/java/org/apache/isis/applib/layout/fixedcols/package-info.java
+++ /dev/null
@@ -1,35 +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.
- */
-
-/**
- * The classes in this package define how to layout the properties, collections and actions of a domain object - the
- * building blocks - as defined in the <code>members.v1</code> package.
- *
- * <p>
- *     The layout is reasonably flexible, being a half-way house between the annotation/JSON style layouts (pre 1.12.0)
- *     vs the fully flexible layouts provided by the <code>bootstrap3</code> layouts.  In particular, they allow
- *     property fieldsets and collections to be grouped into tabs, with collections being laid out in any column.
- *     However, tab groups only appear in the central area of the page, and may only have a maximum of three columns.
- * </p>
- */
-@javax.xml.bind.annotation.XmlSchema(
-        namespace = "http://isis.apache.org/schema/applib/layout/fixedcols",
-        elementFormDefault = javax.xml.bind.annotation.XmlNsForm.QUALIFIED
-)
-package org.apache.isis.applib.layout.fixedcols;
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/isis/blob/5c831757/core/applib/src/main/java/org/apache/isis/applib/services/layout/GridNormalizerService.java
----------------------------------------------------------------------
diff --git a/core/applib/src/main/java/org/apache/isis/applib/services/layout/GridNormalizerService.java b/core/applib/src/main/java/org/apache/isis/applib/services/layout/GridNormalizerService.java
new file mode 100644
index 0000000..ced93eb
--- /dev/null
+++ b/core/applib/src/main/java/org/apache/isis/applib/services/layout/GridNormalizerService.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.applib.services.layout;
+
+import org.apache.isis.applib.annotation.Programmatic;
+import org.apache.isis.applib.layout.common.Grid;
+
+/**
+ * Provides implementation of {@link Grid}.
+ * @param <G>
+ */
+public interface GridNormalizerService<G extends Grid> {
+
+    /**
+     * Which grid implementation is understood by this.
+     */
+    @Programmatic
+    Class<? extends Grid> gridImplementation();
+
+    @Programmatic
+    String tns();
+
+    @Programmatic
+    String schemaLocation();
+
+    @Programmatic
+    void normalize(G grid, Class<?> domainClass);
+
+    @Programmatic
+    void complete(G grid, Class<?> domainClass);
+
+    @Programmatic
+    void minimal(G grid, Class<?> domainClass);
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/5c831757/core/applib/src/main/java/org/apache/isis/applib/services/layout/GridService.java
----------------------------------------------------------------------
diff --git a/core/applib/src/main/java/org/apache/isis/applib/services/layout/GridService.java b/core/applib/src/main/java/org/apache/isis/applib/services/layout/GridService.java
index a641889..ec2d699 100644
--- a/core/applib/src/main/java/org/apache/isis/applib/services/layout/GridService.java
+++ b/core/applib/src/main/java/org/apache/isis/applib/services/layout/GridService.java
@@ -16,6 +16,8 @@
  */
 package org.apache.isis.applib.services.layout;
 
+import java.util.List;
+
 import org.apache.isis.applib.annotation.Programmatic;
 import org.apache.isis.applib.layout.common.Grid;
 
@@ -24,8 +26,7 @@ public interface GridService {
     /**
      * Whether any metadata exists for this domain class, and if so then whether it is valid or invalid.
      */
-    @Programmatic
-    boolean exists(Class<?> domainClass);
+    @Programmatic boolean exists(Class<?> domainClass);
 
     /**
      * Returns (raw unnormalized) metadata, eg per the <code>.layout.xml</code> file.
@@ -33,14 +34,56 @@ public interface GridService {
     @Programmatic Grid fromXml(Class<?> domainClass);
 
     /**
+     * Normalize the grid with respect to the specified domain class.
+     *
+     * <p>
+     *     This will (often) modify the grid graph in order to add in any unreferenced actions and so forth.
+     * </p>
+     */
+    Grid normalize(Grid grid, final Class<?> domainClass);
+
+    Grid complete(Grid grid, final Class<?> domainClass);
+
+    Grid minimal(Grid grid, final Class<?> domainClass);
+
+    enum Style {
+
+        COMPLETE,
+        /**
+         * Default, corresponding to raw state along with any additional regions added
+         * as a result of normalization process.
+         */
+        NORMALIZED,
+        MINIMAL
+    }
+
+    /**
      * Obtains the (normalized) layout metadata, if any, for the (domain class of the) specified domain object.
+     *
+     * @param style - whether the returned grid should be complete (having been normalized), or should be as
+     *              minimal as possible.
      */
-    @Programmatic Grid toGrid(Object domainObject);
+    @Programmatic Grid toGrid(Object domainObject, final Style style);
 
     /**
      * Obtains the (normalized) layout metadata, if any, for the specified domain class.
+     *
+     * @param style - whether the returned grid should be complete (having been normalized), or should be as
+     *              minimal as possible.
      */
-    @Programmatic Grid toGrid(Class<?> domainClass);
+    @Programmatic Grid toGrid(Class<?> domainClass, final Style style);
 
     String tnsAndSchemaLocation(final Grid grid);
-}
\ No newline at end of file
+
+    /**
+     * For all of the available {@link GridNormalizerService}s available, return only the first one for any that
+     * are for the same grid implementation.
+     * <p/>
+     * <p>
+     * This allows default implementations (eg for bootstrap3) to be overridden while also allowing for the more
+     * general idea of multiple implementations.
+     * </p>
+     */
+    @Programmatic
+    List<GridNormalizerService<?>> gridNormalizerServices();
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/5c831757/core/applib/src/main/java/org/apache/isis/applib/services/layout/Object_downloadLayoutXml.java
----------------------------------------------------------------------
diff --git a/core/applib/src/main/java/org/apache/isis/applib/services/layout/Object_downloadLayoutXml.java b/core/applib/src/main/java/org/apache/isis/applib/services/layout/Object_downloadLayoutXml.java
index 01beb41..10fee16 100644
--- a/core/applib/src/main/java/org/apache/isis/applib/services/layout/Object_downloadLayoutXml.java
+++ b/core/applib/src/main/java/org/apache/isis/applib/services/layout/Object_downloadLayoutXml.java
@@ -54,8 +54,9 @@ public class Object_downloadLayoutXml {
     @MemberOrder(sequence = "550.1")
     public Object $$(
             @ParameterLayout(named = "File name")
-            final String fileName) {
-        final Grid grid = getPage();
+            final String fileName,
+            final GridService.Style style) {
+        final Grid grid = getGrid(style);
         final String xml = jaxbService.toXml(grid,
                 ImmutableMap.<String,Object>of(
                         Marshaller.JAXB_SCHEMA_LOCATION,
@@ -66,14 +67,18 @@ public class Object_downloadLayoutXml {
     }
 
     public boolean hide$$() {
-        return getPage() == null;
+        // can use either style to determine whether this action should be hidden
+        return getGrid(GridService.Style.COMPLETE) == null;
     }
     public String default0$$() {
         return Util.withSuffix(object.getClass().getSimpleName(), "layout.xml");
     }
+    public GridService.Style default1$$() {
+        return GridService.Style.NORMALIZED;
+    }
 
-    protected Grid getPage() {
-        return gridService.toGrid(object);
+    protected Grid getGrid(final GridService.Style style) {
+        return gridService.toGrid(object, style);
     }
 
     @Inject

http://git-wip-us.apache.org/repos/asf/isis/blob/5c831757/core/applib/src/main/java/org/apache/isis/applib/services/layout/Object_viewLayout.java
----------------------------------------------------------------------
diff --git a/core/applib/src/main/java/org/apache/isis/applib/services/layout/Object_viewLayout.java b/core/applib/src/main/java/org/apache/isis/applib/services/layout/Object_viewLayout.java
index df38db2..6d2e3a7 100644
--- a/core/applib/src/main/java/org/apache/isis/applib/services/layout/Object_viewLayout.java
+++ b/core/applib/src/main/java/org/apache/isis/applib/services/layout/Object_viewLayout.java
@@ -51,16 +51,16 @@ public class Object_viewLayout {
     )
     @MemberOrder(sequence = "550.2")
     public Grid $$() {
-        return getPage();
+        return getGrid(GridService.Style.NORMALIZED);
     }
 
     @Programmatic // TODO ... excluded for now (getting an Isis framework exception in the view model rendering).
     public boolean hide$$() {
-        return getPage() == null;
+        return getGrid(GridService.Style.NORMALIZED) == null;
     }
 
-    protected Grid getPage() {
-        return gridService.toGrid(object);
+    protected Grid getGrid(final GridService.Style style) {
+        return gridService.toGrid(object, style);
     }
 
 

http://git-wip-us.apache.org/repos/asf/isis/blob/5c831757/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/object/grid/GridFacetDefault.java
----------------------------------------------------------------------
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/object/grid/GridFacetDefault.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/object/grid/GridFacetDefault.java
index 596829d..4f5f0ae 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/object/grid/GridFacetDefault.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/object/grid/GridFacetDefault.java
@@ -22,13 +22,11 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import org.apache.isis.applib.layout.common.Grid;
-import org.apache.isis.applib.services.i18n.TranslationService;
 import org.apache.isis.applib.services.layout.GridService;
 import org.apache.isis.core.metamodel.deployment.DeploymentCategory;
 import org.apache.isis.core.metamodel.facetapi.Facet;
 import org.apache.isis.core.metamodel.facetapi.FacetAbstract;
 import org.apache.isis.core.metamodel.facetapi.FacetHolder;
-import org.apache.isis.core.metamodel.services.grid.GridNormalizerService;
 import org.apache.isis.core.metamodel.spec.ObjectSpecification;
 
 public class GridFacetDefault
@@ -45,15 +43,11 @@ public class GridFacetDefault
 
     public static GridFacet create(
             final FacetHolder facetHolder,
-            final TranslationService translationService,
             final GridService gridService,
-            final GridNormalizerService gridNormalizerService, final DeploymentCategory deploymentCategory) {
-        return new GridFacetDefault(facetHolder, translationService, gridService, gridNormalizerService,
-                deploymentCategory);
+            final DeploymentCategory deploymentCategory) {
+        return new GridFacetDefault(facetHolder, gridService, deploymentCategory);
     }
 
-    private final TranslationService translationService;
-    private final GridNormalizerService gridNormalizerService;
     private final DeploymentCategory deploymentCategory;
     private final GridService gridService;
 
@@ -62,14 +56,10 @@ public class GridFacetDefault
 
     private GridFacetDefault(
             final FacetHolder facetHolder,
-            final TranslationService translationService,
             final GridService gridService,
-            final GridNormalizerService gridNormalizerService,
             final DeploymentCategory deploymentCategory) {
         super(GridFacetDefault.type(), facetHolder, Derivation.NOT_DERIVED);
         this.gridService = gridService;
-        this.translationService = translationService;
-        this.gridNormalizerService = gridNormalizerService;
         this.deploymentCategory = deploymentCategory;
     }
 
@@ -93,21 +83,9 @@ public class GridFacetDefault
         if(grid == null) {
             return null;
         }
-
-        // if have .layout.json and then add a .layout.xml without restarting, then note that
-        // the changes won't be picked up.  Normalizing would be required
-        // in order to trample over the .layout.json's original facets
-        if(grid.isNormalized()) {
-            return grid;
-        }
-
         final Class<?> domainClass = getSpecification().getCorrespondingClass();
 
-        gridNormalizerService.normalize(grid, domainClass);
-
-        grid.setNormalized(true);
-
-        return grid;
+        return gridService.normalize(grid, domainClass);
     }
 
     private ObjectSpecification getSpecification() {
@@ -115,5 +93,4 @@ public class GridFacetDefault
     }
 
 
-
 }

http://git-wip-us.apache.org/repos/asf/isis/blob/5c831757/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/object/grid/GridFacetFactory.java
----------------------------------------------------------------------
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/object/grid/GridFacetFactory.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/object/grid/GridFacetFactory.java
index 9c4f9cd..1cfcd04 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/object/grid/GridFacetFactory.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/object/grid/GridFacetFactory.java
@@ -16,10 +16,12 @@
  * under the License. */
 package org.apache.isis.core.metamodel.facets.object.grid;
 
+import java.util.List;
+
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import org.apache.isis.applib.services.i18n.TranslationService;
+import org.apache.isis.applib.services.layout.GridNormalizerService;
 import org.apache.isis.applib.services.layout.GridService;
 import org.apache.isis.core.metamodel.facetapi.FacetHolder;
 import org.apache.isis.core.metamodel.facetapi.FacetUtil;
@@ -27,7 +29,6 @@ import org.apache.isis.core.metamodel.facetapi.FeatureType;
 import org.apache.isis.core.metamodel.facets.FacetFactoryAbstract;
 import org.apache.isis.core.metamodel.runtimecontext.ServicesInjector;
 import org.apache.isis.core.metamodel.runtimecontext.ServicesInjectorAware;
-import org.apache.isis.core.metamodel.services.grid.GridNormalizerService;
 
 public class GridFacetFactory extends FacetFactoryAbstract implements ServicesInjectorAware {
 
@@ -41,16 +42,12 @@ public class GridFacetFactory extends FacetFactoryAbstract implements ServicesIn
     public void process(final ProcessClassContext processClassContext) {
         final FacetHolder facetHolder = processClassContext.getFacetHolder();
 
-        final TranslationService translationService =
-                servicesInjector.lookupService(TranslationService.class);
         final GridService gridService =
                 servicesInjector.lookupService(GridService.class);
-        final GridNormalizerService gridNormalizerService =
-                servicesInjector.lookupService(GridNormalizerService.class);
 
         FacetUtil.addFacet(
                 GridFacetDefault.create(facetHolder,
-                        translationService, gridService, gridNormalizerService, getDeploymentCategory()));
+                        gridService, getDeploymentCategory()));
     }
 
     private ServicesInjector servicesInjector;

http://git-wip-us.apache.org/repos/asf/isis/blob/5c831757/core/metamodel/src/main/java/org/apache/isis/core/metamodel/layoutmetadata/json/LayoutMetadataReaderFromJson.java
----------------------------------------------------------------------
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/layoutmetadata/json/LayoutMetadataReaderFromJson.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/layoutmetadata/json/LayoutMetadataReaderFromJson.java
index 779cf66..05b84fc 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/layoutmetadata/json/LayoutMetadataReaderFromJson.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/layoutmetadata/json/LayoutMetadataReaderFromJson.java
@@ -84,7 +84,7 @@ import org.apache.isis.core.metamodel.runtimecontext.ServicesInjectorAware;
 import org.apache.isis.core.metamodel.spec.ActionType;
 import org.apache.isis.core.metamodel.spec.ObjectSpecification;
 import org.apache.isis.core.metamodel.spec.ObjectSpecifications;
-import org.apache.isis.applib.layout.fixedcols.FCColumn.Hint;
+import org.apache.isis.core.metamodel.services.grid.fixedcols.applib.FCColumn.Hint;
 import org.apache.isis.core.metamodel.spec.feature.Contributed;
 import org.apache.isis.core.metamodel.spec.feature.ObjectAction;
 import org.apache.isis.core.metamodel.spec.feature.ObjectAssociation;

http://git-wip-us.apache.org/repos/asf/isis/blob/5c831757/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/grid/GridNormalizerService.java
----------------------------------------------------------------------
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/grid/GridNormalizerService.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/grid/GridNormalizerService.java
deleted file mode 100644
index 844168b..0000000
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/grid/GridNormalizerService.java
+++ /dev/null
@@ -1,38 +0,0 @@
-/**
- *  Licensed to the Apache Software Foundation (ASF) under one or more
- *  contributor license agreements.  See the NOTICE file distributed with
- *  this work for additional information regarding copyright ownership.
- *  The ASF licenses this file to You under the Apache License, Version 2.0
- *  (the "License"); you may not use this file except in compliance with
- *  the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing, software
- *  distributed under the License is distributed on an "AS IS" BASIS,
- *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *  See the License for the specific language governing permissions and
- *  limitations under the License.
- */
-package org.apache.isis.core.metamodel.services.grid;
-
-import org.apache.isis.applib.annotation.Programmatic;
-import org.apache.isis.applib.layout.common.Grid;
-
-public interface GridNormalizerService<G extends Grid> {
-
-    @Programmatic
-    Class<? extends Grid> gridImplementation();
-
-    @Programmatic
-    String tns();
-
-    @Programmatic
-    String schemaLocation();
-
-    @Programmatic
-    void normalize(G grid, Class<?> domainClass);
-
-
-
-}

http://git-wip-us.apache.org/repos/asf/isis/blob/5c831757/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/grid/GridNormalizerServiceAbstract.java
----------------------------------------------------------------------
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/grid/GridNormalizerServiceAbstract.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/grid/GridNormalizerServiceAbstract.java
index 91d8aba..79d3548 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/grid/GridNormalizerServiceAbstract.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/grid/GridNormalizerServiceAbstract.java
@@ -31,6 +31,7 @@ import org.slf4j.LoggerFactory;
 
 import org.apache.isis.applib.DomainObjectContainer;
 import org.apache.isis.applib.annotation.Programmatic;
+import org.apache.isis.applib.services.layout.GridNormalizerService;
 import org.apache.isis.applib.layout.common.ActionLayoutData;
 import org.apache.isis.applib.layout.common.ActionLayoutDataOwner;
 import org.apache.isis.applib.layout.common.CollectionLayoutData;
@@ -38,10 +39,10 @@ import org.apache.isis.applib.layout.common.FieldSet;
 import org.apache.isis.applib.layout.common.Grid;
 import org.apache.isis.applib.layout.common.MemberRegionOwner;
 import org.apache.isis.applib.layout.common.PropertyLayoutData;
-import org.apache.isis.applib.layout.fixedcols.FCColumn;
-import org.apache.isis.applib.layout.fixedcols.FCColumnOwner;
-import org.apache.isis.applib.layout.fixedcols.FCGrid;
-import org.apache.isis.applib.layout.fixedcols.FCTab;
+import org.apache.isis.core.metamodel.services.grid.fixedcols.applib.FCColumn;
+import org.apache.isis.core.metamodel.services.grid.fixedcols.applib.FCColumnOwner;
+import org.apache.isis.core.metamodel.services.grid.fixedcols.applib.FCGrid;
+import org.apache.isis.core.metamodel.services.grid.fixedcols.applib.FCTab;
 import org.apache.isis.applib.services.i18n.TranslationService;
 import org.apache.isis.applib.services.jaxb.JaxbService;
 import org.apache.isis.core.metamodel.facetapi.Facet;
@@ -119,6 +120,10 @@ public abstract class GridNormalizerServiceAbstract<G extends Grid>
     @Override
     public void normalize(final G grid, final Class<?> domainClass) {
 
+        if(!gridImplementation.isAssignableFrom(grid.getClass())) {
+            // ignore any other grid implementations
+            return;
+        }
         final ObjectSpecification objectSpec = specificationLookup.loadSpecification(domainClass);
 
         final Map<String, OneToOneAssociation> oneToOneAssociationById =
@@ -142,6 +147,19 @@ public abstract class GridNormalizerServiceAbstract<G extends Grid>
         }
     }
 
+    @Programmatic
+    @Override
+    public void complete(final G grid, final Class<?> domainClass) {
+        // TODO: do some different logic here...
+        normalize(grid, domainClass);
+    }
+
+    @Programmatic
+    @Override
+    public void minimal(final G grid, final Class<?> domainClass) {
+        // TODO: do some different logic here...
+        normalize(grid, domainClass);
+    }
     /**
      * Ensures that all object members (properties, collections and actions) are in the metadata.
      */

http://git-wip-us.apache.org/repos/asf/isis/blob/5c831757/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/grid/GridServiceDefault.java
----------------------------------------------------------------------
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/grid/GridServiceDefault.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/grid/GridServiceDefault.java
index 66d4f9e..1d078f5 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/grid/GridServiceDefault.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/grid/GridServiceDefault.java
@@ -29,6 +29,7 @@ import javax.xml.bind.JAXBContext;
 
 import com.google.common.base.Function;
 import com.google.common.base.Joiner;
+import com.google.common.base.Predicate;
 import com.google.common.collect.FluentIterable;
 import com.google.common.collect.Lists;
 import com.google.common.collect.Maps;
@@ -40,6 +41,7 @@ import org.slf4j.LoggerFactory;
 import org.apache.isis.applib.annotation.DomainService;
 import org.apache.isis.applib.annotation.NatureOfService;
 import org.apache.isis.applib.annotation.Programmatic;
+import org.apache.isis.applib.services.layout.GridNormalizerService;
 import org.apache.isis.applib.layout.common.Grid;
 import org.apache.isis.applib.services.jaxb.JaxbService;
 import org.apache.isis.applib.services.layout.GridService;
@@ -146,6 +148,48 @@ public class GridServiceDefault
         }
     }
 
+    @Override
+    @Programmatic
+    public Grid normalize(final Grid grid, final Class<?> domainClass) {
+
+        // if have .layout.json and then add a .layout.xml without restarting, then note that
+        // the changes won't be picked up.  Normalizing would be required
+        // in order to trample over the .layout.json's original facets
+        if(grid.isNormalized()) {
+            return grid;
+        }
+
+        for (GridNormalizerService gridNormalizerService : gridNormalizerServices()) {
+            gridNormalizerService.normalize(grid, domainClass);
+        }
+
+        grid.setNormalized(true);
+
+        return grid;
+    }
+
+    @Override
+    @Programmatic
+    public Grid complete(final Grid grid, final Class<?> domainClass) {
+
+        for (GridNormalizerService gridNormalizerService : gridNormalizerServices()) {
+            gridNormalizerService.complete(grid, domainClass);
+        }
+
+        return grid;
+    }
+
+    @Override
+    @Programmatic
+    public Grid minimal(final Grid grid, final Class<?> domainClass) {
+
+        for (GridNormalizerService gridNormalizerService : gridNormalizerServices()) {
+            gridNormalizerService.minimal(grid, domainClass);
+        }
+
+        return grid;
+    }
+
     private static String resourceContentOf(final Class<?> cls, final String resourceName) throws IOException {
         final URL url = Resources.getResource(cls, resourceName);
         return Resources.toString(url, Charset.defaultCharset());
@@ -158,17 +202,39 @@ public class GridServiceDefault
 
     @Override
     @Programmatic
-    public Grid toGrid(final Object domainObject) {
-        return toGrid(domainObject.getClass());
+    public Grid toGrid(final Object domainObject, final Style style) {
+        return toGrid(domainObject.getClass(), style);
     }
 
     @Override
-    public Grid toGrid(final Class<?> domainClass) {
-        final ObjectSpecification objectSpec = specificationLookup.loadSpecification(domainClass);
-        final GridFacet facet = objectSpec.getFacet(GridFacet.class);
-        return facet != null? facet.getGrid(): null;
+    public Grid toGrid(final Class<?> domainClass, final Style style) {
+        switch (style) {
+        case NORMALIZED:
+            // obtain the already normalized grid, if available.
+            // (if there is none, then the facet will delegate back to this service to do the normalization,
+            // but then will cache it for any subsequent requests).
+            final ObjectSpecification objectSpec = specificationLookup.loadSpecification(domainClass);
+            final GridFacet facet = objectSpec.getFacet(GridFacet.class);
+            return facet != null? facet.getGrid(): null;
+        case COMPLETE:
+            return completeGridFor(domainClass);
+        case MINIMAL:
+            return minimalGridFor(domainClass);
+        default:
+            throw new IllegalArgumentException("unsupported style");
+        }
     }
 
+    protected Grid minimalGridFor(final Class<?> domainClass) {
+        Grid grid = fromXml(domainClass);
+        return grid;
+    }
+
+    protected Grid completeGridFor(final Class<?> domainClass) {
+        Grid grid = fromXml(domainClass);
+        grid = normalize(grid, domainClass);
+        return grid;
+    }
 
     @Override
     public String tnsAndSchemaLocation(final Grid grid) {
@@ -189,6 +255,38 @@ public class GridServiceDefault
 
     ////////////////////////////////////////////////////////
 
+    private List<GridNormalizerService<?>> filteredGridNormalizerServices;
+
+    /**
+     * For all of the available {@link GridNormalizerService}s available, return only the first one for any that
+     * are for the same grid implementation.
+     */
+    @Programmatic
+    public List<GridNormalizerService<?>> gridNormalizerServices() {
+
+        if (filteredGridNormalizerServices == null) {
+            List<GridNormalizerService<?>> services = Lists.newArrayList();
+
+            for (GridNormalizerService gridNormalizerService : this.gridNormalizerServices) {
+                final Class gridImplementation = gridNormalizerService.gridImplementation();
+                final boolean notSeenBefore = FluentIterable.from(services).filter(new Predicate<GridNormalizerService<?>>() {
+                    @Override public boolean apply(@Nullable final GridNormalizerService<?> gridNormalizerService) {
+                        return gridNormalizerService.gridImplementation() == gridImplementation;
+                    }
+                }).isEmpty();
+                if(notSeenBefore) {
+                    services.add(gridNormalizerService);
+                }
+            }
+
+            filteredGridNormalizerServices = services;
+
+        }
+        return filteredGridNormalizerServices;
+    }
+
+    ////////////////////////////////////////////////////////
+
 
     //region > injected dependencies
 

http://git-wip-us.apache.org/repos/asf/isis/blob/5c831757/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/grid/fixedcols/GridNormalizerServiceFC.java
----------------------------------------------------------------------
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/grid/fixedcols/GridNormalizerServiceFC.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/grid/fixedcols/GridNormalizerServiceFC.java
index c617c73..d48fddb 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/grid/fixedcols/GridNormalizerServiceFC.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/grid/fixedcols/GridNormalizerServiceFC.java
@@ -30,10 +30,10 @@ import org.apache.isis.applib.layout.common.CollectionLayoutData;
 import org.apache.isis.applib.layout.common.FieldSet;
 import org.apache.isis.applib.layout.common.Grid;
 import org.apache.isis.applib.layout.common.PropertyLayoutData;
-import org.apache.isis.applib.layout.fixedcols.FCColumn;
-import org.apache.isis.applib.layout.fixedcols.FCGrid;
-import org.apache.isis.applib.layout.fixedcols.FCTab;
-import org.apache.isis.applib.layout.fixedcols.FCTabGroup;
+import org.apache.isis.core.metamodel.services.grid.fixedcols.applib.FCColumn;
+import org.apache.isis.core.metamodel.services.grid.fixedcols.applib.FCGrid;
+import org.apache.isis.core.metamodel.services.grid.fixedcols.applib.FCTab;
+import org.apache.isis.core.metamodel.services.grid.fixedcols.applib.FCTabGroup;
 import org.apache.isis.core.metamodel.facets.object.membergroups.MemberGroupLayoutFacet;
 import org.apache.isis.core.metamodel.services.grid.GridNormalizerServiceAbstract;
 import org.apache.isis.core.metamodel.spec.ObjectSpecification;

http://git-wip-us.apache.org/repos/asf/isis/blob/5c831757/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/grid/fixedcols/applib/FCColumn.java
----------------------------------------------------------------------
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/grid/fixedcols/applib/FCColumn.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/grid/fixedcols/applib/FCColumn.java
new file mode 100644
index 0000000..157428a
--- /dev/null
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/grid/fixedcols/applib/FCColumn.java
@@ -0,0 +1,161 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.apache.isis.core.metamodel.services.grid.fixedcols.applib;
+
+import java.io.Serializable;
+import java.util.List;
+
+import javax.xml.bind.annotation.XmlAttribute;
+import javax.xml.bind.annotation.XmlElementRef;
+import javax.xml.bind.annotation.XmlTransient;
+import javax.xml.bind.annotation.XmlType;
+
+import com.google.common.collect.Lists;
+
+import org.apache.isis.applib.annotation.MemberGroupLayout;
+import org.apache.isis.applib.layout.common.CollectionLayoutData;
+import org.apache.isis.applib.layout.common.CollectionLayoutDataOwner;
+import org.apache.isis.applib.layout.common.FieldSet;
+import org.apache.isis.applib.layout.common.FieldSetOwner;
+import org.apache.isis.applib.layout.common.Owned;
+import org.apache.isis.applib.layout.common.PropertyLayoutData;
+
+/**
+ * The column contains a mixture of {@link FieldSet}s (of {@link PropertyLayoutData properties}) and also
+ * {@link CollectionLayoutData collection}s.
+ *
+ * <p>
+ * A column generally is used within a {@link FCTab}; there can be up to three such (left, middle and right).  It is
+ * also possible for their to be a column far-left on the top-level {@link FCGrid page}, and another far-right.
+ * </p>
+ *
+ */
+@XmlType(
+        propOrder = {
+                "fieldSets"
+                , "collections"
+        }
+)
+public class FCColumn implements Serializable, FieldSetOwner, CollectionLayoutDataOwner, Owned<FCColumnOwner> {
+
+    private static final long serialVersionUID = 1L;
+
+    public FCColumn() {
+    }
+
+    public FCColumn(final int span) {
+        setSpan(span);
+    }
+
+    private int span = 4;
+
+    @XmlAttribute(required = true)
+    public int getSpan() {
+        return span;
+    }
+
+    public void setSpan(final int span) {
+        this.span = span;
+    }
+
+
+
+    private List<FieldSet> fieldSets = Lists.newArrayList();
+
+    // no wrapper
+    @XmlElementRef(type = FieldSet.class, name = "fieldSet", required = false)
+    public List<FieldSet> getFieldSets() {
+        return fieldSets;
+    }
+
+    public void setFieldSets(final List<FieldSet> fieldSets) {
+        this.fieldSets = fieldSets;
+    }
+
+
+    private List<CollectionLayoutData> collections = Lists.newArrayList();
+
+    // no wrapper
+    @XmlElementRef(type = CollectionLayoutData.class, name = "collection", required = false)
+    public List<CollectionLayoutData> getCollections() {
+        return collections;
+    }
+
+    public void setCollections(final List<CollectionLayoutData> collections) {
+        this.collections = collections;
+    }
+
+
+    private FCColumnOwner owner;
+    /**
+     * Owner.
+     *
+     * <p>
+     *     Set programmatically by framework after reading in from XML.
+     * </p>
+     */
+    @XmlTransient
+    public FCColumnOwner getOwner() {
+        return owner;
+    }
+
+    public void setOwner(final FCColumnOwner owner) {
+        this.owner = owner;
+    }
+
+
+
+
+    private Hint hint;
+
+    @XmlTransient
+    public Hint getHint() {
+        return hint;
+    }
+
+    public void setHint(final Hint hint) {
+        this.hint = hint;
+    }
+
+
+
+    public enum Hint {
+        LEFT,
+        MIDDLE,
+        RIGHT;
+
+        public int from(MemberGroupLayout.ColumnSpans columnSpans) {
+            if(this == LEFT) return columnSpans.getLeft();
+            if(this == MIDDLE) return columnSpans.getMiddle();
+            if(this == RIGHT) return columnSpans.getRight();
+            throw new IllegalStateException();
+        }
+
+        public FCColumn from(final FCTab fcTab) {
+            if(fcTab == null) {
+                return null;
+            }
+            if(this == LEFT) return fcTab.getLeft();
+            if(this == MIDDLE) return fcTab.getMiddle();
+            if(this == RIGHT) return fcTab.getRight();
+            throw new IllegalStateException();
+        }
+
+    }
+}

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

http://git-wip-us.apache.org/repos/asf/isis/blob/5c831757/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/grid/fixedcols/applib/FCGrid.java
----------------------------------------------------------------------
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/grid/fixedcols/applib/FCGrid.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/grid/fixedcols/applib/FCGrid.java
new file mode 100644
index 0000000..b7625af
--- /dev/null
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/grid/fixedcols/applib/FCGrid.java
@@ -0,0 +1,204 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.apache.isis.core.metamodel.services.grid.fixedcols.applib;
+
+import java.io.Serializable;
+import java.util.List;
+
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlElementRef;
+import javax.xml.bind.annotation.XmlRootElement;
+import javax.xml.bind.annotation.XmlType;
+
+import org.apache.isis.applib.layout.common.ActionLayoutData;
+import org.apache.isis.applib.layout.common.ActionLayoutDataOwner;
+import org.apache.isis.applib.layout.common.CollectionLayoutData;
+import org.apache.isis.applib.layout.common.DomainObjectLayoutData;
+import org.apache.isis.applib.layout.common.FieldSet;
+import org.apache.isis.applib.layout.common.Grid;
+import org.apache.isis.applib.layout.common.GridAbstract;
+import org.apache.isis.applib.layout.common.PropertyLayoutData;
+import org.apache.isis.applib.services.dto.Dto;
+
+/**
+ * Top-level page, consisting of an optional {@link FCColumn column} on the far left and another (also optional) on the
+ * far right, with the middle consisting of a number of {@link FCTabGroup tabgroup}s, stacked vertically.
+ */
+@XmlRootElement(
+        name = "grid"
+)
+@XmlType(
+        name = "grid"
+        , propOrder = {
+                "actions"
+                , "left"
+                , "tabGroups"
+                , "right"
+        }
+)
+public class FCGrid extends GridAbstract implements Dto, ActionLayoutDataOwner, Serializable, FCColumnOwner, FCTabGroupOwner {
+
+    private static final long serialVersionUID = 1L;
+
+    private List<ActionLayoutData> actions;
+
+    // no wrapper
+    @XmlElementRef(type = ActionLayoutData.class, name="action", required = false)
+    public List<ActionLayoutData> getActions() {
+        return actions;
+    }
+
+    public void setActions(List<ActionLayoutData> actionLayoutDatas) {
+        this.actions = actionLayoutDatas;
+    }
+
+
+
+    private FCColumn left;
+
+    @XmlElement(required = false)
+    public FCColumn getLeft() {
+        return left;
+    }
+
+    public void setLeft(final FCColumn left) {
+        this.left = left;
+        left.setHint(FCColumn.Hint.LEFT);
+    }
+
+
+
+    private List<FCTabGroup> tabGroups;
+
+    // no wrapper
+    @XmlElement(name = "tabGroup", required = true)
+    public List<FCTabGroup> getTabGroups() {
+        return tabGroups;
+    }
+
+    public void setTabGroups(List<FCTabGroup> tabGroups) {
+        this.tabGroups = tabGroups;
+    }
+
+
+
+    private FCColumn right;
+
+    @XmlElement(required = false)
+    public FCColumn getRight() {
+        return right;
+    }
+
+    public void setRight(final FCColumn right) {
+        this.right = right;
+        right.setHint(FCColumn.Hint.RIGHT);
+    }
+
+
+
+    interface Visitor extends Grid.Visitor {
+        void visit(final FCGrid fcPage);
+        void visit(final FCTabGroup fcTabGroup);
+        void visit(final FCTab fcTab);
+        void visit(final FCColumn fcColumn);
+    }
+
+    public static class VisitorAdapter extends Grid.VisitorAdapter implements Visitor {
+        @Override
+        public void visit(final FCGrid fcPage) { }
+        @Override
+        public void visit(final FCTabGroup fcTabGroup) { }
+        @Override
+        public void visit(final FCTab fcTab) { }
+        @Override
+        public void visit(final FCColumn fcColumn) { }
+        @Override
+        public void visit(final PropertyLayoutData propertyLayoutData) {}
+        @Override
+        public void visit(final CollectionLayoutData collectionLayoutData) {}
+        @Override
+        public void visit(final ActionLayoutData actionLayoutData) { }
+    }
+
+
+    /**
+     * Visits all elements of the graph.  The {@link Visitor} implementation
+     * can assume that all "owner" references are populated.
+     */
+    public void visit(final Grid.Visitor visitor) {
+        FCGrid.Visitor fcVisitor = asFcVisitor(visitor);
+        fcVisitor.visit(this);
+        traverseActions(this, visitor);
+        traverseColumn(getLeft(), this, visitor);
+        final List<FCTabGroup> tabGroups = getTabGroups();
+        for (final FCTabGroup fcTabGroup : tabGroups) {
+            fcTabGroup.setOwner(this);
+            fcVisitor.visit(fcTabGroup);
+            final List<FCTab> tabs = fcTabGroup.getTabs();
+            for (final FCTab fcTab : tabs) {
+                fcTab.setOwner(fcTabGroup);
+                fcVisitor.visit(fcTab);
+                traverseColumn(fcTab.getLeft(), fcTab, visitor);
+                traverseColumn(fcTab.getMiddle(), fcTab, visitor);
+                traverseColumn(fcTab.getRight(), fcTab, visitor);
+            }
+        }
+        traverseColumn(getRight(), this, visitor);
+    }
+
+    private void traverseColumn(
+            final FCColumn fcColumn,
+            final FCColumnOwner fcColumnOwner,
+            final Grid.Visitor visitor) {
+        if(fcColumn == null) {
+            return;
+        }
+        FCGrid.Visitor fcVisitor = asFcVisitor(visitor);
+        fcColumn.setOwner(fcColumnOwner);
+        fcVisitor.visit(fcColumn);
+        traverseFieldSets(fcColumn, visitor);
+        traverseCollections(fcColumn, visitor);
+    }
+
+    private static Visitor asFcVisitor(final Grid.Visitor visitor) {
+        return visitor instanceof Visitor? (Visitor) visitor : new VisitorAdapter() {
+            @Override public void visit(final DomainObjectLayoutData domainObjectLayoutData) {
+                visitor.visit(domainObjectLayoutData);
+            }
+
+            @Override public void visit(final ActionLayoutData actionLayoutData) {
+                visitor.visit(actionLayoutData);
+            }
+
+            @Override public void visit(final PropertyLayoutData propertyLayoutData) {
+                visitor.visit(propertyLayoutData);
+            }
+
+            @Override public void visit(final CollectionLayoutData collectionLayoutData) {
+                visitor.visit(collectionLayoutData);
+            }
+
+            @Override public void visit(final FieldSet fieldSet) {
+                visitor.visit(fieldSet);
+            }
+        };
+    }
+
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/5c831757/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/grid/fixedcols/applib/FCTab.java
----------------------------------------------------------------------
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/grid/fixedcols/applib/FCTab.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/grid/fixedcols/applib/FCTab.java
new file mode 100644
index 0000000..3ce6df8
--- /dev/null
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/grid/fixedcols/applib/FCTab.java
@@ -0,0 +1,159 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.apache.isis.core.metamodel.services.grid.fixedcols.applib;
+
+import java.io.Serializable;
+import java.util.List;
+
+import javax.xml.bind.annotation.XmlAttribute;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlTransient;
+import javax.xml.bind.annotation.XmlType;
+
+import com.google.common.base.Predicate;
+import com.google.common.collect.Lists;
+
+import org.apache.isis.applib.annotation.Programmatic;
+import org.apache.isis.applib.layout.common.CollectionLayoutData;
+import org.apache.isis.applib.layout.common.MemberRegion;
+import org.apache.isis.applib.layout.common.Owned;
+import org.apache.isis.applib.layout.common.FieldSet;
+
+@XmlType(
+        name="tab"
+        , propOrder = {
+                "name"
+                , "left"
+                , "middle"
+                , "right"
+        }
+)
+public class FCTab implements FCColumnOwner, Serializable, Owned<FCTabGroup> {
+
+    private static final long serialVersionUID = 1L;
+
+    private String name;
+
+    @XmlAttribute(required = true)
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+
+
+    private FCColumn left = new FCColumn();
+
+    @XmlElement(required = true)
+    public FCColumn getLeft() {
+        return left;
+    }
+
+    public void setLeft(final FCColumn left) {
+        this.left = left;
+        left.setHint(FCColumn.Hint.LEFT);
+    }
+
+
+    private FCColumn middle;
+
+    @XmlElement(required = false)
+    public FCColumn getMiddle() {
+        return middle;
+    }
+
+    public void setMiddle(final FCColumn middle) {
+        this.middle = middle;
+        middle.setHint(FCColumn.Hint.MIDDLE);
+    }
+
+
+    private FCColumn right;
+
+    @XmlElement(required = false)
+    public FCColumn getRight() {
+        return right;
+    }
+
+    public void setRight(final FCColumn right) {
+        this.right = right;
+        right.setHint(FCColumn.Hint.RIGHT);
+    }
+
+
+
+    private FCTabGroup owner;
+    /**
+     * Owner.
+     *
+     * <p>
+     *     Set programmatically by framework after reading in from XML.
+     * </p>
+     */
+    @XmlTransient
+    public FCTabGroup getOwner() {
+        return owner;
+    }
+
+    public void setOwner(final FCTabGroup owner) {
+        this.owner = owner;
+    }
+
+    /**
+     * Aggregates the contents of all collections on this tab.
+     */
+    @Programmatic
+    public List<MemberRegion> getContents() {
+        final List<MemberRegion> contents = Lists.newArrayList();
+        appendContent(contents, getLeft());
+        appendContent(contents, getMiddle());
+        appendContent(contents, getRight());
+        return contents;
+    }
+
+
+
+    private static void appendContent(final List<MemberRegion> contents, final FCColumn FCColumn) {
+        if(FCColumn == null) {
+            return;
+        }
+        final List<FieldSet> fieldSets = FCColumn.getFieldSets();
+        if(fieldSets != null) {
+            contents.addAll(fieldSets);
+        }
+        final List<CollectionLayoutData> collectionLayoutDatas = FCColumn.getCollections();
+        if(collectionLayoutDatas != null) {
+            contents.addAll(collectionLayoutDatas);
+        }
+    }
+
+    public static class Predicates {
+        public static Predicate<FCTab> notEmpty() {
+            return new Predicate<FCTab>() {
+                @Override
+                public boolean apply(final FCTab FCTab) {
+                    return !FCTab.getContents().isEmpty();
+                }
+            };
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/5c831757/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/grid/fixedcols/applib/FCTabGroup.java
----------------------------------------------------------------------
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/grid/fixedcols/applib/FCTabGroup.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/grid/fixedcols/applib/FCTabGroup.java
new file mode 100644
index 0000000..d27eae8
--- /dev/null
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/grid/fixedcols/applib/FCTabGroup.java
@@ -0,0 +1,94 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.apache.isis.core.metamodel.services.grid.fixedcols.applib;
+
+import java.io.Serializable;
+import java.util.List;
+
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlTransient;
+import javax.xml.bind.annotation.XmlType;
+
+import com.google.common.base.Predicate;
+import com.google.common.collect.FluentIterable;
+import com.google.common.collect.Lists;
+
+import org.apache.isis.applib.layout.common.Owned;
+
+@XmlType(
+        propOrder = {
+                "tabs"
+        }
+
+)
+public class FCTabGroup implements FCColumnOwner, Serializable, Owned<FCTabGroupOwner> {
+
+    private static final long serialVersionUID = 1L;
+
+    private List<FCTab> tabs = Lists.newArrayList();
+
+    // no wrapper
+    @XmlElement(name = "tab", required = false)
+    public List<FCTab> getTabs() {
+        return tabs;
+    }
+
+    public void setTabs(List<FCTab> tabs) {
+        this.tabs = tabs;
+    }
+
+
+
+    private FCTabGroupOwner owner;
+
+    /**
+     * Owner.
+     *
+     * <p>
+     *     Set programmatically by framework after reading in from XML.
+     * </p>
+     */
+    @XmlTransient
+    public FCTabGroupOwner getOwner() {
+        return owner;
+    }
+
+    public void setOwner(final FCTabGroupOwner owner) {
+        this.owner = owner;
+    }
+
+
+
+
+
+
+    public static class Predicates {
+        public static Predicate<FCTabGroup> notEmpty() {
+            return new Predicate<FCTabGroup>() {
+                @Override
+                public boolean apply(final FCTabGroup tabGroup) {
+                    return FluentIterable
+                            .from(tabGroup.getTabs())
+                            .anyMatch(FCTab.Predicates.notEmpty());
+                }
+            };
+        }
+    }
+
+}

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

http://git-wip-us.apache.org/repos/asf/isis/blob/5c831757/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/grid/fixedcols/applib/package-info.java
----------------------------------------------------------------------
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/grid/fixedcols/applib/package-info.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/grid/fixedcols/applib/package-info.java
new file mode 100644
index 0000000..3cdd1f1
--- /dev/null
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/grid/fixedcols/applib/package-info.java
@@ -0,0 +1,44 @@
+/*
+ *  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.
+ */
+
+/**
+ * THIS STUFF ALL WORKS, BUT REMOVED FROM PUBLIC APPLIB BECAUSE IT LOOKS
+ * LIKE THE BOOTSTRAP3 GRID WILL BE SUFFICIENT.
+ *
+ * The benefit of keeping this stuff around is that it reinforces where
+ * separation of responsibilities are for grid components (tab etc)
+ * vs common components (fieldset, action, property, collection, domainobject).
+ *
+ * --------------------
+ *
+ * The classes in this package define how to layout the properties, collections and actions of a domain object - the
+ * building blocks - as defined in the <code>members.v1</code> package.
+ *
+ * <p>
+ *     The layout is reasonably flexible, being a half-way house between the annotation/JSON style layouts (pre 1.12.0)
+ *     vs the fully flexible layouts provided by the <code>bootstrap3</code> layouts.  In particular, they allow
+ *     property fieldsets and collections to be grouped into tabs, with collections being laid out in any column.
+ *     However, tab groups only appear in the central area of the page, and may only have a maximum of three columns.
+ * </p>
+ */
+@javax.xml.bind.annotation.XmlSchema(
+        namespace = "http://isis.apache.org/schema/applib/layout/fixedcols",
+        elementFormDefault = javax.xml.bind.annotation.XmlNsForm.QUALIFIED
+)
+package org.apache.isis.core.metamodel.services.grid.fixedcols.applib;
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/isis/blob/5c831757/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/metamodel/MetadataMenu.java
----------------------------------------------------------------------
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/metamodel/MetadataMenu.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/metamodel/MetadataMenu.java
index c017adf..76a436e 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/metamodel/MetadataMenu.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/metamodel/MetadataMenu.java
@@ -108,7 +108,7 @@ public class MetadataMenu implements SpecificationLoaderSpiAware {
             final OutputStreamWriter writer = new OutputStreamWriter(zos);
             for (final ObjectSpecification objectSpec : domainObjectSpecs) {
                 final Class<?> domainClass = objectSpec.getCorrespondingClass();
-                final Grid grid = gridService.toGrid(domainClass);
+                final Grid grid = gridService.toGrid(domainClass, GridService.Style.NORMALIZED);
                 if(grid != null) {
                     zos.putNextEntry(new ZipEntry(zipEntryNameFor(objectSpec)));
                     String xml = jaxbService.toXml(grid);

http://git-wip-us.apache.org/repos/asf/isis/blob/5c831757/core/metamodel/src/main/java/org/apache/isis/core/metamodel/spec/ObjectSpecifications.java
----------------------------------------------------------------------
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/spec/ObjectSpecifications.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/spec/ObjectSpecifications.java
index 686ab7c..a47e9c5 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/spec/ObjectSpecifications.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/spec/ObjectSpecifications.java
@@ -25,7 +25,7 @@ import java.util.Set;
 
 import com.google.common.collect.Lists;
 
-import org.apache.isis.applib.layout.fixedcols.FCColumn;
+import org.apache.isis.core.metamodel.services.grid.fixedcols.applib.FCColumn;
 import org.apache.isis.core.metamodel.facets.object.membergroups.MemberGroupLayoutFacet;