001: /* ===========================================================
002: * JFreeChart : a free chart library for the Java(tm) platform
003: * ===========================================================
004: *
005: * (C) Copyright 2000-2006, 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: * DefaultIntervalXYDataset.java
029: * -----------------------------
030: * (C) Copyright 2006, by Object Refinery Limited and Contributors.
031: *
032: * Original Author: David Gilbert (for Object Refinery Limited);
033: * Contributor(s): -;
034: *
035: * $Id: DefaultIntervalXYDataset.java,v 1.1.2.3 2006/11/28 14:06:14 mungady Exp $
036: *
037: * Changes
038: * -------
039: * 23-Oct-2006 : Version 1 (DG);
040: * 02-Nov-2006 : Fixed a problem with adding a new series with the same key
041: * as an existing series (see bug 1589392) (DG);
042: * 28-Nov-2006 : New override for clone() (DG);
043: *
044: */
045:
046: package org.jfree.data.xy;
047:
048: import java.util.ArrayList;
049: import java.util.Arrays;
050: import java.util.List;
051:
052: import org.jfree.data.general.DatasetChangeEvent;
053:
054: /**
055: * A dataset that defines a range (interval) for both the x-values and the
056: * y-values. This implementation uses six arrays to store the x, start-x,
057: * end-x, y, start-y and end-y values.
058: * <br><br>
059: * An alternative implementation of the {@link IntervalXYDataset} interface
060: * is provided by the {@link XYIntervalSeriesCollection} class.
061: *
062: * @since 1.0.3
063: */
064: public class DefaultIntervalXYDataset extends AbstractIntervalXYDataset {
065:
066: /**
067: * Storage for the series keys. This list must be kept in sync with the
068: * seriesList.
069: */
070: private List seriesKeys;
071:
072: /**
073: * Storage for the series in the dataset. We use a list because the
074: * order of the series is significant. This list must be kept in sync
075: * with the seriesKeys list.
076: */
077: private List seriesList;
078:
079: /**
080: * Creates a new <code>DefaultIntervalXYDataset</code> instance, initially
081: * containing no data.
082: */
083: public DefaultIntervalXYDataset() {
084: this .seriesKeys = new java.util.ArrayList();
085: this .seriesList = new java.util.ArrayList();
086: }
087:
088: /**
089: * Returns the number of series in the dataset.
090: *
091: * @return The series count.
092: */
093: public int getSeriesCount() {
094: return this .seriesList.size();
095: }
096:
097: /**
098: * Returns the key for a series.
099: *
100: * @param series the series index (in the range <code>0</code> to
101: * <code>getSeriesCount() - 1</code>).
102: *
103: * @return The key for the series.
104: *
105: * @throws IllegalArgumentException if <code>series</code> is not in the
106: * specified range.
107: */
108: public Comparable getSeriesKey(int series) {
109: if ((series < 0) || (series >= getSeriesCount())) {
110: throw new IllegalArgumentException(
111: "Series index out of bounds");
112: }
113: return (Comparable) this .seriesKeys.get(series);
114: }
115:
116: /**
117: * Returns the number of items in the specified series.
118: *
119: * @param series the series index (in the range <code>0</code> to
120: * <code>getSeriesCount() - 1</code>).
121: *
122: * @return The item count.
123: *
124: * @throws IllegalArgumentException if <code>series</code> is not in the
125: * specified range.
126: */
127: public int getItemCount(int series) {
128: if ((series < 0) || (series >= getSeriesCount())) {
129: throw new IllegalArgumentException(
130: "Series index out of bounds");
131: }
132: double[][] seriesArray = (double[][]) this .seriesList
133: .get(series);
134: return seriesArray[0].length;
135: }
136:
137: /**
138: * Returns the x-value for an item within a series.
139: *
140: * @param series the series index (in the range <code>0</code> to
141: * <code>getSeriesCount() - 1</code>).
142: * @param item the item index (in the range <code>0</code> to
143: * <code>getItemCount(series)</code>).
144: *
145: * @return The x-value.
146: *
147: * @throws ArrayIndexOutOfBoundsException if <code>series</code> is not
148: * within the specified range.
149: * @throws ArrayIndexOutOfBoundsException if <code>item</code> is not
150: * within the specified range.
151: *
152: * @see #getX(int, int)
153: */
154: public double getXValue(int series, int item) {
155: double[][] seriesData = (double[][]) this .seriesList
156: .get(series);
157: return seriesData[0][item];
158: }
159:
160: /**
161: * Returns the y-value for an item within a series.
162: *
163: * @param series the series index (in the range <code>0</code> to
164: * <code>getSeriesCount() - 1</code>).
165: * @param item the item index (in the range <code>0</code> to
166: * <code>getItemCount(series)</code>).
167: *
168: * @return The y-value.
169: *
170: * @throws ArrayIndexOutOfBoundsException if <code>series</code> is not
171: * within the specified range.
172: * @throws ArrayIndexOutOfBoundsException if <code>item</code> is not
173: * within the specified range.
174: *
175: * @see #getY(int, int)
176: */
177: public double getYValue(int series, int item) {
178: double[][] seriesData = (double[][]) this .seriesList
179: .get(series);
180: return seriesData[3][item];
181: }
182:
183: /**
184: * Returns the starting x-value for an item within a series.
185: *
186: * @param series the series index (in the range <code>0</code> to
187: * <code>getSeriesCount() - 1</code>).
188: * @param item the item index (in the range <code>0</code> to
189: * <code>getItemCount(series)</code>).
190: *
191: * @return The starting x-value.
192: *
193: * @throws ArrayIndexOutOfBoundsException if <code>series</code> is not
194: * within the specified range.
195: * @throws ArrayIndexOutOfBoundsException if <code>item</code> is not
196: * within the specified range.
197: *
198: * @see #getStartX(int, int)
199: */
200: public double getStartXValue(int series, int item) {
201: double[][] seriesData = (double[][]) this .seriesList
202: .get(series);
203: return seriesData[1][item];
204: }
205:
206: /**
207: * Returns the ending x-value for an item within a series.
208: *
209: * @param series the series index (in the range <code>0</code> to
210: * <code>getSeriesCount() - 1</code>).
211: * @param item the item index (in the range <code>0</code> to
212: * <code>getItemCount(series)</code>).
213: *
214: * @return The ending x-value.
215: *
216: * @throws ArrayIndexOutOfBoundsException if <code>series</code> is not
217: * within the specified range.
218: * @throws ArrayIndexOutOfBoundsException if <code>item</code> is not
219: * within the specified range.
220: *
221: * @see #getEndX(int, int)
222: */
223: public double getEndXValue(int series, int item) {
224: double[][] seriesData = (double[][]) this .seriesList
225: .get(series);
226: return seriesData[2][item];
227: }
228:
229: /**
230: * Returns the starting y-value for an item within a series.
231: *
232: * @param series the series index (in the range <code>0</code> to
233: * <code>getSeriesCount() - 1</code>).
234: * @param item the item index (in the range <code>0</code> to
235: * <code>getItemCount(series)</code>).
236: *
237: * @return The starting y-value.
238: *
239: * @throws ArrayIndexOutOfBoundsException if <code>series</code> is not
240: * within the specified range.
241: * @throws ArrayIndexOutOfBoundsException if <code>item</code> is not
242: * within the specified range.
243: *
244: * @see #getStartY(int, int)
245: */
246: public double getStartYValue(int series, int item) {
247: double[][] seriesData = (double[][]) this .seriesList
248: .get(series);
249: return seriesData[4][item];
250: }
251:
252: /**
253: * Returns the ending y-value for an item within a series.
254: *
255: * @param series the series index (in the range <code>0</code> to
256: * <code>getSeriesCount() - 1</code>).
257: * @param item the item index (in the range <code>0</code> to
258: * <code>getItemCount(series)</code>).
259: *
260: * @return The ending y-value.
261: *
262: * @throws ArrayIndexOutOfBoundsException if <code>series</code> is not
263: * within the specified range.
264: * @throws ArrayIndexOutOfBoundsException if <code>item</code> is not
265: * within the specified range.
266: *
267: * @see #getEndY(int, int)
268: */
269: public double getEndYValue(int series, int item) {
270: double[][] seriesData = (double[][]) this .seriesList
271: .get(series);
272: return seriesData[5][item];
273: }
274:
275: /**
276: * Returns the ending x-value for an item within a series.
277: *
278: * @param series the series index (in the range <code>0</code> to
279: * <code>getSeriesCount() - 1</code>).
280: * @param item the item index (in the range <code>0</code> to
281: * <code>getItemCount(series)</code>).
282: *
283: * @return The ending x-value.
284: *
285: * @throws ArrayIndexOutOfBoundsException if <code>series</code> is not
286: * within the specified range.
287: * @throws ArrayIndexOutOfBoundsException if <code>item</code> is not
288: * within the specified range.
289: *
290: * @see #getEndXValue(int, int)
291: */
292: public Number getEndX(int series, int item) {
293: return new Double(getEndXValue(series, item));
294: }
295:
296: /**
297: * Returns the ending y-value for an item within a series.
298: *
299: * @param series the series index (in the range <code>0</code> to
300: * <code>getSeriesCount() - 1</code>).
301: * @param item the item index (in the range <code>0</code> to
302: * <code>getItemCount(series)</code>).
303: *
304: * @return The ending y-value.
305: *
306: * @throws ArrayIndexOutOfBoundsException if <code>series</code> is not
307: * within the specified range.
308: * @throws ArrayIndexOutOfBoundsException if <code>item</code> is not
309: * within the specified range.
310: *
311: * @see #getEndYValue(int, int)
312: */
313: public Number getEndY(int series, int item) {
314: return new Double(getEndYValue(series, item));
315: }
316:
317: /**
318: * Returns the starting x-value for an item within a series.
319: *
320: * @param series the series index (in the range <code>0</code> to
321: * <code>getSeriesCount() - 1</code>).
322: * @param item the item index (in the range <code>0</code> to
323: * <code>getItemCount(series)</code>).
324: *
325: * @return The starting x-value.
326: *
327: * @throws ArrayIndexOutOfBoundsException if <code>series</code> is not
328: * within the specified range.
329: * @throws ArrayIndexOutOfBoundsException if <code>item</code> is not
330: * within the specified range.
331: *
332: * @see #getStartXValue(int, int)
333: */
334: public Number getStartX(int series, int item) {
335: return new Double(getStartXValue(series, item));
336: }
337:
338: /**
339: * Returns the starting y-value for an item within a series.
340: *
341: * @param series the series index (in the range <code>0</code> to
342: * <code>getSeriesCount() - 1</code>).
343: * @param item the item index (in the range <code>0</code> to
344: * <code>getItemCount(series)</code>).
345: *
346: * @return The starting y-value.
347: *
348: * @throws ArrayIndexOutOfBoundsException if <code>series</code> is not
349: * within the specified range.
350: * @throws ArrayIndexOutOfBoundsException if <code>item</code> is not
351: * within the specified range.
352: *
353: * @see #getStartYValue(int, int)
354: */
355: public Number getStartY(int series, int item) {
356: return new Double(getStartYValue(series, item));
357: }
358:
359: /**
360: * Returns the x-value for an item within a series.
361: *
362: * @param series the series index (in the range <code>0</code> to
363: * <code>getSeriesCount() - 1</code>).
364: * @param item the item index (in the range <code>0</code> to
365: * <code>getItemCount(series)</code>).
366: *
367: * @return The x-value.
368: *
369: * @throws ArrayIndexOutOfBoundsException if <code>series</code> is not
370: * within the specified range.
371: * @throws ArrayIndexOutOfBoundsException if <code>item</code> is not
372: * within the specified range.
373: *
374: * @see #getXValue(int, int)
375: */
376: public Number getX(int series, int item) {
377: return new Double(getXValue(series, item));
378: }
379:
380: /**
381: * Returns the y-value for an item within a series.
382: *
383: * @param series the series index (in the range <code>0</code> to
384: * <code>getSeriesCount() - 1</code>).
385: * @param item the item index (in the range <code>0</code> to
386: * <code>getItemCount(series)</code>).
387: *
388: * @return The y-value.
389: *
390: * @throws ArrayIndexOutOfBoundsException if <code>series</code> is not
391: * within the specified range.
392: * @throws ArrayIndexOutOfBoundsException if <code>item</code> is not
393: * within the specified range.
394: *
395: * @see #getYValue(int, int)
396: */
397: public Number getY(int series, int item) {
398: return new Double(getYValue(series, item));
399: }
400:
401: /**
402: * Adds a series or if a series with the same key already exists replaces
403: * the data for that series, then sends a {@link DatasetChangeEvent} to
404: * all registered listeners.
405: *
406: * @param seriesKey the series key (<code>null</code> not permitted).
407: * @param data the data (must be an array with length 6, containing six
408: * arrays of equal length, the first containing the x-values and the
409: * second containing the y-values).
410: */
411: public void addSeries(Comparable seriesKey, double[][] data) {
412: if (seriesKey == null) {
413: throw new IllegalArgumentException(
414: "The 'seriesKey' cannot be null.");
415: }
416: if (data == null) {
417: throw new IllegalArgumentException("The 'data' is null.");
418: }
419: if (data.length != 6) {
420: throw new IllegalArgumentException(
421: "The 'data' array must have length == 6.");
422: }
423: int length = data[0].length;
424: if (length != data[1].length || length != data[2].length
425: || length != data[3].length || length != data[4].length
426: || length != data[5].length) {
427: throw new IllegalArgumentException(
428: "The 'data' array must contain two arrays with equal length.");
429: }
430: int seriesIndex = indexOf(seriesKey);
431: if (seriesIndex == -1) { // add a new series
432: this .seriesKeys.add(seriesKey);
433: this .seriesList.add(data);
434: } else { // replace an existing series
435: this .seriesList.remove(seriesIndex);
436: this .seriesList.add(seriesIndex, data);
437: }
438: notifyListeners(new DatasetChangeEvent(this , this ));
439: }
440:
441: /**
442: * Tests this <code>DefaultIntervalXYDataset</code> instance for equality
443: * with an arbitrary object. This method returns <code>true</code> if and
444: * only if:
445: * <ul>
446: * <li><code>obj</code> is not <code>null</code>;</li>
447: * <li><code>obj</code> is an instance of
448: * <code>DefaultIntervalXYDataset</code>;</li>
449: * <li>both datasets have the same number of series, each containing
450: * exactly the same values.</li>
451: * </ul>
452: *
453: * @param obj the object (<code>null</code> permitted).
454: *
455: * @return A boolean.
456: */
457: public boolean equals(Object obj) {
458: if (obj == this ) {
459: return true;
460: }
461: if (!(obj instanceof DefaultIntervalXYDataset)) {
462: return false;
463: }
464: DefaultIntervalXYDataset that = (DefaultIntervalXYDataset) obj;
465: if (!this .seriesKeys.equals(that.seriesKeys)) {
466: return false;
467: }
468: for (int i = 0; i < this .seriesList.size(); i++) {
469: double[][] d1 = (double[][]) this .seriesList.get(i);
470: double[][] d2 = (double[][]) that.seriesList.get(i);
471: double[] d1x = d1[0];
472: double[] d2x = d2[0];
473: if (!Arrays.equals(d1x, d2x)) {
474: return false;
475: }
476: double[] d1xs = d1[1];
477: double[] d2xs = d2[1];
478: if (!Arrays.equals(d1xs, d2xs)) {
479: return false;
480: }
481: double[] d1xe = d1[2];
482: double[] d2xe = d2[2];
483: if (!Arrays.equals(d1xe, d2xe)) {
484: return false;
485: }
486: double[] d1y = d1[3];
487: double[] d2y = d2[3];
488: if (!Arrays.equals(d1y, d2y)) {
489: return false;
490: }
491: double[] d1ys = d1[4];
492: double[] d2ys = d2[4];
493: if (!Arrays.equals(d1ys, d2ys)) {
494: return false;
495: }
496: double[] d1ye = d1[5];
497: double[] d2ye = d2[5];
498: if (!Arrays.equals(d1ye, d2ye)) {
499: return false;
500: }
501: }
502: return true;
503: }
504:
505: /**
506: * Returns a hash code for this instance.
507: *
508: * @return A hash code.
509: */
510: public int hashCode() {
511: int result;
512: result = this .seriesKeys.hashCode();
513: result = 29 * result + this .seriesList.hashCode();
514: return result;
515: }
516:
517: /**
518: * Returns a clone of this dataset.
519: *
520: * @return A clone.
521: *
522: * @throws CloneNotSupportedException if the dataset contains a series with
523: * a key that cannot be cloned.
524: */
525: public Object clone() throws CloneNotSupportedException {
526: DefaultIntervalXYDataset clone = (DefaultIntervalXYDataset) super
527: .clone();
528: clone.seriesKeys = new java.util.ArrayList(this .seriesKeys);
529: clone.seriesList = new ArrayList(this .seriesList.size());
530: for (int i = 0; i < this .seriesList.size(); i++) {
531: double[][] data = (double[][]) this .seriesList.get(i);
532: double[] x = data[0];
533: double[] xStart = data[1];
534: double[] xEnd = data[2];
535: double[] y = data[3];
536: double[] yStart = data[4];
537: double[] yEnd = data[5];
538: double[] xx = new double[x.length];
539: double[] xxStart = new double[xStart.length];
540: double[] xxEnd = new double[xEnd.length];
541: double[] yy = new double[y.length];
542: double[] yyStart = new double[yStart.length];
543: double[] yyEnd = new double[yEnd.length];
544: System.arraycopy(x, 0, xx, 0, x.length);
545: System.arraycopy(xStart, 0, xxStart, 0, xStart.length);
546: System.arraycopy(xEnd, 0, xxEnd, 0, xEnd.length);
547: System.arraycopy(y, 0, yy, 0, y.length);
548: System.arraycopy(yStart, 0, yyStart, 0, yStart.length);
549: System.arraycopy(yEnd, 0, yyEnd, 0, yEnd.length);
550: clone.seriesList.add(i, new double[][] { xx, xxStart,
551: xxEnd, yy, yyStart, yyEnd });
552: }
553: return clone;
554: }
555:
556: }
|