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