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