    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    }