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