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: * DefaultXYZDataset.java
029: * ----------------------
030: * (C) Copyright 2006, by Object Refinery Limited.
031: *
032: * Original Author: David Gilbert (for Object Refinery Limited);
033: * Contributor(s): -;
034: *
035: * $Id: DefaultXYZDataset.java,v 1.1.2.4 2006/11/28 14:06:14 mungady Exp $
036: *
037: * Changes
038: * -------
039: * 12-Jul-2006 : Version 1 (DG);
040: * 06-Oct-2006 : Fixed API doc warnings (DG);
041: * 02-Nov-2006 : Fixed a problem with adding a new series with the same key
042: * as an existing series (see bug 1589392) (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.DomainOrder;
053: import org.jfree.data.general.DatasetChangeEvent;
054:
055: /**
056: * A default implementation of the {@link XYZDataset} interface that stores
057: * data values in arrays of double primitives.
058: *
059: * @since 1.0.2
060: */
061: public class DefaultXYZDataset extends AbstractXYZDataset implements
062: XYZDataset {
063:
064: /**
065: * Storage for the series keys. This list must be kept in sync with the
066: * seriesList.
067: */
068: private List seriesKeys;
069:
070: /**
071: * Storage for the series in the dataset. We use a list because the
072: * order of the series is significant. This list must be kept in sync
073: * with the seriesKeys list.
074: */
075: private List seriesList;
076:
077: /**
078: * Creates a new <code>DefaultXYZDataset</code> instance, initially
079: * containing no data.
080: */
081: public DefaultXYZDataset() {
082: this .seriesKeys = new java.util.ArrayList();
083: this .seriesList = new java.util.ArrayList();
084: }
085:
086: /**
087: * Returns the number of series in the dataset.
088: *
089: * @return The series count.
090: */
091: public int getSeriesCount() {
092: return this .seriesList.size();
093: }
094:
095: /**
096: * Returns the key for a series.
097: *
098: * @param series the series index (in the range <code>0</code> to
099: * <code>getSeriesCount() - 1</code>).
100: *
101: * @return The key for the series.
102: *
103: * @throws IllegalArgumentException if <code>series</code> is not in the
104: * specified range.
105: */
106: public Comparable getSeriesKey(int series) {
107: if ((series < 0) || (series >= getSeriesCount())) {
108: throw new IllegalArgumentException(
109: "Series index out of bounds");
110: }
111: return (Comparable) this .seriesKeys.get(series);
112: }
113:
114: /**
115: * Returns the index of the series with the specified key, or -1 if there
116: * is no such series in the dataset.
117: *
118: * @param seriesKey the series key (<code>null</code> permitted).
119: *
120: * @return The index, or -1.
121: */
122: public int indexOf(Comparable seriesKey) {
123: return this .seriesKeys.indexOf(seriesKey);
124: }
125:
126: /**
127: * Returns the order of the domain (x-) values in the dataset. In this
128: * implementation, we cannot guarantee that the x-values are ordered, so
129: * this method returns <code>DomainOrder.NONE</code>.
130: *
131: * @return <code>DomainOrder.NONE</code>.
132: */
133: public DomainOrder getDomainOrder() {
134: return DomainOrder.NONE;
135: }
136:
137: /**
138: * Returns the number of items in the specified series.
139: *
140: * @param series the series index (in the range <code>0</code> to
141: * <code>getSeriesCount() - 1</code>).
142: *
143: * @return The item count.
144: *
145: * @throws IllegalArgumentException if <code>series</code> is not in the
146: * specified range.
147: */
148: public int getItemCount(int series) {
149: if ((series < 0) || (series >= getSeriesCount())) {
150: throw new IllegalArgumentException(
151: "Series index out of bounds");
152: }
153: double[][] seriesArray = (double[][]) this .seriesList
154: .get(series);
155: return seriesArray[0].length;
156: }
157:
158: /**
159: * Returns the x-value for an item within a series.
160: *
161: * @param series the series index (in the range <code>0</code> to
162: * <code>getSeriesCount() - 1</code>).
163: * @param item the item index (in the range <code>0</code> to
164: * <code>getItemCount(series)</code>).
165: *
166: * @return The x-value.
167: *
168: * @throws ArrayIndexOutOfBoundsException if <code>series</code> is not
169: * within the specified range.
170: * @throws ArrayIndexOutOfBoundsException if <code>item</code> is not
171: * within the specified range.
172: *
173: * @see #getX(int, int)
174: */
175: public double getXValue(int series, int item) {
176: double[][] seriesData = (double[][]) this .seriesList
177: .get(series);
178: return seriesData[0][item];
179: }
180:
181: /**
182: * Returns the x-value for an item within a series.
183: *
184: * @param series the series index (in the range <code>0</code> to
185: * <code>getSeriesCount() - 1</code>).
186: * @param item the item index (in the range <code>0</code> to
187: * <code>getItemCount(series)</code>).
188: *
189: * @return The x-value.
190: *
191: * @throws ArrayIndexOutOfBoundsException if <code>series</code> is not
192: * within the specified range.
193: * @throws ArrayIndexOutOfBoundsException if <code>item</code> is not
194: * within the specified range.
195: *
196: * @see #getXValue(int, int)
197: */
198: public Number getX(int series, int item) {
199: return new Double(getXValue(series, item));
200: }
201:
202: /**
203: * Returns the y-value for an item within a series.
204: *
205: * @param series the series index (in the range <code>0</code> to
206: * <code>getSeriesCount() - 1</code>).
207: * @param item the item index (in the range <code>0</code> to
208: * <code>getItemCount(series)</code>).
209: *
210: * @return The y-value.
211: *
212: * @throws ArrayIndexOutOfBoundsException if <code>series</code> is not
213: * within the specified range.
214: * @throws ArrayIndexOutOfBoundsException if <code>item</code> is not
215: * within the specified range.
216: *
217: * @see #getY(int, int)
218: */
219: public double getYValue(int series, int item) {
220: double[][] seriesData = (double[][]) this .seriesList
221: .get(series);
222: return seriesData[1][item];
223: }
224:
225: /**
226: * Returns the y-value for an item within a series.
227: *
228: * @param series the series index (in the range <code>0</code> to
229: * <code>getSeriesCount() - 1</code>).
230: * @param item the item index (in the range <code>0</code> to
231: * <code>getItemCount(series)</code>).
232: *
233: * @return The y-value.
234: *
235: * @throws ArrayIndexOutOfBoundsException if <code>series</code> is not
236: * within the specified range.
237: * @throws ArrayIndexOutOfBoundsException if <code>item</code> is not
238: * within the specified range.
239: *
240: * @see #getX(int, int)
241: */
242: public Number getY(int series, int item) {
243: return new Double(getYValue(series, item));
244: }
245:
246: /**
247: * Returns the z-value for an item within a series.
248: *
249: * @param series the series index (in the range <code>0</code> to
250: * <code>getSeriesCount() - 1</code>).
251: * @param item the item index (in the range <code>0</code> to
252: * <code>getItemCount(series)</code>).
253: *
254: * @return The z-value.
255: *
256: * @throws ArrayIndexOutOfBoundsException if <code>series</code> is not
257: * within the specified range.
258: * @throws ArrayIndexOutOfBoundsException if <code>item</code> is not
259: * within the specified range.
260: *
261: * @see #getZ(int, int)
262: */
263: public double getZValue(int series, int item) {
264: double[][] seriesData = (double[][]) this .seriesList
265: .get(series);
266: return seriesData[2][item];
267: }
268:
269: /**
270: * Returns the z-value for an item within a series.
271: *
272: * @param series the series index (in the range <code>0</code> to
273: * <code>getSeriesCount() - 1</code>).
274: * @param item the item index (in the range <code>0</code> to
275: * <code>getItemCount(series)</code>).
276: *
277: * @return The z-value.
278: *
279: * @throws ArrayIndexOutOfBoundsException if <code>series</code> is not
280: * within the specified range.
281: * @throws ArrayIndexOutOfBoundsException if <code>item</code> is not
282: * within the specified range.
283: *
284: * @see #getZ(int, int)
285: */
286: public Number getZ(int series, int item) {
287: return new Double(getZValue(series, item));
288: }
289:
290: /**
291: * Adds a series or if a series with the same key already exists replaces
292: * the data for that series, then sends a {@link DatasetChangeEvent} to
293: * all registered listeners.
294: *
295: * @param seriesKey the series key (<code>null</code> not permitted).
296: * @param data the data (must be an array with length 3, containing three
297: * arrays of equal length, the first containing the x-values, the
298: * second containing the y-values and the third containing the
299: * z-values).
300: */
301: public void addSeries(Comparable seriesKey, double[][] data) {
302: if (seriesKey == null) {
303: throw new IllegalArgumentException(
304: "The 'seriesKey' cannot be null.");
305: }
306: if (data == null) {
307: throw new IllegalArgumentException("The 'data' is null.");
308: }
309: if (data.length != 3) {
310: throw new IllegalArgumentException(
311: "The 'data' array must have length == 3.");
312: }
313: if (data[0].length != data[1].length
314: || data[0].length != data[2].length) {
315: throw new IllegalArgumentException(
316: "The 'data' array must contain "
317: + "three arrays all having the same length.");
318: }
319: int seriesIndex = indexOf(seriesKey);
320: if (seriesIndex == -1) { // add a new series
321: this .seriesKeys.add(seriesKey);
322: this .seriesList.add(data);
323: } else { // replace an existing series
324: this .seriesList.remove(seriesIndex);
325: this .seriesList.add(seriesIndex, data);
326: }
327: notifyListeners(new DatasetChangeEvent(this , this ));
328: }
329:
330: /**
331: * Removes a series from the dataset, then sends a
332: * {@link DatasetChangeEvent} to all registered listeners.
333: *
334: * @param seriesKey the series key (<code>null</code> not permitted).
335: *
336: */
337: public void removeSeries(Comparable seriesKey) {
338: int seriesIndex = indexOf(seriesKey);
339: if (seriesIndex >= 0) {
340: this .seriesKeys.remove(seriesIndex);
341: this .seriesList.remove(seriesIndex);
342: notifyListeners(new DatasetChangeEvent(this , this ));
343: }
344: }
345:
346: /**
347: * Tests this <code>DefaultXYDataset</code> instance for equality with an
348: * arbitrary object. This method returns <code>true</code> if and only if:
349: * <ul>
350: * <li><code>obj</code> is not <code>null</code>;</li>
351: * <li><code>obj</code> is an instance of
352: * <code>DefaultXYDataset</code>;</li>
353: * <li>both datasets have the same number of series, each containing
354: * exactly the same values.</li>
355: * </ul>
356: *
357: * @param obj the object (<code>null</code> permitted).
358: *
359: * @return A boolean.
360: */
361: public boolean equals(Object obj) {
362: if (obj == this ) {
363: return true;
364: }
365: if (!(obj instanceof DefaultXYZDataset)) {
366: return false;
367: }
368: DefaultXYZDataset that = (DefaultXYZDataset) obj;
369: if (!this .seriesKeys.equals(that.seriesKeys)) {
370: return false;
371: }
372: for (int i = 0; i < this .seriesList.size(); i++) {
373: double[][] d1 = (double[][]) this .seriesList.get(i);
374: double[][] d2 = (double[][]) that.seriesList.get(i);
375: double[] d1x = d1[0];
376: double[] d2x = d2[0];
377: if (!Arrays.equals(d1x, d2x)) {
378: return false;
379: }
380: double[] d1y = d1[1];
381: double[] d2y = d2[1];
382: if (!Arrays.equals(d1y, d2y)) {
383: return false;
384: }
385: double[] d1z = d1[2];
386: double[] d2z = d2[2];
387: if (!Arrays.equals(d1z, d2z)) {
388: return false;
389: }
390: }
391: return true;
392: }
393:
394: /**
395: * Returns a hash code for this instance.
396: *
397: * @return A hash code.
398: */
399: public int hashCode() {
400: int result;
401: result = this .seriesKeys.hashCode();
402: result = 29 * result + this .seriesList.hashCode();
403: return result;
404: }
405:
406: /**
407: * Creates an independent copy of this dataset.
408: *
409: * @return The cloned dataset.
410: *
411: * @throws CloneNotSupportedException if there is a problem cloning the
412: * dataset (for instance, if a non-cloneable object is used for a
413: * series key).
414: */
415: public Object clone() throws CloneNotSupportedException {
416: DefaultXYZDataset clone = (DefaultXYZDataset) super .clone();
417: clone.seriesKeys = new java.util.ArrayList(this .seriesKeys);
418: clone.seriesList = new ArrayList(this .seriesList.size());
419: for (int i = 0; i < this .seriesList.size(); i++) {
420: double[][] data = (double[][]) this .seriesList.get(i);
421: double[] x = data[0];
422: double[] y = data[1];
423: double[] z = data[2];
424: double[] xx = new double[x.length];
425: double[] yy = new double[y.length];
426: double[] zz = new double[z.length];
427: System.arraycopy(x, 0, xx, 0, x.length);
428: System.arraycopy(y, 0, yy, 0, y.length);
429: System.arraycopy(z, 0, zz, 0, z.length);
430: clone.seriesList.add(i, new double[][] { xx, yy, zz });
431: }
432: return clone;
433: }
434:
435: }
|