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