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 }