001    /* ===========================================================
002     * JFreeChart : a free chart library for the Java(tm) platform
003     * ===========================================================
004     *
005     * (C) Copyright 2000-2007, 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, 2007, 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.3 2007/02/02 15:14:53 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     * 02-Feb-2007 : Removed author tags all over JFreeChart sources (DG);
047     *
048     */
049    
050    package org.jfree.data.xy;
051    
052    import org.jfree.data.DefaultKeyedValues2D;
053    import org.jfree.data.DomainInfo;
054    import org.jfree.data.Range;
055    import org.jfree.data.general.DatasetChangeEvent;
056    import org.jfree.data.general.DatasetUtilities;
057    
058    /**
059     * An implementation variant of the {@link TableXYDataset} where every series 
060     * shares the same x-values (required for generating stacked area charts). 
061     * This implementation uses a {@link DefaultKeyedValues2D} Object as backend 
062     * implementation and is hence more "category oriented" than the {@link 
063     * DefaultTableXYDataset} implementation.
064     * <p>
065     * This implementation provides no means to remove data items yet.
066     * This is due to the lack of such facility in the DefaultKeyedValues2D class.
067     * <p>
068     * This class also implements the {@link IntervalXYDataset} interface, but this
069     * implementation is provisional. 
070     */
071    public class CategoryTableXYDataset extends AbstractIntervalXYDataset
072                                        implements TableXYDataset, 
073                                                   IntervalXYDataset, 
074                                                   DomainInfo {
075        
076        /**
077         * The backing data structure.
078         */
079        private DefaultKeyedValues2D values;
080        
081        /** A delegate for controlling the interval width. */
082        private IntervalXYDelegate intervalDelegate;
083    
084        /**
085         * Creates a new empty CategoryTableXYDataset.
086         */
087        public CategoryTableXYDataset() {
088            this.values = new DefaultKeyedValues2D(true);
089            this.intervalDelegate = new IntervalXYDelegate(this);
090            addChangeListener(this.intervalDelegate);
091        }
092    
093        /**
094         * Adds a data item to this dataset and sends a {@link DatasetChangeEvent} 
095         * to all registered listeners.
096         * 
097         * @param x  the x value.
098         * @param y  the y value.
099         * @param seriesName  the name of the series to add the data item.
100         */
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         */
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         */
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         */
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        /**
147         * Returns the number of series in the collection.
148         *
149         * @return The series count.
150         */
151        public int getSeriesCount() {
152            return this.values.getColumnCount();
153        }
154    
155        /**
156         * Returns the key for a series.
157         *
158         * @param series  the series index (zero-based).
159         *
160         * @return The key for a series.
161         */
162        public Comparable getSeriesKey(int series) {
163            return this.values.getColumnKey(series);
164        }
165    
166        /**
167         * Returns the number of x values in the dataset.
168         *
169         * @return The item count.
170         */
171        public int getItemCount() {
172            return this.values.getRowCount();
173        }
174    
175        /**
176         * Returns the number of items in the specified series.
177         * Returns the same as {@link CategoryTableXYDataset#getItemCount()}.
178         *
179         * @param series  the series index (zero-based).
180         *
181         * @return The item count.
182         */
183        public int getItemCount(int series) {
184            return getItemCount();  // all series have the same number of items in 
185                                    // this dataset
186        }
187    
188        /**
189         * Returns the x-value for the specified series and item.
190         *
191         * @param series  the series index (zero-based).
192         * @param item  the item index (zero-based).
193         *
194         * @return The value.
195         */
196        public Number getX(int series, int item) {
197            return (Number) this.values.getRowKey(item);
198        }
199    
200        /**
201         * Returns the starting X value for the specified series and item.
202         *
203         * @param series  the series index (zero-based).
204         * @param item  the item index (zero-based).
205         *
206         * @return The starting X value.
207         */
208        public Number getStartX(int series, int item) {
209            return this.intervalDelegate.getStartX(series, item);
210        }
211    
212        /**
213         * Returns the ending X value for the specified series and item.
214         *
215         * @param series  the series index (zero-based).
216         * @param item  the item index (zero-based).
217         *
218         * @return The ending X value.
219         */
220        public Number getEndX(int series, int item) {
221            return this.intervalDelegate.getEndX(series, item);
222        }
223    
224        /**
225         * Returns the y-value for the specified series and item.
226         *
227         * @param series  the series index (zero-based).
228         * @param item  the item index (zero-based).
229         *
230         * @return The y value (possibly <code>null</code>).
231         */
232        public Number getY(int series, int item) {
233            return this.values.getValue(item, series);
234        }
235    
236        /**
237         * Returns the starting Y value for the specified series and item.
238         *
239         * @param series  the series index (zero-based).
240         * @param item  the item index (zero-based).
241         *
242         * @return The starting Y value.
243         */
244        public Number getStartY(int series, int item) {
245            return getY(series, item);
246        }
247    
248        /**
249         * Returns the ending Y value for the specified series and item.
250         *
251         * @param series  the series index (zero-based).
252         * @param item  the item index (zero-based).
253         *
254         * @return The ending Y value.
255         */
256        public Number getEndY(int series, int item) {
257            return getY(series, item);
258        }
259        
260        /**
261         * Returns the minimum x-value in the dataset.
262         *
263         * @param includeInterval  a flag that determines whether or not the
264         *                         x-interval is taken into account.
265         * 
266         * @return The minimum value.
267         */
268        public double getDomainLowerBound(boolean includeInterval) {
269            return this.intervalDelegate.getDomainLowerBound(includeInterval);
270        }
271    
272        /**
273         * Returns the maximum x-value in the dataset.
274         *
275         * @param includeInterval  a flag that determines whether or not the
276         *                         x-interval is taken into account.
277         * 
278         * @return The maximum value.
279         */
280        public double getDomainUpperBound(boolean includeInterval) {
281            return this.intervalDelegate.getDomainUpperBound(includeInterval);
282        }
283    
284        /**
285         * Returns the range of the values in this dataset's domain.
286         *
287         * @param includeInterval  a flag that determines whether or not the
288         *                         x-interval is taken into account.
289         * 
290         * @return The range.
291         */
292        public Range getDomainBounds(boolean includeInterval) {
293            if (includeInterval) {
294                return this.intervalDelegate.getDomainBounds(includeInterval);
295            }
296            else {
297                return DatasetUtilities.iterateDomainBounds(this, includeInterval);
298            }
299        }
300        
301        /**
302         * Returns the interval position factor. 
303         * 
304         * @return The interval position factor.
305         */
306        public double getIntervalPositionFactor() {
307            return this.intervalDelegate.getIntervalPositionFactor();
308        }
309    
310        /**
311         * Sets the interval position factor. Must be between 0.0 and 1.0 inclusive.
312         * If the factor is 0.5, the gap is in the middle of the x values. If it
313         * is lesser than 0.5, the gap is farther to the left and if greater than
314         * 0.5 it gets farther to the right.
315         *  
316         * @param d  the new interval position factor.
317         */
318        public void setIntervalPositionFactor(double d) {
319            this.intervalDelegate.setIntervalPositionFactor(d);
320            fireDatasetChanged();
321        }
322    
323        /**
324         * Returns the full interval width. 
325         * 
326         * @return The interval width to use.
327         */
328        public double getIntervalWidth() {
329            return this.intervalDelegate.getIntervalWidth();
330        }
331    
332        /**
333         * Sets the interval width to a fixed value, and sends a 
334         * {@link DatasetChangeEvent} to all registered listeners. 
335         * 
336         * @param d  the new interval width (must be > 0).
337         */
338        public void setIntervalWidth(double d) {
339            this.intervalDelegate.setFixedIntervalWidth(d);
340            fireDatasetChanged();
341        }
342    
343        /**
344         * Returns whether the interval width is automatically calculated or not.
345         * 
346         * @return whether the width is automatically calculated or not.
347         */
348        public boolean isAutoWidth() {
349            return this.intervalDelegate.isAutoWidth();
350        }
351    
352        /**
353         * Sets the flag that indicates whether the interval width is automatically
354         * calculated or not. 
355         * 
356         * @param b  the flag.
357         */
358        public void setAutoWidth(boolean b) {
359            this.intervalDelegate.setAutoWidth(b);
360            fireDatasetChanged();
361        }
362        
363        /**
364         * Tests this dataset for equality with an arbitrary object.
365         * 
366         * @param obj  the object (<code>null</code> permitted).
367         * 
368         * @return A boolean.
369         */
370        public boolean equals(Object obj) {
371            if (!(obj instanceof CategoryTableXYDataset)) {
372                return false;
373            }
374            CategoryTableXYDataset that = (CategoryTableXYDataset) obj;
375            if (!this.intervalDelegate.equals(that.intervalDelegate)) {
376                return false;
377            }
378            if (!this.values.equals(that.values)) {
379                return false;
380            }
381            return true;
382        }
383        
384    }