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