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