001/* ===========================================================
002 * JFreeChart : a free chart library for the Java(tm) platform
003 * ===========================================================
004 *
005 * (C) Copyright 2000-2007, 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-2007, 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 */
043
044package org.jfree.chart.labels;
045
046import java.io.Serializable;
047import java.text.DateFormat;
048import java.text.MessageFormat;
049import java.text.NumberFormat;
050
051import org.jfree.data.DataUtilities;
052import org.jfree.data.category.CategoryDataset;
053import org.jfree.util.ObjectUtilities;
054import org.jfree.util.PublicCloneable;
055
056/**
057 * A base class that can be used to create a label or tooltip generator that 
058 * can be assigned to a 
059 * {@link org.jfree.chart.renderer.category.CategoryItemRenderer}.
060 */
061public abstract class AbstractCategoryItemLabelGenerator 
062                implements PublicCloneable, Cloneable, Serializable {
063
064    /** For serialization. */
065    private static final long serialVersionUID = -7108591260223293197L;
066    
067    /** 
068     * The label format string used by a <code>MessageFormat</code> object to 
069     * combine the standard items:  {0} = series name, {1} = category, 
070     * {2} = value, {3} = value as a percentage of the column total.
071     */
072    private String labelFormat;
073    
074    /** The string used to represent a null value. */
075    private String nullValueString;
076    
077    /** 
078     * A number formatter used to preformat the value before it is passed to 
079     * the MessageFormat object. 
080     */
081    private NumberFormat numberFormat;
082
083    /**
084     * A date formatter used to preformat the value before it is passed to the
085     * MessageFormat object.
086     */ 
087    private DateFormat dateFormat;
088    
089    /**
090     * A number formatter used to preformat the percentage value before it is 
091     * passed to the MessageFormat object.
092     */ 
093    private NumberFormat percentFormat;
094    
095    /**
096     * Creates a label generator with the specified number formatter.
097     *
098     * @param labelFormat  the label format string (<code>null</code> not 
099     *                     permitted).
100     * @param formatter  the number formatter (<code>null</code> not permitted).
101     */
102    protected AbstractCategoryItemLabelGenerator(String labelFormat, 
103                                                 NumberFormat formatter) {
104        this(labelFormat, formatter, NumberFormat.getPercentInstance());
105    }
106
107    /**
108     * Creates a label generator with the specified number formatter.
109     *
110     * @param labelFormat  the label format string (<code>null</code> not 
111     *                     permitted).
112     * @param formatter  the number formatter (<code>null</code> not permitted).
113     * @param percentFormatter  the percent formatter (<code>null</code> not
114     *     permitted).
115     *     
116     * @since 1.0.2
117     */
118    protected AbstractCategoryItemLabelGenerator(String labelFormat, 
119            NumberFormat formatter, NumberFormat percentFormatter) {
120        if (labelFormat == null) {
121            throw new IllegalArgumentException("Null 'labelFormat' argument.");
122        }
123        if (formatter == null) {
124            throw new IllegalArgumentException("Null 'formatter' argument.");
125        }
126        if (percentFormatter == null) {
127            throw new IllegalArgumentException(
128                    "Null 'percentFormatter' argument.");
129        }
130        this.labelFormat = labelFormat;
131        this.numberFormat = formatter;
132        this.percentFormat = percentFormatter;
133        this.dateFormat = null;
134        this.nullValueString = "-";
135    }
136
137    /**
138     * Creates a label generator with the specified date formatter.
139     *
140     * @param labelFormat  the label format string (<code>null</code> not 
141     *                     permitted).
142     * @param formatter  the date formatter (<code>null</code> not permitted).
143     */
144    protected AbstractCategoryItemLabelGenerator(String labelFormat, 
145                                                 DateFormat formatter) {
146        if (labelFormat == null) {
147            throw new IllegalArgumentException("Null 'labelFormat' argument.");
148        }
149        if (formatter == null) {
150            throw new IllegalArgumentException("Null 'formatter' argument.");
151        }
152        this.labelFormat = labelFormat;
153        this.numberFormat = null;
154        this.percentFormat = NumberFormat.getPercentInstance();
155        this.dateFormat = formatter;
156        this.nullValueString = "-";
157    }
158    
159    /**
160     * Generates a label for the specified row.
161     * 
162     * @param dataset  the dataset (<code>null</code> not permitted).
163     * @param row  the row index (zero-based).
164     * 
165     * @return The label.
166     */
167    public String generateRowLabel(CategoryDataset dataset, int row) {
168        return dataset.getRowKey(row).toString();   
169    }
170    
171    /**
172     * Generates a label for the specified row.
173     * 
174     * @param dataset  the dataset (<code>null</code> not permitted).
175     * @param column  the column index (zero-based).
176     * 
177     * @return The label.
178     */
179    public String generateColumnLabel(CategoryDataset dataset, int column) {
180        return dataset.getColumnKey(column).toString();   
181    }
182
183    /**
184     * Returns the label format string.
185     * 
186     * @return The label format string (never <code>null</code>).
187     */
188    public String getLabelFormat() {
189        return this.labelFormat;   
190    }
191    
192    /**
193     * Returns the number formatter.
194     *
195     * @return The number formatter (possibly <code>null</code>).
196     */
197    public NumberFormat getNumberFormat() {
198        return this.numberFormat;
199    }
200
201    /**
202     * Returns the date formatter.
203     *
204     * @return The date formatter (possibly <code>null</code>).
205     */
206    public DateFormat getDateFormat() {
207        return this.dateFormat;
208    }
209
210    /**
211     * Generates a for the specified item.
212     *
213     * @param dataset  the dataset (<code>null</code> not permitted).
214     * @param row  the row index (zero-based).
215     * @param column  the column index (zero-based).
216     *
217     * @return The label (possibly <code>null</code>).
218     */
219    protected String generateLabelString(CategoryDataset dataset, 
220                                         int row, int column) {
221        if (dataset == null) {
222            throw new IllegalArgumentException("Null 'dataset' argument.");
223        }
224        String result = null;   
225        Object[] items = createItemArray(dataset, row, column);
226        result = MessageFormat.format(this.labelFormat, items);
227        return result;
228
229    }
230
231    /**
232     * Creates the array of items that can be passed to the 
233     * {@link MessageFormat} class for creating labels.
234     *
235     * @param dataset  the dataset (<code>null</code> not permitted).
236     * @param row  the row index (zero-based).
237     * @param column  the column index (zero-based).
238     *
239     * @return The items (never <code>null</code>).
240     */
241    protected Object[] createItemArray(CategoryDataset dataset, 
242                                       int row, int column) {
243        Object[] result = new Object[4];
244        result[0] = dataset.getRowKey(row).toString();
245        result[1] = dataset.getColumnKey(column).toString();
246        Number value = dataset.getValue(row, column);
247        if (value != null) {
248            if (this.numberFormat != null) {
249                result[2] = this.numberFormat.format(value);  
250            }
251            else if (this.dateFormat != null) {
252                result[2] = this.dateFormat.format(value);
253            }
254        }
255        else {
256            result[2] = this.nullValueString;   
257        }
258        if (value != null) {
259            double total = DataUtilities.calculateColumnTotal(dataset, column);
260            double percent = value.doubleValue() / total;
261            result[3] = this.percentFormat.format(percent);
262        }
263       
264        return result;
265    }
266
267    /**
268     * Tests this object for equality with an arbitrary object.
269     *
270     * @param obj  the other object (<code>null</code> permitted).
271     *
272     * @return A boolean.
273     */
274    public boolean equals(Object obj) {
275        if (obj == this) {
276            return true;
277        }
278        if (!(obj instanceof AbstractCategoryItemLabelGenerator)) {
279            return false;
280        }
281        
282        AbstractCategoryItemLabelGenerator that 
283            = (AbstractCategoryItemLabelGenerator) obj;
284        if (!this.labelFormat.equals(that.labelFormat)) {
285            return false;
286        }
287        if (!ObjectUtilities.equal(this.dateFormat, that.dateFormat)) {
288            return false;
289        }
290        if (!ObjectUtilities.equal(this.numberFormat, that.numberFormat)) {
291            return false;
292        }
293        return true;
294    }
295    
296    /**
297     * Returns an independent copy of the generator.
298     * 
299     * @return A clone.
300     * 
301     * @throws CloneNotSupportedException  should not happen.
302     */
303    public Object clone() throws CloneNotSupportedException {
304        AbstractCategoryItemLabelGenerator clone 
305            = (AbstractCategoryItemLabelGenerator) super.clone();
306        if (this.numberFormat != null) {
307            clone.numberFormat = (NumberFormat) this.numberFormat.clone();
308        } 
309        if (this.dateFormat != null) {
310            clone.dateFormat = (DateFormat) this.dateFormat.clone();
311        } 
312        return clone;
313    }
314
315}