KickJava   Java API By Example, From Geeks To Geeks.

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


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  * IntervalXYDelegate.java
29  * -----------------------
30  * (C) Copyright 2004, 2005, by Andreas Schroeder and Contributors.
31  *
32  * Original Author: Andreas Schroeder;
33  * Contributor(s): David Gilbert (for Object Refinery Limited);
34  *
35  * $Id: IntervalXYDelegate.java,v 1.10.2.2 2005/10/25 21:36:51 mungady Exp $
36  *
37  * Changes (from 31-Mar-2004)
38  * --------------------------
39  * 31-Mar-2004 : Version 1 (AS);
40  * 15-Jul-2004 : Switched getX() with getXValue() and getY() with
41  * getYValue() (DG);
42  * 18-Aug-2004 : Moved from org.jfree.data --> org.jfree.data.xy (DG);
43  * 04-Nov-2004 : Added argument check for setIntervalWidth() method (DG);
44  * 17-Nov-2004 : New methods to reflect changes in DomainInfo (DG);
45  * 11-Jan-2005 : Removed deprecated methods in preparation for the 1.0.0
46  * release (DG);
47  * 21-Feb-2005 : Made public and added equals() method (DG);
48  * 06-Oct-2005 : Implemented DatasetChangeListener to recalculate
49  * autoIntervalWidth (DG);
50  *
51  */

52
53 package org.jfree.data.xy;
54
55 import java.io.Serializable JavaDoc;
56
57 import org.jfree.data.DomainInfo;
58 import org.jfree.data.Range;
59 import org.jfree.data.RangeInfo;
60 import org.jfree.data.general.DatasetChangeEvent;
61 import org.jfree.data.general.DatasetChangeListener;
62 import org.jfree.data.general.DatasetUtilities;
63 import org.jfree.util.PublicCloneable;
64
65 /**
66  * A delegate that handles the specification or automatic calculation of the
67  * interval surrounding the x-values in a dataset. This is used to extend
68  * a regular {@link XYDataset} to support the {@link IntervalXYDataset}
69  * interface.
70  * <p>
71  * The decorator pattern was not used because of the several possibly
72  * implemented interfaces of the decorated instance (e.g.
73  * {@link TableXYDataset}, {@link RangeInfo}, {@link DomainInfo} etc.).
74  * <p>
75  * The width can be set manually or calculated automatically. The switch
76  * autoWidth allows to determine which behavior is used. The auto width
77  * calculation tries to find the smallest gap between two x-values in the
78  * dataset. If there is only one item in the series, the auto width
79  * calculation fails and falls back on the manually set interval width (which
80  * is itself defaulted to 1.0).
81  *
82  * @author andreas.schroeder
83  */

84 public class IntervalXYDelegate implements DatasetChangeListener,
85                                            DomainInfo, Serializable JavaDoc,
86                                            Cloneable JavaDoc, PublicCloneable {
87     
88     /** For serialization. */
89     private static final long serialVersionUID = -685166711639592857L;
90     
91     /**
92      * The dataset to enhance.
93      */

94     private XYDataset dataset;
95
96     /**
97      * A flag to indicate whether the width should be calculated automatically.
98      */

99     private boolean autoWidth;
100     
101     /**
102      * A value between 0.0 and 1.0 that indicates the position of the x-value
103      * within the interval.
104      */

105     private double intervalPositionFactor;
106     
107     /**
108      * The fixed interval width (defaults to 1.0).
109      */

110     private double fixedIntervalWidth;
111     
112     /**
113      * The automatically calculated interval width.
114      */

115     private double autoIntervalWidth;
116     
117     /**
118      * Creates a new delegate that.
119      *
120      * @param dataset the underlying dataset (<code>null</code> not permitted).
121      */

122     public IntervalXYDelegate(XYDataset dataset) {
123         this(dataset, true);
124     }
125     
126     /**
127      * Creates a new delegate for the specified dataset.
128      *
129      * @param dataset the underlying dataset (<code>null</code> not permitted).
130      * @param autoWidth a flag that controls whether the interval width is
131      * calculated automatically.
132      */

133     public IntervalXYDelegate(XYDataset dataset, boolean autoWidth) {
134         if (dataset == null) {
135             throw new IllegalArgumentException JavaDoc("Null 'dataset' argument.");
136         }
137         this.dataset = dataset;
138         this.autoWidth = autoWidth;
139         this.intervalPositionFactor = 0.5;
140         this.autoIntervalWidth = Double.POSITIVE_INFINITY;
141         this.fixedIntervalWidth = 1.0;
142     }
143     
144     /**
145      * Returns <code>true</code> if the interval width is automatically
146      * calculated, and <code>false</code> otherwise.
147      *
148      * @return A boolean.
149      */

150     public boolean isAutoWidth() {
151         return this.autoWidth;
152     }
153     
154     /**
155      * Sets the flag that indicates whether the interval width is automatically
156      * calculated. If the flag is set to <code>true</code>, the interval is
157      * recalculated.
158      * <p>
159      * Note: recalculating the interval amounts to changing the data values
160      * represented by the dataset. The calling dataset must fire an
161      * appropriate {@link DatasetChangeEvent}.
162      *
163      * @param b a boolean.
164      */

165     public void setAutoWidth(boolean b) {
166         this.autoWidth = b;
167         if (b) {
168             this.autoIntervalWidth = recalculateInterval();
169         }
170     }
171     
172     /**
173      * Returns the interval position factor.
174      *
175      * @return The interval position factor.
176      */

177     public double getIntervalPositionFactor() {
178         return this.intervalPositionFactor;
179     }
180
181     /**
182      * Sets the interval position factor. This controls how the interval is
183      * aligned to the x-value. For a value of 0.5, the interval is aligned
184      * with the x-value in the center. For a value of 0.0, the interval is
185      * aligned with the x-value at the lower end of the interval, and for a
186      * value of 1.0, the interval is aligned with the x-value at the upper
187      * end of the interval.
188      *
189      * Note that changing the interval position factor amounts to changing the
190      * data values represented by the dataset. Therefore, the dataset that is
191      * using this delegate is responsible for generating the
192      * appropriate {@link DatasetChangeEvent}.
193      *
194      * @param d the new interval position factor (in the range
195      * <code>0.0</code> to <code>1.0</code> inclusive).
196      */

197     public void setIntervalPositionFactor(double d) {
198         if (d < 0.0 || 1.0 < d) {
199             throw new IllegalArgumentException JavaDoc(
200                     "Argument 'd' outside valid range.");
201         }
202         this.intervalPositionFactor = d;
203     }
204
205     /**
206      * Returns the fixed interval width.
207      *
208      * @return The fixed interval width.
209      */

210     public double getFixedIntervalWidth() {
211         return this.fixedIntervalWidth;
212     }
213     
214     /**
215      * Sets the fixed interval width and, as a side effect, sets the
216      * <code>autoWidth</code> flag to <code>false</code>.
217      *
218      * Note that changing the interval width amounts to changing the data
219      * values represented by the dataset. Therefore, the dataset
220      * that is using this delegate is responsible for generating the
221      * appropriate {@link DatasetChangeEvent}.
222      *
223      * @param w the width (negative values not permitted).
224      */

225     public void setFixedIntervalWidth(double w) {
226         if (w < 0.0) {
227             throw new IllegalArgumentException JavaDoc("Negative 'w' argument.");
228         }
229         this.fixedIntervalWidth = w;
230         this.autoWidth = false;
231     }
232     
233     /**
234      * Returns the interval width. This method will return either the
235      * auto calculated interval width or the manually specified interval
236      * width, depending on the {@link #isAutoWidth()} result.
237      *
238      * @return The interval width to use.
239      */

240     public double getIntervalWidth() {
241         if (isAutoWidth() && !Double.isInfinite(this.autoIntervalWidth)) {
242             // everything is fine: autoWidth is on, and an autoIntervalWidth
243
// was set.
244
return this.autoIntervalWidth;
245         }
246         else {
247             // either autoWidth is off or autoIntervalWidth was not set.
248
return this.fixedIntervalWidth;
249         }
250     }
251
252     /**
253      * Returns the start value of the x-interval for an item within a series.
254      *
255      * @param series the series index.
256      * @param item the item index.
257      *
258      * @return The start value of the x-interval (possibly <code>null</code>).
259      *
260      * @see #getStartXValue(int, int)
261      */

262     public Number JavaDoc getStartX(int series, int item) {
263         Number JavaDoc startX = null;
264         Number JavaDoc x = this.dataset.getX(series, item);
265         if (x != null) {
266             startX = new Double JavaDoc(x.doubleValue()
267                      - (getIntervalPositionFactor() * getIntervalWidth()));
268         }
269         return startX;
270     }
271     
272     /**
273      * Returns the start value of the x-interval for an item within a series.
274      *
275      * @param series the series index.
276      * @param item the item index.
277      *
278      * @return The start value of the x-interval.
279      *
280      * @see #getStartX(int, int)
281      */

282     public double getStartXValue(int series, int item) {
283         return dataset.getXValue(series, item) - getIntervalPositionFactor()
284             * getIntervalWidth();
285     }
286     
287     /**
288      * Returns the end value of the x-interval for an item within a series.
289      *
290      * @param series the series index.
291      * @param item the item index.
292      *
293      * @return The end value of the x-interval (possibly <code>null</code>).
294      *
295      * @see #getEndXValue(int, int)
296      */

297     public Number JavaDoc getEndX(int series, int item) {
298         Number JavaDoc endX = null;
299         Number JavaDoc x = this.dataset.getX(series, item);
300         if (x != null) {
301             endX = new Double JavaDoc(x.doubleValue()
302                 + ((1.0 - getIntervalPositionFactor()) * getIntervalWidth()));
303         }
304         return endX;
305     }
306
307     /**
308      * Returns the end value of the x-interval for an item within a series.
309      *
310      * @param series the series index.
311      * @param item the item index.
312      *
313      * @return The end value of the x-interval.
314      *
315      * @see #getEndX(int, int)
316      */

317     public double getEndXValue(int series, int item) {
318         return dataset.getXValue(series, item)
319             + (1.0 - getIntervalPositionFactor()) * getIntervalWidth();
320     }
321     
322     /**
323      * Returns the minimum x-value in the dataset.
324      *
325      * @param includeInterval a flag that determines whether or not the
326      * x-interval is taken into account.
327      *
328      * @return The minimum value.
329      */

330     public double getDomainLowerBound(boolean includeInterval) {
331         double result = Double.NaN;
332         Range r = getDomainBounds(includeInterval);
333         if (r != null) {
334             result = r.getLowerBound();
335         }
336         return result;
337     }
338
339     /**
340      * Returns the maximum x-value in the dataset.
341      *
342      * @param includeInterval a flag that determines whether or not the
343      * x-interval is taken into account.
344      *
345      * @return The maximum value.
346      */

347     public double getDomainUpperBound(boolean includeInterval) {
348         double result = Double.NaN;
349         Range r = getDomainBounds(includeInterval);
350         if (r != null) {
351             result = r.getUpperBound();
352         }
353         return result;
354     }
355
356     /**
357      * Returns the range of the values in the dataset's domain, including
358      * or excluding the interval around each x-value as specified.
359      *
360      * @param includeInterval a flag that determines whether or not the
361      * x-interval should be taken into account.
362      *
363      * @return The range.
364      */

365     public Range getDomainBounds(boolean includeInterval) {
366         // first get the range without the interval, then expand it for the
367
// interval width
368
Range range = DatasetUtilities.findDomainBounds(this.dataset, false);
369         if (includeInterval && range != null) {
370             double lowerAdj = getIntervalWidth() * getIntervalPositionFactor();
371             double upperAdj = getIntervalWidth() - lowerAdj;
372             range = new Range(range.getLowerBound() - lowerAdj,
373                 range.getUpperBound() + upperAdj);
374         }
375         return range;
376     }
377     
378     /**
379      * Handles events from the dataset by recalculating the interval if
380      * necessary.
381      *
382      * @param e the event.
383      */

384     public void datasetChanged(DatasetChangeEvent e) {
385         // TODO: by coding the event with some information about what changed
386
// in the dataset, we could make the recalculation of the interval
387
// more efficient in some cases...
388
if (this.autoWidth) {
389             this.autoIntervalWidth = recalculateInterval();
390         }
391     }
392     
393     /**
394      * Recalculate the minimum width "from scratch".
395      */

396     private double recalculateInterval() {
397         double result = Double.POSITIVE_INFINITY;
398         int seriesCount = this.dataset.getSeriesCount();
399         for (int series = 0; series < seriesCount; series++) {
400             result = Math.min(result, calculateIntervalForSeries(series));
401         }
402         return result;
403     }
404     
405     /**
406      * Calculates the interval width for a given series.
407      *
408      * @param series the series index.
409      */

410     private double calculateIntervalForSeries(int series) {
411         double result = Double.POSITIVE_INFINITY;
412         int itemCount = this.dataset.getItemCount(series);
413         if (itemCount > 1) {
414             double prev = this.dataset.getXValue(series, 0);
415             for (int item = 1; item < itemCount; item++) {
416                 double x = this.dataset.getXValue(series, item);
417                 result = Math.min(result, x - prev);
418                 prev = x;
419             }
420         }
421         return result;
422     }
423     
424     /**
425      * Tests the delegate for equality with an arbitrary object.
426      *
427      * @param obj the object (<code>null</code> permitted).
428      *
429      * @return A boolean.
430      */

431     public boolean equals(Object JavaDoc obj) {
432         if (obj == this) {
433             return true;
434         }
435         if (!(obj instanceof IntervalXYDelegate)) {
436             return false;
437         }
438         IntervalXYDelegate that = (IntervalXYDelegate) obj;
439         if (this.autoWidth != that.autoWidth) {
440             return false;
441         }
442         if (this.intervalPositionFactor != that.intervalPositionFactor) {
443             return false;
444         }
445         if (this.fixedIntervalWidth != that.fixedIntervalWidth) {
446             return false;
447         }
448         return true;
449     }
450     
451     /**
452      * @return A clone of this delegate.
453      *
454      * @throws CloneNotSupportedException if the object cannot be cloned.
455      */

456     public Object JavaDoc clone() throws CloneNotSupportedException JavaDoc {
457         return super.clone();
458     }
459     
460 }
461
Popular Tags