    001/* ===========================================================
    002 * JFreeChart : a free chart library for the Java(tm) platform
    003 * ===========================================================
    004 *
    005 * (C) Copyright 2000-2011, 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 * [Oracle and Java are registered trademarks of Oracle and/or its affiliates.
    025 * Other names may be trademarks of their respective owners.]
    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
    049package org.jfree.data.category;
    050
    051import java.util.Collections;
    052import java.util.List;
    053
    054import org.jfree.data.general.AbstractDataset;
    055import org.jfree.data.general.DatasetChangeEvent;
    056import org.jfree.data.general.DatasetChangeListener;
    057import org.jfree.data.general.PieDataset;
    058import 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 */
    064public 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}