net.sf.jasperreports.crosstabs.fill.calculation.BucketDefinition.java Source code

Java tutorial

Introduction

Here is the source code for net.sf.jasperreports.crosstabs.fill.calculation.BucketDefinition.java

Source

/*
 * JasperReports - Free Java Reporting Library.
 * Copyright (C) 2001 - 2019 TIBCO Software Inc. 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 org.apache.commons.collections4.comparators.ComparableComparator;
import org.apache.commons.collections4.comparators.ReverseComparator;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import net.sf.jasperreports.crosstabs.fill.BucketOrderer;
import net.sf.jasperreports.crosstabs.fill.calculation.BucketValueOrderDecorator.OrderPosition;
import net.sf.jasperreports.crosstabs.type.CrosstabTotalPositionEnum;
import net.sf.jasperreports.engine.JRException;
import net.sf.jasperreports.engine.JRRuntimeException;
import net.sf.jasperreports.engine.analytics.dataset.BucketOrder;

/**
 * Bucket definition.
 * 
 * @author Lucian Chirita (lucianc@users.sourceforge.net)
 */
public class BucketDefinition {

    private static final Log log = LogFactory.getLog(BucketDefinition.class);
    public static final String EXCEPTION_MESSAGE_KEY_UNSUPPORTED_ORDER_TYPE = "crosstabs.calculation.unsupported.order.type";

    /**
     * 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<Object> bucketValueComparator;

    protected final BucketOrderer orderer;
    //FIXME totalPosition and mergeHeaderCells only apply to crosstabs
    private final CrosstabTotalPositionEnum totalPosition;
    private boolean mergeHeaderCells;

    private final BucketOrder order;

    private boolean computeTotal;

    /**
     * Creates a bucket.
     * 
     * @param valueClass the class of the bucket values
     * @param orderer bucket entries orderer
     * @param comparator the comparator to use for bucket sorting
     * @param order the order type, {@link BucketOrder#ASCENDING}, {@link BucketOrder#DESCENDING} or {@link BucketOrder#NONE}
     * @param totalPosition the position of the total bucket
     * @throws JRException
     */
    public BucketDefinition(Class<?> valueClass, BucketOrderer orderer, Comparator<Object> comparator,
            BucketOrder order, CrosstabTotalPositionEnum totalPosition) throws JRException {
        this.orderer = orderer;
        this.order = order;

        if (orderer == null) {
            // we don't have a bucket orderer
            if (order == BucketOrder.NONE) {
                // no ordering, values are inserted in the order in which they come
                this.bucketValueComparator = null;
            } else {
                // the buckets are ordered using the bucket values
                // if there's no comparator, we're assuming that the values are Comparable
                this.bucketValueComparator = createOrderComparator(comparator, order);
            }
        } 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.INSTANCE;
            } else {
                // using an arbitrary rank comparator
                // TODO lucianc couldn't we just set here bucketValueComparator to null?
                if (log.isDebugEnabled()) {
                    log.debug("Using arbitrary rank comparator for bucket");
                }

                this.bucketValueComparator = new ArbitraryRankComparator();
            }
        }

        this.totalPosition = totalPosition;
        computeTotal = totalPosition != CrosstabTotalPositionEnum.NONE || orderer != null;
    }

    public static Comparator<Object> createOrderComparator(Comparator<Object> comparator, BucketOrder order) {
        Comparator<Object> orderComparator;
        switch (order) {
        case DESCENDING: {
            if (comparator == null) {
                orderComparator = new ReverseComparator<Object>();
            } else {
                orderComparator = new ReverseComparator<Object>(comparator);
            }
            break;
        }
        case ASCENDING: {
            if (comparator == null) {
                orderComparator = ComparableComparator.INSTANCE;
            } else {
                orderComparator = comparator;
            }
            break;
        }
        case NONE:
        default:
            throw new JRRuntimeException(EXCEPTION_MESSAGE_KEY_UNSUPPORTED_ORDER_TYPE, new Object[] { order });
        }
        return orderComparator;
    }

    public boolean isSorted() {
        return bucketValueComparator != null;
    }

    /**
     * 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 BucketOrderer getOrderer() {
        return orderer;
    }

    public BucketOrder getOrder() {
        return order;
    }

    public boolean isMergeHeaderCells() {
        return mergeHeaderCells;
    }

    public void setMergeHeaderCells(boolean mergeHeaderCells) {
        this.mergeHeaderCells = mergeHeaderCells;
    }

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

        if (value instanceof BucketValueOrderDecorator) {
            // create only when orderPosition != normal?
            return new OrderDecoratorBucket((BucketValueOrderDecorator<?>) value);
        }

        return new Bucket(value);
    }

    /**
     * Bucket value class.
     * 
     * @author Lucian Chirita (lucianc@users.sourceforge.net)
     */
    public class Bucket implements Comparable<Object> {
        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;
        }

        public Bucket(Object value, byte type) {
            this.value = value;
            this.type = type;
        }

        /**
         * Returns the bucket value.
         * 
         * @return the bucket value
         */
        public Object getValue() {
            return value;
        }

        @Override
        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);
        }

        @Override
        public int hashCode() {
            int hash = type;

            if (type == VALUE_TYPE_VALUE) {
                hash = 37 * hash + value.hashCode();
            }

            return hash;
        }

        @Override
        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);
            }
        }

        @Override
        public int compareTo(Object o) {
            Bucket val = (Bucket) o;
            if (type != val.type) {
                return type - val.type;
            }

            if (type != VALUE_TYPE_VALUE) {
                return 0;
            }

            OrderPosition orderPosition = getOrderPosition();
            OrderPosition otherOrderPosition = val.getOrderPosition();
            if (orderPosition != otherOrderPosition) {
                return orderPosition.comparePosition(otherOrderPosition);
            }

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

        public OrderPosition getOrderPosition() {
            return OrderPosition.NORMAL;
        }
    }

    public class OrderDecoratorBucket extends Bucket {
        private OrderPosition orderPosition;

        protected OrderDecoratorBucket(BucketValueOrderDecorator<?> value) {
            super(value.getValue());

            orderPosition = value.getOrderPosition();
        }

        @Override
        public OrderPosition getOrderPosition() {
            return orderPosition;
        }
    }
}