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 * DefaultCategoryDataset.java 029 * --------------------------- 030 * (C) Copyright 2002-2022, by David Gilbert. 031 * 032 * Original Author: David Gilbert; 033 * Contributor(s): -; 034 * 035 * Changes 036 * ------- 037 * 21-Jan-2003 : Added standard header, and renamed DefaultCategoryDataset (DG); 038 * 13-Mar-2003 : Inserted DefaultKeyedValues2DDataset into class hierarchy (DG); 039 * 06-Oct-2003 : Added incrementValue() method (DG); 040 * 05-Apr-2004 : Added clear() method (DG); 041 * 18-Aug-2004 : Moved from org.jfree.data --> org.jfree.data.category (DG); 042 * ------------- JFREECHART 1.0.x --------------------------------------------- 043 * 26-Feb-2007 : Updated API docs (DG); 044 * 08-Mar-2007 : Implemented clone() (DG); 045 * 09-May-2008 : Implemented PublicCloneable (DG); 046 * 047 */ 048 049package org.jfree.data.category; 050 051import java.io.Serializable; 052import java.util.List; 053import org.jfree.chart.api.PublicCloneable; 054 055import org.jfree.data.DefaultKeyedValues2D; 056import org.jfree.data.UnknownKeyException; 057import org.jfree.data.general.AbstractDataset; 058import org.jfree.data.general.DatasetChangeEvent; 059 060/** 061 * A default implementation of the {@link CategoryDataset} interface. 062 * 063 * @param <R> The type for the row (series) keys. 064 * @param <C> The type for the column (item) keys. 065 */ 066public class DefaultCategoryDataset<R extends Comparable<R>, C extends Comparable<C>> 067 extends AbstractDataset implements CategoryDataset<R, C>, 068 PublicCloneable, Serializable { 069 070 /** For serialization. */ 071 private static final long serialVersionUID = -8168173757291644622L; 072 073 /** A storage structure for the data. */ 074 private DefaultKeyedValues2D<R, C> data; 075 076 /** 077 * Creates a new (empty) dataset. 078 */ 079 public DefaultCategoryDataset() { 080 this.data = new DefaultKeyedValues2D<>(); 081 } 082 083 /** 084 * Returns the number of rows in the table. 085 * 086 * @return The row count. 087 * 088 * @see #getColumnCount() 089 */ 090 @Override 091 public int getRowCount() { 092 return this.data.getRowCount(); 093 } 094 095 /** 096 * Returns the number of columns in the table. 097 * 098 * @return The column count. 099 * 100 * @see #getRowCount() 101 */ 102 @Override 103 public int getColumnCount() { 104 return this.data.getColumnCount(); 105 } 106 107 /** 108 * Returns a value from the table. 109 * 110 * @param row the row index (zero-based). 111 * @param column the column index (zero-based). 112 * 113 * @return The value (possibly {@code null}). 114 * 115 * @see #addValue(Number, Comparable, Comparable) 116 * @see #removeValue(Comparable, Comparable) 117 */ 118 @Override 119 public Number getValue(int row, int column) { 120 return this.data.getValue(row, column); 121 } 122 123 /** 124 * Returns the key for the specified row. 125 * 126 * @param row the row index (zero-based). 127 * 128 * @return The row key. 129 * 130 * @see #getRowIndex(Comparable) 131 * @see #getRowKeys() 132 * @see #getColumnKey(int) 133 */ 134 @Override 135 public R getRowKey(int row) { 136 return this.data.getRowKey(row); 137 } 138 139 /** 140 * Returns the row index for a given key. 141 * 142 * @param key the row key ({@code null} not permitted). 143 * 144 * @return The row index. 145 * 146 * @see #getRowKey(int) 147 */ 148 @Override 149 public int getRowIndex(R key) { 150 // defer null argument check 151 return this.data.getRowIndex(key); 152 } 153 154 /** 155 * Returns the row keys. 156 * 157 * @return The keys. 158 * 159 * @see #getRowKey(int) 160 */ 161 @Override 162 public List<R> getRowKeys() { 163 return this.data.getRowKeys(); 164 } 165 166 /** 167 * Returns a column key. 168 * 169 * @param column the column index (zero-based). 170 * 171 * @return The column key. 172 * 173 * @see #getColumnIndex(Comparable) 174 */ 175 @Override 176 public C getColumnKey(int column) { 177 return this.data.getColumnKey(column); 178 } 179 180 /** 181 * Returns the column index for a given key. 182 * 183 * @param key the column key ({@code null} not permitted). 184 * 185 * @return The column index. 186 * 187 * @see #getColumnKey(int) 188 */ 189 @Override 190 public int getColumnIndex(C key) { 191 // defer null argument check 192 return this.data.getColumnIndex(key); 193 } 194 195 /** 196 * Returns the column keys. 197 * 198 * @return The keys. 199 * 200 * @see #getColumnKey(int) 201 */ 202 @Override 203 public List<C> getColumnKeys() { 204 return this.data.getColumnKeys(); 205 } 206 207 /** 208 * Returns the value for a pair of keys. 209 * 210 * @param rowKey the row key ({@code null} not permitted). 211 * @param columnKey the column key ({@code null} not permitted). 212 * 213 * @return The value (possibly {@code null}). 214 * 215 * @throws UnknownKeyException if either key is not defined in the dataset. 216 * 217 * @see #addValue(Number, Comparable, Comparable) 218 */ 219 @Override 220 public Number getValue(R rowKey, C columnKey) { 221 return this.data.getValue(rowKey, columnKey); 222 } 223 224 /** 225 * Adds a value to the table. Performs the same function as setValue(). 226 * 227 * @param value the value. 228 * @param rowKey the row key. 229 * @param columnKey the column key. 230 * 231 * @see #getValue(Comparable, Comparable) 232 * @see #removeValue(Comparable, Comparable) 233 */ 234 public void addValue(Number value, R rowKey, C columnKey) { 235 this.data.addValue(value, rowKey, columnKey); 236 fireDatasetChanged(); 237 } 238 239 /** 240 * Adds a value to the table. 241 * 242 * @param value the value. 243 * @param rowKey the row key. 244 * @param columnKey the column key. 245 * 246 * @see #getValue(Comparable, Comparable) 247 */ 248 public void addValue(double value, R rowKey, C columnKey) { 249 addValue(Double.valueOf(value), rowKey, columnKey); 250 } 251 252 /** 253 * Adds or updates a value in the table and sends a 254 * {@link DatasetChangeEvent} to all registered listeners. 255 * 256 * @param value the value ({@code null} permitted). 257 * @param rowKey the row key ({@code null} not permitted). 258 * @param columnKey the column key ({@code null} not permitted). 259 * 260 * @see #getValue(Comparable, Comparable) 261 */ 262 public void setValue(Number value, R rowKey, C columnKey) { 263 this.data.setValue(value, rowKey, columnKey); 264 fireDatasetChanged(); 265 } 266 267 /** 268 * Adds or updates a value in the table and sends a 269 * {@link DatasetChangeEvent} to all registered listeners. 270 * 271 * @param value the value. 272 * @param rowKey the row key ({@code null} not permitted). 273 * @param columnKey the column key ({@code null} not permitted). 274 * 275 * @see #getValue(Comparable, Comparable) 276 */ 277 public void setValue(double value, R rowKey, C columnKey) { 278 setValue(Double.valueOf(value), rowKey, columnKey); 279 } 280 281 /** 282 * Adds the specified value to an existing value in the dataset (if the 283 * existing value is {@code null}, it is treated as if it were 0.0). 284 * 285 * @param value the value. 286 * @param rowKey the row key ({@code null} not permitted). 287 * @param columnKey the column key ({@code null} not permitted). 288 * 289 * @throws UnknownKeyException if either key is not defined in the dataset. 290 */ 291 public void incrementValue(double value, R rowKey, C columnKey) { 292 double existing = 0.0; 293 Number n = getValue(rowKey, columnKey); 294 if (n != null) { 295 existing = n.doubleValue(); 296 } 297 setValue(existing + value, rowKey, columnKey); 298 } 299 300 /** 301 * Removes a value from the dataset and sends a {@link DatasetChangeEvent} 302 * to all registered listeners. 303 * 304 * @param rowKey the row key. 305 * @param columnKey the column key. 306 * 307 * @see #addValue(Number, Comparable, Comparable) 308 */ 309 public void removeValue(R rowKey, C columnKey) { 310 this.data.removeValue(rowKey, columnKey); 311 fireDatasetChanged(); 312 } 313 314 /** 315 * Removes a row from the dataset and sends a {@link DatasetChangeEvent} 316 * to all registered listeners. 317 * 318 * @param rowIndex the row index. 319 * 320 * @see #removeColumn(int) 321 */ 322 public void removeRow(int rowIndex) { 323 this.data.removeRow(rowIndex); 324 fireDatasetChanged(); 325 } 326 327 /** 328 * Removes a row from the dataset and sends a {@link DatasetChangeEvent} 329 * to all registered listeners. 330 * 331 * @param rowKey the row key. 332 * 333 * @see #removeColumn(Comparable) 334 */ 335 public void removeRow(R rowKey) { 336 this.data.removeRow(rowKey); 337 fireDatasetChanged(); 338 } 339 340 /** 341 * Removes a column from the dataset and sends a {@link DatasetChangeEvent} 342 * to all registered listeners. 343 * 344 * @param columnIndex the column index. 345 * 346 * @see #removeRow(int) 347 */ 348 public void removeColumn(int columnIndex) { 349 this.data.removeColumn(columnIndex); 350 fireDatasetChanged(); 351 } 352 353 /** 354 * Removes a column from the dataset and sends a {@link DatasetChangeEvent} 355 * to all registered listeners. 356 * 357 * @param columnKey the column key ({@code null} not permitted). 358 * 359 * @see #removeRow(Comparable) 360 * 361 * @throws UnknownKeyException if {@code columnKey} is not defined 362 * in the dataset. 363 */ 364 public void removeColumn(C columnKey) { 365 this.data.removeColumn(columnKey); 366 fireDatasetChanged(); 367 } 368 369 /** 370 * Clears all data from the dataset and sends a {@link DatasetChangeEvent} 371 * to all registered listeners. 372 */ 373 public void clear() { 374 this.data.clear(); 375 fireDatasetChanged(); 376 } 377 378 /** 379 * Tests this dataset for equality with an arbitrary object. 380 * 381 * @param obj the object ({@code null} permitted). 382 * 383 * @return A boolean. 384 */ 385 @Override 386 public boolean equals(Object obj) { 387 if (obj == this) { 388 return true; 389 } 390 if (!(obj instanceof CategoryDataset)) { 391 return false; 392 } 393 CategoryDataset<R, C> that = (CategoryDataset) obj; 394 if (!getRowKeys().equals(that.getRowKeys())) { 395 return false; 396 } 397 if (!getColumnKeys().equals(that.getColumnKeys())) { 398 return false; 399 } 400 int rowCount = getRowCount(); 401 int colCount = getColumnCount(); 402 for (int r = 0; r < rowCount; r++) { 403 for (int c = 0; c < colCount; c++) { 404 Number v1 = getValue(r, c); 405 Number v2 = that.getValue(r, c); 406 if (v1 == null) { 407 if (v2 != null) { 408 return false; 409 } 410 } 411 else if (!v1.equals(v2)) { 412 return false; 413 } 414 } 415 } 416 return true; 417 } 418 419 /** 420 * Returns a hash code for the dataset. 421 * 422 * @return A hash code. 423 */ 424 @Override 425 public int hashCode() { 426 return this.data.hashCode(); 427 } 428 429 /** 430 * Returns a clone of the dataset. 431 * 432 * @return A clone. 433 * 434 * @throws CloneNotSupportedException if there is a problem cloning the 435 * dataset. 436 */ 437 @Override 438 public Object clone() throws CloneNotSupportedException { 439 DefaultCategoryDataset<R, C> clone = (DefaultCategoryDataset) super.clone(); 440 clone.data = (DefaultKeyedValues2D) this.data.clone(); 441 return clone; 442 } 443 444}