    001    /* ===========================================================
    002     * JFreeChart : a free chart library for the Java(tm) platform
    003     * ===========================================================
    004     *
    005     * (C) Copyright 2000-2008, 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     * CategoryToPieDataset.java
    029     * -------------------------
    030     * (C) Copyright 2003-2008, by Object Refinery Limited.
    031     *
    032     * Original Author:  David Gilbert (for Object Refinery Limited);
    033     * Contributor(s):   Christian W. Zuckschwerdt;
    034     *
    035     * Changes
    036     * -------
    037     * 23-Jan-2003 : Version 1 (DG);
    038     * 30-Jul-2003 : Pass through DatasetChangeEvent (CZ);
    039     * 29-Jan-2004 : Replaced 'extract' int with TableOrder (DG);
    040     * 11-Jan-2005 : Removed deprecated code in preparation for the 1.0.0
    041     *               release (DG);
    042     * ------------- JFREECHART 1.0.0 RELEASED ------------------------------------
    043     * 26-Jul-2006 : Added serialVersionUID, changed constructor to allow null
    044     *               for source, and added getSource(), getExtractType() and
    045     *               getExtractIndex() methods - see feature request 1477915 (DG);
    046     *
    047     */
    048
    049    package org.jfree.data.category;
    050
    051    import java.util.Collections;
    052    import java.util.List;
    053
    054    import org.jfree.data.general.AbstractDataset;
    055    import org.jfree.data.general.DatasetChangeEvent;
    056    import org.jfree.data.general.DatasetChangeListener;
    057    import org.jfree.data.general.PieDataset;
    058    import org.jfree.util.TableOrder;
    059
    060    /**
    061     * A {@link PieDataset} implementation that obtains its data from one row or
    062     * column of a {@link CategoryDataset}.
    063     */
    064    public class CategoryToPieDataset extends AbstractDataset
    065            implements PieDataset, DatasetChangeListener {
    066
    067        /** For serialization. */
    068        static final long serialVersionUID = 5516396319762189617L;
    069
    070        /** The source. */
    071        private CategoryDataset source;
    072
    073        /** The extract type. */
    074        private TableOrder extract;
    075
    076        /** The row or column index. */
    077        private int index;
    078
    079        /**
    080         * An adaptor class that converts any {@link CategoryDataset} into a
    081         * {@link PieDataset}, by taking the values from a single row or column.
    082         * <p>
    083         * If <code>source</code> is <code>null</code>, the created dataset will
    084         * be empty.
    085         *
    086         * @param source  the source dataset (<code>null</code> permitted).
    087         * @param extract  extract data from rows or columns? (<code>null</code>
    088         *                 not permitted).
    089         * @param index  the row or column index.
    090         */
    091        public CategoryToPieDataset(CategoryDataset source,
    092                                    TableOrder extract,
    093                                    int index) {
    094            if (extract == null) {
    095                throw new IllegalArgumentException("Null 'extract' argument.");
    096            }
    097            this.source = source;
    098            if (this.source != null) {
    099                this.source.addChangeListener(this);
    100            }
    101            this.extract = extract;
    102            this.index = index;
    103        }
    104
    105        /**
    106         * Returns the underlying dataset.
    107         *
    108         * @return The underlying dataset (possibly <code>null</code>).
    109         *
    110         * @since 1.0.2
    111         */
    112        public CategoryDataset getUnderlyingDataset() {
    113            return this.source;
    114        }
    115
    116        /**
    117         * Returns the extract type, which determines whether data is read from
    118         * one row or one column of the underlying dataset.
    119         *
    120         * @return The extract type.
    121         *
    122         * @since 1.0.2
    123         */
    124        public TableOrder getExtractType() {
    125            return this.extract;
    126        }
    127
    128        /**
    129         * Returns the index of the row or column from which to extract the data.
    130         *
    131         * @return The extract index.
    132         *
    133         * @since 1.0.2
    134         */
    135        public int getExtractIndex() {
    136            return this.index;
    137        }
    138
    139        /**
    140         * Returns the number of items (values) in the collection.  If the
    141         * underlying dataset is <code>null</code>, this method returns zero.
    142         *
    143         * @return The item count.
    144         */
    145        public int getItemCount() {
    146            int result = 0;
    147            if (this.source != null) {
    148                if (this.extract == TableOrder.BY_ROW) {
    149                    result = this.source.getColumnCount();
    150                }
    151                else if (this.extract == TableOrder.BY_COLUMN) {
    152                    result = this.source.getRowCount();
    153                }
    154            }
    155            return result;
    156        }
    157
    158        /**
    159         * Returns a value from the dataset.
    160         *
    161         * @param item  the item index (zero-based).
    162         *
    163         * @return The value (possibly <code>null</code>).
    164         *
    165         * @throws IndexOutOfBoundsException if <code>item</code> is not in the
    166         *     range <code>0</code> to <code>getItemCount() - 1</code>.
    167         */
    168        public Number getValue(int item) {
    169            Number result = null;
    170            if (item < 0 || item >= getItemCount()) {
    171                // this will include the case where the underlying dataset is null
    172                throw new IndexOutOfBoundsException(
    173                        "The 'item' index is out of bounds.");
    174            }
    175            if (this.extract == TableOrder.BY_ROW) {
    176                result = this.source.getValue(this.index, item);
    177            }
    178            else if (this.extract == TableOrder.BY_COLUMN) {
    179                result = this.source.getValue(item, this.index);
    180            }
    181            return result;
    182        }
    183
    184        /**
    185         * Returns the key at the specified index.
    186         *
    187         * @param index  the item index (in the range <code>0</code> to
    188         *     <code>getItemCount() - 1</code>).
    189         *
    190         * @return The key.
    191         *
    192         * @throws IndexOutOfBoundsException if <code>index</code> is not in the
    193         *     specified range.
    194         */
    195        public Comparable getKey(int index) {
    196            Comparable result = null;
    197            if (index < 0 || index >= getItemCount()) {
    198                // this includes the case where the underlying dataset is null
    199                throw new IndexOutOfBoundsException("Invalid 'index': " + index);
    200            }
    201            if (this.extract == TableOrder.BY_ROW) {
    202                result = this.source.getColumnKey(index);
    203            }
    204            else if (this.extract == TableOrder.BY_COLUMN) {
    205                result = this.source.getRowKey(index);
    206            }
    207            return result;
    208        }
    209
    210        /**
    211         * Returns the index for a given key, or <code>-1</code> if there is no
    212         * such key.
    213         *
    214         * @param key  the key.
    215         *
    216         * @return The index for the key, or <code>-1</code>.
    217         */
    218        public int getIndex(Comparable key) {
    219            int result = -1;
    220            if (this.source != null) {
    221                if (this.extract == TableOrder.BY_ROW) {
    222                    result = this.source.getColumnIndex(key);
    223                }
    224                else if (this.extract == TableOrder.BY_COLUMN) {
    225                    result = this.source.getRowIndex(key);
    226                }
    227            }
    228            return result;
    229        }
    230
    231        /**
    232         * Returns the keys for the dataset.
    233         * <p>
    234         * If the underlying dataset is <code>null</code>, this method returns an
    235         * empty list.
    236         *
    237         * @return The keys.
    238         */
    239        public List getKeys() {
    240            List result = Collections.EMPTY_LIST;
    241            if (this.source != null) {
    242                if (this.extract == TableOrder.BY_ROW) {
    243                    result = this.source.getColumnKeys();
    244                }
    245                else if (this.extract == TableOrder.BY_COLUMN) {
    246                    result = this.source.getRowKeys();
    247                }
    248            }
    249            return result;
    250        }
    251
    252        /**
    253         * Returns the value for a given key.  If the key is not recognised, the
    254         * method should return <code>null</code> (but note that <code>null</code>
    255         * can be associated with a valid key also).
    256         *
    257         * @param key  the key.
    258         *
    259         * @return The value (possibly <code>null</code>).
    260         */
    261        public Number getValue(Comparable key) {
    262            Number result = null;
    263            int keyIndex = getIndex(key);
    264            if (keyIndex != -1) {
    265                if (this.extract == TableOrder.BY_ROW) {
    266                    result = this.source.getValue(this.index, keyIndex);
    267                }
    268                else if (this.extract == TableOrder.BY_COLUMN) {
    269                    result = this.source.getValue(keyIndex, this.index);
    270                }
    271            }
    272            return result;
    273        }
    274
    275        /**
    276         * Sends a {@link DatasetChangeEvent} to all registered listeners, with
    277         * this (not the underlying) dataset as the source.
    278         *
    279         * @param event  the event (ignored, a new event with this dataset as the
    280         *     source is sent to the listeners).
    281         */
    282        public void datasetChanged(DatasetChangeEvent event) {
    283            fireDatasetChanged();
    284        }
    285
    286        /**
    287         * Tests this dataset for equality with an arbitrary object, returning
    288         * <code>true</code> if <code>obj</code> is a dataset containing the same
    289         * keys and values in the same order as this dataset.
    290         *
    291         * @param obj  the object to test (<code>null</code> permitted).
    292         *
    293         * @return A boolean.
    294         */
    295        public boolean equals(Object obj) {
    296            if (obj == this) {
    297                return true;
    298            }
    299            if (!(obj instanceof PieDataset)) {
    300                return false;
    301            }
    302            PieDataset that = (PieDataset) obj;
    303            int count = getItemCount();
    304            if (that.getItemCount() != count) {
    305                return false;
    306            }
    307            for (int i = 0; i < count; i++) {
    308                Comparable k1 = getKey(i);
    309                Comparable k2 = that.getKey(i);
    310                if (!k1.equals(k2)) {
    311                    return false;
    312                }
    313
    314                Number v1 = getValue(i);
    315                Number v2 = that.getValue(i);
    316                if (v1 == null) {
    317                    if (v2 != null) {
    318                        return false;
    319                    }
    320                }
    321                else {
    322                    if (!v1.equals(v2)) {
    323                        return false;
    324                    }
    325                }
    326            }
    327            return true;
    328        }
    329
    330    }