001/* ===========================================================
002 * Orson Charts : a 3D chart library for the Java(tm) platform
003 * ===========================================================
004 * 
005 * (C)opyright 2013-2022, by David Gilbert.  All rights reserved.
006 * 
007 * https://github.com/jfree/orson-charts
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 org.jfree.chart3d.data;
034
035import java.io.Serializable;
036import java.util.ArrayList;
037import java.util.List;
038
039import org.jfree.chart3d.internal.Args;
040
041/**
042 * A two dimensional grid of (typically numerical) data that is accessible by 
043 * row and column keys.
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 <R> the row key type
050 * @param <C> the column key type
051 * @param <T> the value type.
052 * 
053 */
054@SuppressWarnings("serial")
055public final class DefaultKeyedValues2D<R extends Comparable<R>, C extends Comparable<C>, T> 
056        implements KeyedValues2D<R, C, T>, Serializable {
057
058    /** The row keys. */
059    List<R> rowKeys;
060    
061    /** The column keys. */
062    List<C> columnKeys;
063    
064    /** The data values. */
065    List<DefaultKeyedValues<C, T>> data;  // one entry per row key
066  
067    /**
068     * Creates a new (empty) instance.
069     */
070    public DefaultKeyedValues2D() {
071        this(new ArrayList<>(), new ArrayList<>());
072    }
073    
074    /**
075     * Creates a new instance with the specified keys and all data values 
076     * initialized to {@code null}.
077     * 
078     * @param rowKeys  the xKeys ({@code null} not permitted).
079     * @param columnKeys  the yKeys ({@code null} not permitted).
080     */
081    public DefaultKeyedValues2D(List<R> rowKeys, List<C> columnKeys) {
082        Args.nullNotPermitted(rowKeys, "rowKeys");
083        Args.nullNotPermitted(columnKeys, "columnKeys");
084        this.rowKeys = new ArrayList<>(rowKeys);
085        this.columnKeys = new ArrayList<>(columnKeys);
086        this.data = new ArrayList<>();    
087        for (int i = 0; i < rowKeys.size(); i++) {
088            this.data.add(new DefaultKeyedValues<>(columnKeys));
089        }
090    }
091
092    /**
093     * Returns the row key corresponding to the specified index.
094     * 
095     * @param rowIndex  the row index.
096     * 
097     * @return The key. 
098     */
099    @Override
100    public R getRowKey(int rowIndex) {
101        return this.rowKeys.get(rowIndex);
102    }
103
104    /**
105     * Returns the column key corresponding to the specified index.
106     * 
107     * @param columnIndex  the column index.
108     * 
109     * @return The key. 
110     */
111    @Override
112    public C getColumnKey(int columnIndex) {
113        return this.columnKeys.get(columnIndex);
114    }
115
116    /**
117     * Returns the index corresponding to the specified row key.
118     * 
119     * @param rowKey  the row key ({@code null} not permitted).
120     * 
121     * @return The index. 
122     */
123    @Override
124    public int getRowIndex(R rowKey) {
125        Args.nullNotPermitted(rowKey, "rowKey");
126        return this.rowKeys.indexOf(rowKey);
127    }
128
129    /**
130     * Returns the index corresponding to the specified column key.
131     * 
132     * @param columnKey  the column key ({@code null} not permitted).
133     * 
134     * @return The index. 
135     */
136    @Override
137    public int getColumnIndex(C columnKey) {
138        Args.nullNotPermitted(columnKey, "columnKey");
139        return this.columnKeys.indexOf(columnKey);
140    }
141
142    /**
143     * Returns a copy of the list of row keys.
144     * 
145     * @return A copy of the list of row keys (never {@code null}). 
146     */
147    @Override
148    public List<R> getRowKeys() {
149        return new ArrayList<>(this.rowKeys);
150    }
151
152    /**
153     * Returns a copy of the list of column keys.
154     * 
155     * @return A copy of the list of column keys (never {@code null}). 
156     */
157    @Override
158    public List<C> getColumnKeys() {
159        return new ArrayList<>(this.columnKeys);
160    }
161
162    /**
163     * Returns the number of row keys in the table.
164     * 
165     * @return The number of row keys in the table.
166     */
167    @Override
168    public int getRowCount() {
169        return this.rowKeys.size();
170    }
171    
172    /**
173     * Returns the number of column keys in the data structure.
174     * 
175     * @return The number of column keys.
176     */
177    @Override
178    public int getColumnCount() {
179        return this.columnKeys.size();
180    }
181
182    /**
183     * Returns a value from one cell in the table.
184     * 
185     * @param rowKey  the row-key ({@code null} not permitted).
186     * @param columnKey  the column-key ({@code null} not permitted).
187     * 
188     * @return The value (possibly {@code null}).
189     */
190    @Override
191    public T getValue(R rowKey, C columnKey) {
192        // arg checking is handled in getXIndex() and getYIndex()
193        int rowIndex = getRowIndex(rowKey);
194        int columnIndex = getColumnIndex(columnKey);
195        return getValue(rowIndex, columnIndex);
196    }
197
198    /**
199     * Returns the value from one cell in the table.
200     * 
201     * @param rowIndex  the row index.
202     * @param columnIndex  the column index.
203     * 
204     * @return The value (possibly {@code null}). 
205     */
206    @Override
207    public T getValue(int rowIndex, int columnIndex) {
208        return this.data.get(rowIndex).getValue(columnIndex);
209    }
210
211    /**
212     * Returns the data item at the specified position as a double primitive.
213     * Where the {@link #getValue(int, int)} method returns {@code null}, 
214     * this method returns {@code Double.NaN}.
215     * 
216     * @param rowIndex  the row index.
217     * @param columnIndex  the column index.
218     * 
219     * @return The data value.
220     */
221    @Override
222    public double getDoubleValue(int rowIndex, int columnIndex) {
223        T n = getValue(rowIndex, columnIndex);
224        if (n != null && n instanceof Number) {
225            return ((Number) n).doubleValue();
226        }
227        return Double.NaN;
228    } 
229
230    /**
231     * Sets a value for one cell in the table.
232     * 
233     * @param n  the value ({@code null} permitted).
234     * @param rowKey  the row key ({@code null} not permitted).
235     * @param columnKey  the column key ({@code null} not permitted).
236     */
237    public void setValue(T n, R rowKey, C columnKey) {
238        Args.nullNotPermitted(rowKey, "rowKey");
239        Args.nullNotPermitted(columnKey, "columnKey");
240        
241        if (this.data.isEmpty()) {  // 1. no data - just add one new entry
242            this.rowKeys.add(rowKey);
243            this.columnKeys.add(columnKey);
244            DefaultKeyedValues<C, T> dkvs = new DefaultKeyedValues<>();
245            dkvs.put(columnKey, n);
246            this.data.add(dkvs);
247        } else {
248            int rowIndex = getRowIndex(rowKey);
249            int columnIndex = getColumnIndex(columnKey);
250            if (rowIndex >= 0) {
251                DefaultKeyedValues<C, T> dkvs = this.data.get(rowIndex);
252                if (columnIndex >= 0) {
253                    // 2.  Both keys exist - just update the value
254                    dkvs.put(columnKey, n);
255                } else {
256                    // 3.  rowKey exists, but columnKey does not (add the 
257                    //     columnKey to each series)
258                    this.columnKeys.add(columnKey);
259                    for (DefaultKeyedValues<C, T> kv : this.data) {
260                        kv.put(columnKey, null);
261                    }
262                    dkvs.put(columnKey, n);
263                }
264            } else {
265                if (columnIndex >= 0) {
266                    // 4.  rowKey does not exist, but columnKey does
267                    this.rowKeys.add(rowKey);
268                    DefaultKeyedValues<C, T> d = new DefaultKeyedValues<>(
269                            this.columnKeys);
270                    d.put(columnKey, n);
271                    this.data.add(d);
272                } else {
273                    // 5.  neither key exists, need to create the new series, 
274                    //     plus the new entry in every series
275                    this.rowKeys.add(rowKey);
276                    this.columnKeys.add(columnKey);
277                    for (DefaultKeyedValues<C, T> kv : this.data) {
278                        kv.put(columnKey, null);
279                    }
280                    DefaultKeyedValues<C, T> d = new DefaultKeyedValues<>(
281                            this.columnKeys);
282                    d.put(columnKey, n);
283                    this.data.add(d);
284                }
285            }
286        }
287    }
288    
289    @Override
290    public boolean equals(Object obj) {
291        if (obj == this) {
292            return true;
293        }
294        if (!(obj instanceof DefaultKeyedValues2D)) {
295            return false;
296        }
297        DefaultKeyedValues2D that = (DefaultKeyedValues2D) obj;
298        if (!this.rowKeys.equals(that.rowKeys)) {
299            return false;
300        }
301        if (!this.columnKeys.equals(that.columnKeys)) {
302            return false;
303        }
304        if (!this.data.equals(that.data)) {
305            return false;
306        }
307        return true;
308    }
309}