KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > jfree > data > xy > DefaultTableXYDataset


1 /* ===========================================================
2  * JFreeChart : a free chart library for the Java(tm) platform
3  * ===========================================================
4  *
5  * (C) Copyright 2000-2005, 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  * [Java is a trademark or registered trademark of Sun Microsystems, Inc.
25  * in the United States and other countries.]
26  *
27  * --------------------------
28  * DefaultTableXYDataset.java
29  * --------------------------
30  * (C) Copyright 2003-2005, by Richard Atkinson and Contributors.
31  *
32  * Original Author: Richard Atkinson;
33  * Contributor(s): Jody Brownell;
34  * David Gilbert (for Object Refinery Limited);
35  * Andreas Schroeder;
36  *
37  * $Id: DefaultTableXYDataset.java,v 1.12.2.2 2005/10/25 21:36:51 mungady Exp $
38  *
39  * Changes:
40  * --------
41  * 27-Jul-2003 : XYDataset that forces each series to have a value for every
42  * X-point which is essential for stacked XY area charts (RA);
43  * 18-Aug-2003 : Fixed event notification when removing and updating
44  * series (RA);
45  * 22-Sep-2003 : Functionality moved from TableXYDataset to
46  * DefaultTableXYDataset (RA);
47  * 23-Dec-2003 : Added patch for large datasets, submitted by Jody
48  * Brownell (DG);
49  * 16-Feb-2004 : Added pruning methods (DG);
50  * 31-Mar-2004 : Provisional implementation of IntervalXYDataset (AS);
51  * 01-Apr-2004 : Sound implementation of IntervalXYDataset (AS);
52  * 05-May-2004 : Now extends AbstractIntervalXYDataset (DG);
53  * 15-Jul-2004 : Switched getX() with getXValue() and getY() with
54  * getYValue() (DG);
55  * 18-Aug-2004 : Moved from org.jfree.data --> org.jfree.data.xy (DG);
56  * 11-Jan-2005 : Removed deprecated code in preparation for the 1.0.0
57  * release (DG);
58  * 05-Oct-2005 : Made the interval delegate a dataset listener (DG);
59  *
60  */

61
62 package org.jfree.data.xy;
63
64 import java.util.ArrayList JavaDoc;
65 import java.util.HashSet JavaDoc;
66 import java.util.Iterator JavaDoc;
67 import java.util.List JavaDoc;
68
69 import org.jfree.data.DomainInfo;
70 import org.jfree.data.Range;
71 import org.jfree.data.general.DatasetChangeEvent;
72 import org.jfree.data.general.DatasetUtilities;
73 import org.jfree.data.general.SeriesChangeEvent;
74 import org.jfree.util.ObjectUtilities;
75
76 /**
77  * An {@link XYDataset} where every series shares the same x-values (required
78  * for generating stacked area charts).
79  *
80  * @author Richard Atkinson
81  */

82 public class DefaultTableXYDataset extends AbstractIntervalXYDataset
83                                    implements TableXYDataset,
84                                               IntervalXYDataset, DomainInfo {
85     
86     /**
87      * Storage for the data - this list will contain zero, one or many
88      * XYSeries objects.
89      */

90     private List JavaDoc data = null;
91     
92     /** Storage for the x values. */
93     private HashSet JavaDoc xPoints = null;
94     
95     /** A flag that controls whether or not events are propogated. */
96     private boolean propagateEvents = true;
97     
98     /** A flag that controls auto pruning. */
99     private boolean autoPrune = false;
100
101     /** The delegate used to control the interval width. */
102     private IntervalXYDelegate intervalDelegate;
103
104     /**
105      * Creates a new empty dataset.
106      */

107     public DefaultTableXYDataset() {
108         this(false);
109     }
110     
111     /**
112      * Creates a new empty dataset.
113      *
114      * @param autoPrune a flag that controls whether or not x-values are
115      * removed whenever the corresponding y-values are all
116      * <code>null</code>.
117      */

118     public DefaultTableXYDataset(boolean autoPrune) {
119         this.autoPrune = autoPrune;
120         this.data = new ArrayList JavaDoc();
121         this.xPoints = new HashSet JavaDoc();
122         this.intervalDelegate = new IntervalXYDelegate(this, false);
123         addChangeListener(this.intervalDelegate);
124     }
125
126     /**
127      * Returns the flag that controls whether or not x-values are removed from
128      * the dataset when the corresponding y-values are all <code>null</code>.
129      *
130      * @return A boolean.
131      */

132     public boolean isAutoPrune() {
133         return this.autoPrune;
134     }
135
136     /**
137      * Adds a series to the collection and sends a {@link DatasetChangeEvent}
138      * to all registered listeners. The series should be configured to NOT
139      * allow duplicate x-values.
140      *
141      * @param series the series (<code>null</code> not permitted).
142      */

143     public void addSeries(XYSeries series) {
144         if (series == null) {
145             throw new IllegalArgumentException JavaDoc("Null 'series' argument.");
146         }
147         if (series.getAllowDuplicateXValues()) {
148             throw new IllegalArgumentException JavaDoc(
149                 "Cannot accept XYSeries that allow duplicate values. "
150                 + "Use XYSeries(seriesName, <sort>, false) constructor."
151             );
152         }
153         updateXPoints(series);
154         this.data.add(series);
155         series.addChangeListener(this);
156         fireDatasetChanged();
157     }
158
159     /**
160      * Adds any unique x-values from 'series' to the dataset, and also adds any
161      * x-values that are in the dataset but not in 'series' to the series.
162      *
163      * @param series the series (<code>null</code> not permitted).
164      */

165     private void updateXPoints(XYSeries series) {
166         if (series == null) {
167             throw new IllegalArgumentException JavaDoc("Null 'series' not permitted.");
168         }
169         HashSet JavaDoc seriesXPoints = new HashSet JavaDoc();
170         boolean savedState = this.propagateEvents;
171         this.propagateEvents = false;
172         for (int itemNo = 0; itemNo < series.getItemCount(); itemNo++) {
173             Number JavaDoc xValue = series.getX(itemNo);
174             seriesXPoints.add(xValue);
175             if (!this.xPoints.contains(xValue)) {
176                 this.xPoints.add(xValue);
177                 int seriesCount = this.data.size();
178                 for (int seriesNo = 0; seriesNo < seriesCount; seriesNo++) {
179                     XYSeries dataSeries = (XYSeries) this.data.get(seriesNo);
180                     if (!dataSeries.equals(series)) {
181                         dataSeries.add(xValue, null);
182                     }
183                 }
184             }
185         }
186         Iterator JavaDoc iterator = this.xPoints.iterator();
187         while (iterator.hasNext()) {
188             Number JavaDoc xPoint = (Number JavaDoc) iterator.next();
189             if (!seriesXPoints.contains(xPoint)) {
190                 series.add(xPoint, null);
191             }
192         }
193         this.propagateEvents = savedState;
194     }
195
196     /**
197      * Updates the x-values for all the series in the dataset.
198      */

199     public void updateXPoints() {
200         this.propagateEvents = false;
201         for (int s = 0; s < this.data.size(); s++) {
202             updateXPoints((XYSeries) this.data.get(s));
203         }
204         if (this.autoPrune) {
205             prune();
206         }
207         this.propagateEvents = true;
208     }
209
210     /**
211      * Returns the number of series in the collection.
212      *
213      * @return The series count.
214      */

215     public int getSeriesCount() {
216         return this.data.size();
217     }
218
219     /**
220      * Returns the number of x values in the dataset.
221      *
222      * @return The number of x values in the dataset.
223      */

224     public int getItemCount() {
225         if (this.xPoints == null) {
226             return 0;
227         }
228         else {
229             return this.xPoints.size();
230         }
231     }
232
233     /**
234      * Returns a series.
235      *
236      * @param series the series (zero-based index).
237      *
238      * @return The series (never <code>null</code>).
239      */

240     public XYSeries getSeries(int series) {
241         if ((series < 0) || (series > getSeriesCount())) {
242             throw new IllegalArgumentException JavaDoc("Index outside valid range.");
243         }
244
245         return (XYSeries) this.data.get(series);
246     }
247
248     /**
249      * Returns the key for a series.
250      *
251      * @param series the series (zero-based index).
252      *
253      * @return The key for a series.
254      */

255     public Comparable JavaDoc getSeriesKey(int series) {
256         // check arguments...delegated
257
return getSeries(series).getKey();
258     }
259
260     /**
261      * Returns the number of items in the specified series.
262      *
263      * @param series the series (zero-based index).
264      *
265      * @return The number of items in the specified series.
266      */

267     public int getItemCount(int series) {
268         // check arguments...delegated
269
return getSeries(series).getItemCount();
270     }
271
272     /**
273      * Returns the x-value for the specified series and item.
274      *
275      * @param series the series (zero-based index).
276      * @param item the item (zero-based index).
277      *
278      * @return The x-value for the specified series and item.
279      */

280     public Number JavaDoc getX(int series, int item) {
281         XYSeries s = (XYSeries) this.data.get(series);
282         XYDataItem dataItem = s.getDataItem(item);
283         return dataItem.getX();
284     }
285     
286     /**
287      * Returns the starting X value for the specified series and item.
288      *
289      * @param series the series (zero-based index).
290      * @param item the item (zero-based index).
291      *
292      * @return The starting X value.
293      */

294     public Number JavaDoc getStartX(int series, int item) {
295         return this.intervalDelegate.getStartX(series, item);
296     }
297
298     /**
299      * Returns the ending X value for the specified series and item.
300      *
301      * @param series the series (zero-based index).
302      * @param item the item (zero-based index).
303      *
304      * @return The ending X value.
305      */

306     public Number JavaDoc getEndX(int series, int item) {
307         return this.intervalDelegate.getEndX(series, item);
308     }
309
310     /**
311      * Returns the y-value for the specified series and item.
312      *
313      * @param series the series (zero-based index).
314      * @param index the index of the item of interest (zero-based).
315      *
316      * @return The y-value for the specified series and item (possibly
317      * <code>null</code>).
318      */

319     public Number JavaDoc getY(int series, int index) {
320         XYSeries ts = (XYSeries) this.data.get(series);
321         XYDataItem dataItem = ts.getDataItem(index);
322         return dataItem.getY();
323     }
324
325     /**
326      * Returns the starting Y value for the specified series and item.
327      *
328      * @param series the series (zero-based index).
329      * @param item the item (zero-based index).
330      *
331      * @return The starting Y value.
332      */

333     public Number JavaDoc getStartY(int series, int item) {
334         return getY(series, item);
335     }
336
337     /**
338      * Returns the ending Y value for the specified series and item.
339      *
340      * @param series the series (zero-based index).
341      * @param item the item (zero-based index).
342      *
343      * @return The ending Y value.
344      */

345     public Number JavaDoc getEndY(int series, int item) {
346         return getY(series, item);
347     }
348
349     /**
350      * Removes all the series from the collection and sends a
351      * {@link DatasetChangeEvent} to all registered listeners.
352      */

353     public void removeAllSeries() {
354
355         // Unregister the collection as a change listener to each series in
356
// the collection.
357
for (int i = 0; i < this.data.size(); i++) {
358             XYSeries series = (XYSeries) this.data.get(i);
359             series.removeChangeListener(this);
360         }
361
362         // Remove all the series from the collection and notify listeners.
363
this.data.clear();
364         this.xPoints.clear();
365         fireDatasetChanged();
366     }
367
368     /**
369      * Removes a series from the collection and sends a
370      * {@link DatasetChangeEvent} to all registered listeners.
371      *
372      * @param series the series (<code>null</code> not permitted).
373      */

374     public void removeSeries(XYSeries series) {
375
376         // check arguments...
377
if (series == null) {
378             throw new IllegalArgumentException JavaDoc("Null 'series' argument.");
379         }
380
381         // remove the series...
382
if (this.data.contains(series)) {
383             series.removeChangeListener(this);
384             this.data.remove(series);
385             if (this.data.size() == 0) {
386                 this.xPoints.clear();
387             }
388             fireDatasetChanged();
389         }
390
391     }
392
393     /**
394      * Removes a series from the collection and sends a
395      * {@link DatasetChangeEvent} to all registered listeners.
396      *
397      * @param series the series (zero based index).
398      */

399     public void removeSeries(int series) {
400
401         // check arguments...
402
if ((series < 0) || (series > getSeriesCount())) {
403             throw new IllegalArgumentException JavaDoc("Index outside valid range.");
404         }
405
406         // fetch the series, remove the change listener, then remove the series.
407
XYSeries s = (XYSeries) this.data.get(series);
408         s.removeChangeListener(this);
409         this.data.remove(series);
410         if (this.data.size() == 0) {
411             this.xPoints.clear();
412         }
413         else if (this.autoPrune) {
414             prune();
415         }
416         fireDatasetChanged();
417
418     }
419
420     /**
421      * Removes the items from all series for a given x value.
422      *
423      * @param x the x-value.
424      */

425     public void removeAllValuesForX(Number JavaDoc x) {
426         if (x == null) {
427             throw new IllegalArgumentException JavaDoc("Null 'x' argument.");
428         }
429         boolean savedState = this.propagateEvents;
430         this.propagateEvents = false;
431         for (int s = 0; s < this.data.size(); s++) {
432             XYSeries series = (XYSeries) this.data.get(s);
433             series.remove(x);
434         }
435         this.propagateEvents = savedState;
436         this.xPoints.remove(x);
437         fireDatasetChanged();
438     }
439
440     /**
441      * Returns <code>true</code> if all the y-values for the specified x-value
442      * are <code>null</code> and <code>false</code> otherwise.
443      *
444      * @param x the x-value.
445      *
446      * @return A boolean.
447      */

448     protected boolean canPrune(Number JavaDoc x) {
449         for (int s = 0; s < this.data.size(); s++) {
450             XYSeries series = (XYSeries) this.data.get(s);
451             if (series.getY(series.indexOf(x)) != null) {
452                 return false;
453             }
454         }
455         return true;
456     }
457     
458     /**
459      * Removes all x-values for which all the y-values are <code>null</code>.
460      */

461     public void prune() {
462         HashSet JavaDoc hs = (HashSet JavaDoc) this.xPoints.clone();
463         Iterator JavaDoc iterator = hs.iterator();
464         while (iterator.hasNext()) {
465             Number JavaDoc x = (Number JavaDoc) iterator.next();
466             if (canPrune(x)) {
467                 removeAllValuesForX(x);
468             }
469         }
470     }
471     
472     /**
473      * This method receives notification when a series belonging to the dataset
474      * changes. It responds by updating the x-points for the entire dataset
475      * and sending a {@link DatasetChangeEvent} to all registered listeners.
476      *
477      * @param event information about the change.
478      */

479     public void seriesChanged(SeriesChangeEvent event) {
480         if (this.propagateEvents) {
481             updateXPoints();
482             fireDatasetChanged();
483         }
484     }
485
486     /**
487      * Tests this collection for equality with an arbitrary object.
488      *
489      * @param obj the object (<code>null</code> permitted).
490      *
491      * @return A boolean.
492      */

493     public boolean equals(Object JavaDoc obj) {
494         if (obj == this) {
495             return true;
496         }
497         if (!(obj instanceof DefaultTableXYDataset)) {
498             return false;
499         }
500         DefaultTableXYDataset that = (DefaultTableXYDataset) obj;
501         if (this.autoPrune != that.autoPrune) {
502             return false;
503         }
504         if (this.propagateEvents != that.propagateEvents) {
505             return false;
506         }
507         if (!this.intervalDelegate.equals(that.intervalDelegate)) {
508             return false;
509         }
510         if (!ObjectUtilities.equal(this.data, that.data)) {
511             return false;
512         }
513         return true;
514     }
515
516     /**
517      * Returns a hash code.
518      *
519      * @return A hash code.
520      */

521     public int hashCode() {
522         int result;
523         result = (this.data != null ? this.data.hashCode() : 0);
524         result = 29 * result
525                  + (this.xPoints != null ? this.xPoints.hashCode() : 0);
526         result = 29 * result + (this.propagateEvents ? 1 : 0);
527         result = 29 * result + (this.autoPrune ? 1 : 0);
528         return result;
529     }
530     
531     /**
532      * Returns the minimum x-value in the dataset.
533      *
534      * @param includeInterval a flag that determines whether or not the
535      * x-interval is taken into account.
536      *
537      * @return The minimum value.
538      */

539     public double getDomainLowerBound(boolean includeInterval) {
540         return this.intervalDelegate.getDomainLowerBound(includeInterval);
541     }
542
543     /**
544      * Returns the maximum x-value in the dataset.
545      *
546      * @param includeInterval a flag that determines whether or not the
547      * x-interval is taken into account.
548      *
549      * @return The maximum value.
550      */

551     public double getDomainUpperBound(boolean includeInterval) {
552         return this.intervalDelegate.getDomainUpperBound(includeInterval);
553     }
554
555     /**
556      * Returns the range of the values in this dataset's domain.
557      *
558      * @param includeInterval a flag that determines whether or not the
559      * x-interval is taken into account.
560      *
561      * @return The range.
562      */

563     public Range getDomainBounds(boolean includeInterval) {
564         if (includeInterval) {
565             return this.intervalDelegate.getDomainBounds(includeInterval);
566         }
567         else {
568             return DatasetUtilities.iterateDomainBounds(this, includeInterval);
569         }
570     }
571     
572     /**
573      * Returns the interval position factor.
574      *
575      * @return The interval position factor.
576      */

577     public double getIntervalPositionFactor() {
578         return this.intervalDelegate.getIntervalPositionFactor();
579     }
580
581     /**
582      * Sets the interval position factor. Must be between 0.0 and 1.0 inclusive.
583      * If the factor is 0.5, the gap is in the middle of the x values. If it
584      * is lesser than 0.5, the gap is farther to the left and if greater than
585      * 0.5 it gets farther to the right.
586      *
587      * @param d the new interval position factor.
588      */

589     public void setIntervalPositionFactor(double d) {
590         this.intervalDelegate.setIntervalPositionFactor(d);
591         fireDatasetChanged();
592     }
593
594     /**
595      * returns the full interval width.
596      *
597      * @return The interval width to use.
598      */

599     public double getIntervalWidth() {
600         return this.intervalDelegate.getIntervalWidth();
601     }
602
603     /**
604      * Sets the interval width to a fixed value, and sends a
605      * {@link DatasetChangeEvent} to all registered listeners.
606      *
607      * @param d the new interval width (must be > 0).
608      */

609     public void setIntervalWidth(double d) {
610         this.intervalDelegate.setFixedIntervalWidth(d);
611         fireDatasetChanged();
612     }
613
614     /**
615      * Returns whether the interval width is automatically calculated or not.
616      *
617      * @return A flag that determines whether or not the interval width is
618      * automatically calculated.
619      */

620     public boolean isAutoWidth() {
621         return this.intervalDelegate.isAutoWidth();
622     }
623
624     /**
625      * Sets the flag that indicates whether the interval width is automatically
626      * calculated or not.
627      *
628      * @param b a boolean.
629      */

630     public void setAutoWidth(boolean b) {
631         this.intervalDelegate.setAutoWidth(b);
632         fireDatasetChanged();
633     }
634  
635 }
636
Popular Tags