001 /* =========================================================== 002 * JFreeChart : a free chart library for the Java(tm) platform 003 * =========================================================== 004 * 005 * (C) Copyright 2000-2005, 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 * [Java is a trademark or registered trademark of Sun Microsystems, Inc. 025 * in the United States and other countries.] 026 * 027 * --------------------------- 028 * SimpleHistogramDataset.java 029 * --------------------------- 030 * (C) Copyright 2005 by Object Refinery Limited and Contributors. 031 * 032 * Original Author: David Gilbert (for Object Refinery Limited); 033 * Contributor(s): -; 034 * 035 * $Id: SimpleHistogramDataset.java,v 1.7.2.1 2005/10/25 21:34:46 mungady Exp $ 036 * 037 * Changes 038 * ------- 039 * 10-Jan-2005 : Version 1 (DG); 040 * 041 */ 042 043 package org.jfree.data.statistics; 044 045 import java.io.Serializable; 046 import java.util.ArrayList; 047 import java.util.Collections; 048 import java.util.Iterator; 049 import java.util.List; 050 051 import org.jfree.data.DomainOrder; 052 import org.jfree.data.general.DatasetChangeEvent; 053 import org.jfree.data.xy.AbstractIntervalXYDataset; 054 import org.jfree.data.xy.IntervalXYDataset; 055 import org.jfree.util.ObjectUtilities; 056 import org.jfree.util.PublicCloneable; 057 058 /** 059 * A dataset used for creating simple histograms with custom defined bins. 060 * 061 * @see HistogramDataset 062 */ 063 public class SimpleHistogramDataset extends AbstractIntervalXYDataset 064 implements IntervalXYDataset, 065 Cloneable, PublicCloneable, 066 Serializable { 067 068 /** For serialization. */ 069 private static final long serialVersionUID = 7997996479768018443L; 070 071 /** The series key. */ 072 private Comparable key; 073 074 /** The bins. */ 075 private List bins; 076 077 /** 078 * A flag that controls whether or not the bin count is divided by the 079 * bin size. 080 */ 081 private boolean adjustForBinSize; 082 083 /** 084 * Creates a new histogram dataset. 085 * 086 * @param key the series key. 087 */ 088 public SimpleHistogramDataset(Comparable key) { 089 this.key = key; 090 this.bins = new ArrayList(); 091 this.adjustForBinSize = true; 092 } 093 094 /** 095 * Returns a flag that controls whether or not the bin count is divided by 096 * the bin size in the {@link #getXValue(int, int)} method. 097 * 098 * @return A boolean. 099 */ 100 public boolean getAdjustForBinSize() { 101 return this.adjustForBinSize; 102 } 103 104 /** 105 * Sets the flag that controls whether or not the bin count is divided by 106 * the bin size in the {@link #getXValue(int, int)} method. 107 * 108 * @param adjust the flag. 109 */ 110 public void setAdjustForBinSize(boolean adjust) { 111 this.adjustForBinSize = adjust; 112 notifyListeners(new DatasetChangeEvent(this, this)); 113 } 114 115 /** 116 * Returns the number of series in the dataset (always 1 for this dataset). 117 * 118 * @return The series count. 119 */ 120 public int getSeriesCount() { 121 return 1; 122 } 123 124 /** 125 * Returns the key for a series. 126 * 127 * @param series the series (zero-based index, ignored in this dataset). 128 * 129 * @return The key for the series. 130 */ 131 public Comparable getSeriesKey(int series) { 132 return this.key; 133 } 134 135 /** 136 * Returns the order of the domain (or X) values returned by the dataset. 137 * 138 * @return The order (never <code>null</code>). 139 */ 140 public DomainOrder getDomainOrder() { 141 return DomainOrder.ASCENDING; 142 } 143 144 /** 145 * Returns the number of items in a series. 146 * 147 * @param series the series index (zero-based, ignored in this dataset). 148 * 149 * @return The item count. 150 */ 151 public int getItemCount(int series) { 152 return this.bins.size(); 153 } 154 155 /** 156 * Adds a bin to the dataset. An exception is thrown if the bin overlaps 157 * with any existing bin in the dataset. 158 * 159 * @param bin the bin (<code>null</code> not permitted). 160 */ 161 public void addBin(SimpleHistogramBin bin) { 162 // check that the new bin doesn't overlap with any existing bin 163 Iterator iterator = this.bins.iterator(); 164 while (iterator.hasNext()) { 165 SimpleHistogramBin existingBin 166 = (SimpleHistogramBin) iterator.next(); 167 if (bin.overlapsWith(existingBin)) { 168 throw new RuntimeException("Overlapping bin"); 169 } 170 } 171 this.bins.add(bin); 172 Collections.sort(this.bins); 173 } 174 175 /** 176 * Adds an observation to the dataset (by incrementing the item count for 177 * the appropriate bin). A runtime exception is thrown if the value does 178 * not fit into any bin. 179 * 180 * @param value the value. 181 */ 182 public void addObservation(double value) { 183 addObservation(value, true); 184 } 185 186 /** 187 * Adds an observation to the dataset (by incrementing the item count for 188 * the appropriate bin). A runtime exception is thrown if the value does 189 * not fit into any bin. 190 * 191 * @param value the value. 192 * @param notify send {@link DatasetChangeEvent} to listeners? 193 */ 194 public void addObservation(double value, boolean notify) { 195 boolean placed = false; 196 Iterator iterator = this.bins.iterator(); 197 while (iterator.hasNext() && !placed) { 198 SimpleHistogramBin bin = (SimpleHistogramBin) iterator.next(); 199 if (bin.accepts(value)) { 200 bin.setItemCount(bin.getItemCount() + 1); 201 placed = true; 202 } 203 } 204 if (!placed) { 205 throw new RuntimeException("No bin."); 206 } 207 if (notify) { 208 notifyListeners(new DatasetChangeEvent(this, this)); 209 } 210 } 211 212 /** 213 * Adds a set of values to the dataset. 214 * 215 * @param values the values. 216 */ 217 public void addObservations(double[] values) { 218 for (int i = 0; i < values.length; i++) { 219 addObservation(values[i], false); 220 } 221 notifyListeners(new DatasetChangeEvent(this, this)); 222 } 223 224 /** 225 * Returns the x-value for an item within a series. The x-values may or 226 * may not be returned in ascending order, that is up to the class 227 * implementing the interface. 228 * 229 * @param series the series index (zero-based). 230 * @param item the item index (zero-based). 231 * 232 * @return The x-value (never <code>null</code>). 233 */ 234 public Number getX(int series, int item) { 235 return new Double(getXValue(series, item)); 236 } 237 238 /** 239 * Returns the x-value (as a double primitive) for an item within a series. 240 * 241 * @param series the series index (zero-based). 242 * @param item the item index (zero-based). 243 * 244 * @return The x-value. 245 */ 246 public double getXValue(int series, int item) { 247 SimpleHistogramBin bin = (SimpleHistogramBin) this.bins.get(item); 248 return (bin.getLowerBound() + bin.getUpperBound()) / 2.0; 249 } 250 251 /** 252 * Returns the y-value for an item within a series. 253 * 254 * @param series the series index (zero-based). 255 * @param item the item index (zero-based). 256 * 257 * @return The y-value (possibly <code>null</code>). 258 */ 259 public Number getY(int series, int item) { 260 return new Double(getYValue(series, item)); 261 } 262 263 /** 264 * Returns the y-value (as a double primitive) for an item within a series. 265 * 266 * @param series the series index (zero-based). 267 * @param item the item index (zero-based). 268 * 269 * @return The y-value. 270 */ 271 public double getYValue(int series, int item) { 272 SimpleHistogramBin bin = (SimpleHistogramBin) this.bins.get(item); 273 if (this.adjustForBinSize) { 274 return bin.getItemCount() 275 / (bin.getUpperBound() - bin.getLowerBound()); 276 } 277 else { 278 return bin.getItemCount(); 279 } 280 } 281 282 /** 283 * Returns the starting X value for the specified series and item. 284 * 285 * @param series the series index (zero-based). 286 * @param item the item index (zero-based). 287 * 288 * @return The value. 289 */ 290 public Number getStartX(int series, int item) { 291 return new Double(getStartXValue(series, item)); 292 } 293 294 /** 295 * Returns the start x-value (as a double primitive) for an item within a 296 * series. 297 * 298 * @param series the series (zero-based index). 299 * @param item the item (zero-based index). 300 * 301 * @return The start x-value. 302 */ 303 public double getStartXValue(int series, int item) { 304 SimpleHistogramBin bin = (SimpleHistogramBin) this.bins.get(item); 305 return bin.getLowerBound(); 306 } 307 308 /** 309 * Returns the ending X value for the specified series and item. 310 * 311 * @param series the series index (zero-based). 312 * @param item the item index (zero-based). 313 * 314 * @return The value. 315 */ 316 public Number getEndX(int series, int item) { 317 return new Double(getEndXValue(series, item)); 318 } 319 320 /** 321 * Returns the end x-value (as a double primitive) for an item within a 322 * series. 323 * 324 * @param series the series index (zero-based). 325 * @param item the item index (zero-based). 326 * 327 * @return The end x-value. 328 */ 329 public double getEndXValue(int series, int item) { 330 SimpleHistogramBin bin = (SimpleHistogramBin) this.bins.get(item); 331 return bin.getUpperBound(); 332 } 333 334 /** 335 * Returns the starting Y value for the specified series and item. 336 * 337 * @param series the series index (zero-based). 338 * @param item the item index (zero-based). 339 * 340 * @return The value. 341 */ 342 public Number getStartY(int series, int item) { 343 return getY(series, item); 344 } 345 346 /** 347 * Returns the start y-value (as a double primitive) for an item within a 348 * series. 349 * 350 * @param series the series index (zero-based). 351 * @param item the item index (zero-based). 352 * 353 * @return The start y-value. 354 */ 355 public double getStartYValue(int series, int item) { 356 return getYValue(series, item); 357 } 358 359 /** 360 * Returns the ending Y value for the specified series and item. 361 * 362 * @param series the series index (zero-based). 363 * @param item the item index (zero-based). 364 * 365 * @return The value. 366 */ 367 public Number getEndY(int series, int item) { 368 return getY(series, item); 369 } 370 371 /** 372 * Returns the end y-value (as a double primitive) for an item within a 373 * series. 374 * 375 * @param series the series index (zero-based). 376 * @param item the item index (zero-based). 377 * 378 * @return The end y-value. 379 */ 380 public double getEndYValue(int series, int item) { 381 return getYValue(series, item); 382 } 383 384 /** 385 * Compares the dataset for equality with an arbitrary object. 386 * 387 * @param obj the object (<code>null</code> permitted). 388 * 389 * @return A boolean. 390 */ 391 public boolean equals(Object obj) { 392 if (obj == this) { 393 return true; 394 } 395 if (!(obj instanceof SimpleHistogramDataset)) { 396 return false; 397 } 398 SimpleHistogramDataset that = (SimpleHistogramDataset) obj; 399 if (!this.key.equals(that.key)) { 400 return false; 401 } 402 if (this.adjustForBinSize != that.adjustForBinSize) { 403 return false; 404 } 405 if (!this.bins.equals(that.bins)) { 406 return false; 407 } 408 return true; 409 } 410 411 /** 412 * Returns a clone of the dataset. 413 * 414 * @return A clone. 415 * 416 * @throws CloneNotSupportedException not thrown by this class, but maybe 417 * by subclasses (if any). 418 */ 419 public Object clone() throws CloneNotSupportedException { 420 SimpleHistogramDataset clone = (SimpleHistogramDataset) super.clone(); 421 clone.bins = (List) ObjectUtilities.deepClone(this.bins); 422 return clone; 423 } 424 425 }