/*
* JasperReports - Free Java Reporting Library.
* Copyright (C) 2001 - 2009 Jaspersoft Corporation. All rights reserved.
* http://www.jaspersoft.com
*
* Unless you have purchased a commercial license agreement from Jaspersoft,
* the following license terms apply:
*
* This program is part of JasperReports.
*
* JasperReports is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* JasperReports is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with JasperReports. If not, see <http://www.gnu.org/licenses/>.
*/
package net.sf.jasperreports.crosstabs.fill.calculation;
import java.util.Comparator;
import net.sf.jasperreports.crosstabs.type.CrosstabTotalPositionEnum;
import net.sf.jasperreports.engine.JRException;
import net.sf.jasperreports.engine.JRExpression;
import net.sf.jasperreports.engine.type.SortOrderEnum;
import org.apache.commons.collections.comparators.ComparableComparator;
import org.apache.commons.collections.comparators.ReverseComparator;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* Bucket definition.
*
* @author Lucian Chirita (lucianc@users.sourceforge.net)
* @version $Id: BucketDefinition.java 3500 2010-03-03 15:56:41Z teodord $
*/
public class BucketDefinition
{
private static final Log log = LogFactory.getLog(BucketDefinition.class);
/**
* Ascending order constant.
* @deprecated Replaced by {@link SortOrderEnum#ASCENDING}.
*/
public static final byte ORDER_ASCENDING = 1;
/**
* Descending order constant.
* @deprecated Replaced by {@link SortOrderEnum#DESCENDING}.
*/
public static final byte ORDER_DESCENDING = 2;
/**
* Constant indicating that total are not required for this bucket.
* @deprecated Replaced by {@link CrosstabTotalPositionEnum#NONE}.
*/
public static final byte TOTAL_POSITION_NONE = 0;
/**
* Constants indicating that totals are to be positioned before the other buckets.
* @deprecated Replaced by {@link CrosstabTotalPositionEnum#START}.
*/
public static final byte TOTAL_POSITION_START = 1;
/**
* Constants indicating that totals are to be positioned at the end of the other buckets.
* @deprecated Replaced by {@link CrosstabTotalPositionEnum#END}.
*/
public static final byte TOTAL_POSITION_END = 2;
/**
* Value type used for non-null values.
*/
protected static final byte VALUE_TYPE_VALUE = 0;
/**
* Value type used for null bucket values.
*/
protected static final byte VALUE_TYPE_NULL = 1;
/**
* Value type used for total buckets.
*/
protected static final byte VALUE_TYPE_TOTAL = 2;
/**
* The total value for this bucket.
*/
protected final Bucket VALUE_TOTAL = new Bucket(VALUE_TYPE_TOTAL);
/**
* The null bucket.
*/
protected final Bucket VALUE_NULL = new Bucket(VALUE_TYPE_NULL);
protected final Comparator bucketValueComparator;
protected final JRExpression orderByExpression;
protected final Comparator orderValueComparator;
private final CrosstabTotalPositionEnum totalPosition;
private boolean computeTotal;
/**
* Creates a bucket.
*
* @param valueClass the class of the bucket values
* @param orderByExpression expression that provides order by values
* @param comparator the comparator to use for bucket sorting
* @param order the order type, {@link #ORDER_ASCENDING ORDER_ASCENDING} or {@link #ORDER_DESCENDING ORDER_DESCENDING} descending
* @param totalPosition the position of the total bucket
* @throws JRException
*/
public BucketDefinition(Class valueClass,
JRExpression orderByExpression, Comparator comparator, SortOrderEnum order,
CrosstabTotalPositionEnum totalPosition) throws JRException
{
if (comparator == null && orderByExpression == null
&& !Comparable.class.isAssignableFrom(valueClass))
{
throw new JRException("The bucket expression values are not comparable and no comparator specified.");
}
this.orderByExpression = orderByExpression;
if (orderByExpression == null)
{
// we don't have an order by expression
// the buckets are ordered using the bucket values
this.bucketValueComparator = createOrderComparator(comparator, order);
this.orderValueComparator = null;
}
else
{
// we have an order by expression
// we only need an internal ordering for bucket values
if (Comparable.class.isAssignableFrom(valueClass))
{
// using natural order
this.bucketValueComparator = ComparableComparator.getInstance();
}
else
{
// using an arbitrary rank comparator
if (log.isDebugEnabled())
{
log.debug("Using arbitrary rank comparator for bucket");
}
this.bucketValueComparator = new ArbitraryRankComparator();
}
// the comparator is used for order by values
this.orderValueComparator = createOrderComparator(comparator, order);
}
this.totalPosition = totalPosition;
computeTotal = totalPosition != CrosstabTotalPositionEnum.NONE;
}
protected static Comparator createOrderComparator(Comparator comparator, SortOrderEnum order)
{
Comparator orderComparator;
switch (order)
{
case DESCENDING:
{
if (comparator == null)
{
orderComparator = new ReverseComparator();
}
else
{
orderComparator = new ReverseComparator(comparator);
}
break;
}
case ASCENDING:
default:
{
if (comparator == null)
{
orderComparator = ComparableComparator.getInstance();
}
else
{
orderComparator = comparator;
}
break;
}
}
return orderComparator;
}
/**
* Whether this bucket needs total calculation.
*
* @return this bucket needs total calculation
*/
public boolean computeTotal()
{
return computeTotal;
}
/**
* Instructs that the bucket will need total bucket calculation.
*
* @see #computeTotal()
*/
public void setComputeTotal()
{
computeTotal = true;
}
/**
* Returns the total bucket position.
*
* @return the total bucket position
*/
public CrosstabTotalPositionEnum getTotalPosition()
{
return totalPosition;
}
public JRExpression getOrderByExpression()
{
return orderByExpression;
}
public boolean hasOrderValues()
{
return orderByExpression != null;
}
public int compareOrderValues(Object v1, Object v2)
{
return orderValueComparator.compare(v1, v2);
}
/**
* Creates a {@link Bucket BucketValue} object for a given value.
*
* @param value the value
* @return the corresponding {@link Bucket BucketValue} object
*/
public Bucket create(Object value)
{
if (value == null)
{
return VALUE_NULL;
}
return new Bucket(value);
}
/**
* Bucket value class.
*
* @author Lucian Chirita (lucianc@users.sourceforge.net)
*/
public class Bucket implements Comparable
{
private final Object value;
private final byte type;
/**
* Creates a bucket for a value type.
*
* @param type the value type
*/
protected Bucket(byte type)
{
this.value = null;
this.type = type;
}
/**
* Creates a bucket for a value.
*
* @param value the value
*/
protected Bucket(Object value)
{
this.value = value;
this.type = VALUE_TYPE_VALUE;
}
/**
* Returns the bucket value.
*
* @return the bucket value
*/
public Object getValue()
{
return value;
}
public boolean equals (Object o)
{
if (o == null || !(o instanceof Bucket))
{
return false;
}
if (o == this)
{
return true;
}
Bucket v = (Bucket) o;
if (type != VALUE_TYPE_VALUE)
{
return type == v.type;
}
return v.type == VALUE_TYPE_VALUE && value.equals(v.value);
}
public int hashCode()
{
int hash = type;
if (type == VALUE_TYPE_VALUE)
{
hash = 37*hash + value.hashCode();
}
return hash;
}
public String toString()
{
switch(type)
{
case VALUE_TYPE_NULL:
return "NULL";
case VALUE_TYPE_TOTAL:
return "TOTAL";
case VALUE_TYPE_VALUE:
default:
return String.valueOf(value);
}
}
public int compareTo(Object o)
{
Bucket val = (Bucket) o;
if (type != val.type)
{
return type - val.type;
}
if (type != VALUE_TYPE_VALUE)
{
return 0;
}
return bucketValueComparator.compare(value, val.value);
}
/**
* Decides whether this is a total bucket.
*
* @return whether this is a total bucket
*/
public boolean isTotal()
{
return type == VALUE_TYPE_TOTAL;
}
}
}