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 * @param <S> the series key (must implement Comparable). 050 * @param <R> the row key (must implement Comparable). 051 * @param <C> the column key (must implement Comparable). 052 * @param <V> the value type. 053 */ 054@SuppressWarnings("serial") 055public final class DefaultKeyedValues3D<S extends Comparable<S>, R extends Comparable<R>, C extends Comparable<C>, V> 056 implements KeyedValues3D<S, R, C, V>, Serializable { 057 058 /** The series keys. */ 059 private List<S> seriesKeys; 060 061 /** The row keys. */ 062 private List<R> rowKeys; 063 064 /** The column keys. */ 065 private List<C> columnKeys; 066 067 /** 068 * The data, one entry per series. Each series *must* contain the same 069 * row and column keys. 070 */ 071 private List<DefaultKeyedValues2D<R, C, V>> data; // one entry per series 072 073 /** 074 * Creates a new (empty) table. 075 */ 076 public DefaultKeyedValues3D() { 077 this.seriesKeys = new ArrayList<S>(); 078 this.rowKeys = new ArrayList<R>(); 079 this.columnKeys = new ArrayList<C>(); 080 this.data = new ArrayList<DefaultKeyedValues2D<R, C, V>>(); 081 } 082 083 /** 084 * Returns the series key with the specified index. 085 * 086 * @param seriesIndex the series index. 087 * 088 * @return The series key. 089 */ 090 @Override 091 public S getSeriesKey(int seriesIndex) { 092 return this.seriesKeys.get(seriesIndex); 093 } 094 095 /** 096 * Returns the row key with the specified index. 097 * 098 * @param rowIndex the row index. 099 * 100 * @return The row key. 101 */ 102 @Override 103 public R getRowKey(int rowIndex) { 104 return this.rowKeys.get(rowIndex); 105 } 106 107 /** 108 * Returns the column key with the specified index. 109 * 110 * @param columnIndex the column index. 111 * 112 * @return The column key. 113 */ 114 @Override 115 public C getColumnKey(int columnIndex) { 116 return this.columnKeys.get(columnIndex); 117 } 118 119 /** 120 * Returns the index for the specified series key, or {@code -1} if 121 * the key is not present in this data structure. 122 * 123 * @param seriesKey the series key ({@code null} not permitted). 124 * 125 * @return The series index or {@code -1}. 126 */ 127 @Override 128 public int getSeriesIndex(S seriesKey) { 129 ArgChecks.nullNotPermitted(seriesKey, "seriesKey"); 130 return this.seriesKeys.indexOf(seriesKey); 131 } 132 133 /** 134 * Returns the index for the specified row key, or {@code -1} if 135 * the key is not present in this data structure. 136 * 137 * @param rowKey the row key ({@code null} not permitted). 138 * 139 * @return The row index or {@code -1}. 140 */ 141 @Override 142 public int getRowIndex(R rowKey) { 143 ArgChecks.nullNotPermitted(rowKey, "rowKey"); 144 return this.rowKeys.indexOf(rowKey); 145 } 146 147 /** 148 * Returns the index for the specified column key, or {@code -1} if 149 * the key is not present in this data structure. 150 * 151 * @param columnKey the column key ({@code null} not permitted). 152 * 153 * @return The column index or {@code -1}. 154 */ 155 @Override 156 public int getColumnIndex(C columnKey) { 157 ArgChecks.nullNotPermitted(columnKey, "columnKey"); 158 return this.columnKeys.indexOf(columnKey); 159 } 160 161 /** 162 * Returns a list of the series keys for the data. Modifying this 163 * list will have no impact on the underlying data. 164 * 165 * @return A list of the series keys (possibly empty, but never 166 * {@code null}). 167 */ 168 @Override 169 public List<S> getSeriesKeys() { 170 return new ArrayList<S>(this.seriesKeys); 171 } 172 173 /** 174 * Returns a list of the row keys for the data. Modifying this 175 * list will have no impact on the underlying data. 176 * 177 * @return A list of the row keys (possibly empty, but never 178 * {@code null}). 179 */ 180 @Override 181 public List<R> getRowKeys() { 182 return new ArrayList<R>(this.rowKeys); 183 } 184 185 /** 186 * Returns a list of the column keys for the data. Modifying this 187 * list will have no impact on the underlying data. 188 * 189 * @return A list of the column keys (possibly empty, but never 190 * {@code null}). 191 */ 192 @Override 193 public List<C> getColumnKeys() { 194 return new ArrayList<C>(this.columnKeys); 195 } 196 197 @Override 198 public int getSeriesCount() { 199 return this.seriesKeys.size(); 200 } 201 202 @Override 203 public int getRowCount() { 204 return this.rowKeys.size(); 205 } 206 207 @Override 208 public int getColumnCount() { 209 return this.columnKeys.size(); 210 } 211 212 @Override 213 public V getValue(int seriesIndex, int rowIndex, int columnIndex) { 214 return this.data.get(seriesIndex).getValue(rowIndex, columnIndex); 215 } 216 217 /** 218 * Returns the value for the specified data item. This method will 219 * throw an {@code IllegalArgumentException} if the dataset does not 220 * contain the specified keys. 221 * 222 * @param seriesKey the series key ({@code null} not permitted). 223 * @param rowKey the row key ({@code null} not permitted). 224 * @param columnKey the column key ({@code null} not permitted). 225 * 226 * @return The value (possibly {@code null}). 227 */ 228 @Override 229 public V getValue(S seriesKey, R rowKey, C columnKey) { 230 int seriesIndex = getSeriesIndex(seriesKey); 231 if (seriesIndex < 0) { 232 throw new IllegalArgumentException("Series '" + seriesKey.toString() 233 + "' is not found."); 234 } 235 int rowIndex = getRowIndex(rowKey); 236 if (rowIndex < 0) { 237 throw new IllegalArgumentException("Row key '" + rowKey.toString() 238 + "' is not found."); 239 } 240 int columnIndex = getColumnIndex(columnKey); 241 if (columnIndex < 0) { 242 throw new IllegalArgumentException("Column key '" 243 + columnKey.toString() + "' is not found."); 244 } 245 return getValue(seriesIndex, rowIndex, columnIndex); 246 } 247 248 @Override 249 public double getDoubleValue(int seriesIndex, int rowIndex, 250 int columnIndex) { 251 V n = getValue(seriesIndex, rowIndex, columnIndex); 252 if (n != null && n instanceof Number) { 253 return ((Number) n).doubleValue(); 254 } 255 return Double.NaN; 256 } 257 258 /** 259 * Sets the value for an item in a series, overwriting any existing value. 260 * 261 * @param n the value ({@code null} permitted). 262 * @param seriesKey the series key ({@code null} not permitted). 263 * @param rowKey the row key ({@code null} not permitted). 264 * @param columnKey the column key ({@code null} not permitted). 265 */ 266 public void setValue(V n, S seriesKey, R rowKey, C columnKey) { 267 268 ArgChecks.nullNotPermitted(seriesKey, "seriesKey"); 269 ArgChecks.nullNotPermitted(rowKey, "rowKey"); 270 ArgChecks.nullNotPermitted(columnKey, "columnKey"); 271 272 // cases: 273 // 1 - the dataset is empty, so we just need to add a new layer with the 274 // given keys; 275 if (this.data.isEmpty()) { 276 this.seriesKeys.add(seriesKey); 277 this.rowKeys.add(rowKey); 278 this.columnKeys.add(columnKey); 279 DefaultKeyedValues2D<R, C, V> d 280 = new DefaultKeyedValues2D<R, C, V>(); 281 d.setValue(n, rowKey, columnKey); 282 this.data.add(d); 283 } 284 285 int seriesIndex = getSeriesIndex(seriesKey); 286 int rowIndex = getRowIndex(rowKey); 287 int columnIndex = getColumnIndex(columnKey); 288 if (rowIndex < 0) { 289 this.rowKeys.add(rowKey); 290 } 291 if (columnIndex < 0) { 292 this.columnKeys.add(columnKey); 293 } 294 if (rowIndex < 0 || columnIndex < 0) { 295 for (DefaultKeyedValues2D<R, C, V> d : this.data) { 296 d.setValue(null, rowKey, columnKey); 297 } 298 } 299 if (seriesIndex >= 0) { 300 DefaultKeyedValues2D<R, C, V> d = this.data.get(seriesIndex); 301 d.setValue(n, rowKey, columnKey); 302 } else { 303 this.seriesKeys.add(seriesKey); 304 DefaultKeyedValues2D<R, C, V> d 305 = new DefaultKeyedValues2D<R, C, V>(this.rowKeys, 306 this.columnKeys); 307 d.setValue(n, rowKey, columnKey); 308 this.data.add(d); 309 } 310 } 311 312 /** 313 * Tests this instance for equality with an arbitrary object. 314 * 315 * @param obj the object ({@code null} permitted). 316 * 317 * @return A boolean. 318 */ 319 @Override 320 public boolean equals(Object obj) { 321 if (obj == this) { 322 return true; 323 } 324 if (!(obj instanceof DefaultKeyedValues3D)) { 325 return false; 326 } 327 DefaultKeyedValues3D that = (DefaultKeyedValues3D) obj; 328 if (!this.seriesKeys.equals(that.seriesKeys)) { 329 return false; 330 } 331 if (!this.rowKeys.equals(that.rowKeys)) { 332 return false; 333 } 334 if (!this.columnKeys.equals(that.columnKeys)) { 335 return false; 336 } 337 if (!this.data.equals(that.data)) { 338 return false; 339 } 340 return true; 341 } 342 343}