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