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/04/14 20:20:13 UTC

git commit: ISIS-384: adding totaling of collections as extra view, in Wicket viewer

Updated Branches:
  refs/heads/master 17a40716f -> 905f8d2db


ISIS-384: adding totaling of collections as extra view, in Wicket viewer


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

Branch: refs/heads/master
Commit: 905f8d2db6e02f75d97521f563297fd5a0eed952
Parents: 17a4071
Author: Dan Haywood <da...@apache.org>
Authored: Sun Apr 14 19:09:06 2013 +0100
Committer: Dan Haywood <da...@apache.org>
Committed: Sun Apr 14 19:09:06 2013 +0100

----------------------------------------------------------------------
 .gitignore                                         |    3 +
 .../ComponentFactoryRegistrarDefault.java          |    8 +-
 .../CollectionContentsLinksSelectorPanel.java      |   31 +++-
 .../summary/CollectionContentsAsSummary.css        |   79 ++++++
 .../summary/CollectionContentsAsSummary.html       |   46 ++++
 .../summary/CollectionContentsAsSummary.java       |  190 +++++++++++++++
 .../CollectionContentsAsSummaryFactory.java        |   72 ++++++
 .../summary/icon_summary_off.png                   |  Bin 0 -> 240 bytes
 .../collectioncontents/summary/icon_summary_on.png |  Bin 0 -> 299 bytes
 .../selector/links/LinksSelectorPanelAbstract.java |    8 +-
 .../CollectionContentsLinksSelectorPanelTest.java  |   51 ++++
 .../dom/src/main/java/dom/todo/ToDoItem.java       |   26 ++-
 .../dom/src/main/java/dom/todo/ToDoItems.java      |   30 ++-
 .../main/java/fixture/todo/ToDoItemsFixture.java   |   27 +--
 .../quickstart_wicket_restful_jdo/pom.xml          |   18 +-
 .../viewer-webapp/pom.xml                          |   11 +
 16 files changed, 556 insertions(+), 44 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/isis/blob/905f8d2d/.gitignore
----------------------------------------------------------------------
diff --git a/.gitignore b/.gitignore
index de717da..e826285 100644
--- a/.gitignore
+++ b/.gitignore
@@ -11,6 +11,9 @@ logs/
 .idea
 *.iml
 
+JArchitectOut/
+*.jdproj
+
 component/objectstore/sql/sql-tests-common/hsql-db/
 component/objectstore/sql/sql-tests-common/xml/
 component/objectstore/xml/tmp/objects/

http://git-wip-us.apache.org/repos/asf/isis/blob/905f8d2d/component/viewer/wicket/impl/src/main/java/org/apache/isis/viewer/wicket/viewer/registries/components/ComponentFactoryRegistrarDefault.java
----------------------------------------------------------------------
diff --git a/component/viewer/wicket/impl/src/main/java/org/apache/isis/viewer/wicket/viewer/registries/components/ComponentFactoryRegistrarDefault.java b/component/viewer/wicket/impl/src/main/java/org/apache/isis/viewer/wicket/viewer/registries/components/ComponentFactoryRegistrarDefault.java
index 1de840b..168ff0b 100644
--- a/component/viewer/wicket/impl/src/main/java/org/apache/isis/viewer/wicket/viewer/registries/components/ComponentFactoryRegistrarDefault.java
+++ b/component/viewer/wicket/impl/src/main/java/org/apache/isis/viewer/wicket/viewer/registries/components/ComponentFactoryRegistrarDefault.java
@@ -37,6 +37,7 @@ import org.apache.isis.viewer.wicket.ui.components.collectioncontents.ajaxtable.
 import org.apache.isis.viewer.wicket.ui.components.collectioncontents.icons.CollectionContentsAsIconsPanelFactory;
 import org.apache.isis.viewer.wicket.ui.components.collectioncontents.selector.dropdown.CollectionContentsDropDownSelectorPanelFactory;
 import org.apache.isis.viewer.wicket.ui.components.collectioncontents.selector.links.CollectionContentsLinksSelectorPanelFactory;
+import org.apache.isis.viewer.wicket.ui.components.collectioncontents.summary.CollectionContentsAsSummaryFactory;
 import org.apache.isis.viewer.wicket.ui.components.collectioncontents.unresolved.CollectionContentsAsUnresolvedPanelFactory;
 import org.apache.isis.viewer.wicket.ui.components.empty.EmptyCollectionPanelFactory;
 import org.apache.isis.viewer.wicket.ui.components.entity.collections.EntityCollectionsPanelFactory;
@@ -159,7 +160,6 @@ public class ComponentFactoryRegistrarDefault implements ComponentFactoryRegistr
 
         // top-level
         componentFactories.add(new EntityCombinedPanelFactory());
-//        componentFactories.add(new EntityTabbedPanelFactory());
         
         // lower-level
         componentFactories.add(new EntityIconAndTitlePanelFactory());
@@ -170,9 +170,11 @@ public class ComponentFactoryRegistrarDefault implements ComponentFactoryRegistr
 
     protected void addComponentFactoriesForEntityCollectionContents(final ComponentFactoryList componentFactories) {
         componentFactories.add(new CollectionContentsAsAjaxTablePanelFactory());
-        // componentFactories.add(new CollectionContentsAsSimpleTableFactory());
+        
         // // work-in-progress
-        componentFactories.add(new CollectionContentsAsIconsPanelFactory());
+        // componentFactories.add(new CollectionContentsAsIconsPanelFactory());
+        
+        componentFactories.add(new CollectionContentsAsSummaryFactory());
     }
 
     protected void addComponentFactoriesForEntityCollection(final ComponentFactoryList componentFactories) {

http://git-wip-us.apache.org/repos/asf/isis/blob/905f8d2d/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/collectioncontents/selector/links/CollectionContentsLinksSelectorPanel.java
----------------------------------------------------------------------
diff --git a/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/collectioncontents/selector/links/CollectionContentsLinksSelectorPanel.java b/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/collectioncontents/selector/links/CollectionContentsLinksSelectorPanel.java
index ba9effa..68db3bf 100644
--- a/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/collectioncontents/selector/links/CollectionContentsLinksSelectorPanel.java
+++ b/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/collectioncontents/selector/links/CollectionContentsLinksSelectorPanel.java
@@ -19,8 +19,11 @@
 
 package org.apache.isis.viewer.wicket.ui.components.collectioncontents.selector.links;
 
+import java.util.ArrayList;
 import java.util.List;
 
+import com.google.common.collect.Lists;
+
 import org.apache.wicket.model.IModel;
 
 import org.apache.isis.applib.annotation.Render.Type;
@@ -68,14 +71,40 @@ public class CollectionContentsLinksSelectorPanel extends LinksSelectorPanelAbst
                 }
             }
         }
+        int ajaxTableIdx = findAjaxTable(componentFactories);
+        if(ajaxTableIdx>=0) {
+            return ajaxTableIdx;
+        }
+        return 0;
+    }
+
+    @Override
+    protected List<ComponentFactory> ordered(List<ComponentFactory> componentFactories) {
+        return orderAjaxTableToEnd(componentFactories);
+    }
+
+    static List<ComponentFactory> orderAjaxTableToEnd(List<ComponentFactory> componentFactories) {
+        int ajaxTableIdx = findAjaxTable(componentFactories);
+        if(ajaxTableIdx>=0) {
+            List<ComponentFactory> orderedFactories = Lists.newArrayList(componentFactories);
+            ComponentFactory ajaxTableFactory = orderedFactories.remove(ajaxTableIdx);
+            orderedFactories.add(ajaxTableFactory);
+            return orderedFactories;
+        } else {
+            return componentFactories;
+        }
+    }
+    
+    private static int findAjaxTable(List<ComponentFactory> componentFactories) {
         for(int i=0; i<componentFactories.size(); i++) {
             if(componentFactories.get(i) instanceof CollectionContentsAsAjaxTablePanelFactory) {
                 return i;
             }
         }
-        return 0;
+        return -1;
     }
 
+
     private static boolean hasResolveEagerlyFacet(IModel<?> model) {
         if(!(model instanceof EntityCollectionModel)) {
             return false;

http://git-wip-us.apache.org/repos/asf/isis/blob/905f8d2d/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/collectioncontents/summary/CollectionContentsAsSummary.css
----------------------------------------------------------------------
diff --git a/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/collectioncontents/summary/CollectionContentsAsSummary.css b/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/collectioncontents/summary/CollectionContentsAsSummary.css
new file mode 100644
index 0000000..29328bd
--- /dev/null
+++ b/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/collectioncontents/summary/CollectionContentsAsSummary.css
@@ -0,0 +1,79 @@
+/*
+ *  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.
+ */
+
+.collectionContentsAsSummary {
+	width: 99%
+}
+
+.collectionContentsAsSummary .propertyNameCell {
+	margin-top: 10px;
+	margin-bottom: 10px;
+	padding-top: 20px;
+}
+
+.collectionContentsAsSummary .propertyNameCell,
+.collectionContentsAsSummary .aggregatesCell {
+	vertical-align: middle;
+}
+
+.collectionContentsAsSummary .propertyName {
+    color: #423D37;
+    font-size: 1.6em;
+    margin-bottom: 10px;
+    margin-right: 20px;
+    line-height: 120%;
+}
+
+.collectionContentsAsSummary .aggregateName {
+	
+	font-style: normal;
+	text-align: left;
+	text-transform: uppercase;
+	font-weight: bold !important;
+	font-size: 0.9em;
+	color: #46413B;
+	padding-top: 24px;
+	padding-left: 20px;
+	padding-right: 10px;
+}
+
+.collectionContentsAsSummary .numberValue {
+    text-align: right;
+    margin: 5px 10px 5px 0px;
+    width: 80px;
+}
+
+.collectionContentsAsSummary .chart {
+    margin-left: 30px;
+    display: inline-block;
+    min-width: 720px;
+}
+
+
+a span.ViewLinkItem.summary {
+    background-image:url('icon_summary_on.png');
+    background-position: center center;
+    background-repeat:no-repeat;
+}
+
+span.ViewLinkItem.summary {
+    background-image:url('icon_summary_off.png');
+    background-position: center center;
+    background-repeat:no-repeat;
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/905f8d2d/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/collectioncontents/summary/CollectionContentsAsSummary.html
----------------------------------------------------------------------
diff --git a/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/collectioncontents/summary/CollectionContentsAsSummary.html b/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/collectioncontents/summary/CollectionContentsAsSummary.html
new file mode 100644
index 0000000..c54dd10
--- /dev/null
+++ b/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/collectioncontents/summary/CollectionContentsAsSummary.html
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  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.
+-->
+<html>
+	<body>
+		<wicket:panel>
+			<div class="collectionContentsAsSummary">
+		        <div wicket:id="feedback"></div>
+
+                <table cellspacing="0">
+                    <tr wicket:id="repeatingSummary">
+                        <td class="propertyNameCell">
+                            <span class="propertyName" wicket:id="propertyName">[propertyName]</span>
+                        </td>
+                        <td class="aggregatesCell">
+                            <table cellspacing="0">
+                                <tr>
+                                    <td class="aggregateName">Sum:</td><td><input class="numberValue" type="text" disabled="true" wicket:id="sum"></input></td>
+                                    <td class="aggregateName">Avg:</td><td><input class="numberValue" type="text" disabled="true" wicket:id="avg"></input></td>
+                                    <td class="aggregateName">Min:</td><td><input class="numberValue" type="text" disabled="true" wicket:id="min"></input></td>
+                                    <td class="aggregateName">Max:</td><td><input class="numberValue" type="text" disabled="true" wicket:id="max"></input></td>
+                                </tr>
+                            </table>
+                        </td>
+                    </tr>
+                </table>
+			</div>
+		</wicket:panel>
+	</body>
+</html>

http://git-wip-us.apache.org/repos/asf/isis/blob/905f8d2d/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/collectioncontents/summary/CollectionContentsAsSummary.java
----------------------------------------------------------------------
diff --git a/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/collectioncontents/summary/CollectionContentsAsSummary.java b/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/collectioncontents/summary/CollectionContentsAsSummary.java
new file mode 100644
index 0000000..03d7731
--- /dev/null
+++ b/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/collectioncontents/summary/CollectionContentsAsSummary.java
@@ -0,0 +1,190 @@
+/*
+ *  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.viewer.wicket.ui.components.collectioncontents.summary;
+
+import java.math.BigDecimal;
+import java.math.RoundingMode;
+import java.util.Collections;
+import java.util.List;
+
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Lists;
+
+import org.apache.wicket.Component;
+import org.apache.wicket.markup.html.basic.Label;
+import org.apache.wicket.markup.html.form.TextField;
+import org.apache.wicket.markup.html.list.AbstractItem;
+import org.apache.wicket.markup.html.panel.FeedbackPanel;
+import org.apache.wicket.markup.repeater.RepeatingView;
+import org.apache.wicket.model.Model;
+
+import org.apache.isis.core.metamodel.adapter.ObjectAdapter;
+import org.apache.isis.core.metamodel.spec.ObjectAdapterUtils;
+import org.apache.isis.core.metamodel.spec.ObjectSpecification;
+import org.apache.isis.core.metamodel.spec.feature.ObjectAssociation;
+import org.apache.isis.viewer.wicket.model.models.EntityCollectionModel;
+import org.apache.isis.viewer.wicket.ui.panels.PanelAbstract;
+
+/**
+ * {@link PanelAbstract Panel} that represents a {@link EntityCollectionModel
+ * collection of entity}s rendered using as a table of summary values with a
+ * chart alongside.
+ */
+public class CollectionContentsAsSummary extends PanelAbstract<EntityCollectionModel> {
+
+    private static final String ID_MAX = "max";
+
+    private static final String ID_MIN = "min";
+
+    private static final String ID_AVG = "avg";
+
+    private static final String ID_SUM = "sum";
+
+    private static final String ID_PROPERTY_NAME = "propertyName";
+
+    private static final String ID_REPEATING_SUMMARY = "repeatingSummary";
+
+    private static final long serialVersionUID = 1L;
+
+    private static final String ID_FEEDBACK = "feedback";
+
+    public CollectionContentsAsSummary(final String id, final EntityCollectionModel model) {
+        super(id, model);
+
+        buildGui();
+    }
+
+    private void buildGui() {
+
+        final EntityCollectionModel model = getModel();
+
+        final ObjectSpecification elementSpec = model.getTypeOfSpecification();
+
+        final FeedbackPanel feedback = new FeedbackPanel(ID_FEEDBACK);
+        feedback.setOutputMarkupId(true);
+        addOrReplace(feedback);
+
+        List<ObjectAssociation> numberAssociations = elementSpec.getAssociations(CollectionContentsAsSummaryFactory.OF_TYPE_BIGDECIMAL);
+
+        RepeatingView repeating = new RepeatingView(ID_REPEATING_SUMMARY);
+        addOrReplace(repeating);
+
+        for (ObjectAssociation numberAssociation : numberAssociations) {
+            AbstractItem item = new AbstractItem(repeating.newChildId());
+
+            repeating.add(item);
+
+            String propertyName = numberAssociation.getName();
+            item.add(new Label(ID_PROPERTY_NAME, new Model<String>(propertyName)));
+
+
+            List<ObjectAdapter> adapters = model.getObject();
+            Summary summary = new Summary(adapters, numberAssociation);
+            addItem(item, ID_SUM, summary.getTotal());
+            addItem(item, ID_AVG, summary.getAverage());
+            addItem(item, ID_MIN, summary.getMin());
+            addItem(item, ID_MAX, summary.getMax());
+        }
+    }
+    
+    public static class Summary {
+
+        private BigDecimal sum = BigDecimal.ZERO;
+        private BigDecimal min = null;
+        private BigDecimal max = null;
+        private final List<String> titles = Lists.newArrayList();
+        private final List<BigDecimal> values = Lists.newArrayList();
+        private BigDecimal average;
+
+        public Summary(List<ObjectAdapter> adapters, ObjectAssociation numberAssociation) {
+            int nonNullCount = 0;
+            for (ObjectAdapter objectAdapter : adapters) {
+                titles.add(objectAdapter.titleString(null));
+                final ObjectAdapter valueAdapter = numberAssociation.get(objectAdapter);
+                if (valueAdapter == null) {
+                    values.add(null);
+                    continue;
+                }
+                final Object valueObj = ObjectAdapterUtils.unwrapObject(valueAdapter);
+                if (valueObj == null) {
+                    values.add(null);
+                    continue;
+                }
+                
+                nonNullCount++;
+                BigDecimal value = (BigDecimal) valueObj;
+                sum = sum.add(value);
+                min = min != null && min.compareTo(value) < 0 ? min : value;
+                max = max != null && max.compareTo(value) > 0 ? max : value;
+                values.add(value);
+            }
+            average = nonNullCount != 0 ? sum.divide(BigDecimal.valueOf(nonNullCount), 2, RoundingMode.HALF_UP) : null;
+        }
+        
+        public BigDecimal getTotal() {
+            return sum;
+        }
+        public BigDecimal getAverage() {
+            return average;
+        }
+        public BigDecimal getMax() {
+            return max;
+        }public BigDecimal getMin() {
+            return min;
+        }
+        public List<String> getTitles() {
+            return Collections.unmodifiableList(titles);
+        }
+
+        public List<BigDecimal> getValues() {
+            return Collections.unmodifiableList(values);
+        }
+        public List<Number> getValuesAsNumbers() {
+            return asNumbers(getValues());
+        }
+        
+        private static List<Number> asNumbers(List<BigDecimal> values) {
+            return Lists.newArrayList(Iterables.transform(values, BIGDECIMAL_TO_NUMBER));
+        }
+
+        private static final com.google.common.base.Function<BigDecimal, Number> BIGDECIMAL_TO_NUMBER = new com.google.common.base.Function<BigDecimal, Number>(){
+            public Number apply(BigDecimal value) {
+                return value;
+            }
+        };
+
+        
+    }
+
+    private void addItem(AbstractItem item, String id, BigDecimal amt) {
+        TextField<String> textField = new TextField<String>(id, new Model<String>(format(amt)));
+        item.add(textField);
+    }
+
+    private String format(BigDecimal amt) {
+        return amt != null ? amt.setScale(2, RoundingMode.HALF_UP).toPlainString() : "";
+    }
+
+    @Override
+    protected void onModelChanged() {
+        buildGui();
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/905f8d2d/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/collectioncontents/summary/CollectionContentsAsSummaryFactory.java
----------------------------------------------------------------------
diff --git a/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/collectioncontents/summary/CollectionContentsAsSummaryFactory.java b/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/collectioncontents/summary/CollectionContentsAsSummaryFactory.java
new file mode 100644
index 0000000..1af1ff3
--- /dev/null
+++ b/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/collectioncontents/summary/CollectionContentsAsSummaryFactory.java
@@ -0,0 +1,72 @@
+/*
+ *  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.viewer.wicket.ui.components.collectioncontents.summary;
+
+import java.util.List;
+
+import org.apache.wicket.Component;
+import org.apache.wicket.model.IModel;
+
+import org.apache.isis.applib.filter.Filter;
+import org.apache.isis.core.metamodel.spec.ObjectSpecification;
+import org.apache.isis.core.metamodel.spec.feature.ObjectAssociation;
+import org.apache.isis.core.progmodel.facets.value.bigdecimal.BigDecimalValueFacet;
+import org.apache.isis.viewer.wicket.model.models.EntityCollectionModel;
+import org.apache.isis.viewer.wicket.ui.ComponentFactory;
+import org.apache.isis.viewer.wicket.ui.ComponentFactoryAbstract;
+import org.apache.isis.viewer.wicket.ui.ComponentType;
+
+/**
+ * {@link ComponentFactory} for {@link CollectionContentsAsSummary}.
+ */
+public class CollectionContentsAsSummaryFactory extends ComponentFactoryAbstract {
+
+    private static final long serialVersionUID = 1L;
+
+    private static final String NAME = "summary";
+
+    final static Filter<ObjectAssociation> OF_TYPE_BIGDECIMAL = new Filter<ObjectAssociation>(){
+
+        public boolean accept(final ObjectAssociation objectAssoc) {
+            ObjectSpecification objectSpec = objectAssoc.getSpecification();
+            return objectSpec.containsDoOpFacet(BigDecimalValueFacet.class);
+        }};
+
+    public CollectionContentsAsSummaryFactory() {
+        super(ComponentType.COLLECTION_CONTENTS, NAME);
+    }
+
+    @Override
+    public ApplicationAdvice appliesTo(final IModel<?> model) {
+        if(!(model instanceof EntityCollectionModel)) {
+            return ApplicationAdvice.DOES_NOT_APPLY;
+        }
+        final EntityCollectionModel entityCollectionModel = (EntityCollectionModel) model;
+        final ObjectSpecification elementSpec = entityCollectionModel.getTypeOfSpecification();
+        List<ObjectAssociation> associations = elementSpec.getAssociations(OF_TYPE_BIGDECIMAL);
+        return appliesIf(!associations.isEmpty());
+    }
+
+    @Override
+    public Component createComponent(final String id, final IModel<?> model) {
+        final EntityCollectionModel collectionModel = (EntityCollectionModel) model;
+        return new CollectionContentsAsSummary(id, collectionModel);
+    }
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/905f8d2d/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/collectioncontents/summary/icon_summary_off.png
----------------------------------------------------------------------
diff --git a/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/collectioncontents/summary/icon_summary_off.png b/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/collectioncontents/summary/icon_summary_off.png
new file mode 100644
index 0000000..da74793
Binary files /dev/null and b/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/collectioncontents/summary/icon_summary_off.png differ

http://git-wip-us.apache.org/repos/asf/isis/blob/905f8d2d/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/collectioncontents/summary/icon_summary_on.png
----------------------------------------------------------------------
diff --git a/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/collectioncontents/summary/icon_summary_on.png b/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/collectioncontents/summary/icon_summary_on.png
new file mode 100644
index 0000000..d653bea
Binary files /dev/null and b/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/collectioncontents/summary/icon_summary_on.png differ

http://git-wip-us.apache.org/repos/asf/isis/blob/905f8d2d/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/selector/links/LinksSelectorPanelAbstract.java
----------------------------------------------------------------------
diff --git a/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/selector/links/LinksSelectorPanelAbstract.java b/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/selector/links/LinksSelectorPanelAbstract.java
index 81986ee..050b9be 100644
--- a/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/selector/links/LinksSelectorPanelAbstract.java
+++ b/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/selector/links/LinksSelectorPanelAbstract.java
@@ -19,6 +19,7 @@
 
 package org.apache.isis.viewer.wicket.ui.selector.links;
 
+import java.util.ArrayList;
 import java.util.List;
 
 import com.google.common.base.Predicate;
@@ -238,12 +239,17 @@ public abstract class LinksSelectorPanelAbstract<T extends IModel<?>> extends Pa
 
     private List<ComponentFactory> findOtherComponentFactories(final T model, final ComponentFactory ignoreFactory) {
         final List<ComponentFactory> componentFactories = getComponentFactoryRegistry().findComponentFactories(componentType, model);
-        return Lists.newArrayList(Collections2.filter(componentFactories, new Predicate<ComponentFactory>() {
+        ArrayList<ComponentFactory> otherFactories = Lists.newArrayList(Collections2.filter(componentFactories, new Predicate<ComponentFactory>() {
             @Override
             public boolean apply(final ComponentFactory input) {
                 return input != ignoreFactory;
             }
         }));
+        return ordered(otherFactories);
+    }
+
+    protected List<ComponentFactory> ordered(List<ComponentFactory> otherFactories) {
+        return otherFactories;
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/isis/blob/905f8d2d/component/viewer/wicket/ui/src/test/java/org/apache/isis/viewer/wicket/ui/components/collectioncontents/selector/links/CollectionContentsLinksSelectorPanelTest.java
----------------------------------------------------------------------
diff --git a/component/viewer/wicket/ui/src/test/java/org/apache/isis/viewer/wicket/ui/components/collectioncontents/selector/links/CollectionContentsLinksSelectorPanelTest.java b/component/viewer/wicket/ui/src/test/java/org/apache/isis/viewer/wicket/ui/components/collectioncontents/selector/links/CollectionContentsLinksSelectorPanelTest.java
new file mode 100644
index 0000000..1096d2e
--- /dev/null
+++ b/component/viewer/wicket/ui/src/test/java/org/apache/isis/viewer/wicket/ui/components/collectioncontents/selector/links/CollectionContentsLinksSelectorPanelTest.java
@@ -0,0 +1,51 @@
+package org.apache.isis.viewer.wicket.ui.components.collectioncontents.selector.links;
+
+import static org.hamcrest.CoreMatchers.*;
+import static org.junit.Assert.assertThat;
+
+import java.util.Arrays;
+import java.util.List;
+
+import org.jmock.auto.Mock;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+
+import org.apache.isis.core.unittestsupport.jmocking.JUnitRuleMockery2;
+import org.apache.isis.core.unittestsupport.jmocking.JUnitRuleMockery2.Mode;
+import org.apache.isis.viewer.wicket.ui.ComponentFactory;
+import org.apache.isis.viewer.wicket.ui.components.collectioncontents.ajaxtable.CollectionContentsAsAjaxTablePanelFactory;
+
+public class CollectionContentsLinksSelectorPanelTest {
+
+    @Rule
+    public JUnitRuleMockery2 context = JUnitRuleMockery2.createFor(Mode.INTERFACES_AND_CLASSES);
+
+    @Mock
+    private ComponentFactory one;
+    
+    @Mock
+    private ComponentFactory two;
+
+    private ComponentFactory ajaxTableComponentFactory;
+    
+    @Before
+    public void setUp() throws Exception {
+        ajaxTableComponentFactory = new CollectionContentsAsAjaxTablePanelFactory();
+    }
+    
+    @Test
+    public void testOrderAjaxTableToEnd() {
+        
+        List<ComponentFactory> componentFactories = 
+                Arrays.<ComponentFactory>asList(
+                        one,
+                        ajaxTableComponentFactory, 
+                        two);
+        List<ComponentFactory> orderAjaxTableToEnd = CollectionContentsLinksSelectorPanel.orderAjaxTableToEnd(componentFactories);
+        assertThat(orderAjaxTableToEnd.get(0), is(one));
+        assertThat(orderAjaxTableToEnd.get(1), is(two));
+        assertThat(orderAjaxTableToEnd.get(2), is(ajaxTableComponentFactory));
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/905f8d2d/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 dcb3d6d..acc0382 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
@@ -18,11 +18,13 @@
  */
 package dom.todo;
 
+import java.math.BigDecimal;
 import java.util.List;
 import java.util.SortedSet;
 import java.util.TreeSet;
 
 import javax.jdo.JDOHelper;
+import javax.jdo.annotations.Column;
 import javax.jdo.annotations.IdentityType;
 import javax.jdo.annotations.VersionStrategy;
 import javax.jdo.spi.PersistenceCapable;
@@ -189,6 +191,23 @@ public class ToDoItem implements Comparable<ToDoItem> /*, Locatable*/ { // GMAP3
         this.complete = complete;
     }
 
+    
+    // {{ Cost (property)
+    private BigDecimal cost;
+
+    @Column(scale = 4)
+    @Optional
+    @MemberOrder(sequence = "4.1")
+    public BigDecimal getCost() {
+        return cost;
+    }
+
+    public void setCost(final BigDecimal cost) {
+        this.cost = cost;
+    }
+    // }}
+
+
     // {{ Notes (property)
     private String notes;
 
@@ -348,8 +367,11 @@ public class ToDoItem implements Comparable<ToDoItem> /*, Locatable*/ { // GMAP3
             ToDoItem.Category category, 
             @Named("Due by") 
             @Optional
-            LocalDate dueBy) {
-        return toDoItems.newToDo(description, category, dueBy);
+            LocalDate dueBy,
+            @Named("Cost") 
+            @Optional
+            BigDecimal cost) {
+        return toDoItems.newToDo(description, category, dueBy, cost);
     }
     public String default0Duplicate() {
         return getDescription() + " - Copy";

http://git-wip-us.apache.org/repos/asf/isis/blob/905f8d2d/example/application/quickstart_wicket_restful_jdo/dom/src/main/java/dom/todo/ToDoItems.java
----------------------------------------------------------------------
diff --git a/example/application/quickstart_wicket_restful_jdo/dom/src/main/java/dom/todo/ToDoItems.java b/example/application/quickstart_wicket_restful_jdo/dom/src/main/java/dom/todo/ToDoItems.java
index dd8879f..9d03ffe 100644
--- a/example/application/quickstart_wicket_restful_jdo/dom/src/main/java/dom/todo/ToDoItems.java
+++ b/example/application/quickstart_wicket_restful_jdo/dom/src/main/java/dom/todo/ToDoItems.java
@@ -18,9 +18,16 @@
  */
 package dom.todo;
 
+import java.math.BigDecimal;
 import java.util.Collections;
 import java.util.List;
 
+import com.google.common.base.Objects;
+
+import dom.todo.ToDoItem.Category;
+
+import org.joda.time.LocalDate;
+
 import org.apache.isis.applib.AbstractFactoryAndRepository;
 import org.apache.isis.applib.annotation.ActionSemantics;
 import org.apache.isis.applib.annotation.ActionSemantics.Of;
@@ -28,15 +35,11 @@ import org.apache.isis.applib.annotation.Hidden;
 import org.apache.isis.applib.annotation.MemberOrder;
 import org.apache.isis.applib.annotation.Named;
 import org.apache.isis.applib.annotation.NotInServiceMenu;
+import org.apache.isis.applib.annotation.Optional;
 import org.apache.isis.applib.annotation.Programmatic;
 import org.apache.isis.applib.annotation.RegEx;
 import org.apache.isis.applib.clock.Clock;
 import org.apache.isis.applib.filter.Filter;
-import org.joda.time.LocalDate;
-
-import com.google.common.base.Objects;
-
-import dom.todo.ToDoItem.Category;
 
 @Named("ToDos")
 public class ToDoItems extends AbstractFactoryAndRepository {
@@ -101,9 +104,12 @@ public class ToDoItems extends AbstractFactoryAndRepository {
             @RegEx(validation = "\\w[@&:\\-\\,\\.\\+ \\w]*") // words, spaces and selected punctuation
             @Named("Description") String description, 
             @Named("Category") Category category,
-            @Named("Due by") LocalDate dueBy) {
+            @Optional
+            @Named("Due by") LocalDate dueBy,
+            @Optional
+            @Named("Cost") BigDecimal cost) {
         final String ownedBy = currentUserName();
-        return newToDo(description, category, ownedBy, dueBy);
+        return newToDo(description, category, ownedBy, dueBy, cost);
     }
     public LocalDate default2NewToDo() {
         return new LocalDate(Clock.getTime()).plusDays(14);
@@ -136,15 +142,17 @@ public class ToDoItems extends AbstractFactoryAndRepository {
     // {{ newToDo  (hidden)
     @Hidden // for use by fixtures
     public ToDoItem newToDo(
-            String description, 
-            Category category, 
-            String userName,
-            LocalDate dueBy) {
+            final String description, 
+            final Category category, 
+            final String userName,
+            final LocalDate dueBy, 
+            final BigDecimal cost) {
         final ToDoItem toDoItem = newTransientInstance(ToDoItem.class);
         toDoItem.setDescription(description);
         toDoItem.setCategory(category);
         toDoItem.setOwnedBy(userName);
         toDoItem.setDueBy(dueBy);
+        toDoItem.setCost(cost);
 
         // 
         // GMAP3: uncomment to use https://github.com/danhaywood/isis-wicket-gmap3        

http://git-wip-us.apache.org/repos/asf/isis/blob/905f8d2d/example/application/quickstart_wicket_restful_jdo/fixture/src/main/java/fixture/todo/ToDoItemsFixture.java
----------------------------------------------------------------------
diff --git a/example/application/quickstart_wicket_restful_jdo/fixture/src/main/java/fixture/todo/ToDoItemsFixture.java b/example/application/quickstart_wicket_restful_jdo/fixture/src/main/java/fixture/todo/ToDoItemsFixture.java
index 8217ec2..aa3c6bb 100644
--- a/example/application/quickstart_wicket_restful_jdo/fixture/src/main/java/fixture/todo/ToDoItemsFixture.java
+++ b/example/application/quickstart_wicket_restful_jdo/fixture/src/main/java/fixture/todo/ToDoItemsFixture.java
@@ -19,6 +19,7 @@
 
 package fixture.todo;
 
+import java.math.BigDecimal;
 import java.util.List;
 
 import org.apache.isis.applib.clock.Clock;
@@ -37,12 +38,8 @@ public class ToDoItemsFixture extends AbstractFixture {
 
         removeAllToDosForCurrentUser();
 
-        createToDoItemForCurrentUser("Buy milk", Category.Domestic, daysFromToday(0));
-        createToDoItemForCurrentUser("Buy stamps", Category.Domestic, daysFromToday(0));
-        createToDoItemForCurrentUser("Pick up laundry", Category.Other, daysFromToday(6));
-        createToDoItemForCurrentUser("Write blog post", Category.Professional, null);
-        createToDoItemForCurrentUser("Organize brown bag", Category.Professional, daysFromToday(14));
-
+        installFor(getContainer().getUser().getName());
+        
         getContainer().flush();
     }
 
@@ -50,11 +47,11 @@ public class ToDoItemsFixture extends AbstractFixture {
 
         removeAllToDosFor(user);
 
-        createToDoItemForUser("Buy milk", Category.Domestic, user, daysFromToday(0));
-        createToDoItemForUser("Buy stamps", Category.Domestic, user, daysFromToday(0));
-        createToDoItemForUser("Pick up laundry", Category.Other, user, daysFromToday(6));
-        createToDoItemForUser("Write blog post", Category.Professional, user, null);
-        createToDoItemForUser("Organize brown bag", Category.Professional, user, daysFromToday(14));
+        createToDoItemForUser("Buy milk", Category.Domestic, user, daysFromToday(0), new BigDecimal("1.50"));
+        createToDoItemForUser("Buy stamps", Category.Domestic, user, daysFromToday(0), new BigDecimal("10.00"));
+        createToDoItemForUser("Pick up laundry", Category.Other, user, daysFromToday(6), new BigDecimal("7.50"));
+        createToDoItemForUser("Write blog post", Category.Professional, user, null, null);
+        createToDoItemForUser("Organize brown bag", Category.Professional, user, daysFromToday(14), null);
 
         getContainer().flush();
     }
@@ -75,12 +72,8 @@ public class ToDoItemsFixture extends AbstractFixture {
         }
     }
 
-    private ToDoItem createToDoItemForCurrentUser(final String description, final Category category, final LocalDate dueBy) {
-        return toDoItems.newToDo(description, category, dueBy);
-    }
-
-    private ToDoItem createToDoItemForUser(final String description, final Category category, String user, final LocalDate dueBy) {
-        return toDoItems.newToDo(description, category, user, dueBy);
+    private ToDoItem createToDoItemForUser(final String description, final Category category, String user, final LocalDate dueBy, final BigDecimal cost) {
+        return toDoItems.newToDo(description, category, user, dueBy, cost);
     }
 
     private static LocalDate daysFromToday(final int i) {

http://git-wip-us.apache.org/repos/asf/isis/blob/905f8d2d/example/application/quickstart_wicket_restful_jdo/pom.xml
----------------------------------------------------------------------
diff --git a/example/application/quickstart_wicket_restful_jdo/pom.xml b/example/application/quickstart_wicket_restful_jdo/pom.xml
index 3bd16ff..229b2d1 100644
--- a/example/application/quickstart_wicket_restful_jdo/pom.xml
+++ b/example/application/quickstart_wicket_restful_jdo/pom.xml
@@ -332,17 +332,17 @@
                 <scope>import</scope>
             </dependency>
             -->
-            
-            <!-- 
-            ActiveMQ/RA Publishing Service:
-            uncomment to use https://github.com/danhaywood/isis-publishingservice-activemq-ra
+
+            <!--
+            WICKEDCHARTS: uncomment to use https://github.com/danhaywood/isis-wicket-wickedcharts
             <dependency>
-            <groupId>com.danhaywood.isis.publishingservice</groupId>
-                <artifactId>danhaywood-isis-publishingservice-activemq-ra</artifactId>
-                <version>0.0.1-SNAPSHOT</version>
+                <groupId>com.danhaywood.isis.wicket.ui.components</groupId>
+                <artifactId>danhaywood-isis-wicket-wickedcharts</artifactId>
+                <version>1.0.0-SNAPSHOT</version>
+                <type>pom</type>
+                <scope>import</scope>
             </dependency>
-             -->
-            
+            -->
 
         </dependencies>
     </dependencyManagement>

http://git-wip-us.apache.org/repos/asf/isis/blob/905f8d2d/example/application/quickstart_wicket_restful_jdo/viewer-webapp/pom.xml
----------------------------------------------------------------------
diff --git a/example/application/quickstart_wicket_restful_jdo/viewer-webapp/pom.xml b/example/application/quickstart_wicket_restful_jdo/viewer-webapp/pom.xml
index 66ee625..1e8cf25 100644
--- a/example/application/quickstart_wicket_restful_jdo/viewer-webapp/pom.xml
+++ b/example/application/quickstart_wicket_restful_jdo/viewer-webapp/pom.xml
@@ -286,6 +286,17 @@
             <artifactId>danhaywood-isis-wicket-gmap3-ui</artifactId>
         </dependency>
          -->
+        <!-- 
+        WICKEDCHARTS: uncomment to use https://github.com/danhaywood/isis-wicket-wickedcharts
+        <dependency>
+            <groupId>com.danhaywood.isis.wicket.ui.components</groupId>
+            <artifactId>danhaywood-isis-wicket-wickedcharts-scalarchart</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.danhaywood.isis.wicket.ui.components</groupId>
+            <artifactId>danhaywood-isis-wicket-wickedcharts-summarycharts</artifactId>
+        </dependency>
+         -->
          
     </dependencies>