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}