1/* 2 * Copyright (C) 2009 Google Inc. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package com.google.common.collect; 18 19import static com.google.common.base.Preconditions.checkNotNull; 20 21import com.google.common.annotations.Beta; 22import com.google.common.annotations.GwtCompatible; 23 24import java.util.Comparator; 25import java.util.List; 26import java.util.Map; 27 28import javax.annotation.Nullable; 29 30/** 31 * An immutable {@link Table} with reliable user-specified iteration order. 32 * Does not permit null keys or values. 33 * 34 * <p><b>Note</b>: Although this class is not final, it cannot be subclassed as 35 * it has no public or protected constructors. Thus, instances of this class are 36 * guaranteed to be immutable. 37 * 38 * @author gak@google.com (Gregory Kick) 39 * @since 11.0 40 */ 41@Beta 42@GwtCompatible 43// TODO(gak): make serializable 44public abstract class ImmutableTable<R, C, V> implements Table<R, C, V> { 45 /** Returns an empty immutable table. */ 46 @SuppressWarnings("unchecked") 47 public static final <R, C, V> ImmutableTable<R, C, V> of() { 48 return (ImmutableTable<R, C, V>) EmptyImmutableTable.INSTANCE; 49 } 50 51 /** Returns an immutable table containing a single cell. */ 52 public static final <R, C, V> ImmutableTable<R, C, V> of(R rowKey, 53 C columnKey, V value) { 54 return new SingletonImmutableTable<R, C, V>(rowKey, columnKey, value); 55 } 56 57 /** 58 * Returns an immutable copy of the provided table. 59 * 60 * <p>The {@link Table#cellSet()} iteration order of the provided table 61 * determines the iteration ordering of all views in the returned table. Note 62 * that some views of the original table and the copied table may have 63 * different iteration orders. For more control over the ordering, create a 64 * {@link Builder} and call {@link Builder#orderRowsBy}, 65 * {@link Builder#orderColumnsBy}, and {@link Builder#putAll} 66 * 67 * <p>Despite the method name, this method attempts to avoid actually copying 68 * the data when it is safe to do so. The exact circumstances under which a 69 * copy will or will not be performed are undocumented and subject to change. 70 */ 71 public static final <R, C, V> ImmutableTable<R, C, V> copyOf( 72 Table<? extends R, ? extends C, ? extends V> table) { 73 if (table instanceof ImmutableTable<?, ?, ?>) { 74 @SuppressWarnings("unchecked") 75 ImmutableTable<R, C, V> parameterizedTable 76 = (ImmutableTable<R, C, V>) table; 77 return parameterizedTable; 78 } else { 79 int size = table.size(); 80 switch (size) { 81 case 0: 82 return of(); 83 case 1: 84 Cell<? extends R, ? extends C, ? extends V> onlyCell 85 = Iterables.getOnlyElement(table.cellSet()); 86 return ImmutableTable.<R, C, V>of(onlyCell.getRowKey(), 87 onlyCell.getColumnKey(), onlyCell.getValue()); 88 default: 89 ImmutableSet.Builder<Cell<R, C, V>> cellSetBuilder 90 = ImmutableSet.builder(); 91 for (Cell<? extends R, ? extends C, ? extends V> cell : 92 table.cellSet()) { 93 /* 94 * Must cast to be able to create a Cell<R, C, V> rather than a 95 * Cell<? extends R, ? extends C, ? extends V> 96 */ 97 cellSetBuilder.add(cellOf((R) cell.getRowKey(), 98 (C) cell.getColumnKey(), (V) cell.getValue())); 99 } 100 return RegularImmutableTable.forCells(cellSetBuilder.build()); 101 } 102 } 103 } 104 105 /** 106 * Returns a new builder. The generated builder is equivalent to the builder 107 * created by the {@link Builder#Builder()} constructor. 108 */ 109 public static final <R, C, V> Builder<R, C, V> builder() { 110 return new Builder<R, C, V>(); 111 } 112 113 /** 114 * Verifies that {@code rowKey}, {@code columnKey} and {@code value} are 115 * non-null, and returns a new entry with those values. 116 */ 117 static <R, C, V> Cell<R, C, V> cellOf(R rowKey, C columnKey, V value) { 118 return Tables.immutableCell(checkNotNull(rowKey), checkNotNull(columnKey), 119 checkNotNull(value)); 120 } 121 122 /** 123 * A builder for creating immutable table instances, especially {@code public 124 * static final} tables ("constant tables"). Example: <pre> {@code 125 * 126 * static final ImmutableTable<Integer, Character, String> SPREADSHEET = 127 * new ImmutableTable.Builder<Integer, Character, String>() 128 * .put(1, 'A', "foo") 129 * .put(1, 'B', "bar") 130 * .put(2, 'A', "baz") 131 * .build();}</pre> 132 * 133 * <p>By default, the order in which cells are added to the builder determines 134 * the iteration ordering of all views in the returned table, with {@link 135 * #putAll} following the {@link Table#cellSet()} iteration order. However, if 136 * {@link #orderRowsBy} or {@link #orderColumnsBy} is called, the views are 137 * sorted by the supplied comparators. 138 * 139 * For empty or single-cell immutable tables, {@link #of()} and 140 * {@link #of(Object, Object, Object)} are even more convenient. 141 * 142 * <p>Builder instances can be reused - it is safe to call {@link #build} 143 * multiple times to build multiple tables in series. Each table is a superset 144 * of the tables created before it. 145 * 146 * @since 11.0 147 */ 148 public static final class Builder<R, C, V> { 149 private final List<Cell<R, C, V>> cells = Lists.newArrayList(); 150 private Comparator<? super R> rowComparator; 151 private Comparator<? super C> columnComparator; 152 153 /** 154 * Creates a new builder. The returned builder is equivalent to the builder 155 * generated by {@link ImmutableTable#builder}. 156 */ 157 public Builder() {} 158 159 /** 160 * Specifies the ordering of the generated table's rows. 161 */ 162 public Builder<R, C, V> orderRowsBy(Comparator<? super R> rowComparator) { 163 this.rowComparator = checkNotNull(rowComparator); 164 return this; 165 } 166 167 /** 168 * Specifies the ordering of the generated table's columns. 169 */ 170 public Builder<R, C, V> orderColumnsBy( 171 Comparator<? super C> columnComparator) { 172 this.columnComparator = checkNotNull(columnComparator); 173 return this; 174 } 175 176 /** 177 * Associates the ({@code rowKey}, {@code columnKey}) pair with {@code 178 * value} in the built table. Duplicate key pairs are not allowed and will 179 * cause {@link #build} to fail. 180 */ 181 public Builder<R, C, V> put(R rowKey, C columnKey, V value) { 182 cells.add(cellOf(rowKey, columnKey, value)); 183 return this; 184 } 185 186 /** 187 * Adds the given {@code cell} to the table, making it immutable if 188 * necessary. Duplicate key pairs are not allowed and will cause {@link 189 * #build} to fail. 190 */ 191 public Builder<R, C, V> put( 192 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( 214 Table<? extends R, ? extends C, ? extends V> table) { 215 for (Cell<? extends R, ? extends C, ? extends V> cell : table.cellSet()) { 216 put(cell); 217 } 218 return this; 219 } 220 221 /** 222 * Returns a newly-created immutable table. 223 * 224 * @throws IllegalArgumentException if duplicate key pairs were added 225 */ 226 public ImmutableTable<R, C, V> build() { 227 int size = cells.size(); 228 switch (size) { 229 case 0: 230 return of(); 231 case 1: 232 return new SingletonImmutableTable<R, C, V>( 233 Iterables.getOnlyElement(cells)); 234 default: 235 return RegularImmutableTable.forCells( 236 cells, rowComparator, columnComparator); 237 } 238 } 239 } 240 241 ImmutableTable() {} 242 243 @Override public abstract ImmutableSet<Cell<R, C, V>> cellSet(); 244 245 /** 246 * {@inheritDoc} 247 * 248 * @throws NullPointerException if {@code columnKey} is {@code null} 249 */ 250 @Override public abstract ImmutableMap<R, V> column(C columnKey); 251 252 @Override public abstract ImmutableSet<C> columnKeySet(); 253 254 /** 255 * {@inheritDoc} 256 * 257 * <p>The value {@code Map<R, V>}s in the returned map are 258 * {@link ImmutableMap}s as well. 259 */ 260 @Override public abstract ImmutableMap<C, Map<R, V>> columnMap(); 261 262 /** 263 * {@inheritDoc} 264 * 265 * @throws NullPointerException if {@code rowKey} is {@code null} 266 */ 267 @Override public abstract ImmutableMap<C, V> row(R rowKey); 268 269 @Override public abstract ImmutableSet<R> rowKeySet(); 270 271 /** 272 * {@inheritDoc} 273 * 274 * <p>The value {@code Map<C, V>}s in the returned map are 275 * {@link ImmutableMap}s as well. 276 */ 277 @Override public abstract ImmutableMap<R, Map<C, V>> rowMap(); 278 279 /** 280 * Guaranteed to throw an exception and leave the table unmodified. 281 * 282 * @throws UnsupportedOperationException always 283 */ 284 @Override public final void clear() { 285 throw new UnsupportedOperationException(); 286 } 287 288 /** 289 * Guaranteed to throw an exception and leave the table unmodified. 290 * 291 * @throws UnsupportedOperationException always 292 */ 293 @Override public final V put(R rowKey, C columnKey, V value) { 294 throw new UnsupportedOperationException(); 295 } 296 297 /** 298 * Guaranteed to throw an exception and leave the table unmodified. 299 * 300 * @throws UnsupportedOperationException always 301 */ 302 @Override public final void putAll( 303 Table<? extends R, ? extends C, ? extends V> table) { 304 throw new UnsupportedOperationException(); 305 } 306 307 /** 308 * Guaranteed to throw an exception and leave the table unmodified. 309 * 310 * @throws UnsupportedOperationException always 311 */ 312 @Override public final V remove(Object rowKey, Object columnKey) { 313 throw new UnsupportedOperationException(); 314 } 315 316 @Override public boolean equals(@Nullable Object obj) { 317 if (obj == this) { 318 return true; 319 } else if (obj instanceof Table<?, ?, ?>) { 320 Table<?, ?, ?> that = (Table<?, ?, ?>) obj; 321 return this.cellSet().equals(that.cellSet()); 322 } else { 323 return false; 324 } 325 } 326 327 @Override public int hashCode() { 328 return cellSet().hashCode(); 329 } 330 331 @Override public String toString() { 332 return rowMap().toString(); 333 } 334} 335