package com.onaro.util.jfc.grouping;
import java.util.Date;
import com.onaro.client.DateFormats;
/**
* A grouping criterion that is associated with multiple columns. The value
* it produces encapsulate an array of node values which its toString()
* produces the concatenation of the node values.
*/
public class MultiColumnCriterion implements Criterion {
/**
* Separates the node values in the string that is returned as the criterion value.
*/
private String delimiter;
/**
* The column numbers of the encapsulated table with which this criterion is
* associated. The order of concatenation follows the order of column numbers.
*/
private int columnNumbers[];
/**
* Set the column numbers associated with this criterion and the delimiter used
* for separating the values.
* @param columnNumbers column numbers of the encapsulated table, the order of
* concatenation follows the order of column numbers
* @param delimiter separates values in the generated criterion value
*/
public MultiColumnCriterion(int[] columnNumbers, String delimiter) {
this.columnNumbers = columnNumbers;
this.delimiter = delimiter;
}
/**
* Gets the value identifying the node. The value is a string containing a
* {@link MultiColumnCriterion#delimiter} separated list of values from the
* columns associated with this criterion.
* @param node the node to get the criterion value for
* @return the a string with the node's column values
*/
public Object getValue(Node node) {
return new Value(node);
}
/**
* Gets the numbers of the columns used by this criterion. Used for unit
* tests only.
* @return an array of the column numbers the criterion is using
*/
public int[] getColumnNumbers() {
return columnNumbers;
}
/**
* Encapsulate node values and produces a toString()
which is
* the concatenation of them.
*/
class Value {
/**
* The node values in order of the columns in this criterion.
*/
Object columnValues[];
/**
* The string representation of this value, initialized the first time it
* is needed.
*/
String strVal;
/**
* The hash code is calculated from all the column values. It is initialized
* once it is needed.
*/
int hash = -1;
/**
* Gets the values from a node.
* @param node the node
*/
public Value(Node node) {
columnValues = new Object[columnNumbers.length];
for (int i = 0; i < columnNumbers.length; i++) {
columnValues[i] = node.getValueAt(columnNumbers[i]);
}
}
/**
* Gets the hash code of this value. The hash code s calculated once by summing
* the hash of all the column values.
* @return the hash code
*/
public int hashCode() {
if (hash < 0) {
hash = 0;
for (int i = 0; i < columnNumbers.length; i++) {
hash += (columnValues[i] != null) ? columnValues[i].hashCode() * 29 : 23;
}
}
return hash;
}
/**
* Tests if two values are the equal by comparing the column values in
* each of them.
* @param obj the other value
* @return true if all column values are equal
*/
public boolean equals(Object obj) {
if (obj == null || !(obj instanceof Value)) return false;
Value other = (Value)obj;
if (columnValues.length != other.columnValues.length) return false;
for (int i = 0; i < columnValues.length; i++) {
Object colVal = columnValues[i];
Object otherColVal = other.columnValues[i];
if (colVal != otherColVal) {
if (colVal == null) return false;
if (!colVal.equals(otherColVal)) return false;
}
}
return true;
}
/**
* If its the first time the string presentation is needed, concatenate
* the values.
* @return the node values concatenated and delimited
*/
public String toString() {
if (strVal == null) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < columnValues.length; i++) {
if (i > 0) {
sb.append(delimiter);
}
Object columnValue = columnValues[i];
if (columnValue != null) {
if (columnValue instanceof Date) {
sb.append(DateFormats.getShortDateTimeFormat().format(columnValue));
} else {
sb.append(columnValue);
}
} else {
sb.append("None"); // TODO: NLS this!
}
}
strVal = sb.toString();
}
return strVal;
}
}
}