Frames | No Frames |
1: /* =========================================================== 2: * JFreeChart : a free chart library for the Java(tm) platform 3: * =========================================================== 4: * 5: * (C) Copyright 2000-2007, by Object Refinery Limited and Contributors. 6: * 7: * Project Info: http://www.jfree.org/jfreechart/index.html 8: * 9: * This library is free software; you can redistribute it and/or modify it 10: * under the terms of the GNU Lesser General Public License as published by 11: * the Free Software Foundation; either version 2.1 of the License, or 12: * (at your option) any later version. 13: * 14: * This library is distributed in the hope that it will be useful, but 15: * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 16: * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public 17: * License for more details. 18: * 19: * You should have received a copy of the GNU Lesser General Public 20: * License along with this library; if not, write to the Free Software 21: * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 22: * USA. 23: * 24: * [Java is a trademark or registered trademark of Sun Microsystems, Inc. 25: * in the United States and other countries.] 26: * 27: * --------------------------- 28: * SimpleHistogramDataset.java 29: * --------------------------- 30: * (C) Copyright 2005, 2007, by Object Refinery Limited and Contributors. 31: * 32: * Original Author: David Gilbert (for Object Refinery Limited); 33: * Contributor(s): Sergei Ivanov; 34: * 35: * Changes 36: * ------- 37: * 10-Jan-2005 : Version 1 (DG); 38: * 21-May-2007 : Added clearObservations() and removeAllBins() (SI); 39: * 10-Jul-2007 : Added null argument check to constructor (DG); 40: * 41: */ 42: 43: package org.jfree.data.statistics; 44: 45: import java.io.Serializable; 46: import java.util.ArrayList; 47: import java.util.Collections; 48: import java.util.Iterator; 49: import java.util.List; 50: 51: import org.jfree.data.DomainOrder; 52: import org.jfree.data.general.DatasetChangeEvent; 53: import org.jfree.data.xy.AbstractIntervalXYDataset; 54: import org.jfree.data.xy.IntervalXYDataset; 55: import org.jfree.util.ObjectUtilities; 56: import org.jfree.util.PublicCloneable; 57: 58: /** 59: * A dataset used for creating simple histograms with custom defined bins. 60: * 61: * @see HistogramDataset 62: */ 63: public class SimpleHistogramDataset extends AbstractIntervalXYDataset 64: implements IntervalXYDataset, 65: Cloneable, PublicCloneable, 66: Serializable { 67: 68: /** For serialization. */ 69: private static final long serialVersionUID = 7997996479768018443L; 70: 71: /** The series key. */ 72: private Comparable key; 73: 74: /** The bins. */ 75: private List bins; 76: 77: /** 78: * A flag that controls whether or not the bin count is divided by the 79: * bin size. 80: */ 81: private boolean adjustForBinSize; 82: 83: /** 84: * Creates a new histogram dataset. Note that the 85: * <code>adjustForBinSize</code> flag defaults to <code>true</code>. 86: * 87: * @param key the series key (<code>null</code> not permitted). 88: */ 89: public SimpleHistogramDataset(Comparable key) { 90: if (key == null) { 91: throw new IllegalArgumentException("Null 'key' argument."); 92: } 93: this.key = key; 94: this.bins = new ArrayList(); 95: this.adjustForBinSize = true; 96: } 97: 98: /** 99: * Returns a flag that controls whether or not the bin count is divided by 100: * the bin size in the {@link #getXValue(int, int)} method. 101: * 102: * @return A boolean. 103: * 104: * @see #setAdjustForBinSize(boolean) 105: */ 106: public boolean getAdjustForBinSize() { 107: return this.adjustForBinSize; 108: } 109: 110: /** 111: * Sets the flag that controls whether or not the bin count is divided by 112: * the bin size in the {@link #getYValue(int, int)} method, and sends a 113: * {@link DatasetChangeEvent} to all registered listeners. 114: * 115: * @param adjust the flag. 116: * 117: * @see #getAdjustForBinSize() 118: */ 119: public void setAdjustForBinSize(boolean adjust) { 120: this.adjustForBinSize = adjust; 121: notifyListeners(new DatasetChangeEvent(this, this)); 122: } 123: 124: /** 125: * Returns the number of series in the dataset (always 1 for this dataset). 126: * 127: * @return The series count. 128: */ 129: public int getSeriesCount() { 130: return 1; 131: } 132: 133: /** 134: * Returns the key for a series. Since this dataset only stores a single 135: * series, the <code>series</code> argument is ignored. 136: * 137: * @param series the series (zero-based index, ignored in this dataset). 138: * 139: * @return The key for the series. 140: */ 141: public Comparable getSeriesKey(int series) { 142: return this.key; 143: } 144: 145: /** 146: * Returns the order of the domain (or X) values returned by the dataset. 147: * 148: * @return The order (never <code>null</code>). 149: */ 150: public DomainOrder getDomainOrder() { 151: return DomainOrder.ASCENDING; 152: } 153: 154: /** 155: * Returns the number of items in a series. Since this dataset only stores 156: * a single series, the <code>series</code> argument is ignored. 157: * 158: * @param series the series index (zero-based, ignored in this dataset). 159: * 160: * @return The item count. 161: */ 162: public int getItemCount(int series) { 163: return this.bins.size(); 164: } 165: 166: /** 167: * Adds a bin to the dataset. An exception is thrown if the bin overlaps 168: * with any existing bin in the dataset. 169: * 170: * @param bin the bin (<code>null</code> not permitted). 171: * 172: * @see #removeAllBins() 173: */ 174: public void addBin(SimpleHistogramBin bin) { 175: // check that the new bin doesn't overlap with any existing bin 176: Iterator iterator = this.bins.iterator(); 177: while (iterator.hasNext()) { 178: SimpleHistogramBin existingBin 179: = (SimpleHistogramBin) iterator.next(); 180: if (bin.overlapsWith(existingBin)) { 181: throw new RuntimeException("Overlapping bin"); 182: } 183: } 184: this.bins.add(bin); 185: Collections.sort(this.bins); 186: } 187: 188: /** 189: * Adds an observation to the dataset (by incrementing the item count for 190: * the appropriate bin). A runtime exception is thrown if the value does 191: * not fit into any bin. 192: * 193: * @param value the value. 194: */ 195: public void addObservation(double value) { 196: addObservation(value, true); 197: } 198: 199: /** 200: * Adds an observation to the dataset (by incrementing the item count for 201: * the appropriate bin). A runtime exception is thrown if the value does 202: * not fit into any bin. 203: * 204: * @param value the value. 205: * @param notify send {@link DatasetChangeEvent} to listeners? 206: */ 207: public void addObservation(double value, boolean notify) { 208: boolean placed = false; 209: Iterator iterator = this.bins.iterator(); 210: while (iterator.hasNext() && !placed) { 211: SimpleHistogramBin bin = (SimpleHistogramBin) iterator.next(); 212: if (bin.accepts(value)) { 213: bin.setItemCount(bin.getItemCount() + 1); 214: placed = true; 215: } 216: } 217: if (!placed) { 218: throw new RuntimeException("No bin."); 219: } 220: if (notify) { 221: notifyListeners(new DatasetChangeEvent(this, this)); 222: } 223: } 224: 225: /** 226: * Adds a set of values to the dataset and sends a 227: * {@link DatasetChangeEvent} to all registered listeners. 228: * 229: * @param values the values (<code>null</code> not permitted). 230: * 231: * @see #clearObservations() 232: */ 233: public void addObservations(double[] values) { 234: for (int i = 0; i < values.length; i++) { 235: addObservation(values[i], false); 236: } 237: notifyListeners(new DatasetChangeEvent(this, this)); 238: } 239: 240: /** 241: * Removes all current observation data and sends a 242: * {@link DatasetChangeEvent} to all registered listeners. 243: * 244: * @since 1.0.6 245: * 246: * @see #addObservations(double[]) 247: * @see #removeAllBins() 248: */ 249: public void clearObservations() { 250: Iterator iterator = this.bins.iterator(); 251: while (iterator.hasNext()) { 252: SimpleHistogramBin bin = (SimpleHistogramBin) iterator.next(); 253: bin.setItemCount(0); 254: } 255: notifyListeners(new DatasetChangeEvent(this, this)); 256: } 257: 258: /** 259: * Removes all bins and sends a {@link DatasetChangeEvent} to all 260: * registered listeners. 261: * 262: * @since 1.0.6 263: * 264: * @see #addBin(SimpleHistogramBin) 265: */ 266: public void removeAllBins() { 267: this.bins = new ArrayList(); 268: notifyListeners(new DatasetChangeEvent(this, this)); 269: } 270: 271: /** 272: * Returns the x-value for an item within a series. The x-values may or 273: * may not be returned in ascending order, that is up to the class 274: * implementing the interface. 275: * 276: * @param series the series index (zero-based). 277: * @param item the item index (zero-based). 278: * 279: * @return The x-value (never <code>null</code>). 280: */ 281: public Number getX(int series, int item) { 282: return new Double(getXValue(series, item)); 283: } 284: 285: /** 286: * Returns the x-value (as a double primitive) for an item within a series. 287: * 288: * @param series the series index (zero-based). 289: * @param item the item index (zero-based). 290: * 291: * @return The x-value. 292: */ 293: public double getXValue(int series, int item) { 294: SimpleHistogramBin bin = (SimpleHistogramBin) this.bins.get(item); 295: return (bin.getLowerBound() + bin.getUpperBound()) / 2.0; 296: } 297: 298: /** 299: * Returns the y-value for an item within a series. 300: * 301: * @param series the series index (zero-based). 302: * @param item the item index (zero-based). 303: * 304: * @return The y-value (possibly <code>null</code>). 305: */ 306: public Number getY(int series, int item) { 307: return new Double(getYValue(series, item)); 308: } 309: 310: /** 311: * Returns the y-value (as a double primitive) for an item within a series. 312: * 313: * @param series the series index (zero-based). 314: * @param item the item index (zero-based). 315: * 316: * @return The y-value. 317: * 318: * @see #getAdjustForBinSize() 319: */ 320: public double getYValue(int series, int item) { 321: SimpleHistogramBin bin = (SimpleHistogramBin) this.bins.get(item); 322: if (this.adjustForBinSize) { 323: return bin.getItemCount() 324: / (bin.getUpperBound() - bin.getLowerBound()); 325: } 326: else { 327: return bin.getItemCount(); 328: } 329: } 330: 331: /** 332: * Returns the starting X value for the specified series and item. 333: * 334: * @param series the series index (zero-based). 335: * @param item the item index (zero-based). 336: * 337: * @return The value. 338: */ 339: public Number getStartX(int series, int item) { 340: return new Double(getStartXValue(series, item)); 341: } 342: 343: /** 344: * Returns the start x-value (as a double primitive) for an item within a 345: * series. 346: * 347: * @param series the series (zero-based index). 348: * @param item the item (zero-based index). 349: * 350: * @return The start x-value. 351: */ 352: public double getStartXValue(int series, int item) { 353: SimpleHistogramBin bin = (SimpleHistogramBin) this.bins.get(item); 354: return bin.getLowerBound(); 355: } 356: 357: /** 358: * Returns the ending X value for the specified series and item. 359: * 360: * @param series the series index (zero-based). 361: * @param item the item index (zero-based). 362: * 363: * @return The value. 364: */ 365: public Number getEndX(int series, int item) { 366: return new Double(getEndXValue(series, item)); 367: } 368: 369: /** 370: * Returns the end x-value (as a double primitive) for an item within a 371: * series. 372: * 373: * @param series the series index (zero-based). 374: * @param item the item index (zero-based). 375: * 376: * @return The end x-value. 377: */ 378: public double getEndXValue(int series, int item) { 379: SimpleHistogramBin bin = (SimpleHistogramBin) this.bins.get(item); 380: return bin.getUpperBound(); 381: } 382: 383: /** 384: * Returns the starting Y value for the specified series and item. 385: * 386: * @param series the series index (zero-based). 387: * @param item the item index (zero-based). 388: * 389: * @return The value. 390: */ 391: public Number getStartY(int series, int item) { 392: return getY(series, item); 393: } 394: 395: /** 396: * Returns the start y-value (as a double primitive) for an item within a 397: * series. 398: * 399: * @param series the series index (zero-based). 400: * @param item the item index (zero-based). 401: * 402: * @return The start y-value. 403: */ 404: public double getStartYValue(int series, int item) { 405: return getYValue(series, item); 406: } 407: 408: /** 409: * Returns the ending Y value for the specified series and item. 410: * 411: * @param series the series index (zero-based). 412: * @param item the item index (zero-based). 413: * 414: * @return The value. 415: */ 416: public Number getEndY(int series, int item) { 417: return getY(series, item); 418: } 419: 420: /** 421: * Returns the end y-value (as a double primitive) for an item within a 422: * series. 423: * 424: * @param series the series index (zero-based). 425: * @param item the item index (zero-based). 426: * 427: * @return The end y-value. 428: */ 429: public double getEndYValue(int series, int item) { 430: return getYValue(series, item); 431: } 432: 433: /** 434: * Compares the dataset for equality with an arbitrary object. 435: * 436: * @param obj the object (<code>null</code> permitted). 437: * 438: * @return A boolean. 439: */ 440: public boolean equals(Object obj) { 441: if (obj == this) { 442: return true; 443: } 444: if (!(obj instanceof SimpleHistogramDataset)) { 445: return false; 446: } 447: SimpleHistogramDataset that = (SimpleHistogramDataset) obj; 448: if (!this.key.equals(that.key)) { 449: return false; 450: } 451: if (this.adjustForBinSize != that.adjustForBinSize) { 452: return false; 453: } 454: if (!this.bins.equals(that.bins)) { 455: return false; 456: } 457: return true; 458: } 459: 460: /** 461: * Returns a clone of the dataset. 462: * 463: * @return A clone. 464: * 465: * @throws CloneNotSupportedException not thrown by this class, but maybe 466: * by subclasses (if any). 467: */ 468: public Object clone() throws CloneNotSupportedException { 469: SimpleHistogramDataset clone = (SimpleHistogramDataset) super.clone(); 470: clone.bins = (List) ObjectUtilities.deepClone(this.bins); 471: return clone; 472: } 473: 474: }