001/* ===========================================================
002 * JFreeChart : a free chart library for the Java(tm) platform
003 * ===========================================================
004 *
005 * (C) Copyright 2000-2022, by David Gilbert 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 * XYBarDataset.java
029 * -----------------
030 * (C) Copyright 2004-2022, by David Gilbert and Contributors.
031 *
032 * Original Author:  David Gilbert;
033 * Contributor(s):   -;
034 *
035 */
036
037package org.jfree.data.xy;
038
039import org.jfree.chart.api.PublicCloneable;
040import org.jfree.data.general.DatasetChangeEvent;
041import org.jfree.data.general.DatasetChangeListener;
042
043/**
044 * A dataset wrapper class that converts a standard {@link XYDataset} into an
045 * {@link IntervalXYDataset} suitable for use in creating XY bar charts.
046 */
047public class XYBarDataset<S extends Comparable<S>> 
048        extends AbstractIntervalXYDataset<S>
049        implements IntervalXYDataset<S>, DatasetChangeListener, PublicCloneable {
050
051    /** The underlying dataset. */
052    private XYDataset<S> underlying;
053
054    /** The bar width. */
055    private double barWidth;
056
057    /**
058     * Creates a new dataset.
059     *
060     * @param underlying  the underlying dataset ({@code null} not
061     *     permitted).
062     * @param barWidth  the width of the bars.
063     */
064    public XYBarDataset(XYDataset<S> underlying, double barWidth) {
065        this.underlying = underlying;
066        this.underlying.addChangeListener(this);
067        this.barWidth = barWidth;
068    }
069
070    /**
071     * Returns the underlying dataset that was specified via the constructor.
072     *
073     * @return The underlying dataset (never {@code null}).
074     */
075    public XYDataset<S> getUnderlyingDataset() {
076        return this.underlying;
077    }
078
079    /**
080     * Returns the bar width.
081     *
082     * @return The bar width.
083     *
084     * @see #setBarWidth(double)
085     */
086    public double getBarWidth() {
087        return this.barWidth;
088    }
089
090    /**
091     * Sets the bar width and sends a {@link DatasetChangeEvent} to all
092     * registered listeners.
093     *
094     * @param barWidth  the bar width.
095     *
096     * @see #getBarWidth()
097     */
098    public void setBarWidth(double barWidth) {
099        this.barWidth = barWidth;
100        notifyListeners(new DatasetChangeEvent(this, this));
101    }
102
103    /**
104     * Returns the number of series in the dataset.
105     *
106     * @return The series count.
107     */
108    @Override
109    public int getSeriesCount() {
110        return this.underlying.getSeriesCount();
111    }
112
113    /**
114     * Returns the key for a series.
115     *
116     * @param series  the series index (in the range {@code 0} to
117     *     {@code getSeriesCount() - 1}).
118     *
119     * @return The series key.
120     */
121    @Override
122    public S getSeriesKey(int series) {
123        return this.underlying.getSeriesKey(series);
124    }
125
126    /**
127     * Returns the number of items in a series.
128     *
129     * @param series  the series index (zero-based).
130     *
131     * @return The item count.
132     */
133    @Override
134    public int getItemCount(int series) {
135        return this.underlying.getItemCount(series);
136    }
137
138    /**
139     * Returns the x-value for an item within a series.
140     *
141     * @param series  the series index (zero-based).
142     * @param item  the item index (zero-based).
143     *
144     * @return The x-value.
145     *
146     * @see #getXValue(int, int)
147     */
148    @Override
149    public Number getX(int series, int item) {
150        return this.underlying.getX(series, item);
151    }
152
153    /**
154     * Returns the x-value (as a double primitive) for an item within a series.
155     *
156     * @param series  the series index (zero-based).
157     * @param item  the item index (zero-based).
158     *
159     * @return The value.
160     *
161     * @see #getX(int, int)
162     */
163    @Override
164    public double getXValue(int series, int item) {
165        return this.underlying.getXValue(series, item);
166    }
167
168    /**
169     * Returns the y-value for an item within a series.
170     *
171     * @param series  the series index (zero-based).
172     * @param item  the item index (zero-based).
173     *
174     * @return The y-value (possibly {@code null}).
175     *
176     * @see #getYValue(int, int)
177     */
178    @Override
179    public Number getY(int series, int item) {
180        return this.underlying.getY(series, item);
181    }
182
183    /**
184     * Returns the y-value (as a double primitive) for an item within a series.
185     *
186     * @param series  the series index (zero-based).
187     * @param item  the item index (zero-based).
188     *
189     * @return The value.
190     *
191     * @see #getY(int, int)
192     */
193    @Override
194    public double getYValue(int series, int item) {
195        return this.underlying.getYValue(series, item);
196    }
197
198    /**
199     * Returns the starting X value for the specified series and item.
200     *
201     * @param series  the series index (zero-based).
202     * @param item  the item index (zero-based).
203     *
204     * @return The value.
205     */
206    @Override
207    public Number getStartX(int series, int item) {
208        Number result = null;
209        Number xnum = this.underlying.getX(series, item);
210        if (xnum != null) {
211             result = xnum.doubleValue() - this.barWidth / 2.0;
212        }
213        return result;
214    }
215
216    /**
217     * Returns the starting x-value (as a double primitive) for an item within
218     * a series.
219     *
220     * @param series  the series index (zero-based).
221     * @param item  the item index (zero-based).
222     *
223     * @return The value.
224     *
225     * @see #getXValue(int, int)
226     */
227    @Override
228    public double getStartXValue(int series, int item) {
229        return getXValue(series, item) - this.barWidth / 2.0;
230    }
231
232    /**
233     * Returns the ending X value for the specified series and item.
234     *
235     * @param series  the series index (zero-based).
236     * @param item  the item index (zero-based).
237     *
238     * @return The value.
239     */
240    @Override
241    public Number getEndX(int series, int item) {
242        Number result = null;
243        Number xnum = this.underlying.getX(series, item);
244        if (xnum != null) {
245             result = xnum.doubleValue() + this.barWidth / 2.0;
246        }
247        return result;
248    }
249
250    /**
251     * Returns the ending x-value (as a double primitive) for an item within
252     * a series.
253     *
254     * @param series  the series index (zero-based).
255     * @param item  the item index (zero-based).
256     *
257     * @return The value.
258     *
259     * @see #getXValue(int, int)
260     */
261    @Override
262    public double getEndXValue(int series, int item) {
263        return getXValue(series, item) + this.barWidth / 2.0;
264    }
265
266    /**
267     * Returns the starting Y value for the specified series and item.
268     *
269     * @param series  the series index (zero-based).
270     * @param item  the item index (zero-based).
271     *
272     * @return The value.
273     */
274    @Override
275    public Number getStartY(int series, int item) {
276        return this.underlying.getY(series, item);
277    }
278
279    /**
280     * Returns the starting y-value (as a double primitive) for an item within
281     * a series.
282     *
283     * @param series  the series index (zero-based).
284     * @param item  the item index (zero-based).
285     *
286     * @return The value.
287     *
288     * @see #getYValue(int, int)
289     */
290    @Override
291    public double getStartYValue(int series, int item) {
292        return getYValue(series, item);
293    }
294
295    /**
296     * Returns the ending Y value for the specified series and item.
297     *
298     * @param series  the series index (zero-based).
299     * @param item  the item index (zero-based).
300     *
301     * @return The value.
302     */
303    @Override
304    public Number getEndY(int series, int item) {
305        return this.underlying.getY(series, item);
306    }
307
308    /**
309     * Returns the ending y-value (as a double primitive) for an item within
310     * a series.
311     *
312     * @param series  the series index (zero-based).
313     * @param item  the item index (zero-based).
314     *
315     * @return The value.
316     *
317     * @see #getYValue(int, int)
318     */
319    @Override
320    public double getEndYValue(int series, int item) {
321        return getYValue(series, item);
322    }
323
324    /**
325     * Receives notification of an dataset change event.
326     *
327     * @param event  information about the event.
328     */
329    @Override
330    public void datasetChanged(DatasetChangeEvent event) {
331        notifyListeners(event);
332    }
333
334    /**
335     * Tests this dataset for equality with an arbitrary object.
336     *
337     * @param obj  the object ({@code null} permitted).
338     *
339     * @return A boolean.
340     */
341    @Override
342    public boolean equals(Object obj) {
343        if (obj == this) {
344            return true;
345        }
346        if (!(obj instanceof XYBarDataset)) {
347            return false;
348        }
349        XYBarDataset<S> that = (XYBarDataset) obj;
350        if (!this.underlying.equals(that.underlying)) {
351            return false;
352        }
353        if (this.barWidth != that.barWidth) {
354            return false;
355        }
356        return true;
357    }
358
359    /**
360     * Returns an independent copy of the dataset.  Note that:
361     * <ul>
362     * <li>the underlying dataset is only cloned if it implements the
363     * {@link PublicCloneable} interface;</li>
364     * <li>the listeners registered with this dataset are not carried over to
365     * the cloned dataset.</li>
366     * </ul>
367     *
368     * @return An independent copy of the dataset.
369     *
370     * @throws CloneNotSupportedException if the dataset cannot be cloned for
371     *         any reason.
372     */
373    @Override
374    public Object clone() throws CloneNotSupportedException {
375        XYBarDataset clone = (XYBarDataset) super.clone();
376        if (this.underlying instanceof PublicCloneable) {
377            PublicCloneable pc = (PublicCloneable) this.underlying;
378            clone.underlying = (XYDataset) pc.clone();
379        }
380        return clone;
381    }
382
383}