001/* =========================================================== 002 * JFreeChart : a free chart library for the Java(tm) platform 003 * =========================================================== 004 * 005 * (C) Copyright 2000-present, 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 * DefaultMultiValueCategoryDataset.java 029 * ------------------------------------- 030 * (C) Copyright 2007-present, by David Forslund and Contributors. 031 * 032 * Original Author: David Forslund; 033 * Contributor(s): David Gilbert; 034 * 035 */ 036 037package org.jfree.data.statistics; 038 039import java.util.ArrayList; 040import java.util.Collections; 041import java.util.Iterator; 042import java.util.List; 043import org.jfree.chart.util.Args; 044import org.jfree.chart.util.PublicCloneable; 045 046import org.jfree.data.KeyedObjects2D; 047import org.jfree.data.Range; 048import org.jfree.data.RangeInfo; 049import org.jfree.data.general.AbstractDataset; 050import org.jfree.data.general.DatasetChangeEvent; 051 052/** 053 * A category dataset that defines multiple values for each item. 054 */ 055public class DefaultMultiValueCategoryDataset extends AbstractDataset 056 implements MultiValueCategoryDataset, RangeInfo, PublicCloneable { 057 058 /** 059 * Storage for the data. 060 */ 061 protected KeyedObjects2D data; 062 063 /** 064 * The minimum range value. 065 */ 066 private Number minimumRangeValue; 067 068 /** 069 * The maximum range value. 070 */ 071 private Number maximumRangeValue; 072 073 /** 074 * The range of values. 075 */ 076 private Range rangeBounds; 077 078 /** 079 * Creates a new dataset. 080 */ 081 public DefaultMultiValueCategoryDataset() { 082 this.data = new KeyedObjects2D(); 083 this.minimumRangeValue = null; 084 this.maximumRangeValue = null; 085 this.rangeBounds = new Range(0.0, 0.0); 086 } 087 088 /** 089 * Adds a list of values to the dataset ({@code null} and Double.NaN 090 * items are automatically removed) and sends a {@link DatasetChangeEvent} 091 * to all registered listeners. 092 * 093 * @param values a list of values ({@code null} not permitted). 094 * @param rowKey the row key ({@code null} not permitted). 095 * @param columnKey the column key ({@code null} not permitted). 096 */ 097 public void add(List values, Comparable rowKey, Comparable columnKey) { 098 099 Args.nullNotPermitted(values, "values"); 100 Args.nullNotPermitted(rowKey, "rowKey"); 101 Args.nullNotPermitted(columnKey, "columnKey"); 102 List vlist = new ArrayList(values.size()); 103 Iterator iterator = values.listIterator(); 104 while (iterator.hasNext()) { 105 Object obj = iterator.next(); 106 if (obj instanceof Number) { 107 Number n = (Number) obj; 108 double v = n.doubleValue(); 109 if (!Double.isNaN(v)) { 110 vlist.add(n); 111 } 112 } 113 } 114 Collections.sort(vlist); 115 this.data.addObject(vlist, rowKey, columnKey); 116 117 if (vlist.size() > 0) { 118 double maxval = Double.NEGATIVE_INFINITY; 119 double minval = Double.POSITIVE_INFINITY; 120 for (int i = 0; i < vlist.size(); i++) { 121 Number n = (Number) vlist.get(i); 122 double v = n.doubleValue(); 123 minval = Math.min(minval, v); 124 maxval = Math.max(maxval, v); 125 } 126 127 // update the cached range values... 128 if (this.maximumRangeValue == null) { 129 this.maximumRangeValue = maxval; 130 } 131 else if (maxval > this.maximumRangeValue.doubleValue()) { 132 this.maximumRangeValue = maxval; 133 } 134 135 if (this.minimumRangeValue == null) { 136 this.minimumRangeValue = minval; 137 } 138 else if (minval < this.minimumRangeValue.doubleValue()) { 139 this.minimumRangeValue = minval; 140 } 141 this.rangeBounds = new Range(this.minimumRangeValue.doubleValue(), 142 this.maximumRangeValue.doubleValue()); 143 } 144 145 fireDatasetChanged(); 146 } 147 148 /** 149 * Returns a list (possibly empty) of the values for the specified item. 150 * The returned list should be unmodifiable. 151 * 152 * @param row the row index (zero-based). 153 * @param column the column index (zero-based). 154 * 155 * @return The list of values. 156 */ 157 @Override 158 public List getValues(int row, int column) { 159 List values = (List) this.data.getObject(row, column); 160 if (values != null) { 161 return Collections.unmodifiableList(values); 162 } 163 else { 164 return Collections.EMPTY_LIST; 165 } 166 } 167 168 /** 169 * Returns a list (possibly empty) of the values for the specified item. 170 * The returned list should be unmodifiable. 171 * 172 * @param rowKey the row key ({@code null} not permitted). 173 * @param columnKey the column key ({@code null} not permitted). 174 * 175 * @return The list of values. 176 */ 177 @Override 178 public List getValues(Comparable rowKey, Comparable columnKey) { 179 return Collections.unmodifiableList((List) this.data.getObject(rowKey, 180 columnKey)); 181 } 182 183 /** 184 * Returns the average value for the specified item. 185 * 186 * @param row the row key. 187 * @param column the column key. 188 * 189 * @return The average value. 190 */ 191 @Override 192 public Number getValue(Comparable row, Comparable column) { 193 List l = (List) this.data.getObject(row, column); 194 double average = 0.0d; 195 int count = 0; 196 if (l != null && l.size() > 0) { 197 for (int i = 0; i < l.size(); i++) { 198 Number n = (Number) l.get(i); 199 average += n.doubleValue(); 200 count += 1; 201 } 202 if (count > 0) { 203 average = average / count; 204 } 205 } 206 if (count == 0) { 207 return null; 208 } 209 return average; 210 } 211 212 /** 213 * Returns the average value for the specified item. 214 * 215 * @param row the row index. 216 * @param column the column index. 217 * 218 * @return The average value. 219 */ 220 @Override 221 public Number getValue(int row, int column) { 222 List l = (List) this.data.getObject(row, column); 223 double average = 0.0d; 224 int count = 0; 225 if (l != null && l.size() > 0) { 226 for (int i = 0; i < l.size(); i++) { 227 Number n = (Number) l.get(i); 228 average += n.doubleValue(); 229 count += 1; 230 } 231 if (count > 0) { 232 average = average / count; 233 } 234 } 235 if (count == 0) { 236 return null; 237 } 238 return average; 239 } 240 241 /** 242 * Returns the column index for a given key. 243 * 244 * @param key the column key. 245 * 246 * @return The column index. 247 */ 248 @Override 249 public int getColumnIndex(Comparable key) { 250 return this.data.getColumnIndex(key); 251 } 252 253 /** 254 * Returns a column key. 255 * 256 * @param column the column index (zero-based). 257 * 258 * @return The column key. 259 */ 260 @Override 261 public Comparable getColumnKey(int column) { 262 return this.data.getColumnKey(column); 263 } 264 265 /** 266 * Returns the column keys. 267 * 268 * @return The keys. 269 */ 270 @Override 271 public List getColumnKeys() { 272 return this.data.getColumnKeys(); 273 } 274 275 /** 276 * Returns the row index for a given key. 277 * 278 * @param key the row key. 279 * 280 * @return The row index. 281 */ 282 @Override 283 public int getRowIndex(Comparable key) { 284 return this.data.getRowIndex(key); 285 } 286 287 /** 288 * Returns a row key. 289 * 290 * @param row the row index (zero-based). 291 * 292 * @return The row key. 293 */ 294 @Override 295 public Comparable getRowKey(int row) { 296 return this.data.getRowKey(row); 297 } 298 299 /** 300 * Returns the row keys. 301 * 302 * @return The keys. 303 */ 304 @Override 305 public List getRowKeys() { 306 return this.data.getRowKeys(); 307 } 308 309 /** 310 * Returns the number of rows in the table. 311 * 312 * @return The row count. 313 */ 314 @Override 315 public int getRowCount() { 316 return this.data.getRowCount(); 317 } 318 319 /** 320 * Returns the number of columns in the table. 321 * 322 * @return The column count. 323 */ 324 @Override 325 public int getColumnCount() { 326 return this.data.getColumnCount(); 327 } 328 329 /** 330 * Returns the minimum y-value in the dataset. 331 * 332 * @param includeInterval a flag that determines whether or not the 333 * y-interval is taken into account. 334 * 335 * @return The minimum value. 336 */ 337 @Override 338 public double getRangeLowerBound(boolean includeInterval) { 339 double result = Double.NaN; 340 if (this.minimumRangeValue != null) { 341 result = this.minimumRangeValue.doubleValue(); 342 } 343 return result; 344 } 345 346 /** 347 * Returns the maximum y-value in the dataset. 348 * 349 * @param includeInterval a flag that determines whether or not the 350 * y-interval is taken into account. 351 * 352 * @return The maximum value. 353 */ 354 @Override 355 public double getRangeUpperBound(boolean includeInterval) { 356 double result = Double.NaN; 357 if (this.maximumRangeValue != null) { 358 result = this.maximumRangeValue.doubleValue(); 359 } 360 return result; 361 } 362 363 /** 364 * Returns the range of the values in this dataset's range. 365 * 366 * @param includeInterval a flag that determines whether or not the 367 * y-interval is taken into account. 368 * @return The range. 369 */ 370 @Override 371 public Range getRangeBounds(boolean includeInterval) { 372 return this.rangeBounds; 373 } 374 375 /** 376 * Tests this dataset for equality with an arbitrary object. 377 * 378 * @param obj the object ({@code null} permitted). 379 * 380 * @return A boolean. 381 */ 382 @Override 383 public boolean equals(Object obj) { 384 if (obj == this) { 385 return true; 386 } 387 if (!(obj instanceof DefaultMultiValueCategoryDataset)) { 388 return false; 389 } 390 DefaultMultiValueCategoryDataset that 391 = (DefaultMultiValueCategoryDataset) obj; 392 return this.data.equals(that.data); 393 } 394 395 /** 396 * Returns a clone of this instance. 397 * 398 * @return A clone. 399 * 400 * @throws CloneNotSupportedException if the dataset cannot be cloned. 401 */ 402 @Override 403 public Object clone() throws CloneNotSupportedException { 404 DefaultMultiValueCategoryDataset clone 405 = (DefaultMultiValueCategoryDataset) super.clone(); 406 clone.data = (KeyedObjects2D) this.data.clone(); 407 return clone; 408 } 409}