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     * AbstractXYItemLabelGenerator.java
029     * ---------------------------------
030     * (C) Copyright 2004-2007, by Object Refinery Limited.
031     *
032     * Original Author:  David Gilbert (for Object Refinery Limited);
033     * Contributor(s):   -;
034     *
035     * Changes
036     * -------
037     * 27-Feb-2004 : Version 1 (DG);
038     * 12-May-2004 : Moved default tool tip format to 
039     *               StandardXYToolTipGenerator (DG);
040     * 15-Jul-2004 : Switched getX() with getXValue() and getY() with 
041     *               getYValue() (DG);
042     * 08-Oct-2004 : Modified createItemArray() method to handle null values (DG);
043     * 10-Jan-2005 : Updated createItemArray() to use x, y primitives if 
044     *               possible (DG);
045     * ------------- JFREECHART 1.0.x --------------------------------------------
046     * 26-Jan-2006 : Minor API doc update (DG);
047     * 25-Jan-2007 : Added new constructor and fixed bug in clone() method (DG);
048     * 16-Oct-2007 : Removed redundant code (DG);
049     * 23-Nov-2007 : Implemented hashCode() (DG);
050     * 
051     */
052    
053    package org.jfree.chart.labels;
054    
055    import java.io.Serializable;
056    import java.text.DateFormat;
057    import java.text.MessageFormat;
058    import java.text.NumberFormat;
059    import java.util.Date;
060    
061    import org.jfree.chart.HashUtilities;
062    import org.jfree.data.xy.XYDataset;
063    import org.jfree.util.ObjectUtilities;
064    
065    /**
066     * A base class for creating item label generators.
067     */
068    public class AbstractXYItemLabelGenerator implements Cloneable, Serializable {
069        
070        /** For serialization. */
071        private static final long serialVersionUID = 5869744396278660636L;
072        
073        /** The item label format string. */
074        private String formatString;
075        
076        /** A number formatter for the x value. */
077        private NumberFormat xFormat;
078        
079        /** A date formatter for the x value. */
080        private DateFormat xDateFormat;
081    
082        /** A formatter for the y value. */
083        private NumberFormat yFormat;
084    
085        /** A date formatter for the y value. */
086        private DateFormat yDateFormat;
087        
088        /** The string used to represent 'null' for the y-value. */
089        private String nullYString = "null";
090        
091        /**
092         * Creates an item label generator using default number formatters.
093         */
094        protected AbstractXYItemLabelGenerator() {
095            this("{2}", NumberFormat.getNumberInstance(), 
096                    NumberFormat.getNumberInstance());
097        }
098    
099        /**
100         * Creates an item label generator using the specified number formatters.
101         *
102         * @param formatString  the item label format string (<code>null</code> 
103         *                      not permitted).
104         * @param xFormat  the format object for the x values (<code>null</code> 
105         *                 not permitted).
106         * @param yFormat  the format object for the y values (<code>null</code> 
107         *                 not permitted).
108         */
109        protected AbstractXYItemLabelGenerator(String formatString,
110                                               NumberFormat xFormat, 
111                                               NumberFormat yFormat) {
112    
113            if (formatString == null) {
114                throw new IllegalArgumentException("Null 'formatString' argument.");
115            }
116            if (xFormat == null) {
117                throw new IllegalArgumentException("Null 'xFormat' argument.");   
118            }
119            if (yFormat == null) {
120                throw new IllegalArgumentException("Null 'yFormat' argument.");   
121            }
122            this.formatString = formatString;
123            this.xFormat = xFormat;
124            this.yFormat = yFormat;
125    
126        }
127    
128        /**
129         * Creates an item label generator using the specified number formatters.
130         *
131         * @param formatString  the item label format string (<code>null</code> 
132         *                      not permitted).
133         * @param xFormat  the format object for the x values (<code>null</code> 
134         *                 permitted).
135         * @param yFormat  the format object for the y values (<code>null</code> 
136         *                 not permitted).
137         */
138        protected AbstractXYItemLabelGenerator(String formatString,
139                                               DateFormat xFormat, 
140                                               NumberFormat yFormat) {
141    
142            this(formatString, NumberFormat.getInstance(), yFormat);
143            this.xDateFormat = xFormat;
144        
145        }
146        
147        /**
148         * Creates an item label generator using the specified formatters (a 
149         * number formatter for the x-values and a date formatter for the 
150         * y-values).
151         *
152         * @param formatString  the item label format string (<code>null</code> 
153         *                      not permitted).
154         * @param xFormat  the format object for the x values (<code>null</code> 
155         *                 permitted).
156         * @param yFormat  the format object for the y values (<code>null</code> 
157         *                 not permitted).
158         *                 
159         * @since 1.0.4
160         */
161        protected AbstractXYItemLabelGenerator(String formatString,
162                NumberFormat xFormat, DateFormat yFormat) {
163            
164            this(formatString, xFormat, NumberFormat.getInstance());
165            this.yDateFormat = yFormat;
166        }
167        
168        /**
169         * Creates an item label generator using the specified number formatters.
170         *
171         * @param formatString  the item label format string (<code>null</code> 
172         *                      not permitted).
173         * @param xFormat  the format object for the x values (<code>null</code> 
174         *                 permitted).
175         * @param yFormat  the format object for the y values (<code>null</code> 
176         *                 not permitted).
177         */
178        protected AbstractXYItemLabelGenerator(String formatString,
179                                               DateFormat xFormat, 
180                                               DateFormat yFormat) {
181    
182            this(formatString, NumberFormat.getInstance(), 
183                    NumberFormat.getInstance());
184            this.xDateFormat = xFormat;
185            this.yDateFormat = yFormat;
186        
187        }
188        
189        /**
190         * Returns the format string (this controls the overall structure of the 
191         * label).
192         * 
193         * @return The format string (never <code>null</code>).
194         */
195        public String getFormatString() {
196            return this.formatString;
197        }
198        
199        /**
200         * Returns the number formatter for the x-values.
201         *
202         * @return The number formatter (possibly <code>null</code>).
203         */
204        public NumberFormat getXFormat() {
205            return this.xFormat;
206        }
207    
208        /**
209         * Returns the date formatter for the x-values.
210         *
211         * @return The date formatter (possibly <code>null</code>).
212         */
213        public DateFormat getXDateFormat() {
214            return this.xDateFormat;
215        }
216    
217        /**
218         * Returns the number formatter for the y-values.
219         *
220         * @return The number formatter (possibly <code>null</code>).
221         */
222        public NumberFormat getYFormat() {
223            return this.yFormat;
224        }
225    
226        /**
227         * Returns the date formatter for the y-values.
228         *
229         * @return The date formatter (possibly <code>null</code>).
230         */
231        public DateFormat getYDateFormat() {
232            return this.yDateFormat;
233        }
234    
235        /**
236         * Generates a label string for an item in the dataset.
237         *
238         * @param dataset  the dataset (<code>null</code> not permitted).
239         * @param series  the series (zero-based index).
240         * @param item  the item (zero-based index).
241         *
242         * @return The label (possibly <code>null</code>).
243         */
244        public String generateLabelString(XYDataset dataset, int series, int item) {
245            String result = null;    
246            Object[] items = createItemArray(dataset, series, item);
247            result = MessageFormat.format(this.formatString, items);
248            return result;
249        }
250    
251        /**
252         * Creates the array of items that can be passed to the 
253         * {@link MessageFormat} class for creating labels.
254         *
255         * @param dataset  the dataset (<code>null</code> not permitted).
256         * @param series  the series (zero-based index).
257         * @param item  the item (zero-based index).
258         *
259         * @return An array of three items from the dataset formatted as
260         *         <code>String</code> objects (never <code>null</code>).
261         */
262        protected Object[] createItemArray(XYDataset dataset, int series, 
263                                           int item) {
264            Object[] result = new Object[3];
265            result[0] = dataset.getSeriesKey(series).toString();
266            
267            double x = dataset.getXValue(series, item);
268            if (this.xDateFormat != null) {
269                result[1] = this.xDateFormat.format(new Date((long) x));   
270            }
271            else {
272                result[1] = this.xFormat.format(x);
273            }
274            
275            double y = dataset.getYValue(series, item);
276            if (Double.isNaN(y) && dataset.getY(series, item) == null) {
277                result[2] = this.nullYString;
278            }
279            else {
280                if (this.yDateFormat != null) {
281                    result[2] = this.yDateFormat.format(new Date((long) y));   
282                }
283                else {
284                    result[2] = this.yFormat.format(y);
285                }
286            }
287            return result;
288        }
289    
290        /**
291         * Tests this object for equality with an arbitrary object.
292         *
293         * @param obj  the other object (<code>null</code> permitted).
294         *
295         * @return A boolean.
296         */
297        public boolean equals(Object obj) {
298            if (obj == this) {
299                return true;
300            }
301            if (!(obj instanceof AbstractXYItemLabelGenerator)) {
302                return false;
303            }
304            AbstractXYItemLabelGenerator that = (AbstractXYItemLabelGenerator) obj;
305            if (!this.formatString.equals(that.formatString)) {
306                return false;   
307            }
308            if (!ObjectUtilities.equal(this.xFormat, that.xFormat)) {
309                return false;   
310            }
311            if (!ObjectUtilities.equal(this.xDateFormat, that.xDateFormat)) {
312                return false;   
313            }
314            if (!ObjectUtilities.equal(this.yFormat, that.yFormat)) {
315                return false;   
316            }
317            if (!ObjectUtilities.equal(this.yDateFormat, that.yDateFormat)) {
318                return false;   
319            }
320            return true;
321        }
322    
323        /**
324         * Returns a hash code for this instance.
325         * 
326         * @return A hash code.
327         */
328        public int hashCode() {
329            int result = 127;
330            result = HashUtilities.hashCode(result, this.formatString);
331            result = HashUtilities.hashCode(result, this.xFormat);
332            result = HashUtilities.hashCode(result, this.xDateFormat);
333            result = HashUtilities.hashCode(result, this.yFormat);
334            result = HashUtilities.hashCode(result, this.yDateFormat);
335            return result;
336        }
337        
338        /**
339         * Returns an independent copy of the generator.
340         * 
341         * @return A clone.
342         * 
343         * @throws CloneNotSupportedException if cloning is not supported.
344         */
345        public Object clone() throws CloneNotSupportedException {
346            AbstractXYItemLabelGenerator clone 
347                    = (AbstractXYItemLabelGenerator) super.clone();
348            if (this.xFormat != null) {
349                clone.xFormat = (NumberFormat) this.xFormat.clone();
350            }
351            if (this.yFormat != null) {
352                clone.yFormat = (NumberFormat) this.yFormat.clone();
353            }
354            if (this.xDateFormat != null) {
355                clone.xDateFormat = (DateFormat) this.xDateFormat.clone();
356            }
357            if (this.yDateFormat != null) {
358                clone.yDateFormat = (DateFormat) this.yDateFormat.clone();
359            }        
360            return clone;
361        }
362        
363    }