Source for org.jfree.data.xy.XYSeries

   1: /* ===========================================================
   2:  * JFreeChart : a free chart library for the Java(tm) platform
   3:  * ===========================================================
   4:  *
   5:  * (C) Copyright 2000-2007, by Object Refinery Limited and Contributors.
   6:  *
   7:  * Project Info:  http://www.jfree.org/jfreechart/index.html
   8:  *
   9:  * This library is free software; you can redistribute it and/or modify it 
  10:  * under the terms of the GNU Lesser General Public License as published by 
  11:  * the Free Software Foundation; either version 2.1 of the License, or 
  12:  * (at your option) any later version.
  13:  *
  14:  * This library is distributed in the hope that it will be useful, but 
  15:  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 
  16:  * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public 
  17:  * License for more details.
  18:  *
  19:  * You should have received a copy of the GNU Lesser General Public
  20:  * License along with this library; if not, write to the Free Software
  21:  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, 
  22:  * USA.  
  23:  *
  24:  * [Java is a trademark or registered trademark of Sun Microsystems, Inc. 
  25:  * in the United States and other countries.]
  26:  *
  27:  * -------------
  28:  * XYSeries.java
  29:  * -------------
  30:  * (C) Copyright 2001-2007, Object Refinery Limited and Contributors.
  31:  *
  32:  * Original Author:  David Gilbert (for Object Refinery Limited);
  33:  * Contributor(s):   Aaron Metzger;
  34:  *                   Jonathan Gabbai;
  35:  *                   Richard Atkinson;
  36:  *                   Michel Santos;
  37:  *
  38:  * Changes
  39:  * -------
  40:  * 15-Nov-2001 : Version 1 (DG);
  41:  * 03-Apr-2002 : Added an add(double, double) method (DG);
  42:  * 29-Apr-2002 : Added a clear() method (ARM);
  43:  * 06-Jun-2002 : Updated Javadoc comments (DG);
  44:  * 29-Aug-2002 : Modified to give user control over whether or not duplicate 
  45:  *               x-values are allowed (DG);
  46:  * 07-Oct-2002 : Fixed errors reported by Checkstyle (DG);
  47:  * 11-Nov-2002 : Added maximum item count, code contributed by Jonathan 
  48:  *               Gabbai (DG);
  49:  * 26-Mar-2003 : Implemented Serializable (DG);
  50:  * 04-Aug-2003 : Added getItems() method (DG);
  51:  * 15-Aug-2003 : Changed 'data' from private to protected, added new add() 
  52:  *               methods with a 'notify' argument (DG);
  53:  * 22-Sep-2003 : Added getAllowDuplicateXValues() method (RA);
  54:  * 29-Jan-2004 : Added autoSort attribute, based on a contribution by 
  55:  *               Michel Santos - see patch 886740 (DG);
  56:  * 03-Feb-2004 : Added indexOf() method (DG);
  57:  * 16-Feb-2004 : Added remove() method (DG);
  58:  * 18-Aug-2004 : Moved from org.jfree.data --> org.jfree.data.xy (DG);
  59:  * 21-Feb-2005 : Added update(Number, Number) and addOrUpdate(Number, Number) 
  60:  *               methods (DG);
  61:  * 03-May-2005 : Added a new constructor, fixed the setMaximumItemCount() 
  62:  *               method to remove items (and notify listeners) if necessary, 
  63:  *               fixed the add() and addOrUpdate() methods to handle unsorted 
  64:  *               series (DG);
  65:  * ------------- JFreeChart 1.0.x ---------------------------------------------
  66:  * 11-Jan-2005 : Renamed update(int, Number) --> updateByIndex() (DG);
  67:  * 15-Jan-2007 : Added toArray() method (DG);
  68:  * 31-Oct-2007 : Implemented faster hashCode() (DG);
  69:  * 
  70:  */
  71: 
  72: package org.jfree.data.xy;
  73: 
  74: import java.io.Serializable;
  75: import java.util.Collections;
  76: import java.util.List;
  77: 
  78: import org.jfree.data.general.Series;
  79: import org.jfree.data.general.SeriesChangeEvent;
  80: import org.jfree.data.general.SeriesException;
  81: import org.jfree.util.ObjectUtilities;
  82: 
  83: /**
  84:  * Represents a sequence of zero or more data items in the form (x, y).  By 
  85:  * default, items in the series will be sorted into ascending order by x-value,
  86:  * and duplicate x-values are permitted.  Both the sorting and duplicate 
  87:  * defaults can be changed in the constructor.  Y-values can be 
  88:  * <code>null</code> to represent missing values.
  89:  */
  90: public class XYSeries extends Series implements Cloneable, Serializable {
  91: 
  92:     /** For serialization. */
  93:     static final long serialVersionUID = -5908509288197150436L;
  94:     
  95:     // In version 0.9.12, in response to several developer requests, I changed 
  96:     // the 'data' attribute from 'private' to 'protected', so that others can 
  97:     // make subclasses that work directly with the underlying data structure.
  98: 
  99:     /** Storage for the data items in the series. */
 100:     protected List data;
 101: 
 102:     /** The maximum number of items for the series. */
 103:     private int maximumItemCount = Integer.MAX_VALUE;
 104: 
 105:     /** A flag that controls whether the items are automatically sorted. */
 106:     private boolean autoSort;
 107:     
 108:     /** A flag that controls whether or not duplicate x-values are allowed. */
 109:     private boolean allowDuplicateXValues;
 110: 
 111:     /**
 112:      * Creates a new empty series.  By default, items added to the series will 
 113:      * be sorted into ascending order by x-value, and duplicate x-values will 
 114:      * be allowed (these defaults can be modified with another constructor.
 115:      *
 116:      * @param key  the series key (<code>null</code> not permitted).
 117:      */
 118:     public XYSeries(Comparable key) {
 119:         this(key, true, true);
 120:     }
 121: 
 122:     /**
 123:      * Constructs a new empty series, with the auto-sort flag set as requested,
 124:      * and duplicate values allowed.  
 125:      * 
 126:      * @param key  the series key (<code>null</code> not permitted).
 127:      * @param autoSort  a flag that controls whether or not the items in the 
 128:      *                  series are sorted.
 129:      */
 130:     public XYSeries(Comparable key, boolean autoSort) {
 131:         this(key, autoSort, true);
 132:     }
 133: 
 134:     /**
 135:      * Constructs a new xy-series that contains no data.  You can specify 
 136:      * whether or not duplicate x-values are allowed for the series.
 137:      *
 138:      * @param key  the series key (<code>null</code> not permitted).
 139:      * @param autoSort  a flag that controls whether or not the items in the 
 140:      *                  series are sorted.
 141:      * @param allowDuplicateXValues  a flag that controls whether duplicate 
 142:      *                               x-values are allowed.
 143:      */
 144:     public XYSeries(Comparable key, 
 145:                     boolean autoSort, 
 146:                     boolean allowDuplicateXValues) {
 147:         super(key);
 148:         this.data = new java.util.ArrayList();
 149:         this.autoSort = autoSort;
 150:         this.allowDuplicateXValues = allowDuplicateXValues;
 151:     }
 152: 
 153:     /**
 154:      * Returns the flag that controls whether the items in the series are 
 155:      * automatically sorted.  There is no setter for this flag, it must be 
 156:      * defined in the series constructor.
 157:      * 
 158:      * @return A boolean.
 159:      */
 160:     public boolean getAutoSort() {
 161:         return this.autoSort;
 162:     }
 163:     
 164:     /**
 165:      * Returns a flag that controls whether duplicate x-values are allowed.  
 166:      * This flag can only be set in the constructor.
 167:      *
 168:      * @return A boolean.
 169:      */
 170:     public boolean getAllowDuplicateXValues() {
 171:         return this.allowDuplicateXValues;
 172:     }
 173: 
 174:     /**
 175:      * Returns the number of items in the series.
 176:      *
 177:      * @return The item count.
 178:      */
 179:     public int getItemCount() {
 180:         return this.data.size();
 181:     }
 182: 
 183:     /**
 184:      * Returns the list of data items for the series (the list contains 
 185:      * {@link XYDataItem} objects and is unmodifiable).
 186:      * 
 187:      * @return The list of data items.
 188:      */
 189:     public List getItems() {
 190:         return Collections.unmodifiableList(this.data);    
 191:     }
 192:     
 193:     /**
 194:      * Returns the maximum number of items that will be retained in the series.
 195:      * The default value is <code>Integer.MAX_VALUE</code>.
 196:      *
 197:      * @return The maximum item count.
 198:      * @see #setMaximumItemCount(int)
 199:      */
 200:     public int getMaximumItemCount() {
 201:         return this.maximumItemCount;
 202:     }
 203: 
 204:     /**
 205:      * Sets the maximum number of items that will be retained in the series.  
 206:      * If you add a new item to the series such that the number of items will 
 207:      * exceed the maximum item count, then the first element in the series is 
 208:      * automatically removed, ensuring that the maximum item count is not 
 209:      * exceeded.
 210:      * <p>
 211:      * Typically this value is set before the series is populated with data,
 212:      * but if it is applied later, it may cause some items to be removed from
 213:      * the series (in which case a {@link SeriesChangeEvent} will be sent to
 214:      * all registered listeners.
 215:      *
 216:      * @param maximum  the maximum number of items for the series.
 217:      */
 218:     public void setMaximumItemCount(int maximum) {
 219:         this.maximumItemCount = maximum;
 220:         boolean dataRemoved = false;
 221:         while (this.data.size() > maximum) {
 222:             this.data.remove(0);   
 223:             dataRemoved = true;
 224:         }
 225:         if (dataRemoved) {
 226:             fireSeriesChanged();
 227:         }
 228:     }
 229: 
 230:     /**
 231:      * Adds a data item to the series and sends a {@link SeriesChangeEvent} to 
 232:      * all registered listeners.
 233:      *
 234:      * @param item  the (x, y) item (<code>null</code> not permitted).
 235:      */
 236:     public void add(XYDataItem item) {
 237:         // argument checking delegated...
 238:         add(item, true);
 239:     }
 240:     
 241:     /**
 242:      * Adds a data item to the series and sends a {@link SeriesChangeEvent} to 
 243:      * all registered listeners.
 244:      *
 245:      * @param x  the x value.
 246:      * @param y  the y value.
 247:      */
 248:     public void add(double x, double y) {
 249:         add(new Double(x), new Double(y), true);
 250:     }
 251: 
 252:     /**
 253:      * Adds a data item to the series and, if requested, sends a 
 254:      * {@link SeriesChangeEvent} to all registered listeners.
 255:      *
 256:      * @param x  the x value.
 257:      * @param y  the y value.
 258:      * @param notify  a flag that controls whether or not a 
 259:      *                {@link SeriesChangeEvent} is sent to all registered 
 260:      *                listeners.
 261:      */
 262:     public void add(double x, double y, boolean notify) {
 263:         add(new Double(x), new Double(y), notify);
 264:     }
 265: 
 266:     /**
 267:      * Adds a data item to the series and sends a {@link SeriesChangeEvent} to 
 268:      * all registered listeners.  The unusual pairing of parameter types is to 
 269:      * make it easier to add <code>null</code> y-values.
 270:      *
 271:      * @param x  the x value.
 272:      * @param y  the y value (<code>null</code> permitted).
 273:      */
 274:     public void add(double x, Number y) {
 275:         add(new Double(x), y);
 276:     }
 277: 
 278:     /**
 279:      * Adds a data item to the series and, if requested, sends a 
 280:      * {@link SeriesChangeEvent} to all registered listeners.  The unusual 
 281:      * pairing of parameter types is to make it easier to add null y-values.
 282:      *
 283:      * @param x  the x value.
 284:      * @param y  the y value (<code>null</code> permitted).
 285:      * @param notify  a flag that controls whether or not a 
 286:      *                {@link SeriesChangeEvent} is sent to all registered 
 287:      *                listeners.
 288:      */
 289:     public void add(double x, Number y, boolean notify) {
 290:         add(new Double(x), y, notify);
 291:     }
 292: 
 293:     /**
 294:      * Adds new data to the series and sends a {@link SeriesChangeEvent} to 
 295:      * all registered listeners.
 296:      * <P>
 297:      * Throws an exception if the x-value is a duplicate AND the 
 298:      * allowDuplicateXValues flag is false.
 299:      *
 300:      * @param x  the x-value (<code>null</code> not permitted).
 301:      * @param y  the y-value (<code>null</code> permitted).
 302:      */
 303:     public void add(Number x, Number y) {
 304:         // argument checking delegated...
 305:         add(x, y, true);
 306:     }
 307:     
 308:     /**
 309:      * Adds new data to the series and, if requested, sends a 
 310:      * {@link SeriesChangeEvent} to all registered listeners.
 311:      * <P>
 312:      * Throws an exception if the x-value is a duplicate AND the 
 313:      * allowDuplicateXValues flag is false.
 314:      *
 315:      * @param x  the x-value (<code>null</code> not permitted).
 316:      * @param y  the y-value (<code>null</code> permitted).
 317:      * @param notify  a flag the controls whether or not a 
 318:      *                {@link SeriesChangeEvent} is sent to all registered 
 319:      *                listeners.
 320:      */
 321:     public void add(Number x, Number y, boolean notify) {
 322:         // delegate argument checking to XYDataItem...
 323:         XYDataItem item = new XYDataItem(x, y);
 324:         add(item, notify);
 325:     }
 326: 
 327:     /**
 328:      * Adds a data item to the series and, if requested, sends a 
 329:      * {@link SeriesChangeEvent} to all registered listeners.
 330:      *
 331:      * @param item  the (x, y) item (<code>null</code> not permitted).
 332:      * @param notify  a flag that controls whether or not a 
 333:      *                {@link SeriesChangeEvent} is sent to all registered 
 334:      *                listeners.
 335:      */
 336:     public void add(XYDataItem item, boolean notify) {
 337: 
 338:         if (item == null) {
 339:             throw new IllegalArgumentException("Null 'item' argument.");
 340:         }
 341: 
 342:         if (this.autoSort) {
 343:             int index = Collections.binarySearch(this.data, item);
 344:             if (index < 0) {
 345:                 this.data.add(-index - 1, item);
 346:             }
 347:             else {
 348:                 if (this.allowDuplicateXValues) {
 349:                     // need to make sure we are adding *after* any duplicates
 350:                     int size = this.data.size();
 351:                     while (index < size 
 352:                            && item.compareTo(this.data.get(index)) == 0) {
 353:                         index++;
 354:                     }
 355:                     if (index < this.data.size()) {
 356:                         this.data.add(index, item);
 357:                     }
 358:                     else {
 359:                         this.data.add(item);
 360:                     }
 361:                 }
 362:                 else {
 363:                     throw new SeriesException("X-value already exists.");
 364:                 }
 365:             }
 366:         }
 367:         else {
 368:             if (!this.allowDuplicateXValues) {
 369:                 // can't allow duplicate values, so we need to check whether
 370:                 // there is an item with the given x-value already
 371:                 int index = indexOf(item.getX());
 372:                 if (index >= 0) {
 373:                     throw new SeriesException("X-value already exists.");      
 374:                 }
 375:             }
 376:             this.data.add(item);
 377:         }
 378:         if (getItemCount() > this.maximumItemCount) {
 379:             this.data.remove(0);
 380:         }                    
 381:         if (notify) {
 382:             fireSeriesChanged();
 383:         }
 384:     }
 385: 
 386:     /**
 387:      * Deletes a range of items from the series and sends a 
 388:      * {@link SeriesChangeEvent} to all registered listeners.
 389:      *
 390:      * @param start  the start index (zero-based).
 391:      * @param end  the end index (zero-based).
 392:      */
 393:     public void delete(int start, int end) {
 394:         for (int i = start; i <= end; i++) {
 395:             this.data.remove(start);
 396:         }
 397:         fireSeriesChanged();
 398:     }
 399: 
 400:     /**
 401:      * Removes the item at the specified index and sends a 
 402:      * {@link SeriesChangeEvent} to all registered listeners.
 403:      * 
 404:      * @param index  the index.
 405:      * 
 406:      * @return The item removed.
 407:      */
 408:     public XYDataItem remove(int index) {
 409:         XYDataItem result = (XYDataItem) this.data.remove(index);
 410:         fireSeriesChanged();
 411:         return result;
 412:     }
 413:     
 414:     /**
 415:      * Removes the item with the specified x-value and sends a 
 416:      * {@link SeriesChangeEvent} to all registered listeners.
 417:      * 
 418:      * @param x  the x-value.
 419: 
 420:      * @return The item removed.
 421:      */
 422:     public XYDataItem remove(Number x) {
 423:         return remove(indexOf(x));
 424:     }
 425:     
 426:     /**
 427:      * Removes all data items from the series.
 428:      */
 429:     public void clear() {
 430:         if (this.data.size() > 0) {
 431:             this.data.clear();
 432:             fireSeriesChanged();
 433:         }
 434:     }
 435: 
 436:     /**
 437:      * Return the data item with the specified index.
 438:      *
 439:      * @param index  the index.
 440:      *
 441:      * @return The data item with the specified index.
 442:      */
 443:     public XYDataItem getDataItem(int index) {
 444:         return (XYDataItem) this.data.get(index);
 445:     }
 446: 
 447:     /**
 448:      * Returns the x-value at the specified index.
 449:      *
 450:      * @param index  the index (zero-based).
 451:      *
 452:      * @return The x-value (never <code>null</code>).
 453:      */
 454:     public Number getX(int index) {
 455:         return getDataItem(index).getX();
 456:     }
 457: 
 458:     /**
 459:      * Returns the y-value at the specified index.
 460:      *
 461:      * @param index  the index (zero-based).
 462:      *
 463:      * @return The y-value (possibly <code>null</code>).
 464:      */
 465:     public Number getY(int index) {
 466:         return getDataItem(index).getY();
 467:     }
 468:     
 469:     /**
 470:      * Updates the value of an item in the series and sends a 
 471:      * {@link SeriesChangeEvent} to all registered listeners.
 472:      *
 473:      * @param index  the item (zero based index).
 474:      * @param y  the new value (<code>null</code> permitted).
 475:      * 
 476:      * @deprecated Renamed {@link #updateByIndex(int, Number)} to avoid 
 477:      *         confusion with the {@link #update(Number, Number)} method.
 478:      */
 479:     public void update(int index, Number y) {
 480:         XYDataItem item = getDataItem(index);
 481:         item.setY(y);
 482:         fireSeriesChanged();
 483:     }
 484:     
 485:     /**
 486:      * Updates the value of an item in the series and sends a 
 487:      * {@link SeriesChangeEvent} to all registered listeners.
 488:      * 
 489:      * @param index  the item (zero based index).
 490:      * @param y  the new value (<code>null</code> permitted).
 491:      * 
 492:      * @since 1.0.1
 493:      */
 494:     public void updateByIndex(int index, Number y) {
 495:         update(index, y);
 496:     }
 497:     
 498:     /**
 499:      * Updates an item in the series.
 500:      * 
 501:      * @param x  the x-value (<code>null</code> not permitted).
 502:      * @param y  the y-value (<code>null</code> permitted).
 503:      * 
 504:      * @throws SeriesException if there is no existing item with the specified
 505:      *         x-value.
 506:      */
 507:     public void update(Number x, Number y) {
 508:         int index = indexOf(x);
 509:         if (index < 0) {
 510:             throw new SeriesException("No observation for x = " + x);
 511:         }
 512:         else {
 513:             XYDataItem item = getDataItem(index);
 514:             item.setY(y);
 515:             fireSeriesChanged();
 516:         }
 517:     }
 518:     
 519:     /**
 520:      * Adds or updates an item in the series and sends a 
 521:      * {@link org.jfree.data.general.SeriesChangeEvent} to all registered 
 522:      * listeners.
 523:      *
 524:      * @param x  the x-value (<code>null</code> not permitted).
 525:      * @param y  the y-value (<code>null</code> permitted).
 526:      *
 527:      * @return A copy of the overwritten data item, or <code>null</code> if no 
 528:      *         item was overwritten.
 529:      */
 530:     public XYDataItem addOrUpdate(Number x, Number y) {
 531:         if (x == null) {
 532:             throw new IllegalArgumentException("Null 'x' argument.");   
 533:         }
 534:         XYDataItem overwritten = null;
 535:         int index = indexOf(x);
 536:         if (index >= 0) {
 537:             XYDataItem existing = (XYDataItem) this.data.get(index);
 538:             try {
 539:                 overwritten = (XYDataItem) existing.clone();
 540:             }
 541:             catch (CloneNotSupportedException e) {
 542:                 throw new SeriesException("Couldn't clone XYDataItem!");   
 543:             }
 544:             existing.setY(y);
 545:         }
 546:         else {
 547:             // if the series is sorted, the negative index is a result from
 548:             // Collections.binarySearch() and tells us where to insert the
 549:             // new item...otherwise it will be just -1 and we should just
 550:             // append the value to the list...
 551:             if (this.autoSort) {
 552:                 this.data.add(-index - 1, new XYDataItem(x, y));
 553:             }
 554:             else {
 555:                 this.data.add(new XYDataItem(x, y));
 556:             }
 557:             // check if this addition will exceed the maximum item count...
 558:             if (getItemCount() > this.maximumItemCount) {
 559:                 this.data.remove(0);
 560:             }
 561:         }            
 562:         fireSeriesChanged();
 563:         return overwritten;
 564:     }
 565: 
 566:     /**
 567:      * Returns the index of the item with the specified x-value, or a negative 
 568:      * index if the series does not contain an item with that x-value.  Be 
 569:      * aware that for an unsorted series, the index is found by iterating 
 570:      * through all items in the series.
 571:      * 
 572:      * @param x  the x-value (<code>null</code> not permitted).
 573:      * 
 574:      * @return The index.
 575:      */
 576:     public int indexOf(Number x) {
 577:         if (this.autoSort) {
 578:             return Collections.binarySearch(this.data, new XYDataItem(x, null));
 579:         }
 580:         else {
 581:             for (int i = 0; i < this.data.size(); i++) {
 582:                 XYDataItem item = (XYDataItem) this.data.get(i);
 583:                 if (item.getX().equals(x)) {
 584:                     return i;   
 585:                 }
 586:             }
 587:             return -1;
 588:         }
 589:     }
 590:     
 591:     /**
 592:      * Returns a new array containing the x and y values from this series.
 593:      * 
 594:      * @return A new array containing the x and y values from this series.
 595:      * 
 596:      * @since 1.0.4
 597:      */
 598:     public double[][] toArray() {
 599:         int itemCount = getItemCount();
 600:         double[][] result = new double[2][itemCount];
 601:         for (int i = 0; i < itemCount; i++) {
 602:             result[0][i] = this.getX(i).doubleValue();
 603:             Number y = getY(i);
 604:             if (y != null) {
 605:                 result[1][i] = y.doubleValue();
 606:             }
 607:             else {
 608:                 result[1][i] = Double.NaN;
 609:             }
 610:         }
 611:         return result;
 612:     }
 613:     
 614:     /**
 615:      * Returns a clone of the series.
 616:      *
 617:      * @return A clone of the time series.
 618:      * 
 619:      * @throws CloneNotSupportedException if there is a cloning problem.
 620:      */
 621:     public Object clone() throws CloneNotSupportedException {
 622:         Object clone = createCopy(0, getItemCount() - 1);
 623:         return clone;
 624:     }
 625: 
 626:     /**
 627:      * Creates a new series by copying a subset of the data in this time series.
 628:      *
 629:      * @param start  the index of the first item to copy.
 630:      * @param end  the index of the last item to copy.
 631:      *
 632:      * @return A series containing a copy of this series from start until end.
 633:      * 
 634:      * @throws CloneNotSupportedException if there is a cloning problem.
 635:      */
 636:     public XYSeries createCopy(int start, int end) 
 637:         throws CloneNotSupportedException {
 638: 
 639:         XYSeries copy = (XYSeries) super.clone();
 640:         copy.data = new java.util.ArrayList();
 641:         if (this.data.size() > 0) {
 642:             for (int index = start; index <= end; index++) {
 643:                 XYDataItem item = (XYDataItem) this.data.get(index);
 644:                 XYDataItem clone = (XYDataItem) item.clone();
 645:                 try {
 646:                     copy.add(clone);
 647:                 }
 648:                 catch (SeriesException e) {
 649:                     System.err.println("Unable to add cloned data item.");
 650:                 }
 651:             }
 652:         }
 653:         return copy;
 654: 
 655:     }
 656: 
 657:     /**
 658:      * Tests this series for equality with an arbitrary object.
 659:      *
 660:      * @param obj  the object to test against for equality 
 661:      *             (<code>null</code> permitted).
 662:      *
 663:      * @return A boolean.
 664:      */
 665:     public boolean equals(Object obj) {
 666:         if (obj == this) {
 667:             return true;
 668:         }
 669:         if (!(obj instanceof XYSeries)) {
 670:             return false;
 671:         }
 672:         if (!super.equals(obj)) {
 673:             return false;
 674:         }
 675:         XYSeries that = (XYSeries) obj;
 676:         if (this.maximumItemCount != that.maximumItemCount) {
 677:             return false;
 678:         }
 679:         if (this.autoSort != that.autoSort) {
 680:             return false;
 681:         }
 682:         if (this.allowDuplicateXValues != that.allowDuplicateXValues) {
 683:             return false;
 684:         }
 685:         if (!ObjectUtilities.equal(this.data, that.data)) {
 686:             return false;
 687:         }
 688:         return true;
 689:     }
 690:     
 691:     /**
 692:      * Returns a hash code.
 693:      * 
 694:      * @return A hash code.
 695:      */
 696:     public int hashCode() {
 697:         int result = super.hashCode();
 698:         // it is too slow to look at every data item, so let's just look at
 699:         // the first, middle and last items...
 700:         int count = getItemCount();
 701:         if (count > 0) {
 702:             XYDataItem item = getDataItem(0);
 703:             result = 29 * result + item.hashCode();
 704:         }
 705:         if (count > 1) {
 706:             XYDataItem item = getDataItem(count - 1);
 707:             result = 29 * result + item.hashCode();
 708:         }
 709:         if (count > 2) {
 710:             XYDataItem item = getDataItem(count / 2);
 711:             result = 29 * result + item.hashCode();
 712:         }
 713:         result = 29 * result + this.maximumItemCount;
 714:         result = 29 * result + (this.autoSort ? 1 : 0);
 715:         result = 29 * result + (this.allowDuplicateXValues ? 1 : 0);
 716:         return result;
 717:     }
 718: 
 719: }
 720: