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