    001    /* ===========================================================
    002     * JFreeChart : a free chart library for the Java(tm) platform
    003     * ===========================================================
    004     *
    005     * (C) Copyright 2000-2008, by Object Refinery Limited and Contributors.
    006     *
    007     * Project Info:  http://www.jfree.org/jfreechart/index.html
    008     *
    009     * This library is free software; you can redistribute it and/or modify it
    010     * under the terms of the GNU Lesser General Public License as published by
    011     * the Free Software Foundation; either version 2.1 of the License, or
    012     * (at your option) any later version.
    013     *
    014     * This library is distributed in the hope that it will be useful, but
    015     * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
    016     * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
    017     * License for more details.
    018     *
    019     * You should have received a copy of the GNU Lesser General Public
    020     * License along with this library; if not, write to the Free Software
    021     * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301,
    022     * USA.
    023     *
    024     * [Java is a trademark or registered trademark of Sun Microsystems, Inc.
    025     * in the United States and other countries.]
    026     *
    027     * ---------------------------------------
    028     * AbstractCategoryItemLabelGenerator.java
    029     * ---------------------------------------
    030     * (C) Copyright 2005-2008, by Object Refinery Limited.
    031     *
    032     * Original Author:  David Gilbert (for Object Refinery Limited);
    033     * Contributor(s):   -;
    034     *
    035     * Changes
    036     * -------
    037     * 11-May-2004 : Version 1, distilled from StandardCategoryLabelGenerator (DG);
    038     * 31-Jan-2005 : Added methods to return row and column labels (DG);
    039     * 17-May-2005 : Added percentage to item array (DG);
    040     * ------------- JFREECHART 1.0.x ---------------------------------------------
    041     * 03-May-2006 : Added new constructor (DG);
    042     * 23-Nov-2007 : Implemented hashCode() (DG);
    043     *
    044     */
    045
    046    package org.jfree.chart.labels;
    047
    048    import java.io.Serializable;
    049    import java.text.DateFormat;
    050    import java.text.MessageFormat;
    051    import java.text.NumberFormat;
    052
    053    import org.jfree.chart.HashUtilities;
    054    import org.jfree.data.DataUtilities;
    055    import org.jfree.data.category.CategoryDataset;
    056    import org.jfree.util.ObjectUtilities;
    057    import org.jfree.util.PublicCloneable;
    058
    059    /**
    060     * A base class that can be used to create a label or tooltip generator that
    061     * can be assigned to a
    062     * {@link org.jfree.chart.renderer.category.CategoryItemRenderer}.
    063     */
    064    public abstract class AbstractCategoryItemLabelGenerator
    065            implements PublicCloneable, Cloneable, Serializable {
    066
    067        /** For serialization. */
    068        private static final long serialVersionUID = -7108591260223293197L;
    069
    070        /**
    071         * The label format string used by a <code>MessageFormat</code> object to
    072         * combine the standard items:  {0} = series name, {1} = category,
    073         * {2} = value, {3} = value as a percentage of the column total.
    074         */
    075        private String labelFormat;
    076
    077        /** The string used to represent a null value. */
    078        private String nullValueString;
    079
    080        /**
    081         * A number formatter used to preformat the value before it is passed to
    082         * the MessageFormat object.
    083         */
    084        private NumberFormat numberFormat;
    085
    086        /**
    087         * A date formatter used to preformat the value before it is passed to the
    088         * MessageFormat object.
    089         */
    090        private DateFormat dateFormat;
    091
    092        /**
    093         * A number formatter used to preformat the percentage value before it is
    094         * passed to the MessageFormat object.
    095         */
    096        private NumberFormat percentFormat;
    097
    098        /**
    099         * Creates a label generator with the specified number formatter.
    100         *
    101         * @param labelFormat  the label format string (<code>null</code> not
    102         *                     permitted).
    103         * @param formatter  the number formatter (<code>null</code> not permitted).
    104         */
    105        protected AbstractCategoryItemLabelGenerator(String labelFormat,
    106                                                     NumberFormat formatter) {
    107            this(labelFormat, formatter, NumberFormat.getPercentInstance());
    108        }
    109
    110        /**
    111         * Creates a label generator with the specified number formatter.
    112         *
    113         * @param labelFormat  the label format string (<code>null</code> not
    114         *                     permitted).
    115         * @param formatter  the number formatter (<code>null</code> not permitted).
    116         * @param percentFormatter  the percent formatter (<code>null</code> not
    117         *     permitted).
    118         *
    119         * @since 1.0.2
    120         */
    121        protected AbstractCategoryItemLabelGenerator(String labelFormat,
    122                NumberFormat formatter, NumberFormat percentFormatter) {
    123            if (labelFormat == null) {
    124                throw new IllegalArgumentException("Null 'labelFormat' argument.");
    125            }
    126            if (formatter == null) {
    127                throw new IllegalArgumentException("Null 'formatter' argument.");
    128            }
    129            if (percentFormatter == null) {
    130                throw new IllegalArgumentException(
    131                        "Null 'percentFormatter' argument.");
    132            }
    133            this.labelFormat = labelFormat;
    134            this.numberFormat = formatter;
    135            this.percentFormat = percentFormatter;
    136            this.dateFormat = null;
    137            this.nullValueString = "-";
    138        }
    139
    140        /**
    141         * Creates a label generator with the specified date formatter.
    142         *
    143         * @param labelFormat  the label format string (<code>null</code> not
    144         *                     permitted).
    145         * @param formatter  the date formatter (<code>null</code> not permitted).
    146         */
    147        protected AbstractCategoryItemLabelGenerator(String labelFormat,
    148                                                     DateFormat formatter) {
    149            if (labelFormat == null) {
    150                throw new IllegalArgumentException("Null 'labelFormat' argument.");
    151            }
    152            if (formatter == null) {
    153                throw new IllegalArgumentException("Null 'formatter' argument.");
    154            }
    155            this.labelFormat = labelFormat;
    156            this.numberFormat = null;
    157            this.percentFormat = NumberFormat.getPercentInstance();
    158            this.dateFormat = formatter;
    159            this.nullValueString = "-";
    160        }
    161
    162        /**
    163         * Generates a label for the specified row.
    164         *
    165         * @param dataset  the dataset (<code>null</code> not permitted).
    166         * @param row  the row index (zero-based).
    167         *
    168         * @return The label.
    169         */
    170        public String generateRowLabel(CategoryDataset dataset, int row) {
    171            return dataset.getRowKey(row).toString();
    172        }
    173
    174        /**
    175         * Generates a label for the specified row.
    176         *
    177         * @param dataset  the dataset (<code>null</code> not permitted).
    178         * @param column  the column index (zero-based).
    179         *
    180         * @return The label.
    181         */
    182        public String generateColumnLabel(CategoryDataset dataset, int column) {
    183            return dataset.getColumnKey(column).toString();
    184        }
    185
    186        /**
    187         * Returns the label format string.
    188         *
    189         * @return The label format string (never <code>null</code>).
    190         */
    191        public String getLabelFormat() {
    192            return this.labelFormat;
    193        }
    194
    195        /**
    196         * Returns the number formatter.
    197         *
    198         * @return The number formatter (possibly <code>null</code>).
    199         */
    200        public NumberFormat getNumberFormat() {
    201            return this.numberFormat;
    202        }
    203
    204        /**
    205         * Returns the date formatter.
    206         *
    207         * @return The date formatter (possibly <code>null</code>).
    208         */
    209        public DateFormat getDateFormat() {
    210            return this.dateFormat;
    211        }
    212
    213        /**
    214         * Generates a for the specified item.
    215         *
    216         * @param dataset  the dataset (<code>null</code> not permitted).
    217         * @param row  the row index (zero-based).
    218         * @param column  the column index (zero-based).
    219         *
    220         * @return The label (possibly <code>null</code>).
    221         */
    222        protected String generateLabelString(CategoryDataset dataset,
    223                                             int row, int column) {
    224            if (dataset == null) {
    225                throw new IllegalArgumentException("Null 'dataset' argument.");
    226            }
    227            String result = null;
    228            Object[] items = createItemArray(dataset, row, column);
    229            result = MessageFormat.format(this.labelFormat, items);
    230            return result;
    231
    232        }
    233
    234        /**
    235         * Creates the array of items that can be passed to the
    236         * {@link MessageFormat} class for creating labels.
    237         *
    238         * @param dataset  the dataset (<code>null</code> not permitted).
    239         * @param row  the row index (zero-based).
    240         * @param column  the column index (zero-based).
    241         *
    242         * @return The items (never <code>null</code>).
    243         */
    244        protected Object[] createItemArray(CategoryDataset dataset,
    245                                           int row, int column) {
    246            Object[] result = new Object[4];
    247            result[0] = dataset.getRowKey(row).toString();
    248            result[1] = dataset.getColumnKey(column).toString();
    249            Number value = dataset.getValue(row, column);
    250            if (value != null) {
    251                if (this.numberFormat != null) {
    252                    result[2] = this.numberFormat.format(value);
    253                }
    254                else if (this.dateFormat != null) {
    255                    result[2] = this.dateFormat.format(value);
    256                }
    257            }
    258            else {
    259                result[2] = this.nullValueString;
    260            }
    261            if (value != null) {
    262                double total = DataUtilities.calculateColumnTotal(dataset, column);
    263                double percent = value.doubleValue() / total;
    264                result[3] = this.percentFormat.format(percent);
    265            }
    266
    267            return result;
    268        }
    269
    270        /**
    271         * Tests this object for equality with an arbitrary object.
    272         *
    273         * @param obj  the other object (<code>null</code> permitted).
    274         *
    275         * @return A boolean.
    276         */
    277        public boolean equals(Object obj) {
    278            if (obj == this) {
    279                return true;
    280            }
    281            if (!(obj instanceof AbstractCategoryItemLabelGenerator)) {
    282                return false;
    283            }
    284
    285            AbstractCategoryItemLabelGenerator that
    286                = (AbstractCategoryItemLabelGenerator) obj;
    287            if (!this.labelFormat.equals(that.labelFormat)) {
    288                return false;
    289            }
    290            if (!ObjectUtilities.equal(this.dateFormat, that.dateFormat)) {
    291                return false;
    292            }
    293            if (!ObjectUtilities.equal(this.numberFormat, that.numberFormat)) {
    294                return false;
    295            }
    296            return true;
    297        }
    298
    299        /**
    300         * Returns a hash code for this instance.
    301         *
    302         * @return A hash code.
    303         */
    304        public int hashCode() {
    305            int result = 127;
    306            result = HashUtilities.hashCode(result, this.labelFormat);
    307            result = HashUtilities.hashCode(result, this.nullValueString);
    308            result = HashUtilities.hashCode(result, this.dateFormat);
    309            result = HashUtilities.hashCode(result, this.numberFormat);
    310            result = HashUtilities.hashCode(result, this.percentFormat);
    311            return result;
    312        }
    313
    314        /**
    315         * Returns an independent copy of the generator.
    316         *
    317         * @return A clone.
    318         *
    319         * @throws CloneNotSupportedException  should not happen.
    320         */
    321        public Object clone() throws CloneNotSupportedException {
    322            AbstractCategoryItemLabelGenerator clone
    323                = (AbstractCategoryItemLabelGenerator) super.clone();
    324            if (this.numberFormat != null) {
    325                clone.numberFormat = (NumberFormat) this.numberFormat.clone();
    326            }
    327            if (this.dateFormat != null) {
    328                clone.dateFormat = (DateFormat) this.dateFormat.clone();
    329            }
    330            return clone;
    331        }
    332
    333    }