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