001/* 002 * Copyright (C) 2009 The Guava Authors 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package com.google.common.collect; 018 019import static com.google.common.base.Preconditions.checkNotNull; 020 021import com.google.common.annotations.GwtCompatible; 022import com.google.common.base.MoreObjects; 023 024import java.util.Comparator; 025import java.util.Iterator; 026import java.util.List; 027import java.util.Map; 028 029import javax.annotation.Nullable; 030 031/** 032 * A {@link Table} whose contents will never change, with many other important 033 * properties detailed at {@link ImmutableCollection}. 034 * 035 * <p>See the Guava User Guide article on <a href= 036 * "https://github.com/google/guava/wiki/ImmutableCollectionsExplained"> 037 * immutable collections</a>. 038 * 039 * @author Gregory Kick 040 * @since 11.0 041 */ 042@GwtCompatible 043// TODO(gak): make serializable 044public abstract class ImmutableTable<R, C, V> extends AbstractTable<R, C, V> { 045 private static final ImmutableTable<Object, Object, Object> EMPTY = 046 new SparseImmutableTable<Object, Object, Object>( 047 ImmutableList.<Cell<Object, Object, Object>>of(), 048 ImmutableSet.of(), 049 ImmutableSet.of()); 050 051 /** Returns an empty immutable table. */ 052 @SuppressWarnings("unchecked") 053 public static <R, C, V> ImmutableTable<R, C, V> of() { 054 return (ImmutableTable<R, C, V>) EMPTY; 055 } 056 057 /** Returns an immutable table containing a single cell. */ 058 public static <R, C, V> ImmutableTable<R, C, V> of(R rowKey, C columnKey, V value) { 059 return new SingletonImmutableTable<R, C, V>(rowKey, columnKey, value); 060 } 061 062 /** 063 * Returns an immutable copy of the provided table. 064 * 065 * <p>The {@link Table#cellSet()} iteration order of the provided table 066 * determines the iteration ordering of all views in the returned table. Note 067 * that some views of the original table and the copied table may have 068 * different iteration orders. For more control over the ordering, create a 069 * {@link Builder} and call {@link Builder#orderRowsBy}, 070 * {@link Builder#orderColumnsBy}, and {@link Builder#putAll} 071 * 072 * <p>Despite the method name, this method attempts to avoid actually copying 073 * the data when it is safe to do so. The exact circumstances under which a 074 * copy will or will not be performed are undocumented and subject to change. 075 */ 076 public static <R, C, V> ImmutableTable<R, C, V> copyOf( 077 Table<? extends R, ? extends C, ? extends V> table) { 078 if (table instanceof ImmutableTable) { 079 @SuppressWarnings("unchecked") 080 ImmutableTable<R, C, V> parameterizedTable = (ImmutableTable<R, C, V>) table; 081 return parameterizedTable; 082 } else { 083 int size = table.size(); 084 switch (size) { 085 case 0: 086 return of(); 087 case 1: 088 Cell<? extends R, ? extends C, ? extends V> onlyCell = 089 Iterables.getOnlyElement(table.cellSet()); 090 return ImmutableTable.<R, C, V>of( 091 onlyCell.getRowKey(), onlyCell.getColumnKey(), onlyCell.getValue()); 092 default: 093 ImmutableSet.Builder<Cell<R, C, V>> cellSetBuilder = 094 new ImmutableSet.Builder<Cell<R, C, V>>(size); 095 for (Cell<? extends R, ? extends C, ? extends V> cell : table.cellSet()) { 096 /* 097 * Must cast to be able to create a Cell<R, C, V> rather than a 098 * Cell<? extends R, ? extends C, ? extends V> 099 */ 100 cellSetBuilder.add( 101 cellOf((R) cell.getRowKey(), (C) cell.getColumnKey(), (V) cell.getValue())); 102 } 103 return RegularImmutableTable.forCells(cellSetBuilder.build()); 104 } 105 } 106 } 107 108 /** 109 * Returns a new builder. The generated builder is equivalent to the builder 110 * created by the {@link Builder#ImmutableTable.Builder()} constructor. 111 */ 112 public static <R, C, V> Builder<R, C, V> builder() { 113 return new Builder<R, C, V>(); 114 } 115 116 /** 117 * Verifies that {@code rowKey}, {@code columnKey} and {@code value} are 118 * non-null, and returns a new entry with those values. 119 */ 120 static <R, C, V> Cell<R, C, V> cellOf(R rowKey, C columnKey, V value) { 121 return Tables.immutableCell(checkNotNull(rowKey), checkNotNull(columnKey), checkNotNull(value)); 122 } 123 124 /** 125 * A builder for creating immutable table instances, especially {@code public 126 * static final} tables ("constant tables"). Example: <pre> {@code 127 * 128 * static final ImmutableTable<Integer, Character, String> SPREADSHEET = 129 * new ImmutableTable.Builder<Integer, Character, String>() 130 * .put(1, 'A', "foo") 131 * .put(1, 'B', "bar") 132 * .put(2, 'A', "baz") 133 * .build();}</pre> 134 * 135 * <p>By default, the order in which cells are added to the builder determines 136 * the iteration ordering of all views in the returned table, with {@link 137 * #putAll} following the {@link Table#cellSet()} iteration order. However, if 138 * {@link #orderRowsBy} or {@link #orderColumnsBy} is called, the views are 139 * sorted by the supplied comparators. 140 * 141 * For empty or single-cell immutable tables, {@link #of()} and 142 * {@link #of(Object, Object, Object)} are even more convenient. 143 * 144 * <p>Builder instances can be reused - it is safe to call {@link #build} 145 * multiple times to build multiple tables in series. Each table is a superset 146 * of the tables created before it. 147 * 148 * @since 11.0 149 */ 150 public static final class Builder<R, C, V> { 151 private final List<Cell<R, C, V>> cells = Lists.newArrayList(); 152 private Comparator<? super R> rowComparator; 153 private Comparator<? super C> columnComparator; 154 155 /** 156 * Creates a new builder. The returned builder is equivalent to the builder 157 * generated by {@link ImmutableTable#builder}. 158 */ 159 public Builder() {} 160 161 /** 162 * Specifies the ordering of the generated table's rows. 163 */ 164 public Builder<R, C, V> orderRowsBy(Comparator<? super R> rowComparator) { 165 this.rowComparator = checkNotNull(rowComparator); 166 return this; 167 } 168 169 /** 170 * Specifies the ordering of the generated table's columns. 171 */ 172 public Builder<R, C, V> orderColumnsBy(Comparator<? super C> columnComparator) { 173 this.columnComparator = checkNotNull(columnComparator); 174 return this; 175 } 176 177 /** 178 * Associates the ({@code rowKey}, {@code columnKey}) pair with {@code 179 * value} in the built table. Duplicate key pairs are not allowed and will 180 * cause {@link #build} to fail. 181 */ 182 public Builder<R, C, V> put(R rowKey, C columnKey, V value) { 183 cells.add(cellOf(rowKey, columnKey, value)); 184 return this; 185 } 186 187 /** 188 * Adds the given {@code cell} to the table, making it immutable if 189 * necessary. Duplicate key pairs are not allowed and will cause {@link 190 * #build} to fail. 191 */ 192 public Builder<R, C, V> put(Cell<? extends R, ? extends C, ? extends V> cell) { 193 if (cell instanceof Tables.ImmutableCell) { 194 checkNotNull(cell.getRowKey()); 195 checkNotNull(cell.getColumnKey()); 196 checkNotNull(cell.getValue()); 197 @SuppressWarnings("unchecked") // all supported methods are covariant 198 Cell<R, C, V> immutableCell = (Cell<R, C, V>) cell; 199 cells.add(immutableCell); 200 } else { 201 put(cell.getRowKey(), cell.getColumnKey(), cell.getValue()); 202 } 203 return this; 204 } 205 206 /** 207 * Associates all of the given table's keys and values in the built table. 208 * Duplicate row key column key pairs are not allowed, and will cause 209 * {@link #build} to fail. 210 * 211 * @throws NullPointerException if any key or value in {@code table} is null 212 */ 213 public Builder<R, C, V> putAll(Table<? extends R, ? extends C, ? extends V> table) { 214 for (Cell<? extends R, ? extends C, ? extends V> cell : table.cellSet()) { 215 put(cell); 216 } 217 return this; 218 } 219 220 /** 221 * Returns a newly-created immutable table. 222 * 223 * @throws IllegalArgumentException if duplicate key pairs were added 224 */ 225 public ImmutableTable<R, C, V> build() { 226 int size = cells.size(); 227 switch (size) { 228 case 0: 229 return of(); 230 case 1: 231 return new SingletonImmutableTable<R, C, V>(Iterables.getOnlyElement(cells)); 232 default: 233 return RegularImmutableTable.forCells(cells, rowComparator, columnComparator); 234 } 235 } 236 } 237 238 ImmutableTable() {} 239 240 @Override 241 public ImmutableSet<Cell<R, C, V>> cellSet() { 242 return (ImmutableSet<Cell<R, C, V>>) super.cellSet(); 243 } 244 245 @Override 246 abstract ImmutableSet<Cell<R, C, V>> createCellSet(); 247 248 @Override 249 final UnmodifiableIterator<Cell<R, C, V>> cellIterator() { 250 throw new AssertionError("should never be called"); 251 } 252 253 @Override 254 public ImmutableCollection<V> values() { 255 return (ImmutableCollection<V>) super.values(); 256 } 257 258 @Override 259 abstract ImmutableCollection<V> createValues(); 260 261 @Override 262 final Iterator<V> valuesIterator() { 263 throw new AssertionError("should never be called"); 264 } 265 266 /** 267 * {@inheritDoc} 268 * 269 * @throws NullPointerException if {@code columnKey} is {@code null} 270 */ 271 @Override 272 public ImmutableMap<R, V> column(C columnKey) { 273 checkNotNull(columnKey); 274 return MoreObjects.firstNonNull( 275 (ImmutableMap<R, V>) columnMap().get(columnKey), 276 ImmutableMap.<R, V>of()); 277 } 278 279 @Override 280 public ImmutableSet<C> columnKeySet() { 281 return columnMap().keySet(); 282 } 283 284 /** 285 * {@inheritDoc} 286 * 287 * <p>The value {@code Map<R, V>} instances in the returned map are 288 * {@link ImmutableMap} instances as well. 289 */ 290 @Override 291 public abstract ImmutableMap<C, Map<R, V>> columnMap(); 292 293 /** 294 * {@inheritDoc} 295 * 296 * @throws NullPointerException if {@code rowKey} is {@code null} 297 */ 298 @Override 299 public ImmutableMap<C, V> row(R rowKey) { 300 checkNotNull(rowKey); 301 return MoreObjects.firstNonNull( 302 (ImmutableMap<C, V>) rowMap().get(rowKey), 303 ImmutableMap.<C, V>of()); 304 } 305 306 @Override 307 public ImmutableSet<R> rowKeySet() { 308 return rowMap().keySet(); 309 } 310 311 /** 312 * {@inheritDoc} 313 * 314 * <p>The value {@code Map<C, V>} instances in the returned map are 315 * {@link ImmutableMap} instances as well. 316 */ 317 @Override 318 public abstract ImmutableMap<R, Map<C, V>> rowMap(); 319 320 @Override 321 public boolean contains(@Nullable Object rowKey, @Nullable Object columnKey) { 322 return get(rowKey, columnKey) != null; 323 } 324 325 @Override 326 public boolean containsValue(@Nullable Object value) { 327 return values().contains(value); 328 } 329 330 /** 331 * Guaranteed to throw an exception and leave the table unmodified. 332 * 333 * @throws UnsupportedOperationException always 334 * @deprecated Unsupported operation. 335 */ 336 @Deprecated 337 @Override 338 public final void clear() { 339 throw new UnsupportedOperationException(); 340 } 341 342 /** 343 * Guaranteed to throw an exception and leave the table unmodified. 344 * 345 * @throws UnsupportedOperationException always 346 * @deprecated Unsupported operation. 347 */ 348 @Deprecated 349 @Override 350 public final V put(R rowKey, C columnKey, V value) { 351 throw new UnsupportedOperationException(); 352 } 353 354 /** 355 * Guaranteed to throw an exception and leave the table unmodified. 356 * 357 * @throws UnsupportedOperationException always 358 * @deprecated Unsupported operation. 359 */ 360 @Deprecated 361 @Override 362 public final void putAll(Table<? extends R, ? extends C, ? extends V> table) { 363 throw new UnsupportedOperationException(); 364 } 365 366 /** 367 * Guaranteed to throw an exception and leave the table unmodified. 368 * 369 * @throws UnsupportedOperationException always 370 * @deprecated Unsupported operation. 371 */ 372 @Deprecated 373 @Override 374 public final V remove(Object rowKey, Object columnKey) { 375 throw new UnsupportedOperationException(); 376 } 377}