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}