    001/* ===========================================================
    002 * Orson Charts : a 3D chart library for the Java(tm) platform
    003 * ===========================================================
    004 *
    005 * (C)opyright 2013-2016, by Object Refinery Limited.  All rights reserved.
    006 *
    007 * http://www.object-refinery.com/orsoncharts/index.html
    008 *
    009 * This program is free software: you can redistribute it and/or modify
    010 * it under the terms of the GNU General Public License as published by
    011 * the Free Software Foundation, either version 3 of the License, or
    012 * (at your option) any later version.
    013 *
    014 * This program is distributed in the hope that it will be useful,
    015 * but WITHOUT ANY WARRANTY; without even the implied warranty of
    016 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    017 * GNU General Public License for more details.
    018 *
    019 * You should have received a copy of the GNU General Public License
    020 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
    021 *
    022 * [Oracle and Java are registered trademarks of Oracle and/or its affiliates.
    023 * Other names may be trademarks of their respective owners.]
    024 *
    025 * If you do not wish to be bound by the terms of the GPL, an alternative
    026 * commercial license can be purchased.  For details, please see visit the
    027 * Orson Charts home page:
    028 *
    029 * http://www.object-refinery.com/orsoncharts/index.html
    030 *
    031 */
    032
    033package com.orsoncharts.data;
    034
    035import java.io.Serializable;
    036import java.util.ArrayList;
    037import java.util.List;
    038
    039import com.orsoncharts.util.ArgChecks;
    040
    041/**
    042 * A three dimensional table of numerical values, implementing the
    043 * {@link KeyedValues3D} interface.
    044 * <br><br>
    045 * NOTE: This class is serializable, but the serialization format is subject
    046 * to change in future releases and should not be relied upon for persisting
    047 * instances of this class.
    048 *
    049 * @param <S>  the series key (must implement Comparable).
    050 * @param <R>  the row key (must implement Comparable).
    051 * @param <C>  the column key (must implement Comparable).
    052 * @param <V>  the value type.
    053 */
    054@SuppressWarnings("serial")
    055public final class DefaultKeyedValues3D<S extends Comparable<S>, R extends Comparable<R>, C extends Comparable<C>, V>
    056        implements KeyedValues3D<S, R, C, V>, Serializable {
    057
    058    /** The series keys. */
    059    private List<S> seriesKeys;
    060
    061    /** The row keys. */
    062    private List<R> rowKeys;
    063
    064    /** The column keys. */
    065    private List<C> columnKeys;
    066
    067    /**
    068     * The data, one entry per series.  Each series *must* contain the same
    069     * row and column keys.
    070     */
    071    private List<DefaultKeyedValues2D<R, C, V>> data; // one entry per series
    072
    073    /**
    074     * Creates a new (empty) table.
    075     */
    076    public DefaultKeyedValues3D() {
    077        this.seriesKeys = new ArrayList<S>();
    078        this.rowKeys = new ArrayList<R>();
    079        this.columnKeys = new ArrayList<C>();
    080        this.data = new ArrayList<DefaultKeyedValues2D<R, C, V>>();
    081    }
    082
    083    /**
    084     * Returns the series key with the specified index.
    085     *
    086     * @param seriesIndex  the series index.
    087     *
    088     * @return The series key.
    089     */
    090    @Override
    091    public S getSeriesKey(int seriesIndex) {
    092        return this.seriesKeys.get(seriesIndex);
    093    }
    094
    095    /**
    096     * Returns the row key with the specified index.
    097     *
    098     * @param rowIndex  the row index.
    099     *
    100     * @return The row key.
    101     */
    102    @Override
    103    public R getRowKey(int rowIndex) {
    104        return this.rowKeys.get(rowIndex);
    105    }
    106
    107    /**
    108     * Returns the column key with the specified index.
    109     *
    110     * @param columnIndex  the column index.
    111     *
    112     * @return The column key.
    113     */
    114    @Override
    115    public C getColumnKey(int columnIndex) {
    116        return this.columnKeys.get(columnIndex);
    117    }
    118
    119    /**
    120     * Returns the index for the specified series key, or {@code -1} if
    121     * the key is not present in this data structure.
    122     *
    123     * @param seriesKey  the series key ({@code null} not permitted).
    124     *
    125     * @return The series index or {@code -1}.
    126     */
    127    @Override
    128    public int getSeriesIndex(S seriesKey) {
    129        ArgChecks.nullNotPermitted(seriesKey, "seriesKey");
    130        return this.seriesKeys.indexOf(seriesKey);
    131    }
    132
    133    /**
    134     * Returns the index for the specified row key, or {@code -1} if
    135     * the key is not present in this data structure.
    136     *
    137     * @param rowKey  the row key ({@code null} not permitted).
    138     *
    139     * @return The row index or {@code -1}.
    140     */
    141    @Override
    142    public int getRowIndex(R rowKey) {
    143        ArgChecks.nullNotPermitted(rowKey, "rowKey");
    144        return this.rowKeys.indexOf(rowKey);
    145    }
    146
    147    /**
    148     * Returns the index for the specified column key, or {@code -1} if
    149     * the key is not present in this data structure.
    150     *
    151     * @param columnKey  the column key ({@code null} not permitted).
    152     *
    153     * @return The column index or {@code -1}.
    154     */
    155    @Override
    156    public int getColumnIndex(C columnKey) {
    157        ArgChecks.nullNotPermitted(columnKey, "columnKey");
    158        return this.columnKeys.indexOf(columnKey);
    159    }
    160
    161    /**
    162     * Returns a list of the series keys for the data.  Modifying this
    163     * list will have no impact on the underlying data.
    164     *
    165     * @return A list of the series keys (possibly empty, but never
    166     *     {@code null}).
    167     */
    168    @Override
    169    public List<S> getSeriesKeys() {
    170        return new ArrayList<S>(this.seriesKeys);
    171    }
    172
    173    /**
    174     * Returns a list of the row keys for the data.  Modifying this
    175     * list will have no impact on the underlying data.
    176     *
    177     * @return A list of the row keys (possibly empty, but never
    178     *     {@code null}).
    179     */
    180    @Override
    181    public List<R> getRowKeys() {
    182        return new ArrayList<R>(this.rowKeys);
    183    }
    184
    185    /**
    186     * Returns a list of the column keys for the data.  Modifying this
    187     * list will have no impact on the underlying data.
    188     *
    189     * @return A list of the column keys (possibly empty, but never
    190     *     {@code null}).
    191     */
    192    @Override
    193    public List<C> getColumnKeys() {
    194        return new ArrayList<C>(this.columnKeys);
    195    }
    196
    197    @Override
    198    public int getSeriesCount() {
    199        return this.seriesKeys.size();
    200    }
    201
    202    @Override
    203    public int getRowCount() {
    204        return this.rowKeys.size();
    205    }
    206
    207    @Override
    208    public int getColumnCount() {
    209        return this.columnKeys.size();
    210    }
    211
    212    @Override
    213    public V getValue(int seriesIndex, int rowIndex, int columnIndex) {
    214        return this.data.get(seriesIndex).getValue(rowIndex, columnIndex);
    215    }
    216
    217    /**
    218     * Returns the value for the specified data item.  This method will
    219     * throw an {@code IllegalArgumentException} if the dataset does not
    220     * contain the specified keys.
    221     *
    222     * @param seriesKey  the series key ({@code null} not permitted).
    223     * @param rowKey  the row key ({@code null} not permitted).
    224     * @param columnKey  the column key ({@code null} not permitted).
    225     *
    226     * @return The value (possibly {@code null}).
    227     */
    228    @Override
    229    public V getValue(S seriesKey, R rowKey, C columnKey) {
    230        int seriesIndex = getSeriesIndex(seriesKey);
    231        if (seriesIndex < 0) {
    232            throw new IllegalArgumentException("Series '" + seriesKey.toString()
    233                    + "' is not found.");
    234        }
    235        int rowIndex = getRowIndex(rowKey);
    236        if (rowIndex < 0) {
    237            throw new IllegalArgumentException("Row key '" + rowKey.toString()
    238                    + "' is not found.");
    239        }
    240        int columnIndex = getColumnIndex(columnKey);
    241        if (columnIndex < 0) {
    242            throw new IllegalArgumentException("Column key '"
    243                    + columnKey.toString() + "' is not found.");
    244        }
    245        return getValue(seriesIndex, rowIndex, columnIndex);
    246    }
    247
    248    @Override
    249    public double getDoubleValue(int seriesIndex, int rowIndex,
    250            int columnIndex) {
    251        V n = getValue(seriesIndex, rowIndex, columnIndex);
    252        if (n != null && n instanceof Number) {
    253            return ((Number) n).doubleValue();
    254        }
    255        return Double.NaN;
    256    }
    257
    258    /**
    259     * Sets the value for an item in a series, overwriting any existing value.
    260     *
    261     * @param n  the value ({@code null} permitted).
    262     * @param seriesKey  the series key ({@code null} not permitted).
    263     * @param rowKey  the row key ({@code null} not permitted).
    264     * @param columnKey  the column key ({@code null} not permitted).
    265     */
    266    public void setValue(V n, S seriesKey, R rowKey, C columnKey) {
    267
    268        ArgChecks.nullNotPermitted(seriesKey, "seriesKey");
    269        ArgChecks.nullNotPermitted(rowKey, "rowKey");
    270        ArgChecks.nullNotPermitted(columnKey, "columnKey");
    271
    272        // cases:
    273        // 1 - the dataset is empty, so we just need to add a new layer with the
    274        //     given keys;
    275        if (this.data.isEmpty()) {
    276            this.seriesKeys.add(seriesKey);
    277            this.rowKeys.add(rowKey);
    278            this.columnKeys.add(columnKey);
    279            DefaultKeyedValues2D<R, C, V> d
    280                    = new DefaultKeyedValues2D<R, C, V>();
    281            d.setValue(n, rowKey, columnKey);
    282            this.data.add(d);
    283        }
    284
    285        int seriesIndex = getSeriesIndex(seriesKey);
    286        int rowIndex = getRowIndex(rowKey);
    287        int columnIndex = getColumnIndex(columnKey);
    288        if (rowIndex < 0) {
    289            this.rowKeys.add(rowKey);
    290        }
    291        if (columnIndex < 0) {
    292            this.columnKeys.add(columnKey);
    293        }
    294        if (rowIndex < 0 || columnIndex < 0) {
    295            for (DefaultKeyedValues2D<R, C, V> d : this.data) {
    296                d.setValue(null, rowKey, columnKey);
    297            }
    298        }
    299        if (seriesIndex >= 0) {
    300            DefaultKeyedValues2D<R, C, V> d = this.data.get(seriesIndex);
    301            d.setValue(n, rowKey, columnKey);
    302        } else {
    303            this.seriesKeys.add(seriesKey);
    304            DefaultKeyedValues2D<R, C, V> d
    305                    = new DefaultKeyedValues2D<R, C, V>(this.rowKeys,
    306                    this.columnKeys);
    307            d.setValue(n, rowKey, columnKey);
    308            this.data.add(d);
    309        }
    310    }
    311
    312    /**
    313     * Tests this instance for equality with an arbitrary object.
    314     *
    315     * @param obj  the object ({@code null} permitted).
    316     *
    317     * @return A boolean.
    318     */
    319    @Override
    320    public boolean equals(Object obj) {
    321        if (obj == this) {
    322            return true;
    323        }
    324        if (!(obj instanceof DefaultKeyedValues3D)) {
    325            return false;
    326        }
    327        DefaultKeyedValues3D that = (DefaultKeyedValues3D) obj;
    328        if (!this.seriesKeys.equals(that.seriesKeys)) {
    329            return false;
    330        }
    331        if (!this.rowKeys.equals(that.rowKeys)) {
    332            return false;
    333        }
    334        if (!this.columnKeys.equals(that.columnKeys)) {
    335            return false;
    336        }
    337        if (!this.data.equals(that.data)) {
    338            return false;
    339        }
    340        return true;
    341    }
    342
    343}