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