Table row subtotals

I have a table with categories and subcategories. The subcategories have values. I want either the category row to show totals or to have an extra row below the last subcategory with totals. The data is in a HierarchicalContainer and I am using a TreeTable. How might I go about creating row subtotals?

                                                       Amount

category 1
-----subcategory 1 $10
-----subcategory 2 $20
-----subcategory 3 $30
category 1 totals $60

or

                                                       Amount

category 1 $60
-----subcategory 1 $10
-----subcategory 2 $20
-----subcategory 3 $30

Sounds like your doing about something similar as what have been done in one of the TreeTable demo’s, found at
http://demo.vaadin.com/treetable/WorkLog
.

The sources for that demo can be found
here
and the rest of the relevant data at the
directory
.

Thanks. That worked well to get me started. I reworked the TreeTable example to work more broadly with Item instances (not Beans) of any type. I am attaching the code below in case anyone finds it useful.

One issue I’ve run into with Vaadin is getting things to work with data types created at run-time, e.g. database results not handled by SQLContainer because they come from a stored procedure. I tried using dynamic Java Beans like DynaBean in the Apache BeanUtils library but BeanItem and BeanItemContainer don’t seem to work with these.

The Bean type in the TreeTable example was a static, hard-coded one.

A dynamic type I have had success with is the Vaadin Item interface and specifically PropertysetItem and the Item implementation in some containers like IndexedContainer.

The below should work for any data converted into the Item format. There are 3 classes: WorkLogApp, ReportTable, and GroupedContainer.

import java.util.Date;

import com.vaadin.Application;
import com.vaadin.data.Item;
import com.vaadin.data.util.ObjectProperty;
import com.vaadin.data.util.PropertysetItem;
import com.vaadin.ui.Window;

/**
 * An example application built entirely around TreeTable component. It is not supposed to be an
 * example of good quality Vaadin application, but to demonstrate various features of TreeTable (and
 * its super class Table).
 * <p>
 * TODO document various features and usage patterns
 */
public class WorkLogApp extends Application {

    private static final long serialVersionUID = 4179155951210885948L;
    final GroupedContainer container = createTestData();

    @Override
    public void init() {
        setTheme("runo");
        final Window window = new Window("WorkLogApp");
        window.getContent().setSizeFull();
        setMainWindow(window);

        final ReportTable table =
                new ReportTable(
                        "Edit names and hours (of leafs), add/delete via context menu, modify hierarchy with drag and drop",
                        container, "name");
        window.addComponent(table);
    }

    public static GroupedContainer createTestData() {
        final GroupedContainer gc = new GroupedContainer("name");
        final Item root = createNewItem("All Projects", 0, new Date());
        gc.addGroupItem(root);
        Item example, example2, example3, example4;
        example = createNewItem("Year 2010", 0, new Date());
        example2 = createNewItem("Customer project foo", 0, new Date());
        example3 = createNewItem("Planning", 5, new Date());
        gc.addGroupItem(example);
        gc.addGroupItem(example2);
        gc.addDataItem(example3);
        gc.setParent(example, root);
        gc.setParent(example2, example);
        gc.setParent(example3, example2);

        example2 = createNewItem("Customer project bar", 0, new Date());
        example4 = createNewItem("Planning", 2, new Date());
        gc.addGroupItem(example2);
        gc.addDataItem(example4);
        gc.setParent(example2, example);
        gc.setParent(example4, example2);

        example3 = createNewItem("Prototype", 6, new Date());
        gc.addDataItem(example3);
        gc.setParent(example3, example2);

        example3 = createNewItem("Implementation", 5, new Date());
        gc.addDataItem(example3);
        gc.setParent(example3, example2);
        return gc;
    }

    public static Item createNewItem(final String name, final Integer hoursDone,
            final Date lastModified) {
        final Item it = new PropertysetItem();
        it.addItemProperty("name", new ObjectProperty<String>(name, String.class));
        it.addItemProperty("hoursDone", new ObjectProperty<Integer>(hoursDone, Integer.class));
        it.addItemProperty("lastModified", new ObjectProperty<Date>(lastModified, Date.class));
        return it;
    }

}
import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Locale;
import java.util.Map;
import java.util.Set;

import com.vaadin.addon.treetable.TreeTable;
import com.vaadin.data.Container;
import com.vaadin.data.Item;
import com.vaadin.data.Property;
import com.vaadin.data.util.ObjectProperty;
import com.vaadin.data.util.PropertysetItem;
import com.vaadin.event.Action;
import com.vaadin.event.ItemClickEvent;
import com.vaadin.event.ItemClickEvent.ItemClickListener;
import com.vaadin.event.dd.DragAndDropEvent;
import com.vaadin.event.dd.DropHandler;
import com.vaadin.event.dd.acceptcriteria.AcceptCriterion;
import com.vaadin.terminal.ThemeResource;
import com.vaadin.ui.Component;
import com.vaadin.ui.Field;
import com.vaadin.ui.Table;
import com.vaadin.ui.TableFieldFactory;
import com.vaadin.ui.TextField;

public class ReportTable extends TreeTable {

    private static final long serialVersionUID = 8209136268468706011L;

    public ReportTable(final String caption, final GroupedContainer gc,
            final String hierarchyPropertyId) {
        super(caption, gc);
        this.setSizeFull();
        this.setSelectable(true);
        this.setNullSelectionAllowed(false);
        this.setColumnCollapsingAllowed(true);
        this.setColumnCollapsingAllowed(true);
        this.setHierarchyColumn(hierarchyPropertyId);
        this.setEditable(true);
        final ArrayList<?> props = new ArrayList<Object>(this.getContainerPropertyIds());
        this.setVisibleColumns(props.toArray());
        this.setTableFieldFactory(new ReportTableFieldFactory());
        this.addListener(new ReportTableItemClickListener());
        this.addActionHandler(new ReportTableActionHandler());
        this.setDragMode(TableDragMode.ROW);
        this.setDropHandler(new ReportTableDropHandler());
        this.setSortContainerPropertyId(this.getHierarchyColumnId());
    }

    @Override
    public GroupedContainer getContainerDataSource() {
        return (GroupedContainer) super.getContainerDataSource();
    }

    @Override
    protected String formatPropertyValue(final Object rowId, final Object colId,
            final Property property) {
        // Format by property type
        if (property.getType() == Double.class) {
            final DecimalFormat df =
                    new DecimalFormat("###,##0.00", new DecimalFormatSymbols(Locale.getDefault()));
            return df.format(property.getValue());
        } else if (property.getType() == Date.class) {
            final SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd");
            return df.format((Date) property.getValue());
        }
        return super.formatPropertyValue(rowId, colId, property);
    }

    private class ReportTableFieldFactory implements TableFieldFactory {
        private static final long serialVersionUID = 7734693608534017469L;

        public Field createField(final Container container2, final Object itemId,
                final Object propertyId, final Component uiContext) {

            if (ReportTable.this.getHierarchyColumnId().equals(propertyId)) {
                return getNameField((Item) itemId);
            }
            final TextField f = new TextField();
            f.setImmediate(true);
            f.setWidth("100%");
            f.addListener(new Property.ValueChangeListener() {
                private static final long serialVersionUID = 4890024453969446944L;
                public void valueChange(final com.vaadin.data.Property.ValueChangeEvent event) {
                    // TODO Auto-generated method stub

                }
            });
            f.setReadOnly(true);
            return f;
        }
    }

    private class ReportTableItemClickListener implements ItemClickListener {
        private static final long serialVersionUID = 2751691654539959784L;

        public void itemClick(final ItemClickEvent event) {
            final Object propertyId = event.getPropertyId();
            final Object itemId = event.getItemId();
            final Object value = ReportTable.this.getValue();
            if (itemId != value) {
                ReportTable.this.setValue(itemId);
            } else if (ReportTable.this.getHierarchyColumnId().equals(propertyId)) {
                final Field nameField = getNameField((Item) event.getItemId());
                nameField.setReadOnly(false);
                nameField.focus();
            }
            for (final Item f : itemToNameField.keySet()) {
                if (itemId != f) {
                    final NameField nameField = itemToNameField.get(f);
                    nameField.setReadOnly(true);
                }
            }
        }
    }

    private class ReportTableActionHandler implements Action.Handler {
        private static final long serialVersionUID = -8164838688856746384L;
        // Damn it would be cool if these could be shortcut actions too
        private final Action DELETE = new Action("Delete", new ThemeResource(
                "../runo/icons/16/document-delete.png"));
        private final Action ADD = new Action("Add row", new ThemeResource(
                "../runo/icons/16/document-add.png"));
        private final Action ADD_GROUP = new Action("Add group", new ThemeResource(
                "../runo/icons/16/folder-add.png"));

        private Action[] actions = new Action[]
{ADD, ADD_GROUP, DELETE};

        @SuppressWarnings({"unchecked", "rawtypes"})
        public void handleAction(final Action action, final Object sender, Object target) {
            Item newItem = null;
            if (action == DELETE) {
                deleteRow((Item) target);
            } else {
                if ((action == ADD) || (action == ADD_GROUP)) {
                    newItem = new PropertysetItem();
                    for (final Object id : ReportTable.this.getContainerDataSource()
                            .getContainerPropertyIds()) {
                        final Class<?> type = ReportTable.this.getContainerDataSource().getType(id);
                        if (ReportTable.this.getHierarchyColumnId().equals(id)) {
                            newItem.addItemProperty(id, new ObjectProperty("-- new item --", type));
                        } else {
                            newItem.addItemProperty(id, getEmptyPropertyOfType(type));
                        }
                    }
                    if (!ReportTable.this.getContainerDataSource().areChildrenAllowed(target)) {
                        /*
                         * If add happens on a DataItem add to its parent.
                         */
                        target = ReportTable.this.getContainerDataSource().getParent(target);
                    }
                    if (null != target) {
                        if (action == ADD) {
                            ReportTable.this.getContainerDataSource().addDataItem(newItem);
                        } else {
                            ReportTable.this.getContainerDataSource().addGroupItem(newItem);
                        }
                        ReportTable.this.getContainerDataSource().setParent(newItem, target);
                        // ensure visible;
                        ReportTable.this.setCollapsed(target, false);
                        ReportTable.this.select(newItem);
                        final NameField nameField = getNameField(newItem);
                        nameField.setReadOnly(false);
                        nameField.selectAll();
                    }
                }
            }
        }

        private void deleteRow(final Item target) {
            // it's composite if it can have children
            if (ReportTable.this.getContainerDataSource().areChildrenAllowed(target)) {
                final Item[] array =
                        ReportTable.this
                                .getContainerDataSource()
                                .getChildren(target)
                                .toArray(
                                        new Item[ReportTable.this.getContainerDataSource()
                                                .getChildren(target).size()]);
                for (int i = 0; i < array.length; i++) {
                    deleteRow(array[i]
);
                }
            }
            ReportTable.this.getContainerDataSource().removeItem(target);
        }

        public Action[] getActions(final Object target, final Object sender) {
            return actions;
        }

        public Property getEmptyPropertyOfType(final Class<?> type) {
            Property returnProp;
            if (Integer.class.equals(type)) {
                returnProp = new ObjectProperty<Integer>(new Integer(0), Integer.class);
            } else if (Long.class.equals(type)) {
                returnProp = new ObjectProperty<Long>(new Long(0), Long.class);
            } else if (Double.class.equals(type)) {
                returnProp = new ObjectProperty<Double>(new Double(0), Double.class);
            } else
            // for Date, just return the latest date
            if (Date.class.equals(type)) {
                returnProp = new ObjectProperty<Date>(new Date(), Date.class);
            } else {
                returnProp = new ObjectProperty<String>("", String.class);
            }
            return returnProp;
        }

    }

    private class ReportTableDropHandler implements DropHandler {
        private static final long serialVersionUID = -2161645426839892921L;

        public AcceptCriterion getAcceptCriterion() {
            return new ReportTableDropCriterion();
        }

        public void drop(final DragAndDropEvent event) {
            final AbstractSelectTargetDetails targetDetails =
                    (AbstractSelectTargetDetails) event.getTargetDetails();
            final TableTransferable transferable = (TableTransferable) event.getTransferable();

            final Item draggedRow = (Item) transferable.getItemId();
            final Item itemIdOver = (Item) targetDetails.getItemIdOver();
            ReportTable.this.getContainerDataSource().setParent(draggedRow, itemIdOver);
            resetPageBuffer();
            refreshRenderedCells();
        }

        class ReportTableDropCriterion extends Table.TableDropCriterion {
            private static final long serialVersionUID = 4050888966037155285L;

            @Override
            protected Set<Object> getAllowedItemIds(final DragAndDropEvent dragEvent,
                    final Table table, final Collection<Object> visibleItemIds) {
                final HashSet<Object> groupitems = new HashSet<Object>();
                final Item dragged =
                        (Item) ((TableTransferable) dragEvent.getTransferable()).getItemId();
                if (ReportTable.this.getContainerDataSource().getParent(dragged) == null) {
                    // Don't allow dropping root nowhere
                    return groupitems;
                }
                for (final Object object : visibleItemIds) {
                    // accept on all GroupItems which are not the same
                    // as dragged or its child
                    if (ReportTable.this.getContainerDataSource().areChildrenAllowed(object)) {
                        boolean sameOrChildren = false;
                        Item item = (Item) object;
                        while (item != null) {
                            if (item == dragged) {
                                sameOrChildren = true;
                                break;
                            }
                            item = ReportTable.this.getContainerDataSource().getParent(item);
                        }
                        if (!sameOrChildren) {
                            groupitems.add(object);
                        }
                    }
                }
                return groupitems;
            }
        }
    }

    private static class NameField extends TextField {
        private static final long serialVersionUID = 5054712622679607858L;

        public NameField() {
            setImmediate(true);
            setWidth("100%");
            setReadOnly(true);
        }
    }

    private Map<Item, NameField> itemToNameField = new HashMap<Item, NameField>();

    protected NameField getNameField(final Item itemId) {
        NameField nameField = itemToNameField.get(itemId);
        if (nameField == null) {
            nameField = new NameField();
            itemToNameField.put(itemId, nameField);
        }
        return nameField;
    }
}

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.logging.Logger;

import com.vaadin.data.Item;
import com.vaadin.data.Property;
import com.vaadin.data.util.HierarchicalContainer;

/*
 * A Container where we ensure that itemIds are also Items
 */
public class GroupedContainer extends HierarchicalContainer {

    private static final long serialVersionUID = 4393172804515242919L;
    protected static final Logger LOGGER = Logger.getLogger(GroupedContainer.class.getName());
    private boolean havePropertiesBeenSet;
    private final Object hierarchyPropertyId;

    public GroupedContainer(final Object hierarchyPropertyId) {
        super();
        this.havePropertiesBeenSet = false;
        this.hierarchyPropertyId = hierarchyPropertyId;
    }

    protected void setContainerPropertiesBasedOnItem(final Item itemToTakePropertiesFrom) {
        for (final Object id : itemToTakePropertiesFrom.getItemPropertyIds()) {
            final Class<?> type = itemToTakePropertiesFrom.getItemProperty(id).getType();
            this.addContainerProperty(id, type, null);
        }
        this.havePropertiesBeenSet = true;
    }

    /**
     * @return the hierarchyPropertyId
     */
    public Object getHierarchyPropertyId() {
        return this.hierarchyPropertyId;
    }

    public Item addDataItem(final Item itemId) {
        if ((!this.havePropertiesBeenSet) && (size() == 0)) {
            setContainerPropertiesBasedOnItem(itemId);
        }
        final Item newContainerItem = super.addItem(itemId);
        setChildrenAllowed(itemId, false);
        Object value;
        /*
         * We iterate through the item property ids and not the container property ids here in case
         * the Item has fewer properties than the container. This way we can add a group item with
         * just the hierarchy property and the remaining values will just be calculated anyway.
         */
        for (final Object propertyId : itemId.getItemPropertyIds()) {
            value = itemId.getItemProperty(propertyId);
            newContainerItem.getItemProperty(propertyId).setValue(value);
        }
        return newContainerItem;
    }

    public Item addGroupItem(final Item itemId) {
        final Item item = addDataItem(itemId);
        setChildrenAllowed(itemId, true);
        return item;
    }

    public void addItems(final GroupedContainer items) {
        final Collection<Item> rootIds = items.rootItemIds();
        for (final Item rootId : rootIds) {
            addItem(rootId);
            final Collection<Item> childrenIds = items.getChildren(rootId);
            for (final Item childId : childrenIds) {
                addItem(childId);
                setParent(childId, rootId);
            }
        }
    }

    @Override
    public Item getItem(final Object itemId) {
        if (!(itemId instanceof Item)) {
            throw new UnsupportedOperationException();
        }
        final Item item = super.getItem(itemId);
        if (null == item) {
            return null;
        }
        return super.getItem(itemId);
    }

    @Override
    public boolean setChildrenAllowed(final Object itemId, final boolean areChildrenAllowed) {
        if (!(itemId instanceof Item)) {
            throw new UnsupportedOperationException();
        }
        return super.setChildrenAllowed(itemId, areChildrenAllowed);
    }

    public boolean setParent(final Object itemId, final Item newParentId) {
        if (!(itemId instanceof Item)) {
            throw new UnsupportedOperationException();
        }
        return super.setParent(itemId, newParentId);
    }

    @Override
    public boolean areChildrenAllowed(final Object itemId) {
        if (null == itemId) {
            return false;
        }
        if (!(itemId instanceof Item)) {
            throw new UnsupportedOperationException();
        }
        return super.areChildrenAllowed(itemId);
    }

    @Override
    @SuppressWarnings("unchecked")
    public Collection<Item> getChildren(final Object itemId) {
        if (!(itemId instanceof Item)) {
            throw new UnsupportedOperationException();
        }
        return (Collection<Item>) super.getChildren(itemId);
    }

    @Override
    public Item getParent(final Object itemId) {
        if (null == itemId) {
            return null;
        }
        if (!(itemId instanceof Item)) {
            throw new UnsupportedOperationException();
        }
        return (Item) super.getParent(itemId);
    }

    @Override
    public boolean isRoot(final Object itemId) {
        if (!(itemId instanceof Item)) {
            throw new UnsupportedOperationException();
        }
        return super.isRoot(itemId);
    }

    /*
     * (non-Javadoc)
     * 
     * @see com.vaadin.data.util.HierarchicalContainer#rootItemIds()
     */
    @SuppressWarnings("unchecked")
    @Override
    public Collection<Item> rootItemIds() {
        return (Collection<Item>) super.rootItemIds();
    }

    @Override
    public boolean setParent(final Object itemId, final Object newParentId)
            throws UnsupportedOperationException {
        if (null == newParentId) {
            return false;
        }
        if ((itemId instanceof Item) && (newParentId instanceof Item)) {
            return this.setParent((Item) itemId, (Item) newParentId);
        }
        throw new UnsupportedOperationException();
    }

    @Override
    protected void doSort() {
        super.doSort();
        // also sort child lists
        for (final Object itemId : getItemIds()) {
            final Collection<Item> children = getChildren(itemId);
            if (null != children) {
                final ArrayList<Item> c = new ArrayList<Item>(children);
                Collections.sort(c, getItemSorter());
            }
        }
    }

    /*
     * (non-Javadoc)
     * 
     * @see com.vaadin.data.util.IndexedContainer#addItemAfter(java.lang.Object)
     */
    @Override
    public Object addItemAfter(final Object previousItemId) throws UnsupportedOperationException {
        if (previousItemId instanceof Item) {
            return super.addItemAfter(previousItemId);
        }
        throw new UnsupportedOperationException();
    }

    /*
     * (non-Javadoc)
     * 
     * @see com.vaadin.data.util.IndexedContainer#addItemAt(int)
     */
    @Override
    public Object addItemAt(final int index) throws UnsupportedOperationException {
        throw new UnsupportedOperationException();
    }

    /*
     * (non-Javadoc)
     * 
     * @see com.vaadin.data.util.HierarchicalContainer#hasChildren(java.lang.Object)
     */
    @Override
    public boolean hasChildren(final Object itemId) throws UnsupportedOperationException {
        if (itemId instanceof Item) {
            return super.hasChildren(itemId);
        }
        throw new UnsupportedOperationException();
    }

    // -------------------------------------------------------------------------------------------
    /*
     * All uses of IndexedContainerItem and IndexedContainerProperty visible externally are going to
     * be replaced with different Item and Property implementations to support calculations of
     * summary (i.e. grouped) items.
     * 
     * IndexedContainerProperty only appears in getContainerProperty() and in the
     * IndexedContainerItem implementation.
     * 
     * IndexedContainerItem appears in getUnfilteredItem, addItem, addItemAfter, and addItemAt.
     */

    /*
     * (non-Javadoc)
     * 
     * @see com.vaadin.data.Container#getContainerProperty(java.lang.Object, java.lang.Object)
     */
    @Override
    public Property getContainerProperty(final Object itemId, final Object propertyId) {
        if (!(itemId instanceof Item)) {
            throw new UnsupportedOperationException();
        }
        if (!containsId(itemId)) {
            return null;
        }
        return new CalculationProperty((Item) itemId, propertyId, super.getContainerProperty(
                itemId, propertyId));
    }

    @Override
    protected Item getUnfilteredItem(final Object itemId) {
        if (!(itemId instanceof Item)) {
            throw new UnsupportedOperationException();
        }
        final Item test = super.getUnfilteredItem(itemId);
        if (null == test) {
            return null;
        }
        return (Item) itemId;
    }

    /*
     * (non-Javadoc)
     * 
     * @see com.vaadin.data.Container#addItem(java.lang.Object)
     */
    @Override
    public Item addItem(final Object itemId) {
        if (!(itemId instanceof Item)) {
            throw new UnsupportedOperationException();
        }
        return super.internalAddItemAtEnd(itemId, (Item) itemId, true);
    }

    /*
     * (non-Javadoc)
     * 
     * @see com.vaadin.data.Container.Ordered#addItemAfter(java.lang.Object, java.lang.Object)
     */
    @Override
    public Item addItemAfter(final Object previousItemId, final Object newItemId) {
        if (!(newItemId instanceof Item)) {
            throw new UnsupportedOperationException();
        }
        return super.internalAddItemAfter(previousItemId, newItemId, (Item) newItemId, true);
    }

    /*
     * (non-Javadoc)
     * 
     * @see com.vaadin.data.Container.Indexed#addItemAt(int, java.lang.Object)
     */
    @Override
    public Item addItemAt(final int index, final Object newItemId) {
        if (!(newItemId instanceof Item)) {
            throw new UnsupportedOperationException();
        }
        return super.internalAddItemAt(index, newItemId, (Item) newItemId, true);
    }

    /*
     * This Property wraps another Property. If the property is from a data item, it behaves exactly
     * like the original property. If the property is from a group item, then it is read only, and
     * returns aggregated/calculated values.
     */
    class CalculationProperty implements Property {

        private static final long serialVersionUID = 2771911409645034428L;
        private final Class<?> type;
        private final Property wrappedProperty;
        private final Object propertyIdInItem;
        private final Item itemId;
        private final boolean isCalculated;

        // this is always a read-only property
        public CalculationProperty(final Item itemId, final Object propertyId,
                final Property property) {
            final boolean isHierarchyProperty;
            final boolean areChildrenAllowed;
            this.type = property.getType();
            this.propertyIdInItem = propertyId;
            this.itemId = itemId;
            this.wrappedProperty = property;
            if (getHierarchyPropertyId().equals(propertyId)) {
                isHierarchyProperty = true;
            } else {
                isHierarchyProperty = false;
            }
            if (areChildrenAllowed(itemId)) {
                areChildrenAllowed = true;
            } else {
                areChildrenAllowed = false;
            }
            if ((isHierarchyProperty) || (!areChildrenAllowed)) {
                isCalculated = false;
            } else {
                isCalculated = true;
            }
        }

        @Override
        public String toString() {
            return getValue().toString();
        }

        public Object getValue() {
            if (!isCalculated) {
                return wrappedProperty.getValue();
            }
            final Collection<Item> children = GroupedContainer.this.getChildren(itemId);
            if (Integer.class.equals(type)) {
                int sum = 0;
                Property propToAdd;
                if (null != children) {
                    for (final Item it : children) {
                        if (GroupedContainer.this.areChildrenAllowed(it)) {
                            propToAdd =
                                    GroupedContainer.this
                                            .getContainerProperty(it, propertyIdInItem);
                        } else {
                            propToAdd = it.getItemProperty(propertyIdInItem);
                        }
                        sum += (Integer) propToAdd.getValue();
                    }
                }
                return sum;
            } else if (Long.class.equals(type)) {
                long sum = 0;
                Property propToAdd;
                if (null != children) {
                    for (final Item it : children) {
                        if (GroupedContainer.this.areChildrenAllowed(it)) {
                            propToAdd =
                                    GroupedContainer.this
                                            .getContainerProperty(it, propertyIdInItem);
                        } else {
                            propToAdd = it.getItemProperty(propertyIdInItem);
                        }
                        sum += (Long) propToAdd.getValue();
                    }
                }
                return sum;
            } else if (Double.class.equals(type)) {
                double sum = 0;
                Property propToAdd;
                if (null != children) {
                    for (final Item it : children) {
                        if (GroupedContainer.this.areChildrenAllowed(it)) {
                            propToAdd =
                                    GroupedContainer.this
                                            .getContainerProperty(it, propertyIdInItem);
                        } else {
                            propToAdd = it.getItemProperty(propertyIdInItem);
                        }
                        sum += (Double) propToAdd.getValue();
                    }
                }
                return sum;
            } else if (Date.class.equals(type)) {
                // for Date, just return the latest date
                Date latestDate = null;
                Property dateProp;
                if (null != children) {
                    for (final Item it : children) {
                        if (GroupedContainer.this.areChildrenAllowed(it)) {
                            dateProp =
                                    GroupedContainer.this
                                            .getContainerProperty(it, propertyIdInItem);
                        } else {
                            dateProp = it.getItemProperty(propertyIdInItem);
                        }
                        final Date date2 = (Date) dateProp.getValue();
                        if ((latestDate == null) || (date2.after(latestDate))) {
                            latestDate = date2;
                        }
                    }
                }
                return latestDate;
            } else {
                return "";
            }
        }

        public void setValue(final Object newValue) throws ReadOnlyException, ConversionException {
            wrappedProperty.setValue(newValue);
        }

        public Class<?> getType() {
            return type;
        }

        public boolean isReadOnly() {
            if (isCalculated) {
                return true;
            }
            return wrappedProperty.isReadOnly();
        }

        public void setReadOnly(final boolean newStatus) throws UnsupportedOperationException {
            if (isCalculated) {
                throw new UnsupportedOperationException();
            }
            wrappedProperty.setReadOnly(newStatus);
        }
    }

}