other)
{
this(other.myObject);
}
/**
* Retrieves the T
representing the "group-by" aggregation.
* This method is used to directly access the property values, when
* aggregating by calling Aggregation.groupByComparable
.
* If Aggregation.groupBy
is called instead, then this
* method will still succeed. However, if super-aggregation is used (cube,
* rollups, grouping sets), the object returned here will not indicate that
* this AggregateValue
represents "all values" for a certain
* property. In this case, either of the two overloaded
* getPropertyValue
methods will correctly return the
* null
value if that property represents "all values".
*
* @return The T
object representing the "group-by" aggregation.
* @see net.sf.jagg.Aggregation#groupByComparable
* @see #getPropertyValue(String)
* @see #getPropertyValue(int)
*/
public T getObject()
{
return myObject;
}
/**
* Retrieves a property value representing a "group-by" category by name.
* This method is used to access the property values, when aggregating by
* calling Aggregation.groupBy
after specifying "group-by"
* properties. Because super-aggregation is possible in this case, if the
* referenced property represents "all values", then this method will return
* null
instead of the actual property value.
* @param property The property name.
* @return The property value.
* @since 0.7.0
* @see net.sf.jagg.Aggregation#groupBy
*/
public Object getPropertyValue(String property)
{
if (myObject == null)
return null;
if (myProperties != null)
{
int index = myProperties.indexOf(property);
// Return null for "super-aggregate" columns.
if (!myGroupingSet.contains(index))
return null;
}
return Aggregator.getValueFromProperty(myObject, property);
}
/**
* Retrieves a property value representing a "group-by" category, with
* the property specified by a 0-based index into the original list of
* properties that was specified in Builder.getProperties
.
* If Aggregation.groupByComparable
was called, then there
* are no "group-by" properties, so this method would return
* null
.
* @param propIndex The 0-based index into the list of properties.
* @return The property value.
* @since 0.7.0
*/
public Object getPropertyValue(int propIndex)
{
if (myProperties != null && myObject != null)
{
return getPropertyValue(myProperties.get(propIndex));
}
return null;
}
/**
* This method is used internally to store the given value
* associated with the given AggregateFunction
for later retrieval.
* It also appends the given value
to an internal list for
* later retrieval by index.
*
* @param agg An AggregateFunction
.
* @param value The aggregated value.
* @see AggregateFunction
*/
public void setAggregateValue(AggregateFunction agg, Object value)
{
myValuesMap.put(agg, value);
myValuesList.add(value);
}
/**
* Retrieves the value for the given AggregateFunction
.
*
* @param agg An AggregateFunction
.
* @return The aggregated value, or null
if no such
* AggregateFunction
is found.
*/
public Object getAggregateValue(AggregateFunction agg)
{
return myValuesMap.get(agg);
}
/**
* Retrieves the value for the AggregateFunction
at the given index.
* @param index The 0-based index.
* @return The aggregated value.
* @throws IndexOutOfBoundsException If the index is out of range.
* @since 0.3.0
*/
public Object getAggregateValue(int index)
{
return myValuesList.get(index);
}
/**
*
Determines whether the referenced field represents the set of all
* values in a super-aggregate value. This can be used to distinguish an
* actual null
value in normal aggregation vs. a
* null
that represents "all values" in super-aggregation.
* For example, if there are 4 group-by properties, and super-aggregation
* is used, and this aggregate value happens to represent the grouping set
* {0, 1}, then properties 2 and 3 are "all values", and ...
*
* isGrouping(0) == isGrouping(1) == false
* isGrouping(2) == isGrouping(3) == true
*
* @param field A field reference integer, from 0 to n - 1, where
* n is the the number of group-by properties.
* @return true
if the field represents "all values" in
* super-aggregation, false
otherwise.
* @throws IllegalArgumentException If the integer field reference is out of
* range.
* @since 0.7.0
*/
public boolean isGrouping(int field)
{
if (field < 0 || field >= myProperties.size())
throw new IllegalArgumentException("isGrouping: integer field reference out of range: " + field);
return !myGroupingSet.contains(field);
}
/**
* Determines whether the referenced field represents the set of all
* values in a super-aggregate value. This can be used to distinguish an
* actual null
value in normal aggregation vs. a
* null
that represents "all values" in super-aggregation.
* For example, if there are 4 group-by properties ({"prop0", "prop1",
* "prop2", "prop3"}), and super-aggregation is used, and this aggregate
* value happens to represent the grouping set {0, 1}, then properties 2
* and 3 are "all values", and ...
*
* isGrouping("prop0") == isGrouping("prop1") == false
* isGrouping("prop2") == isGrouping("prop3") == true
*
* @param propertyName A property name.
* @return true
if the field represents "all values" in
* super-aggregation, false
otherwise.
* @throws IllegalArgumentException If the property name is not a group-by
* property.
* @since 0.7.0
*/
public boolean isGrouping(String propertyName)
{
int index = myProperties.indexOf(propertyName);
if (index == -1)
{
// Property name not found.
// Try as integer field reference.
try
{
index = Integer.parseInt(propertyName);
return isGrouping(index);
}
catch (NumberFormatException e)
{
// Property name not found and it's not an integer.
throw new IllegalArgumentException("isGrouping: Not a group-by property name or an integer field reference: " +
propertyName);
}
}
return isGrouping(myProperties.indexOf(propertyName));
}
/**
* Determines the distinct grouping ID of the given referenced fields by
* determining whether each given referenced field represents "all values"
* in super-aggregation.
* For example, if there are 4 group-by properties ({"prop0", "prop1",
* "prop2", "prop3"}), and super-aggregation is used, and this aggregate
* value happens to represent the grouping set {0, 1}, then properties 2 and
* 3 are "all values", and ...
*
* getGroupingId({0}) == getGroupingId({1}) == 0
* getGroupingId({2}) == getGroupingId({3}) == 1
* getGroupingId({0, 1}) == getGroupingId({1, 0}) == 0
* getGroupingId({0, 2}) == getGroupingId({0, 3}) == 1
* getGroupingId({1, 2}) == getGroupingId({1, 3}) == 1
* getGroupingId({2, 0}) == getGroupingId({2, 1}) == 2
* getGroupingId({3, 0}) == getGroupingId({3, 1}) == 2
* getGroupingId({2, 3}) == getGroupingId({3, 2}) == 3
*
* In the above examples, each integer n reference can be freely
* substituted with the equivalent property name, e.g. 0
is
* equivalent to "prop0"
.
* @param fields A List
of field references, which can be
* integer field references, from 0 to n - 1, where n
* is the number of group-by properties, or they can be property names.
* Each field reference maps to a bit in the returned number.
* @return An integer, with each set bit corresponding to an "all values"
* determination. The most significant bit corresponds to the first
* element.
* @throws IllegalArgumentException If any of the fields represent integer
* field references that are out of range, or they represent string
* property names that aren't group-by properties.
* @since 0.7.0
*/
public int getGroupingId(List> fields)
{
// Note: This assumes that there are 32 or less fields passed in.
int groupingId = 0;
for (Object field : fields)
{
groupingId <<= 1;
int fieldRef;
if (field instanceof Number)
{
fieldRef = ((Number) field).intValue();
}
else
{
String propertyName = field.toString();
fieldRef = myProperties.indexOf(propertyName);
if (fieldRef == -1)
{
// Property name not found.
// Try as integer field reference.
try
{
fieldRef = Integer.parseInt(propertyName);
}
catch (NumberFormatException e)
{
// Property name not found and it's not an integer.
throw new IllegalArgumentException("getGroupingId: Not a group-by property name or an integer field reference: " +
propertyName);
}
}
}
if (fieldRef < 0 || fieldRef >= myProperties.size())
{
throw new IllegalArgumentException("getGroupingId: integer field reference out of range: " + field);
}
// Mark the bit if it's "all values".
if (isGrouping(fieldRef))
{
groupingId |= 1;
}
}
return groupingId;
}
/**
* Assign a List
of AggregateFunctions
used to
* make this AggregateValue
. These AggregateFunctions
* are stored here for the purposes of super-aggregation later.
* @param aggs A List
of AggregateFunctions
.
* @since 0.7.0
*/
public void assignAggregators(List aggs)
{
myAggregators = aggs;
}
/**
* Retrieves the List
of AggregateFunctions
that
* are responsible for the aggregate values in this AggregateValue
.
* @return The List
of AggregateFunctions
.
* @since 0.7.0
*/
public List retrieveAggregators()
{
return myAggregators;
}
/**
* Mark all AggregateFunctions
as no longer in use and no
* longer maintain the list of assigned AggregateFunctions
.
* @since 0.7.0
*/
public void releaseAggregators()
{
for (AggregateFunction agg : myAggregators)
agg.setInUse(false);
myAggregators.clear();
}
/**
* Assign a List
of properties and a grouping set, which is a
* List
of 0-based property name indexes.
* @param properties A List
of property names.
* @param groupingSet A List
of 0-based property name indexes.
* @since 0.7.0
*/
public void assignPropsAndGroupingSet(List properties, List groupingSet)
{
myProperties = properties;
myGroupingSet = groupingSet;
}
/**
* Returns the string representation.
* @return The string representation.
* @since 0.7.0
*/
@Override
public String toString()
{
StringBuffer buf = new StringBuffer();
buf.append("AggregateValue:(object => ");
buf.append(myObject.toString());
buf.append(", valuesList =>");
buf.append(myValuesList.toString());
buf.append(", aggregators =>");
buf.append(myAggregators.toString());
buf.append(", properties =>");
buf.append(myProperties.toString());
buf.append(", groupingSet =>");
buf.append(myGroupingSet.toString());
buf.append(")");
return buf.toString();
}
}