/* * 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; } } }