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}