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-2005, 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 by Object Refinery Limited and Contributors.
  31:  *
  32:  * Original Author:  David Gilbert (for Object Refinery Limited);
  33:  * Contributor(s):   -;
  34:  *
  35:  * $Id: SimpleHistogramDataset.java,v 1.7.2.1 2005/10/25 21:34:46 mungady Exp $
  36:  *
  37:  * Changes
  38:  * -------
  39:  * 10-Jan-2005 : Version 1 (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.
  85:      * 
  86:      * @param key  the series key.
  87:      */
  88:     public SimpleHistogramDataset(Comparable key) {
  89:         this.key = key;
  90:         this.bins = new ArrayList();
  91:         this.adjustForBinSize = true;
  92:     }
  93:     
  94:     /**
  95:      * Returns a flag that controls whether or not the bin count is divided by 
  96:      * the bin size in the {@link #getXValue(int, int)} method.
  97:      * 
  98:      * @return A boolean.
  99:      */
 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: }