Source for org.jfree.data.statistics.SimpleHistogramDataset

   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: }