001/* =========================================================== 002 * Orson Charts : a 3D chart library for the Java(tm) platform 003 * =========================================================== 004 * 005 * (C)opyright 2013-2022, by David Gilbert. All rights reserved. 006 * 007 * https://github.com/jfree/orson-charts 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 org.jfree.chart3d.data; 034 035import java.io.Serializable; 036import java.util.ArrayList; 037import java.util.List; 038 039import org.jfree.chart3d.internal.Args; 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 final List<S> seriesKeys; 060 061 /** The row keys. */ 062 private final 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<>(); 078 this.rowKeys = new ArrayList<>(); 079 this.columnKeys = new ArrayList<>(); 080 this.data = new ArrayList<>(); 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 Args.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 Args.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 Args.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<>(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<>(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<>(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 Args.nullNotPermitted(seriesKey, "seriesKey"); 269 Args.nullNotPermitted(rowKey, "rowKey"); 270 Args.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 = new DefaultKeyedValues2D<>(); 280 d.setValue(n, rowKey, columnKey); 281 this.data.add(d); 282 } 283 284 int seriesIndex = getSeriesIndex(seriesKey); 285 int rowIndex = getRowIndex(rowKey); 286 int columnIndex = getColumnIndex(columnKey); 287 if (rowIndex < 0) { 288 this.rowKeys.add(rowKey); 289 } 290 if (columnIndex < 0) { 291 this.columnKeys.add(columnKey); 292 } 293 if (rowIndex < 0 || columnIndex < 0) { 294 for (DefaultKeyedValues2D<R, C, V> d : this.data) { 295 d.setValue(null, rowKey, columnKey); 296 } 297 } 298 if (seriesIndex >= 0) { 299 DefaultKeyedValues2D<R, C, V> d = this.data.get(seriesIndex); 300 d.setValue(n, rowKey, columnKey); 301 } else { 302 this.seriesKeys.add(seriesKey); 303 DefaultKeyedValues2D<R, C, V> d = new DefaultKeyedValues2D<>( 304 this.rowKeys, this.columnKeys); 305 d.setValue(n, rowKey, columnKey); 306 this.data.add(d); 307 } 308 } 309 310 /** 311 * Tests this instance for equality with an arbitrary object. 312 * 313 * @param obj the object ({@code null} permitted). 314 * 315 * @return A boolean. 316 */ 317 @Override 318 public boolean equals(Object obj) { 319 if (obj == this) { 320 return true; 321 } 322 if (!(obj instanceof DefaultKeyedValues3D)) { 323 return false; 324 } 325 DefaultKeyedValues3D that = (DefaultKeyedValues3D) obj; 326 if (!this.seriesKeys.equals(that.seriesKeys)) { 327 return false; 328 } 329 if (!this.rowKeys.equals(that.rowKeys)) { 330 return false; 331 } 332 if (!this.columnKeys.equals(that.columnKeys)) { 333 return false; 334 } 335 if (!this.data.equals(that.data)) { 336 return false; 337 } 338 return true; 339 } 340 341}