package com.onaro.sanscreen.client.view.tabular.value; import java.util.HashSet; import java.util.Iterator; import java.util.Set; import com.onaro.sanscreen.client.view.init.SummaryInitInfo; import com.onaro.sanscreen.client.view.tabular.NumberColumn; import com.onaro.util.jfc.grouping.Node; import com.onaro.util.jfc.grouping.Summarizer; /** * Sum the number values in a {@link NumberColumn} for the rows that match the specified condition. * Multiple values that are equal in terms of {@link NumberValue#equals(Object)} and {@link NumberValue#hashCode()} * are summed only once. *

If all children of a group are equal (not in value, but when testing the equals and hashcode methods), * then one of the values becomes the summary. *

If any of the children isn't a {@link NumberValue}, the summary will be empty. *

Usage example: Summing the capacity of volumes in a group where the capacicty value is * identified by the volume name. */ public class ConditionalSumUniqueNumberColumnSummarizer implements Summarizer { /** * Condition to check if the child node should be included in the summary */ protected NodeCondition condition; /** * The column to put the sum in. */ protected int column; /** * Defined the format-name used by {@link NumberValue} to format the number. */ protected String format; /** * Initialize a summarizer for the given column. * @param condition the condition to check on the child row * @param column the column to put the sum in */ public ConditionalSumUniqueNumberColumnSummarizer(NodeCondition condition, int column) { this(condition, column, NumberValue.FORMAT_DEFAULT); } /** * Initialize a summarizer for the given column. * @param condition the condition to check on the child row * @param column the column to put the sum in * @param format the format to display the number value */ public ConditionalSumUniqueNumberColumnSummarizer(NodeCondition condition, int column, String format) { this.condition = condition; this.column = column; this.format = format; } /** * Sum the number values in a {@link NumberColumn} for the rows in a group meeting the condition. Multiple values that * are equal in terms of {@link NumberValue#equals(Object)} and {@link NumberValue#hashCode()} * are summed only once. *

If all children of a group are equal (not in value, but when testing the equals and hashcode methods), * then one of the values becomes the summary. *

If any of the children isn't a {@link NumberValue}, the summary will be empty. * @param node the node */ public void updateSummary(Node node) { Set valueSet = new HashSet(); int otherValuesCount = 0; for (int i = 0; i < node.getChildCount(); ++i) { Node child = node.getChild(i); if (condition == null || condition.count(child)) { Object value = child.getValueAt(column); valueSet.add(value); if ((value instanceof SumNumberValue)) ++otherValuesCount; } } if (!valueSet.isEmpty() && otherValuesCount == 0) { Iterator valueItr = valueSet.iterator(); if (valueSet.size() == 1) { Object value = valueItr.next(); if (value instanceof NumberValue) { node.setValueAt(column, new SumNumberValue((NumberValue)value)); } else { node.setValueAt(column, value); } } else { double sum = 0; while (valueItr.hasNext()) { Object value = valueItr.next(); if (value instanceof NumberValue) { sum += ((NumberValue) value).getNumber(); } } node.setValueAt(column, new SumNumberValue(sum)); } } } /** * Defines how a row is evaluated. */ public interface NodeCondition { /** * Tells if this node row should be counted or not. * @param node the node to test * @return true if the node should be counted */ public boolean count(Node node); } /** * Condition that checks to see whether the value in a given column on the row * equals/not equals the target value. * */ public static class ColumnEqualsCondition implements NodeCondition { private int targetColumn; private boolean equals = true; private Object targetValue = null; /** * Construct a condition based on the target column and reading the target value and * equals/not equals setting from the config iterator. * * @param targetColumn target column for the value being checked * @param confItr the xml configuration to read from */ public ColumnEqualsCondition(int targetColumn, SummaryInitInfo.SummarizerInitInfo summarizerInitInfo) { this.targetColumn = targetColumn; Object equalsValue = summarizerInitInfo.getObjectProperty(SummaryInitInfo.PROP_EQUALS); Object notEqualsValue = summarizerInitInfo.getObjectProperty(SummaryInitInfo.PROP_NOT_EQUALS); if (equalsValue!=null) { equals = true; targetValue = equalsValue; } else if (notEqualsValue!=null) { equals = false; targetValue = notEqualsValue; } } /** * Construct a condition * @param targetColumn target column for the value being checked * @param equals true to check for equality to the targetValue, false for not equals * @param targetValue the value to compare with */ public ColumnEqualsCondition(int targetColumn, boolean equals, Object targetValue) { this.targetColumn = targetColumn; this.equals = equals; this.targetValue = targetValue; } public boolean count(Node node) { Object columnValue = node.getValueAt(targetColumn); if (targetValue == null) { return getEquals(columnValue == null); } else { return getEquals(targetValue.equals(columnValue)); } } public boolean getEquals(boolean value) { if (equals) { return value; } else { return !value; } } } /** * Allows to distinguish values generated by the summarizer from the values that it * is summarizing. */ public class SumNumberValue extends NumberValue { private static final long serialVersionUID = 1L; public SumNumberValue(double number) { super(number, null, format); } public SumNumberValue(NumberValue numberValue) { this(numberValue.getNumber()); } } }