    001/* ===========================================================
    002 * JFreeChart : a free chart library for the Java(tm) platform
    003 * ===========================================================
    004 *
    005 * (C) Copyright 2000-2011, 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 * [Oracle and Java are registered trademarks of Oracle and/or its affiliates.
    025 * Other names may be trademarks of their respective owners.]
    026 *
    027 * ----------------------
    028 * DefaultPieDataset.java
    029 * ----------------------
    030 * (C) Copyright 2001-2008, by Object Refinery Limited.
    031 *
    032 * Original Author:  David Gilbert (for Object Refinery Limited);
    033 * Contributor(s):   Sam (oldman);
    034 *
    035 * Changes
    036 * -------
    037 * 17-Nov-2001 : Version 1 (DG);
    038 * 22-Jan-2002 : Removed legend methods from dataset implementations (DG);
    039 * 07-Apr-2002 : Modified implementation to guarantee data sequence to remain
    040 *               in the order categories are added (oldman);
    041 * 23-Oct-2002 : Added getCategory(int) method and getItemCount() method, in
    042 *               line with changes to the PieDataset interface (DG);
    043 * 04-Feb-2003 : Changed underlying data storage to DefaultKeyedValues (DG);
    044 * 04-Mar-2003 : Inserted DefaultKeyedValuesDataset class into hierarchy (DG);
    045 * 24-Apr-2003 : Switched places with DefaultKeyedValuesDataset (DG);
    046 * 18-Aug-2003 : Implemented Cloneable (DG);
    047 * 03-Mar-2005 : Implemented PublicCloneable (DG);
    048 * 29-Jun-2005 : Added remove() method (DG);
    049 * ------------- JFREECHART 1.0.0 ---------------------------------------------
    050 * 31-Jul-2006 : Added a clear() method to clear all values from the
    051 *               dataset (DG);
    052 * 28-Sep-2006 : Added sortByKeys() and sortByValues() methods (DG);
    053 * 30-Apr-2007 : Added new insertValues() methods (DG);
    054 *
    055 */
    056
    057package org.jfree.data.general;
    058
    059import java.io.Serializable;
    060import java.util.Collections;
    061import java.util.List;
    062
    063import org.jfree.data.DefaultKeyedValues;
    064import org.jfree.data.KeyedValues;
    065import org.jfree.data.UnknownKeyException;
    066import org.jfree.util.PublicCloneable;
    067import org.jfree.util.SortOrder;
    068
    069/**
    070 * A default implementation of the {@link PieDataset} interface.
    071 */
    072public class DefaultPieDataset extends AbstractDataset
    073        implements PieDataset, Cloneable, PublicCloneable, Serializable {
    074
    075    /** For serialization. */
    076    private static final long serialVersionUID = 2904745139106540618L;
    077
    078    /** Storage for the data. */
    079    private DefaultKeyedValues data;
    080
    081    /**
    082     * Constructs a new dataset, initially empty.
    083     */
    084    public DefaultPieDataset() {
    085        this.data = new DefaultKeyedValues();
    086    }
    087
    088    /**
    089     * Creates a new dataset by copying data from a {@link KeyedValues}
    090     * instance.
    091     *
    092     * @param data  the data (<code>null</code> not permitted).
    093     */
    094    public DefaultPieDataset(KeyedValues data) {
    095        if (data == null) {
    096            throw new IllegalArgumentException("Null 'data' argument.");
    097        }
    098        this.data = new DefaultKeyedValues();
    099        for (int i = 0; i < data.getItemCount(); i++) {
    100            this.data.addValue(data.getKey(i), data.getValue(i));
    101        }
    102    }
    103
    104    /**
    105     * Returns the number of items in the dataset.
    106     *
    107     * @return The item count.
    108     */
    109    public int getItemCount() {
    110        return this.data.getItemCount();
    111    }
    112
    113    /**
    114     * Returns the categories in the dataset.  The returned list is
    115     * unmodifiable.
    116     *
    117     * @return The categories in the dataset.
    118     */
    119    public List getKeys() {
    120        return Collections.unmodifiableList(this.data.getKeys());
    121    }
    122
    123    /**
    124     * Returns the key for the specified item, or <code>null</code>.
    125     *
    126     * @param item  the item index (in the range <code>0</code> to
    127     *     <code>getItemCount() - 1</code>).
    128     *
    129     * @return The key, or <code>null</code>.
    130     *
    131     * @throws IndexOutOfBoundsException if <code>item</code> is not in the
    132     *     specified range.
    133     */
    134    public Comparable getKey(int item) {
    135        return this.data.getKey(item);
    136    }
    137
    138    /**
    139     * Returns the index for a key, or -1 if the key is not recognised.
    140     *
    141     * @param key  the key (<code>null</code> not permitted).
    142     *
    143     * @return The index, or <code>-1</code> if the key is unrecognised.
    144     *
    145     * @throws IllegalArgumentException if <code>key</code> is
    146     *     <code>null</code>.
    147     */
    148    public int getIndex(Comparable key) {
    149        return this.data.getIndex(key);
    150    }
    151
    152    /**
    153     * Returns a value.
    154     *
    155     * @param item  the value index.
    156     *
    157     * @return The value (possibly <code>null</code>).
    158     */
    159    public Number getValue(int item) {
    160
    161        Number result = null;
    162        if (getItemCount() > item) {
    163            result = this.data.getValue(item);
    164        }
    165        return result;
    166
    167    }
    168
    169    /**
    170     * Returns the data value associated with a key.
    171     *
    172     * @param key  the key (<code>null</code> not permitted).
    173     *
    174     * @return The value (possibly <code>null</code>).
    175     *
    176     * @throws UnknownKeyException if the key is not recognised.
    177     */
    178    public Number getValue(Comparable key) {
    179        if (key == null) {
    180            throw new IllegalArgumentException("Null 'key' argument.");
    181        }
    182        return this.data.getValue(key);
    183    }
    184
    185    /**
    186     * Sets the data value for a key and sends a {@link DatasetChangeEvent} to
    187     * all registered listeners.
    188     *
    189     * @param key  the key (<code>null</code> not permitted).
    190     * @param value  the value.
    191     *
    192     * @throws IllegalArgumentException if <code>key</code> is
    193     *     <code>null</code>.
    194     */
    195    public void setValue(Comparable key, Number value) {
    196        this.data.setValue(key, value);
    197        fireDatasetChanged();
    198    }
    199
    200    /**
    201     * Sets the data value for a key and sends a {@link DatasetChangeEvent} to
    202     * all registered listeners.
    203     *
    204     * @param key  the key (<code>null</code> not permitted).
    205     * @param value  the value.
    206     *
    207     * @throws IllegalArgumentException if <code>key</code> is
    208     *     <code>null</code>.
    209     */
    210    public void setValue(Comparable key, double value) {
    211        setValue(key, new Double(value));
    212    }
    213
    214    /**
    215     * Inserts a new value at the specified position in the dataset or, if
    216     * there is an existing item with the specified key, updates the value
    217     * for that item and moves it to the specified position.  After the change
    218     * is made, this methods sends a {@link DatasetChangeEvent} to all
    219     * registered listeners.
    220     *
    221     * @param position  the position (in the range 0 to getItemCount()).
    222     * @param key  the key (<code>null</code> not permitted).
    223     * @param value  the value (<code>null</code> permitted).
    224     *
    225     * @since 1.0.6
    226     */
    227    public void insertValue(int position, Comparable key, double value) {
    228        insertValue(position, key, new Double(value));
    229    }
    230
    231    /**
    232     * Inserts a new value at the specified position in the dataset or, if
    233     * there is an existing item with the specified key, updates the value
    234     * for that item and moves it to the specified position.  After the change
    235     * is made, this methods sends a {@link DatasetChangeEvent} to all
    236     * registered listeners.
    237     *
    238     * @param position  the position (in the range 0 to getItemCount()).
    239     * @param key  the key (<code>null</code> not permitted).
    240     * @param value  the value (<code>null</code> permitted).
    241     *
    242     * @since 1.0.6
    243     */
    244    public void insertValue(int position, Comparable key, Number value) {
    245        this.data.insertValue(position, key, value);
    246        fireDatasetChanged();
    247    }
    248
    249    /**
    250     * Removes an item from the dataset and sends a {@link DatasetChangeEvent}
    251     * to all registered listeners.
    252     *
    253     * @param key  the key (<code>null</code> not permitted).
    254     *
    255     * @throws IllegalArgumentException if <code>key</code> is
    256     *     <code>null</code>.
    257     */
    258    public void remove(Comparable key) {
    259        this.data.removeValue(key);
    260        fireDatasetChanged();
    261    }
    262
    263    /**
    264     * Clears all data from this dataset and sends a {@link DatasetChangeEvent}
    265     * to all registered listeners (unless the dataset was already empty).
    266     *
    267     * @since 1.0.2
    268     */
    269    public void clear() {
    270        if (getItemCount() > 0) {
    271            this.data.clear();
    272            fireDatasetChanged();
    273        }
    274    }
    275
    276    /**
    277     * Sorts the dataset's items by key and sends a {@link DatasetChangeEvent}
    278     * to all registered listeners.
    279     *
    280     * @param order  the sort order (<code>null</code> not permitted).
    281     *
    282     * @since 1.0.3
    283     */
    284    public void sortByKeys(SortOrder order) {
    285        this.data.sortByKeys(order);
    286        fireDatasetChanged();
    287    }
    288
    289    /**
    290     * Sorts the dataset's items by value and sends a {@link DatasetChangeEvent}
    291     * to all registered listeners.
    292     *
    293     * @param order  the sort order (<code>null</code> not permitted).
    294     *
    295     * @since 1.0.3
    296     */
    297    public void sortByValues(SortOrder order) {
    298        this.data.sortByValues(order);
    299        fireDatasetChanged();
    300    }
    301
    302    /**
    303     * Tests if this object is equal to another.
    304     *
    305     * @param obj  the other object.
    306     *
    307     * @return A boolean.
    308     */
    309    public boolean equals(Object obj) {
    310        if (obj == this) {
    311            return true;
    312        }
    313
    314        if (!(obj instanceof PieDataset)) {
    315            return false;
    316        }
    317        PieDataset that = (PieDataset) obj;
    318        int count = getItemCount();
    319        if (that.getItemCount() != count) {
    320            return false;
    321        }
    322
    323        for (int i = 0; i < count; i++) {
    324            Comparable k1 = getKey(i);
    325            Comparable k2 = that.getKey(i);
    326            if (!k1.equals(k2)) {
    327                return false;
    328            }
    329
    330            Number v1 = getValue(i);
    331            Number v2 = that.getValue(i);
    332            if (v1 == null) {
    333                if (v2 != null) {
    334                    return false;
    335                }
    336            }
    337            else {
    338                if (!v1.equals(v2)) {
    339                    return false;
    340                }
    341            }
    342        }
    343        return true;
    344
    345    }
    346
    347    /**
    348     * Returns a hash code.
    349     *
    350     * @return A hash code.
    351     */
    352    public int hashCode() {
    353        return this.data.hashCode();
    354    }
    355
    356    /**
    357     * Returns a clone of the dataset.
    358     *
    359     * @return A clone.
    360     *
    361     * @throws CloneNotSupportedException This class will not throw this
    362     *         exception, but subclasses (if any) might.
    363     */
    364    public Object clone() throws CloneNotSupportedException {
    365        DefaultPieDataset clone = (DefaultPieDataset) super.clone();
    366        clone.data = (DefaultKeyedValues) this.data.clone();
    367        return clone;
    368    }
    369
    370}