001    /* ===========================================================
002     * JFreeChart : a free chart library for the Java(tm) platform
003     * ===========================================================
004     *
005     * (C) Copyright 2000-2005, 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     * CategoryTableXYDataset.java
029     * ---------------------------
030     * (C) Copyright 2004, 2005, by Andreas Schroeder and Contributors.
031     *
032     * Original Author:  Andreas Schroeder;
033     * Contributor(s):   David Gilbert (for Object Refinery Limited);
034     *
035     * $Id: CategoryTableXYDataset.java,v 1.7.2.2 2005/10/25 21:36:51 mungady Exp $
036     *
037     * Changes
038     * -------
039     * 31-Mar-2004 : Version 1 (AS);
040     * 05-May-2004 : Now extends AbstractIntervalXYDataset (DG);
041     * 15-Jul-2004 : Switched interval access method names (DG);
042     * 18-Aug-2004 : Moved from org.jfree.data --> org.jfree.data.xy (DG);
043     * 17-Nov-2004 : Updates required by changes to DomainInfo interface (DG);
044     * 11-Jan-2005 : Removed deprecated code in preparation for 1.0.0 release (DG);
045     * 05-Oct-2005 : Made the interval delegate a dataset change listener (DG);
046     *
047     */
048    
049    package org.jfree.data.xy;
050    
051    import org.jfree.data.DefaultKeyedValues2D;
052    import org.jfree.data.DomainInfo;
053    import org.jfree.data.Range;
054    import org.jfree.data.general.DatasetChangeEvent;
055    import org.jfree.data.general.DatasetUtilities;
056    
057    /**
058     * An implementation variant of the {@link TableXYDataset} where every series 
059     * shares the same x-values (required for generating stacked area charts). 
060     * This implementation uses a {@link DefaultKeyedValues2D} Object as backend 
061     * implementation and is hence more "category oriented" than the {@link 
062     * DefaultTableXYDataset} implementation.
063     * <p>
064     * This implementation provides no means to remove data items yet.
065     * This is due to the lack of such facility in the DefaultKeyedValues2D class.
066     * <p>
067     * This class also implements the {@link IntervalXYDataset} interface, but this
068     * implementation is provisional. 
069     * 
070     * @author Andreas Schroeder
071     */
072    public class CategoryTableXYDataset extends AbstractIntervalXYDataset
073                                        implements TableXYDataset, 
074                                                   IntervalXYDataset, 
075                                                   DomainInfo {
076        
077        /**
078         * The backing data structure.
079         */
080        private DefaultKeyedValues2D values;
081        
082        /** A delegate for controlling the interval width. */
083        private IntervalXYDelegate intervalDelegate;
084    
085        /**
086         * Creates a new empty CategoryTableXYDataset.
087         */
088        public CategoryTableXYDataset() {
089            this.values = new DefaultKeyedValues2D(true);
090            this.intervalDelegate = new IntervalXYDelegate(this);
091            addChangeListener(this.intervalDelegate);
092        }
093    
094        /**
095         * Adds a data item to this dataset and sends a {@link DatasetChangeEvent} 
096         * to all registered listeners.
097         * 
098         * @param x  the x value.
099         * @param y  the y value.
100         * @param seriesName  the name of the series to add the data item.
101         */
102        public void add(double x, double y, String seriesName) {
103            add(new Double(x), new Double(y), seriesName, true);
104        }
105        
106        /**
107         * Adds a data item to this dataset and, if requested, sends a 
108         * {@link DatasetChangeEvent} to all registered listeners.
109         * 
110         * @param x  the x value.
111         * @param y  the y value.
112         * @param seriesName  the name of the series to add the data item.
113         * @param notify  notify listeners?
114         */
115        public void add(Number x, Number y, String seriesName, boolean notify) {
116            this.values.addValue(y, (Comparable) x, seriesName);
117            if (notify) {
118                fireDatasetChanged();
119            }
120        }
121    
122        /**
123         * Removes a value from the dataset.
124         * 
125         * @param x  the x-value.
126         * @param seriesName  the series name.
127         */
128        public void remove(double x, String seriesName) {
129            remove(new Double(x), seriesName, true);
130        }
131        
132        /**
133         * Removes an item from the dataset.
134         * 
135         * @param x  the x-value.
136         * @param seriesName  the series name.
137         * @param notify  notify listeners?
138         */
139        public void remove(Number x, String seriesName, boolean notify) {
140            this.values.removeValue((Comparable) x, seriesName);
141            if (notify) {
142                fireDatasetChanged();
143            }
144        }
145    
146    
147        /**
148         * Returns the number of series in the collection.
149         *
150         * @return The series count.
151         */
152        public int getSeriesCount() {
153            return this.values.getColumnCount();
154        }
155    
156        /**
157         * Returns the key for a series.
158         *
159         * @param series  the series index (zero-based).
160         *
161         * @return The key for a series.
162         */
163        public Comparable getSeriesKey(int series) {
164            return this.values.getColumnKey(series);
165        }
166    
167        /**
168         * Returns the number of x values in the dataset.
169         *
170         * @return The item count.
171         */
172        public int getItemCount() {
173            return this.values.getRowCount();
174        }
175    
176        /**
177         * Returns the number of items in the specified series.
178         * Returns the same as {@link CategoryTableXYDataset#getItemCount()}.
179         *
180         * @param series  the series index (zero-based).
181         *
182         * @return The item count.
183         */
184        public int getItemCount(int series) {
185            return getItemCount();  // all series have the same number of items in 
186                                    // this dataset
187        }
188    
189        /**
190         * Returns the x-value for the specified series and item.
191         *
192         * @param series  the series index (zero-based).
193         * @param item  the item index (zero-based).
194         *
195         * @return The value.
196         */
197        public Number getX(int series, int item) {
198            return (Number) this.values.getRowKey(item);
199        }
200    
201        /**
202         * Returns the starting X value for the specified series and item.
203         *
204         * @param series  the series index (zero-based).
205         * @param item  the item index (zero-based).
206         *
207         * @return The starting X value.
208         */
209        public Number getStartX(int series, int item) {
210            return this.intervalDelegate.getStartX(series, item);
211        }
212    
213        /**
214         * Returns the ending X value for the specified series and item.
215         *
216         * @param series  the series index (zero-based).
217         * @param item  the item index (zero-based).
218         *
219         * @return The ending X value.
220         */
221        public Number getEndX(int series, int item) {
222            return this.intervalDelegate.getEndX(series, item);
223        }
224    
225        /**
226         * Returns the y-value for the specified series and item.
227         *
228         * @param series  the series index (zero-based).
229         * @param item  the item index (zero-based).
230         *
231         * @return The y value (possibly <code>null</code>).
232         */
233        public Number getY(int series, int item) {
234            return this.values.getValue(item, series);
235        }
236    
237        /**
238         * Returns the starting Y value for the specified series and item.
239         *
240         * @param series  the series index (zero-based).
241         * @param item  the item index (zero-based).
242         *
243         * @return The starting Y value.
244         */
245        public Number getStartY(int series, int item) {
246            return getY(series, item);
247        }
248    
249        /**
250         * Returns the ending Y value for the specified series and item.
251         *
252         * @param series  the series index (zero-based).
253         * @param item  the item index (zero-based).
254         *
255         * @return The ending Y value.
256         */
257        public Number getEndY(int series, int item) {
258            return getY(series, item);
259        }
260        
261        /**
262         * Returns the minimum x-value in the dataset.
263         *
264         * @param includeInterval  a flag that determines whether or not the
265         *                         x-interval is taken into account.
266         * 
267         * @return The minimum value.
268         */
269        public double getDomainLowerBound(boolean includeInterval) {
270            return this.intervalDelegate.getDomainLowerBound(includeInterval);
271        }
272    
273        /**
274         * Returns the maximum x-value in the dataset.
275         *
276         * @param includeInterval  a flag that determines whether or not the
277         *                         x-interval is taken into account.
278         * 
279         * @return The maximum value.
280         */
281        public double getDomainUpperBound(boolean includeInterval) {
282            return this.intervalDelegate.getDomainUpperBound(includeInterval);
283        }
284    
285        /**
286         * Returns the range of the values in this dataset's domain.
287         *
288         * @param includeInterval  a flag that determines whether or not the
289         *                         x-interval is taken into account.
290         * 
291         * @return The range.
292         */
293        public Range getDomainBounds(boolean includeInterval) {
294            if (includeInterval) {
295                return this.intervalDelegate.getDomainBounds(includeInterval);
296            }
297            else {
298                return DatasetUtilities.iterateDomainBounds(this, includeInterval);
299            }
300        }
301        
302        /**
303         * Returns the interval position factor. 
304         * 
305         * @return The interval position factor.
306         */
307        public double getIntervalPositionFactor() {
308            return this.intervalDelegate.getIntervalPositionFactor();
309        }
310    
311        /**
312         * Sets the interval position factor. Must be between 0.0 and 1.0 inclusive.
313         * If the factor is 0.5, the gap is in the middle of the x values. If it
314         * is lesser than 0.5, the gap is farther to the left and if greater than
315         * 0.5 it gets farther to the right.
316         *  
317         * @param d  the new interval position factor.
318         */
319        public void setIntervalPositionFactor(double d) {
320            this.intervalDelegate.setIntervalPositionFactor(d);
321            fireDatasetChanged();
322        }
323    
324        /**
325         * Returns the full interval width. 
326         * 
327         * @return The interval width to use.
328         */
329        public double getIntervalWidth() {
330            return this.intervalDelegate.getIntervalWidth();
331        }
332    
333        /**
334         * Sets the interval width to a fixed value, and sends a 
335         * {@link DatasetChangeEvent} to all registered listeners. 
336         * 
337         * @param d  the new interval width (must be > 0).
338         */
339        public void setIntervalWidth(double d) {
340            this.intervalDelegate.setFixedIntervalWidth(d);
341            fireDatasetChanged();
342        }
343    
344        /**
345         * Returns whether the interval width is automatically calculated or not.
346         * 
347         * @return whether the width is automatically calculated or not.
348         */
349        public boolean isAutoWidth() {
350            return this.intervalDelegate.isAutoWidth();
351        }
352    
353        /**
354         * Sets the flag that indicates whether the interval width is automatically
355         * calculated or not. 
356         * 
357         * @param b  the flag.
358         */
359        public void setAutoWidth(boolean b) {
360            this.intervalDelegate.setAutoWidth(b);
361            fireDatasetChanged();
362        }
363        
364        /**
365         * Tests this dataset for equality with an arbitrary object.
366         * 
367         * @param obj  the object (<code>null</code> permitted).
368         * 
369         * @return A boolean.
370         */
371        public boolean equals(Object obj) {
372            if (!(obj instanceof CategoryTableXYDataset)) {
373                return false;
374            }
375            CategoryTableXYDataset that = (CategoryTableXYDataset) obj;
376            if (!this.intervalDelegate.equals(that.intervalDelegate)) {
377                return false;
378            }
379            if (!this.values.equals(that.values)) {
380                return false;
381            }
382            return true;
383        }
384        
385    }