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