    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@SuppressWarnings("serial")
    050public final class DefaultKeyedValues3D<V> implements KeyedValues3D<V>,
    051        Serializable {
    052
    053    /** The series keys. */
    054    private List<Comparable<?>> seriesKeys;
    055
    056    /** The row keys. */
    057    private List<Comparable<?>> rowKeys;
    058
    059    /** The column keys. */
    060    private List<Comparable<?>> columnKeys;
    061
    062    /**
    063     * The data, one entry per series.  Each series *must* contain the same
    064     * row and column keys.
    065     */
    066    private List<DefaultKeyedValues2D<V>> data; // one entry per series
    067
    068    /**
    069     * Creates a new (empty) table.
    070     */
    071    public DefaultKeyedValues3D() {
    072        this.seriesKeys = new ArrayList<Comparable<?>>();
    073        this.rowKeys = new ArrayList<Comparable<?>>();
    074        this.columnKeys = new ArrayList<Comparable<?>>();
    075        this.data = new ArrayList<DefaultKeyedValues2D<V>>();
    076    }
    077
    078    /**
    079     * Returns the series key with the specified index.
    080     *
    081     * @param seriesIndex  the series index.
    082     *
    083     * @return The series key.
    084     */
    085    @Override
    086    public Comparable<?> getSeriesKey(int seriesIndex) {
    087        return this.seriesKeys.get(seriesIndex);
    088    }
    089
    090    /**
    091     * Returns the row key with the specified index.
    092     *
    093     * @param rowIndex  the row index.
    094     *
    095     * @return The row key.
    096     */
    097    @Override
    098    public Comparable<?> getRowKey(int rowIndex) {
    099        return this.rowKeys.get(rowIndex);
    100    }
    101
    102    /**
    103     * Returns the column key with the specified index.
    104     *
    105     * @param columnIndex  the column index.
    106     *
    107     * @return The column key.
    108     */
    109    @Override
    110    public Comparable<?> getColumnKey(int columnIndex) {
    111        return this.columnKeys.get(columnIndex);
    112    }
    113
    114    /**
    115     * Returns the index for the specified series key, or {@code -1} if
    116     * the key is not present in this data structure.
    117     *
    118     * @param seriesKey  the series key ({@code null} not permitted).
    119     *
    120     * @return The series index or {@code -1}.
    121     */
    122    @Override
    123    public int getSeriesIndex(Comparable<?> seriesKey) {
    124        ArgChecks.nullNotPermitted(seriesKey, "seriesKey");
    125        return this.seriesKeys.indexOf(seriesKey);
    126    }
    127
    128    /**
    129     * Returns the index for the specified row key, or {@code -1} if
    130     * the key is not present in this data structure.
    131     *
    132     * @param rowKey  the row key ({@code null} not permitted).
    133     *
    134     * @return The row index or {@code -1}.
    135     */
    136    @Override
    137    public int getRowIndex(Comparable<?> rowKey) {
    138        ArgChecks.nullNotPermitted(rowKey, "rowKey");
    139        return this.rowKeys.indexOf(rowKey);
    140    }
    141
    142    /**
    143     * Returns the index for the specified column key, or {@code -1} if
    144     * the key is not present in this data structure.
    145     *
    146     * @param columnKey  the column key ({@code null} not permitted).
    147     *
    148     * @return The column index or {@code -1}.
    149     */
    150    @Override
    151    public int getColumnIndex(Comparable<?> columnKey) {
    152        ArgChecks.nullNotPermitted(columnKey, "columnKey");
    153        return this.columnKeys.indexOf(columnKey);
    154    }
    155
    156    /**
    157     * Returns a list of the series keys for the data.  Modifying this
    158     * list will have no impact on the underlying data.
    159     *
    160     * @return A list of the series keys (possibly empty, but never
    161     *     {@code null}).
    162     */
    163    @Override
    164    public List<Comparable<?>> getSeriesKeys() {
    165        return new ArrayList<Comparable<?>>(this.seriesKeys);
    166    }
    167
    168    /**
    169     * Returns a list of the row keys for the data.  Modifying this
    170     * list will have no impact on the underlying data.
    171     *
    172     * @return A list of the row keys (possibly empty, but never
    173     *     {@code null}).
    174     */
    175    @Override
    176    public List<Comparable<?>> getRowKeys() {
    177        return new ArrayList<Comparable<?>>(this.rowKeys);
    178    }
    179
    180    /**
    181     * Returns a list of the column keys for the data.  Modifying this
    182     * list will have no impact on the underlying data.
    183     *
    184     * @return A list of the column keys (possibly empty, but never
    185     *     {@code null}).
    186     */
    187    @Override
    188    public List<Comparable<?>> getColumnKeys() {
    189        return new ArrayList<Comparable<?>>(this.columnKeys);
    190    }
    191
    192    @Override
    193    public int getSeriesCount() {
    194        return this.seriesKeys.size();
    195    }
    196
    197    @Override
    198    public int getRowCount() {
    199        return this.rowKeys.size();
    200    }
    201
    202    @Override
    203    public int getColumnCount() {
    204        return this.columnKeys.size();
    205    }
    206
    207    @Override
    208    public V getValue(int seriesIndex, int rowIndex, int columnIndex) {
    209        return this.data.get(seriesIndex).getValue(rowIndex, columnIndex);
    210    }
    211
    212    /**
    213     * Returns the value for the specified data item.  This method will
    214     * throw an {@code IllegalArgumentException} if the dataset does not
    215     * contain the specified keys.
    216     *
    217     * @param seriesKey  the series key ({@code null} not permitted).
    218     * @param rowKey  the row key ({@code null} not permitted).
    219     * @param columnKey  the column key ({@code null} not permitted).
    220     *
    221     * @return The value (possibly {@code null}).
    222     */
    223    @Override
    224    public V getValue(Comparable<?> seriesKey, Comparable<?> rowKey,
    225            Comparable<?> columnKey) {
    226        int seriesIndex = getSeriesIndex(seriesKey);
    227        if (seriesIndex < 0) {
    228            throw new IllegalArgumentException("Series '" + seriesKey.toString()
    229                    + "' is not found.");
    230        }
    231        int rowIndex = getRowIndex(rowKey);
    232        if (rowIndex < 0) {
    233            throw new IllegalArgumentException("Row key '" + rowKey.toString()
    234                    + "' is not found.");
    235        }
    236        int columnIndex = getColumnIndex(columnKey);
    237        if (columnIndex < 0) {
    238            throw new IllegalArgumentException("Column key '"
    239                    + columnKey.toString() + "' is not found.");
    240        }
    241        return getValue(seriesIndex, rowIndex, columnIndex);
    242    }
    243
    244    @Override
    245    public double getDoubleValue(int seriesIndex, int rowIndex, int columnIndex) {
    246        V n = getValue(seriesIndex, rowIndex, columnIndex);
    247        if (n != null && n instanceof Number) {
    248            return ((Number) n).doubleValue();
    249        }
    250        return Double.NaN;
    251    }
    252
    253    /**
    254     * Sets the value for an item in a series, overwriting any existing value.
    255     *
    256     * @param n  the value ({@code null} permitted).
    257     * @param seriesKey  the series key ({@code null} not permitted).
    258     * @param rowKey  the row key ({@code null} not permitted).
    259     * @param columnKey  the column key ({@code null} not permitted).
    260     */
    261    public void setValue(V n, Comparable<?> seriesKey, Comparable<?> rowKey,
    262            Comparable<?> columnKey) {
    263
    264        ArgChecks.nullNotPermitted(seriesKey, "seriesKey");
    265        ArgChecks.nullNotPermitted(rowKey, "rowKey");
    266        ArgChecks.nullNotPermitted(columnKey, "columnKey");
    267
    268        // cases:
    269        // 1 - the dataset is empty, so we just need to add a new layer with the
    270        //     given keys;
    271        if (this.data.isEmpty()) {
    272            this.seriesKeys.add(seriesKey);
    273            this.rowKeys.add(rowKey);
    274            this.columnKeys.add(columnKey);
    275            DefaultKeyedValues2D<V> d = new DefaultKeyedValues2D<V>();
    276            d.setValue(n, rowKey, columnKey);
    277            this.data.add(d);
    278        }
    279
    280        int seriesIndex = getSeriesIndex(seriesKey);
    281        int rowIndex = getRowIndex(rowKey);
    282        int columnIndex = getColumnIndex(columnKey);
    283        if (rowIndex < 0) {
    284            this.rowKeys.add(rowKey);
    285        }
    286        if (columnIndex < 0) {
    287            this.columnKeys.add(columnKey);
    288        }
    289        if (rowIndex < 0 || columnIndex < 0) {
    290            for (DefaultKeyedValues2D<V> d : this.data) {
    291                d.setValue(null, rowKey, columnKey);
    292            }
    293        }
    294        if (seriesIndex >= 0) {
    295            DefaultKeyedValues2D<V> d = this.data.get(seriesIndex);
    296            d.setValue(n, rowKey, columnKey);
    297        } else {
    298            this.seriesKeys.add(seriesKey);
    299            DefaultKeyedValues2D<V> d = new DefaultKeyedValues2D<V>(this.rowKeys,
    300                    this.columnKeys);
    301            d.setValue(n, rowKey, columnKey);
    302            this.data.add(d);
    303        }
    304    }
    305
    306    /**
    307     * Tests this instance for equality with an arbitrary object.
    308     *
    309     * @param obj  the object ({@code null} permitted).
    310     *
    311     * @return A boolean.
    312     */
    313    @Override
    314    public boolean equals(Object obj) {
    315        if (obj == this) {
    316            return true;
    317        }
    318        if (!(obj instanceof DefaultKeyedValues3D<?>)) {
    319            return false;
    320        }
    321        DefaultKeyedValues3D<?> that = (DefaultKeyedValues3D<?>) obj;
    322        if (!this.seriesKeys.equals(that.seriesKeys)) {
    323            return false;
    324        }
    325        if (!this.rowKeys.equals(that.rowKeys)) {
    326            return false;
    327        }
    328        if (!this.columnKeys.equals(that.columnKeys)) {
    329            return false;
    330        }
    331        if (!this.data.equals(that.data)) {
    332            return false;
    333        }
    334        return true;
    335    }
    336
    337}