/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-2012, by Object Refinery Limited and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ---------------------- * DefaultXYZDataset.java * ---------------------- * (C) Copyright 2006-2012, by Object Refinery Limited. * * Original Author: David Gilbert (for Object Refinery Limited); * Contributor(s): -; * * Changes * ------- * 12-Jul-2006 : Version 1 (DG); * 06-Oct-2006 : Fixed API doc warnings (DG); * 02-Nov-2006 : Fixed a problem with adding a new series with the same key * as an existing series (see bug 1589392) (DG); * 22-Apr-2008 : Implemented PublicCloneable (DG); * 17-Jun-2012 : Removed JCommon dependencies (DG); * */ package org.jfree.data.xy; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import org.jfree.chart.util.ParamChecks; import org.jfree.chart.util.PublicCloneable; import org.jfree.data.DomainOrder; import org.jfree.data.general.DatasetChangeEvent; /** * A default implementation of the {@link XYZDataset} interface that stores * data values in arrays of double primitives. * * @since 1.0.2 */ public class DefaultXYZDataset extends AbstractXYZDataset implements XYZDataset, PublicCloneable { /** * Storage for the series keys. This list must be kept in sync with the * seriesList. */ private List<Comparable> seriesKeys; /** * Storage for the series in the dataset. We use a list because the * order of the series is significant. This list must be kept in sync * with the seriesKeys list. */ private List<double[][]> seriesList; /** * Creates a new <code>DefaultXYZDataset</code> instance, initially * containing no data. */ public DefaultXYZDataset() { this.seriesKeys = new java.util.ArrayList<Comparable>(); this.seriesList = new java.util.ArrayList<double[][]>(); } /** * Returns the number of series in the dataset. * * @return The series count. */ @Override public int getSeriesCount() { return this.seriesList.size(); } /** * Returns the key for a series. * * @param series the series index (in the range <code>0</code> to * <code>getSeriesCount() - 1</code>). * * @return The key for the series. * * @throws IllegalArgumentException if <code>series</code> is not in the * specified range. */ @Override public Comparable getSeriesKey(int series) { if ((series < 0) || (series >= getSeriesCount())) { throw new IllegalArgumentException("Series index out of bounds"); } return this.seriesKeys.get(series); } /** * Returns the index of the series with the specified key, or -1 if there * is no such series in the dataset. * * @param seriesKey the series key (<code>null</code> permitted). * * @return The index, or -1. */ @Override public int indexOf(Comparable seriesKey) { return this.seriesKeys.indexOf(seriesKey); } /** * Returns the order of the domain (x-) values in the dataset. In this * implementation, we cannot guarantee that the x-values are ordered, so * this method returns <code>DomainOrder.NONE</code>. * * @return <code>DomainOrder.NONE</code>. */ @Override public DomainOrder getDomainOrder() { return DomainOrder.NONE; } /** * Returns the number of items in the specified series. * * @param series the series index (in the range <code>0</code> to * <code>getSeriesCount() - 1</code>). * * @return The item count. * * @throws IllegalArgumentException if <code>series</code> is not in the * specified range. */ @Override public int getItemCount(int series) { if ((series < 0) || (series >= getSeriesCount())) { throw new IllegalArgumentException("Series index out of bounds"); } double[][] seriesArray = this.seriesList.get(series); return seriesArray[0].length; } /** * Returns the x-value for an item within a series. * * @param series the series index (in the range <code>0</code> to * <code>getSeriesCount() - 1</code>). * @param item the item index (in the range <code>0</code> to * <code>getItemCount(series)</code>). * * @return The x-value. * * @throws ArrayIndexOutOfBoundsException if <code>series</code> or <code>item</code> is not * within the specified range. * * @see #getX(int, int) */ @Override public double getXValue(int series, int item) { double[][] seriesData = this.seriesList.get(series); return seriesData[0][item]; } /** * Returns the x-value for an item within a series. * * @param series the series index (in the range <code>0</code> to * <code>getSeriesCount() - 1</code>). * @param item the item index (in the range <code>0</code> to * <code>getItemCount(series)</code>). * * @return The x-value. * * @throws ArrayIndexOutOfBoundsException if <code>series</code> or <code>item</code> is not * within the specified range. * * @see #getXValue(int, int) */ @Override public Number getX(int series, int item) { return getXValue(series, item); } /** * Returns the y-value for an item within a series. * * @param series the series index (in the range <code>0</code> to * <code>getSeriesCount() - 1</code>). * @param item the item index (in the range <code>0</code> to * <code>getItemCount(series)</code>). * * @return The y-value. * * @throws ArrayIndexOutOfBoundsException if <code>series</code> or <code>item</code> is not * within the specified range. * * @see #getY(int, int) */ @Override public double getYValue(int series, int item) { double[][] seriesData = this.seriesList.get(series); return seriesData[1][item]; } /** * Returns the y-value for an item within a series. * * @param series the series index (in the range <code>0</code> to * <code>getSeriesCount() - 1</code>). * @param item the item index (in the range <code>0</code> to * <code>getItemCount(series)</code>). * * @return The y-value. * * @throws ArrayIndexOutOfBoundsException if <code>series</code> or <code>item</code> is not * within the specified range. * * @see #getX(int, int) */ @Override public Number getY(int series, int item) { return getYValue(series, item); } /** * Returns the z-value for an item within a series. * * @param series the series index (in the range <code>0</code> to * <code>getSeriesCount() - 1</code>). * @param item the item index (in the range <code>0</code> to * <code>getItemCount(series)</code>). * * @return The z-value. * * @throws ArrayIndexOutOfBoundsException if <code>series</code> or <code>item</code> is not * within the specified range. * * @see #getZ(int, int) */ @Override public double getZValue(int series, int item) { double[][] seriesData = this.seriesList.get(series); return seriesData[2][item]; } /** * Returns the z-value for an item within a series. * * @param series the series index (in the range <code>0</code> to * <code>getSeriesCount() - 1</code>). * @param item the item index (in the range <code>0</code> to * <code>getItemCount(series)</code>). * * @return The z-value. * * @throws ArrayIndexOutOfBoundsException if <code>series</code> or <code>item</code> is not * within the specified range. * * @see #getZ(int, int) */ @Override public Number getZ(int series, int item) { return getZValue(series, item); } /** * Adds a series or if a series with the same key already exists replaces * the data for that series, then sends a {@link DatasetChangeEvent} to * all registered listeners. * * @param seriesKey the series key (<code>null</code> not permitted). * @param data the data (must be an array with length 3, containing three * arrays of equal length, the first containing the x-values, the * second containing the y-values and the third containing the * z-values). */ public void addSeries(Comparable seriesKey, double[][] data) { ParamChecks.nullNotPermitted(seriesKey, "seriesKey"); ParamChecks.nullNotPermitted(data, "data"); if (data.length != 3) { throw new IllegalArgumentException( "The 'data' array must have length == 3."); } if (data[0].length != data[1].length || data[0].length != data[2].length) { throw new IllegalArgumentException("The 'data' array must contain " + "three arrays all having the same length."); } int seriesIndex = indexOf(seriesKey); if (seriesIndex == -1) { // add a new series this.seriesKeys.add(seriesKey); this.seriesList.add(data); } else { // replace an existing series this.seriesList.remove(seriesIndex); this.seriesList.add(seriesIndex, data); } notifyListeners(new DatasetChangeEvent(this, this)); } /** * Removes a series from the dataset, then sends a * {@link DatasetChangeEvent} to all registered listeners. * * @param seriesKey the series key (<code>null</code> not permitted). * */ public void removeSeries(Comparable seriesKey) { int seriesIndex = indexOf(seriesKey); if (seriesIndex >= 0) { this.seriesKeys.remove(seriesIndex); this.seriesList.remove(seriesIndex); notifyListeners(new DatasetChangeEvent(this, this)); } } /** * Tests this <code>DefaultXYDataset</code> instance for equality with an * arbitrary object. This method returns <code>true</code> if and only if: * <ul> * <li><code>obj</code> is not <code>null</code>;</li> * <li><code>obj</code> is an instance of * <code>DefaultXYDataset</code>;</li> * <li>both datasets have the same number of series, each containing * exactly the same values.</li> * </ul> * * @param obj the object (<code>null</code> permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof DefaultXYZDataset)) { return false; } DefaultXYZDataset that = (DefaultXYZDataset) obj; if (!this.seriesKeys.equals(that.seriesKeys)) { return false; } for (int i = 0; i < this.seriesList.size(); i++) { double[][] d1 = this.seriesList.get(i); double[][] d2 = that.seriesList.get(i); double[] d1x = d1[0]; double[] d2x = d2[0]; if (!Arrays.equals(d1x, d2x)) { return false; } double[] d1y = d1[1]; double[] d2y = d2[1]; if (!Arrays.equals(d1y, d2y)) { return false; } double[] d1z = d1[2]; double[] d2z = d2[2]; if (!Arrays.equals(d1z, d2z)) { return false; } } return true; } /** * Returns a hash code for this instance. * * @return A hash code. */ @Override public int hashCode() { int result; result = this.seriesKeys.hashCode(); result = 29 * result + this.seriesList.hashCode(); return result; } /** * Creates an independent copy of this dataset. * * @return The cloned dataset. * * @throws CloneNotSupportedException if there is a problem cloning the * dataset (for instance, if a non-cloneable object is used for a * series key). */ @Override public Object clone() throws CloneNotSupportedException { DefaultXYZDataset clone = (DefaultXYZDataset) super.clone(); clone.seriesKeys = new java.util.ArrayList<Comparable>(this.seriesKeys); clone.seriesList = new ArrayList<double[][]>(this.seriesList.size()); for (int i = 0; i < this.seriesList.size(); i++) { double[][] data = this.seriesList.get(i); double[] x = data[0]; double[] y = data[1]; double[] z = data[2]; double[] xx = new double[x.length]; double[] yy = new double[y.length]; double[] zz = new double[z.length]; System.arraycopy(x, 0, xx, 0, x.length); System.arraycopy(y, 0, yy, 0, y.length); System.arraycopy(z, 0, zz, 0, z.length); clone.seriesList.add(i, new double[][] {xx, yy, zz}); } return clone; } }