/* =========================================================== * Orson Charts : a 3D chart library for the Java(tm) platform * =========================================================== * * (C)opyright 2013-2016, by Object Refinery Limited. All rights reserved. * * http://www.object-refinery.com/orsoncharts/index.html * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * If you do not wish to be bound by the terms of the GPL, an alternative * commercial license can be purchased. For details, please see visit the * Orson Charts home page: * * http://www.object-refinery.com/orsoncharts/index.html * */ package com.orsoncharts.data; import java.io.Serializable; import java.util.ArrayList; import java.util.List; import com.orsoncharts.util.ArgChecks; /** * A two dimensional grid of (typically numerical) data that is accessible by * row and column keys. * <br><br> * NOTE: This class is serializable, but the serialization format is subject * to change in future releases and should not be relied upon for persisting * instances of this class. * * @param <R> the row key type * @param <C> the column key type * @param <T> the value type. * */ @SuppressWarnings("serial") public final class DefaultKeyedValues2D<R extends Comparable<R>, C extends Comparable<C>, T> implements KeyedValues2D<R, C, T>, Serializable { /** The row keys. */ List<R> rowKeys; /** The column keys. */ List<C> columnKeys; /** The data values. */ List<DefaultKeyedValues<C, T>> data; // one entry per row key /** * Creates a new (empty) instance. */ public DefaultKeyedValues2D() { this(new ArrayList<R>(), new ArrayList<C>()); } /** * Creates a new instance with the specified keys and all data values * initialized to {@code null}. * * @param rowKeys the xKeys ({@code null} not permitted). * @param columnKeys the yKeys ({@code null} not permitted). */ public DefaultKeyedValues2D(List<R> rowKeys, List<C> columnKeys) { ArgChecks.nullNotPermitted(rowKeys, "rowKeys"); ArgChecks.nullNotPermitted(columnKeys, "columnKeys"); this.rowKeys = new ArrayList<R>(rowKeys); this.columnKeys = new ArrayList<C>(columnKeys); this.data = new ArrayList<DefaultKeyedValues<C, T>>(); for (int i = 0; i < rowKeys.size(); i++) { this.data.add(new DefaultKeyedValues<C, T>(columnKeys)); } } /** * Returns the row key corresponding to the specified index. * * @param rowIndex the row index. * * @return The key. */ @Override public R getRowKey(int rowIndex) { return this.rowKeys.get(rowIndex); } /** * Returns the column key corresponding to the specified index. * * @param columnIndex the column index. * * @return The key. */ @Override public C getColumnKey(int columnIndex) { return this.columnKeys.get(columnIndex); } /** * Returns the index corresponding to the specified row key. * * @param rowKey the row key ({@code null} not permitted). * * @return The index. */ @Override public int getRowIndex(R rowKey) { ArgChecks.nullNotPermitted(rowKey, "rowKey"); return this.rowKeys.indexOf(rowKey); } /** * Returns the index corresponding to the specified column key. * * @param columnKey the column key ({@code null} not permitted). * * @return The index. */ @Override public int getColumnIndex(C columnKey) { ArgChecks.nullNotPermitted(columnKey, "columnKey"); return this.columnKeys.indexOf(columnKey); } /** * Returns a copy of the list of row keys. * * @return A copy of the list of row keys (never {@code null}). */ @Override public List<R> getRowKeys() { return new ArrayList<R>(this.rowKeys); } /** * Returns a copy of the list of column keys. * * @return A copy of the list of column keys (never {@code null}). */ @Override public List<C> getColumnKeys() { return new ArrayList<C>(this.columnKeys); } /** * Returns the number of row keys in the table. * * @return The number of row keys in the table. */ @Override public int getRowCount() { return this.rowKeys.size(); } /** * Returns the number of column keys in the data structure. * * @return The number of column keys. */ @Override public int getColumnCount() { return this.columnKeys.size(); } /** * Returns a value from one cell in the table. * * @param rowKey the row-key ({@code null} not permitted). * @param columnKey the column-key ({@code null} not permitted). * * @return The value (possibly {@code null}). */ @Override public T getValue(R rowKey, C columnKey) { // arg checking is handled in getXIndex() and getYIndex() int rowIndex = getRowIndex(rowKey); int columnIndex = getColumnIndex(columnKey); return getValue(rowIndex, columnIndex); } /** * Returns the value from one cell in the table. * * @param rowIndex the row index. * @param columnIndex the column index. * * @return The value (possibly {@code null}). */ @Override public T getValue(int rowIndex, int columnIndex) { return this.data.get(rowIndex).getValue(columnIndex); } /** * Returns the data item at the specified position as a double primitive. * Where the {@link #getValue(int, int)} method returns {@code null}, * this method returns {@code Double.NaN}. * * @param rowIndex the row index. * @param columnIndex the column index. * * @return The data value. */ @Override public double getDoubleValue(int rowIndex, int columnIndex) { T n = getValue(rowIndex, columnIndex); if (n != null && n instanceof Number) { return ((Number) n).doubleValue(); } return Double.NaN; } /** * Sets a value for one cell in the table. * * @param n the value ({@code null} permitted). * @param rowKey the row key ({@code null} not permitted). * @param columnKey the column key ({@code null} not permitted). */ public void setValue(T n, R rowKey, C columnKey) { ArgChecks.nullNotPermitted(rowKey, "rowKey"); ArgChecks.nullNotPermitted(columnKey, "columnKey"); if (this.data.isEmpty()) { // 1. no data - just add one new entry this.rowKeys.add(rowKey); this.columnKeys.add(columnKey); DefaultKeyedValues<C, T> dkvs = new DefaultKeyedValues<C, T>(); dkvs.put(columnKey, n); this.data.add(dkvs); } else { int rowIndex = getRowIndex(rowKey); int columnIndex = getColumnIndex(columnKey); if (rowIndex >= 0) { DefaultKeyedValues<C, T> dkvs = this.data.get(rowIndex); if (columnIndex >= 0) { // 2. Both keys exist - just update the value dkvs.put(columnKey, n); } else { // 3. rowKey exists, but columnKey does not (add the // columnKey to each series) this.columnKeys.add(columnKey); for (DefaultKeyedValues<C, T> kv : this.data) { kv.put(columnKey, null); } dkvs.put(columnKey, n); } } else { if (columnIndex >= 0) { // 4. rowKey does not exist, but columnKey does this.rowKeys.add(rowKey); DefaultKeyedValues<C, T> d = new DefaultKeyedValues<C, T>( this.columnKeys); d.put(columnKey, n); this.data.add(d); } else { // 5. neither key exists, need to create the new series, // plus the new entry in every series this.rowKeys.add(rowKey); this.columnKeys.add(columnKey); for (DefaultKeyedValues<C, T> kv : this.data) { kv.put(columnKey, null); } DefaultKeyedValues<C, T> d = new DefaultKeyedValues<C, T>( this.columnKeys); d.put(columnKey, n); this.data.add(d); } } } } @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof DefaultKeyedValues2D)) { return false; } DefaultKeyedValues2D that = (DefaultKeyedValues2D) obj; if (!this.rowKeys.equals(that.rowKeys)) { return false; } if (!this.columnKeys.equals(that.columnKeys)) { return false; } if (!this.data.equals(that.data)) { return false; } return true; } }