- /* ===========================================================
- * JFreeChart : a free chart library for the Java(tm) platform
- * ===========================================================
- *
- * (C) Copyright 2000-2005, 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
- *
- * [Java is a trademark or registered trademark of Sun Microsystems, Inc.
- * in the United States and other countries.]
- *
- * -------------
- * XYSeries.java
- * -------------
- * (C) Copyright 2001-2005, Object Refinery Limited and Contributors.
- *
- * Original Author: David Gilbert (for Object Refinery Limited);
- * Contributor(s): Aaron Metzger;
- * Jonathan Gabbai;
- * Richard Atkinson;
- * Michel Santos;
- *
- * $Id: XYSeries.java,v 1.8 2005/02/21 17:29:39 mungady Exp $
- *
- * Changes
- * -------
- * 15-Nov-2001 : Version 1 (DG);
- * 03-Apr-2002 : Added an add(double, double) method (DG);
- * 29-Apr-2002 : Added a clear() method (ARM);
- * 06-Jun-2002 : Updated Javadoc comments (DG);
- * 29-Aug-2002 : Modified to give user control over whether or not duplicate
- * x-values are allowed (DG);
- * 07-Oct-2002 : Fixed errors reported by Checkstyle (DG);
- * 11-Nov-2002 : Added maximum item count, code contributed by Jonathan
- * Gabbai (DG);
- * 26-Mar-2003 : Implemented Serializable (DG);
- * 04-Aug-2003 : Added getItems() method (DG);
- * 15-Aug-2003 : Changed 'data' from private to protected, added new add()
- * methods with a 'notify' argument (DG);
- * 22-Sep-2003 : Added getAllowDuplicateXValues() method (RA);
- * 29-Jan-2004 : Added autoSort attribute, based on a contribution by
- * Michel Santos - see patch 886740 (DG);
- * 03-Feb-2004 : Added indexOf() method (DG);
- * 16-Feb-2004 : Added remove() method (DG);
- * 18-Aug-2004 : Moved from org.jfree.data --> org.jfree.data.xy (DG);
- * 21-Feb-2005 : Added update(Number, Number) and addOrUpdate(Number, Number)
- * methods (DG);
- *
- */
- package org.jfree.data.xy;
- import java.io.Serializable;
- import java.util.Collections;
- import java.util.List;
- import org.jfree.data.general.Series;
- import org.jfree.data.general.SeriesChangeEvent;
- import org.jfree.data.general.SeriesException;
- import org.jfree.util.ObjectUtilities;
- /**
- * Represents a sequence of zero or more data items in the form (x, y). Items
- * in the series will be sorted into ascending order by X-value, and duplicate
- * X-values are permitted. Both the sorting and duplicate defaults can be
- * changed in the constructor. Y-values can be <code>null</code> to represent
- * missing values.
- */
- public class XYSeries extends Series implements Cloneable, Serializable {
- // In version 0.9.12, in response to several developer requests, I changed
- // the 'data' attribute from 'private' to 'protected', so that others can
- // make subclasses that work directly with the underlying data structure.
- /** Storage for the data items in the series. */
- protected List data;
- /** The maximum number of items for the series. */
- private int maximumItemCount = Integer.MAX_VALUE;
- /** A flag that controls whether the items are automatically sorted. */
- private boolean autoSort;
- /** A flag that controls whether or not duplicate x-values are allowed. */
- private boolean allowDuplicateXValues;
- /**
- * Creates a new empty series. By default, items added to the series will
- * be sorted into ascending order by x-value, and duplicate x-values will
- * be allowed (these defaults can be modified with another constructor.
- *
- * @param name the series name (<code>null</code> not permitted).
- */
- public XYSeries(String name) {
- this(name, true, true);
- }
- /**
- * Constructs a new xy-series that contains no data. You can specify
- * whether or not duplicate x-values are allowed for the series.
- *
- * @param name the series name (<code>null</code> not permitted).
- * @param autoSort a flag that controls whether or not the items in the
- * series are sorted.
- * @param allowDuplicateXValues a flag that controls whether duplicate
- * x-values are allowed.
- */
- public XYSeries(String name,
- boolean autoSort,
- boolean allowDuplicateXValues) {
- super(name);
- this.data = new java.util.ArrayList();
- this.autoSort = autoSort;
- this.allowDuplicateXValues = allowDuplicateXValues;
- }
- /**
- * Returns the flag that controls whether the items in the series are
- * automatically sorted. There is no setter for this flag, it must be
- * defined in the series constructor.
- *
- * @return A boolean.
- */
- public boolean getAutoSort() {
- return this.autoSort;
- }
- /**
- * Returns a flag that controls whether duplicate x-values are allowed.
- * This flag can only be set in the constructor.
- *
- * @return A boolean.
- */
- public boolean getAllowDuplicateXValues() {
- return this.allowDuplicateXValues;
- }
- /**
- * Returns the number of items in the series.
- *
- * @return The item count.
- */
- public int getItemCount() {
- return this.data.size();
- }
- /**
- * Returns the list of data items for the series (the list contains
- * {@link XYDataItem} objects and is unmodifiable).
- *
- * @return The list of data items.
- */
- public List getItems() {
- return Collections.unmodifiableList(this.data);
- }
- /**
- * Returns the maximum number of items that will be retained in the series.
- * The default value is <code>Integer.MAX_VALUE</code>).
- *
- * @return The maximum item count.
- */
- public int getMaximumItemCount() {
- return this.maximumItemCount;
- }
- /**
- * Sets the maximum number of items that will be retained in the series.
- * <P>
- * If you add a new item to the series such that the number of items will
- * exceed the maximum item count, then the FIRST element in the series is
- * automatically removed, ensuring that the maximum item count is not
- * exceeded.
- *
- * @param maximum the maximum.
- */
- public void setMaximumItemCount(int maximum) {
- this.maximumItemCount = maximum;
- }
- /**
- * Adds a data item to the series and sends a {@link SeriesChangeEvent} to
- * all registered listeners.
- *
- * @param item the (x, y) item (<code>null</code> not permitted).
- */
- public void add(XYDataItem item) {
- // argument checking delegated...
- add(item, true);
- }
- /**
- * Adds a data item to the series and, if requested, sends a
- * {@link SeriesChangeEvent} to all registered listeners.
- *
- * @param item the (x, y) item (<code>null</code> not permitted).
- * @param notify a flag that controls whether or not a
- * {@link SeriesChangeEvent} is sent to all registered
- * listeners.
- */
- public void add(XYDataItem item, boolean notify) {
- if (item == null) {
- throw new IllegalArgumentException("Null 'item' argument.");
- }
- int index = Collections.binarySearch(this.data, item);
- if (index < 0) {
- if (this.autoSort) {
- this.data.add(-index - 1, item);
- }
- else {
- this.data.add(item);
- }
- if (getItemCount() > this.maximumItemCount) {
- this.data.remove(0);
- }
- }
- else {
- if (this.allowDuplicateXValues) {
- if (this.autoSort) {
- // need to make sure we are adding *after* any duplicates
- int size = this.data.size();
- while (index < size
- && item.compareTo(this.data.get(index)) == 0) {
- index++;
- }
- if (index < this.data.size()) {
- this.data.add(index, item);
- }
- else {
- this.data.add(item);
- }
- }
- else {
- this.data.add(item);
- }
- if (getItemCount() > this.maximumItemCount) {
- this.data.remove(0);
- }
- }
- else {
- throw new SeriesException("X-value already exists.");
- }
- }
- if (notify) {
- fireSeriesChanged();
- }
- }
- /**
- * Adds a data item to the series and sends a {@link SeriesChangeEvent} to
- * all registered listeners.
- *
- * @param x the x value.
- * @param y the y value.
- */
- public void add(double x, double y) {
- add(new Double(x), new Double(y), true);
- }
- /**
- * Adds a data item to the series and, if requested, sends a
- * {@link SeriesChangeEvent} to all registered listeners.
- *
- * @param x the x value.
- * @param y the y value.
- * @param notify a flag that controls whether or not a
- * {@link SeriesChangeEvent} is sent to all registered
- * listeners.
- */
- public void add(double x, double y, boolean notify) {
- add(new Double(x), new Double(y), notify);
- }
- /**
- * Adds a data item to the series and sends a {@link SeriesChangeEvent} to
- * all registered listeners. The unusual pairing of parameter types is to
- * make it easier to add <code>null</code> y-values.
- *
- * @param x the x value.
- * @param y the y value (<code>null</code> permitted).
- */
- public void add(double x, Number y) {
- add(new Double(x), y);
- }
- /**
- * Adds a data item to the series and, if requested, sends a
- * {@link SeriesChangeEvent} to all registered listeners. The unusual
- * pairing of parameter types is to make it easier to add null y-values.
- *
- * @param x the x value.
- * @param y the y value (<code>null</code> permitted).
- * @param notify a flag that controls whether or not a
- * {@link SeriesChangeEvent} is sent to all registered
- * listeners.
- */
- public void add(double x, Number y, boolean notify) {
- add(new Double(x), y, notify);
- }
- /**
- * Adds new data to the series and sends a {@link SeriesChangeEvent} to
- * all registered listeners.
- * <P>
- * Throws an exception if the x-value is a duplicate AND the
- * allowDuplicateXValues flag is false.
- *
- * @param x the x-value (<code>null</code> not permitted).
- * @param y the y-value (<code>null</code> permitted).
- */
- public void add(Number x, Number y) {
- // argument checking delegated...
- add(x, y, true);
- }
- /**
- * Adds new data to the series and, if requested, sends a
- * {@link SeriesChangeEvent} to all registered listeners.
- * <P>
- * Throws an exception if the x-value is a duplicate AND the
- * allowDuplicateXValues flag is false.
- *
- * @param x the x-value (<code>null</code> not permitted).
- * @param y the y-value (<code>null</code> permitted).
- * @param notify a flag the controls whether or not a
- * {@link SeriesChangeEvent} is sent to all registered
- * listeners.
- */
- public void add(Number x, Number y, boolean notify) {
- if (x == null) {
- throw new IllegalArgumentException("Null 'x' argument.");
- }
- XYDataItem item = new XYDataItem(x, y);
- add(item, notify);
- }
- /**
- * Deletes a range of items from the series and sends a
- * {@link SeriesChangeEvent} to all registered listeners.
- *
- * @param start the start index (zero-based).
- * @param end the end index (zero-based).
- */
- public void delete(int start, int end) {
- for (int i = start; i <= end; i++) {
- this.data.remove(start);
- }
- fireSeriesChanged();
- }
- /**
- * Removes the item at the specified index.
- *
- * @param index the index.
- *
- * @return The item removed.
- */
- public XYDataItem remove(int index) {
- return (XYDataItem) this.data.remove(index);
- }
- /**
- * Removes the item(s) with the specified x-value.
- *
- * @param x the x-value.
- * @return The item removed.
- */
- public XYDataItem remove(Number x) {
- return remove(indexOf(x));
- }
- /**
- * Removes all data items from the series.
- */
- public void clear() {
- if (this.data.size() > 0) {
- this.data.clear();
- fireSeriesChanged();
- }
- }
- /**
- * Return the data item with the specified index.
- *
- * @param index the index.
- *
- * @return The data item with the specified index.
- */
- public XYDataItem getDataItem(int index) {
- return (XYDataItem) this.data.get(index);
- }
- /**
- * Returns the x-value at the specified index.
- *
- * @param index the index (zero-based).
- *
- * @return The x-value (never <code>null</code>).
- */
- public Number getX(int index) {
- return getDataItem(index).getX();
- }
- /**
- * Returns the y-value at the specified index.
- *
- * @param index the index (zero-based).
- *
- * @return The y-value (possibly <code>null</code>).
- */
- public Number getY(int index) {
- return getDataItem(index).getY();
- }
- /**
- * Updates the value of an item in the series and sends a
- * {@link SeriesChangeEvent} to all registered listeners.
- *
- * @param index the item (zero based index).
- * @param y the new value (<code>null</code> permitted).
- */
- public void update(int index, Number y) {
- XYDataItem item = getDataItem(index);
- item.setY(y);
- fireSeriesChanged();
- }
- /**
- * Updates an item in the series.
- *
- * @param x the x-value (<code>null</code> not permitted).
- * @param y the y-value (<code>null</code> permitted).
- *
- * @throws SeriesException if there is no existing item with the specified
- * x-value.
- */
- public void update(Number x, Number y) {
- XYDataItem item = new XYDataItem(x, y);
- int index = Collections.binarySearch(this.data, item);
- if (index < 0) {
- throw new SeriesException("No observation for x = " + x);
- }
- else {
- this.data.set(index, item);
- }
- }
- /**
- * Adds or updates an item in the series and sends a
- * {@link org.jfree.data.general.SeriesChangeEvent} to all registered
- * listeners.
- *
- * @param x the x-value (<code>null</code> not permitted).
- * @param y the y-value (<code>null</code> permitted).
- *
- * @return A copy of the overwritten data item, or <code>null</code> if no
- * item was overwritten.
- */
- public XYDataItem addOrUpdate(Number x,Number y) {
- if (x == null) {
- throw new IllegalArgumentException("Null 'x' argument.");
- }
- XYDataItem overwritten = null;
- XYDataItem key = new XYDataItem(x, y);
- int index = Collections.binarySearch(this.data, key);
- if (index >= 0) {
- XYDataItem existing = (XYDataItem) this.data.get(index);
- try {
- overwritten = (XYDataItem) existing.clone();
- }
- catch (CloneNotSupportedException e) {
- throw new SeriesException("Couldn't clone XYDataItem!");
- }
- existing.setY(y);
- fireSeriesChanged();
- }
- else {
- this.data.add(-index - 1, new XYDataItem(x, y));
- // check if this addition will exceed the maximum item count...
- if (getItemCount() > this.maximumItemCount) {
- this.data.remove(0);
- }
- fireSeriesChanged();
- }
- return overwritten;
- }
- /**
- * Returns the index of the item with the specified x-value. Note: if the
- * series is not sorted in order of ascending x-values, the result is
- * undefined.
- *
- * @param x the x-value (<code>null</code> not permitted).
- *
- * @return The index.
- */
- public int indexOf(Number x) {
- return Collections.binarySearch(this.data, new XYDataItem(x, null));
- }
- /**
- * Returns a clone of the series.
- *
- * @return A clone of the time series.
- *
- * @throws CloneNotSupportedException if there is a cloning problem.
- */
- public Object clone() throws CloneNotSupportedException {
- Object clone = createCopy(0, getItemCount() - 1);
- return clone;
- }
- /**
- * Creates a new series by copying a subset of the data in this time series.
- *
- * @param start the index of the first item to copy.
- * @param end the index of the last item to copy.
- *
- * @return A series containing a copy of this series from start until end.
- *
- * @throws CloneNotSupportedException if there is a cloning problem.
- */
- public XYSeries createCopy(int start, int end)
- throws CloneNotSupportedException {
- XYSeries copy = (XYSeries) super.clone();
- copy.data = new java.util.ArrayList();
- if (this.data.size() > 0) {
- for (int index = start; index <= end; index++) {
- XYDataItem item = (XYDataItem) this.data.get(index);
- XYDataItem clone = (XYDataItem) item.clone();
- try {
- copy.add(clone);
- }
- catch (SeriesException e) {
- System.err.println("Unable to add cloned data item.");
- }
- }
- }
- return copy;
- }
- /**
- * Tests this series for equality with an arbitrary object.
- *
- * @param object the object to test against for equality
- * (<code>null</code> permitted).
- *
- * @return A boolean.
- */
- public boolean equals(Object object) {
- if (object == null) {
- return false;
- }
- if (object == this) {
- return true;
- }
- if (!super.equals(object)) {
- return false;
- }
- if (!(object instanceof XYSeries)) {
- return false;
- }
- XYSeries s = (XYSeries) object;
- if (this.maximumItemCount != s.maximumItemCount) {
- return false;
- }
- if (this.autoSort != s.autoSort) {
- return false;
- }
- if (this.allowDuplicateXValues != s.allowDuplicateXValues) {
- return false;
- }
- if (!ObjectUtilities.equal(this.data, s.data)) {
- return false;
- }
- return true;
- }
- /**
- * Returns a hash code.
- *
- * @return A hash code.
- */
- public int hashCode() {
- int result = super.hashCode();
- result = 29 * result + (this.data != null ? this.data.hashCode() : 0);
- result = 29 * result + this.maximumItemCount;
- result = 29 * result + (this.autoSort ? 1 : 0);
- result = 29 * result + (this.allowDuplicateXValues ? 1 : 0);
- return result;
- }
- }