1 /* ===========================================================
2  * JFreeChart : a free chart library for the Java(tm) platform
3  * ===========================================================
4  *
5  * (C) Copyright 2000-2013, by Object Refinery Limited and Contributors.
6  *
7  * Project Info:  http://www.jfree.org/jfreechart/index.html
8  *
9  * This library is free software; you can redistribute it and/or modify it
10  * under the terms of the GNU Lesser General Public License as published by
11  * the Free Software Foundation; either version 2.1 of the License, or
12  * (at your option) any later version.
13  *
14  * This library is distributed in the hope that it will be useful, but
15  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
16  * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
17  * License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public
20  * License along with this library; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301,
22  * USA.
23  *
24  * [Oracle and Java are registered trademarks of Oracle and/or its affiliates.
25  * Other names may be trademarks of their respective owners.]
26  *
27  * ---------------------------
28  * CategoryTableXYDataset.java
29  * ---------------------------
30  * (C) Copyright 2004-2011, by Andreas Schroeder and Contributors.
31  *
32  * Original Author:  Andreas Schroeder;
33  * Contributor(s):   David Gilbert (for Object Refinery Limited);
34  *
35  * Changes
36  * -------
37  * 31-Mar-2004 : Version 1 (AS);
38  * 05-May-2004 : Now extends AbstractIntervalXYDataset (DG);
39  * 15-Jul-2004 : Switched interval access method names (DG);
40  * 18-Aug-2004 : Moved from org.jfree.data --> org.jfree.data.xy (DG);
41  * 17-Nov-2004 : Updates required by changes to DomainInfo interface (DG);
42  * 11-Jan-2005 : Removed deprecated code in preparation for 1.0.0 release (DG);
43  * 05-Oct-2005 : Made the interval delegate a dataset change listener (DG);
44  * 02-Feb-2007 : Removed author tags all over JFreeChart sources (DG);
45  * 22-Apr-2008 : Implemented PublicCloneable, and fixed clone() method (DG);
46  * 18-Oct-2011 : Fixed bug 3190615 - added clear() method (DG);
47  *
48  */
49 
50 package org.jfree.data.xy;
51 
52 import org.jfree.data.DefaultKeyedValues2D;
53 import org.jfree.data.DomainInfo;
54 import org.jfree.data.Range;
55 import org.jfree.data.general.DatasetChangeEvent;
56 import org.jfree.data.general.DatasetUtilities;
57 import org.jfree.util.PublicCloneable;
58 
59 /**
60  * An implementation variant of the {@link TableXYDataset} where every series
61  * shares the same x-values (required for generating stacked area charts).
62  * This implementation uses a {@link DefaultKeyedValues2D} Object as backend
63  * implementation and is hence more "category oriented" than the {@link
64  * DefaultTableXYDataset} implementation.
65  * <p>
66  * This implementation provides no means to remove data items yet.
67  * This is due to the lack of such facility in the DefaultKeyedValues2D class.
68  * <p>
69  * This class also implements the {@link IntervalXYDataset} interface, but this
70  * implementation is provisional.
71  */
72 public class CategoryTableXYDataset extends AbstractIntervalXYDataset
73         implements TableXYDataset, IntervalXYDataset, DomainInfo,
74                    PublicCloneable {
75 
76     /**
77      * The backing data structure.
78      */
79     private DefaultKeyedValues2D values;
80 
81     /** A delegate for controlling the interval width. */
82     private IntervalXYDelegate intervalDelegate;
83 
84     /**
85      * Creates a new empty CategoryTableXYDataset.
86      */
CategoryTableXYDataset()87     public CategoryTableXYDataset() {
88         this.values = new DefaultKeyedValues2D(true);
89         this.intervalDelegate = new IntervalXYDelegate(this);
90         addChangeListener(this.intervalDelegate);
91     }
92 
93     /**
94      * Adds a data item to this dataset and sends a {@link DatasetChangeEvent}
95      * to all registered listeners.
96      *
97      * @param x  the x value.
98      * @param y  the y value.
99      * @param seriesName  the name of the series to add the data item.
100      */
add(double x, double y, String seriesName)101     public void add(double x, double y, String seriesName) {
102         add(new Double(x), new Double(y), seriesName, true);
103     }
104 
105     /**
106      * Adds a data item to this dataset and, if requested, sends a
107      * {@link DatasetChangeEvent} to all registered listeners.
108      *
109      * @param x  the x value.
110      * @param y  the y value.
111      * @param seriesName  the name of the series to add the data item.
112      * @param notify  notify listeners?
113      */
add(Number x, Number y, String seriesName, boolean notify)114     public void add(Number x, Number y, String seriesName, boolean notify) {
115         this.values.addValue(y, (Comparable) x, seriesName);
116         if (notify) {
117             fireDatasetChanged();
118         }
119     }
120 
121     /**
122      * Removes a value from the dataset.
123      *
124      * @param x  the x-value.
125      * @param seriesName  the series name.
126      */
remove(double x, String seriesName)127     public void remove(double x, String seriesName) {
128         remove(new Double(x), seriesName, true);
129     }
130 
131     /**
132      * Removes an item from the dataset.
133      *
134      * @param x  the x-value.
135      * @param seriesName  the series name.
136      * @param notify  notify listeners?
137      */
remove(Number x, String seriesName, boolean notify)138     public void remove(Number x, String seriesName, boolean notify) {
139         this.values.removeValue((Comparable) x, seriesName);
140         if (notify) {
141             fireDatasetChanged();
142         }
143     }
144 
145     /**
146      * Clears all data from the dataset and sends a {@link DatasetChangeEvent}
147      * to all registered listeners.
148      *
149      * @since 1.0.14
150      */
clear()151     public void clear() {
152         this.values.clear();
153         fireDatasetChanged();
154     }
155 
156     /**
157      * Returns the number of series in the collection.
158      *
159      * @return The series count.
160      */
161     @Override
getSeriesCount()162     public int getSeriesCount() {
163         return this.values.getColumnCount();
164     }
165 
166     /**
167      * Returns the key for a series.
168      *
169      * @param series  the series index (zero-based).
170      *
171      * @return The key for a series.
172      */
173     @Override
getSeriesKey(int series)174     public Comparable getSeriesKey(int series) {
175         return this.values.getColumnKey(series);
176     }
177 
178     /**
179      * Returns the number of x values in the dataset.
180      *
181      * @return The item count.
182      */
183     @Override
getItemCount()184     public int getItemCount() {
185         return this.values.getRowCount();
186     }
187 
188     /**
189      * Returns the number of items in the specified series.
190      * Returns the same as {@link CategoryTableXYDataset#getItemCount()}.
191      *
192      * @param series  the series index (zero-based).
193      *
194      * @return The item count.
195      */
196     @Override
getItemCount(int series)197     public int getItemCount(int series) {
198         return getItemCount();  // all series have the same number of items in
199                                 // this dataset
200     }
201 
202     /**
203      * Returns the x-value for the specified series and item.
204      *
205      * @param series  the series index (zero-based).
206      * @param item  the item index (zero-based).
207      *
208      * @return The value.
209      */
210     @Override
getX(int series, int item)211     public Number getX(int series, int item) {
212         return (Number) this.values.getRowKey(item);
213     }
214 
215     /**
216      * Returns the starting X value for the specified series and item.
217      *
218      * @param series  the series index (zero-based).
219      * @param item  the item index (zero-based).
220      *
221      * @return The starting X value.
222      */
223     @Override
getStartX(int series, int item)224     public Number getStartX(int series, int item) {
225         return this.intervalDelegate.getStartX(series, item);
226     }
227 
228     /**
229      * Returns the ending X value for the specified series and item.
230      *
231      * @param series  the series index (zero-based).
232      * @param item  the item index (zero-based).
233      *
234      * @return The ending X value.
235      */
236     @Override
getEndX(int series, int item)237     public Number getEndX(int series, int item) {
238         return this.intervalDelegate.getEndX(series, item);
239     }
240 
241     /**
242      * Returns the y-value for the specified series and item.
243      *
244      * @param series  the series index (zero-based).
245      * @param item  the item index (zero-based).
246      *
247      * @return The y value (possibly <code>null</code>).
248      */
249     @Override
getY(int series, int item)250     public Number getY(int series, int item) {
251         return this.values.getValue(item, series);
252     }
253 
254     /**
255      * Returns the starting Y value for the specified series and item.
256      *
257      * @param series  the series index (zero-based).
258      * @param item  the item index (zero-based).
259      *
260      * @return The starting Y value.
261      */
262     @Override
getStartY(int series, int item)263     public Number getStartY(int series, int item) {
264         return getY(series, item);
265     }
266 
267     /**
268      * Returns the ending Y value for the specified series and item.
269      *
270      * @param series  the series index (zero-based).
271      * @param item  the item index (zero-based).
272      *
273      * @return The ending Y value.
274      */
275     @Override
getEndY(int series, int item)276     public Number getEndY(int series, int item) {
277         return getY(series, item);
278     }
279 
280     /**
281      * Returns the minimum x-value in the dataset.
282      *
283      * @param includeInterval  a flag that determines whether or not the
284      *                         x-interval is taken into account.
285      *
286      * @return The minimum value.
287      */
288     @Override
getDomainLowerBound(boolean includeInterval)289     public double getDomainLowerBound(boolean includeInterval) {
290         return this.intervalDelegate.getDomainLowerBound(includeInterval);
291     }
292 
293     /**
294      * Returns the maximum x-value in the dataset.
295      *
296      * @param includeInterval  a flag that determines whether or not the
297      *                         x-interval is taken into account.
298      *
299      * @return The maximum value.
300      */
301     @Override
getDomainUpperBound(boolean includeInterval)302     public double getDomainUpperBound(boolean includeInterval) {
303         return this.intervalDelegate.getDomainUpperBound(includeInterval);
304     }
305 
306     /**
307      * Returns the range of the values in this dataset's domain.
308      *
309      * @param includeInterval  a flag that determines whether or not the
310      *                         x-interval is taken into account.
311      *
312      * @return The range.
313      */
314     @Override
getDomainBounds(boolean includeInterval)315     public Range getDomainBounds(boolean includeInterval) {
316         if (includeInterval) {
317             return this.intervalDelegate.getDomainBounds(includeInterval);
318         }
319         else {
320             return DatasetUtilities.iterateDomainBounds(this, includeInterval);
321         }
322     }
323 
324     /**
325      * Returns the interval position factor.
326      *
327      * @return The interval position factor.
328      */
getIntervalPositionFactor()329     public double getIntervalPositionFactor() {
330         return this.intervalDelegate.getIntervalPositionFactor();
331     }
332 
333     /**
334      * Sets the interval position factor. Must be between 0.0 and 1.0 inclusive.
335      * If the factor is 0.5, the gap is in the middle of the x values. If it
336      * is lesser than 0.5, the gap is farther to the left and if greater than
337      * 0.5 it gets farther to the right.
338      *
339      * @param d  the new interval position factor.
340      */
setIntervalPositionFactor(double d)341     public void setIntervalPositionFactor(double d) {
342         this.intervalDelegate.setIntervalPositionFactor(d);
343         fireDatasetChanged();
344     }
345 
346     /**
347      * Returns the full interval width.
348      *
349      * @return The interval width to use.
350      */
getIntervalWidth()351     public double getIntervalWidth() {
352         return this.intervalDelegate.getIntervalWidth();
353     }
354 
355     /**
356      * Sets the interval width to a fixed value, and sends a
357      * {@link DatasetChangeEvent} to all registered listeners.
358      *
359      * @param d  the new interval width (must be > 0).
360      */
setIntervalWidth(double d)361     public void setIntervalWidth(double d) {
362         this.intervalDelegate.setFixedIntervalWidth(d);
363         fireDatasetChanged();
364     }
365 
366     /**
367      * Returns whether the interval width is automatically calculated or not.
368      *
369      * @return whether the width is automatically calculated or not.
370      */
isAutoWidth()371     public boolean isAutoWidth() {
372         return this.intervalDelegate.isAutoWidth();
373     }
374 
375     /**
376      * Sets the flag that indicates whether the interval width is automatically
377      * calculated or not.
378      *
379      * @param b  the flag.
380      */
setAutoWidth(boolean b)381     public void setAutoWidth(boolean b) {
382         this.intervalDelegate.setAutoWidth(b);
383         fireDatasetChanged();
384     }
385 
386     /**
387      * Tests this dataset for equality with an arbitrary object.
388      *
389      * @param obj  the object (<code>null</code> permitted).
390      *
391      * @return A boolean.
392      */
393     @Override
equals(Object obj)394     public boolean equals(Object obj) {
395         if (!(obj instanceof CategoryTableXYDataset)) {
396             return false;
397         }
398         CategoryTableXYDataset that = (CategoryTableXYDataset) obj;
399         if (!this.intervalDelegate.equals(that.intervalDelegate)) {
400             return false;
401         }
402         if (!this.values.equals(that.values)) {
403             return false;
404         }
405         return true;
406     }
407 
408     /**
409      * Returns an independent copy of this dataset.
410      *
411      * @return A clone.
412      *
413      * @throws CloneNotSupportedException if there is some reason that cloning
414      *     cannot be performed.
415      */
416     @Override
clone()417     public Object clone() throws CloneNotSupportedException {
418         CategoryTableXYDataset clone = (CategoryTableXYDataset) super.clone();
419         clone.values = (DefaultKeyedValues2D) this.values.clone();
420         clone.intervalDelegate = new IntervalXYDelegate(clone);
421         // need to configure the intervalDelegate to match the original
422         clone.intervalDelegate.setFixedIntervalWidth(getIntervalWidth());
423         clone.intervalDelegate.setAutoWidth(isAutoWidth());
424         clone.intervalDelegate.setIntervalPositionFactor(
425                 getIntervalPositionFactor());
426         return clone;
427     }
428 
429 }
430