001 /* 002 * Copyright (C) 2009 Google Inc. 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 017 package com.google.common.collect; 018 019 import static com.google.common.base.Preconditions.checkNotNull; 020 021 import com.google.common.annotations.GwtCompatible; 022 023 import java.util.Comparator; 024 import java.util.List; 025 import java.util.Map; 026 027 import javax.annotation.Nullable; 028 029 /** 030 * An immutable {@link Table} with reliable user-specified iteration order. 031 * Does not permit null keys or values. 032 * 033 * <p><b>Note</b>: Although this class is not final, it cannot be subclassed as 034 * it has no public or protected constructors. Thus, instances of this class are 035 * guaranteed to be immutable. 036 * 037 * <p>See the Guava User Guide article on <a href= 038 * "http://code.google.com/p/guava-libraries/wiki/ImmutableCollectionsExplained"> 039 * immutable collections</a>. 040 * 041 * @author gak@google.com (Gregory Kick) 042 * @since 11.0 043 */ 044 @GwtCompatible 045 // TODO(gak): make serializable 046 public abstract class ImmutableTable<R, C, V> implements Table<R, C, V> { 047 /** Returns an empty immutable table. */ 048 @SuppressWarnings("unchecked") 049 public static final <R, C, V> ImmutableTable<R, C, V> of() { 050 return (ImmutableTable<R, C, V>) EmptyImmutableTable.INSTANCE; 051 } 052 053 /** Returns an immutable table containing a single cell. */ 054 public static final <R, C, V> ImmutableTable<R, C, V> of(R rowKey, 055 C columnKey, V value) { 056 return new SingletonImmutableTable<R, C, V>(rowKey, columnKey, value); 057 } 058 059 /** 060 * Returns an immutable copy of the provided table. 061 * 062 * <p>The {@link Table#cellSet()} iteration order of the provided table 063 * determines the iteration ordering of all views in the returned table. Note 064 * that some views of the original table and the copied table may have 065 * different iteration orders. For more control over the ordering, create a 066 * {@link Builder} and call {@link Builder#orderRowsBy}, 067 * {@link Builder#orderColumnsBy}, and {@link Builder#putAll} 068 * 069 * <p>Despite the method name, this method attempts to avoid actually copying 070 * the data when it is safe to do so. The exact circumstances under which a 071 * copy will or will not be performed are undocumented and subject to change. 072 */ 073 public static final <R, C, V> ImmutableTable<R, C, V> copyOf( 074 Table<? extends R, ? extends C, ? extends V> table) { 075 if (table instanceof ImmutableTable<?, ?, ?>) { 076 @SuppressWarnings("unchecked") 077 ImmutableTable<R, C, V> parameterizedTable 078 = (ImmutableTable<R, C, V>) table; 079 return parameterizedTable; 080 } else { 081 int size = table.size(); 082 switch (size) { 083 case 0: 084 return of(); 085 case 1: 086 Cell<? extends R, ? extends C, ? extends V> onlyCell 087 = Iterables.getOnlyElement(table.cellSet()); 088 return ImmutableTable.<R, C, V>of(onlyCell.getRowKey(), 089 onlyCell.getColumnKey(), onlyCell.getValue()); 090 default: 091 ImmutableSet.Builder<Cell<R, C, V>> cellSetBuilder 092 = ImmutableSet.builder(); 093 for (Cell<? extends R, ? extends C, ? extends V> cell : 094 table.cellSet()) { 095 /* 096 * Must cast to be able to create a Cell<R, C, V> rather than a 097 * Cell<? extends R, ? extends C, ? extends V> 098 */ 099 cellSetBuilder.add(cellOf((R) cell.getRowKey(), 100 (C) cell.getColumnKey(), (V) cell.getValue())); 101 } 102 return RegularImmutableTable.forCells(cellSetBuilder.build()); 103 } 104 } 105 } 106 107 /** 108 * Returns a new builder. The generated builder is equivalent to the builder 109 * created by the {@link Builder#ImmutableTable.Builder()} constructor. 110 */ 111 public static final <R, C, V> Builder<R, C, V> builder() { 112 return new Builder<R, C, V>(); 113 } 114 115 /** 116 * Verifies that {@code rowKey}, {@code columnKey} and {@code value} are 117 * non-null, and returns a new entry with those values. 118 */ 119 static <R, C, V> Cell<R, C, V> cellOf(R rowKey, C columnKey, V value) { 120 return Tables.immutableCell(checkNotNull(rowKey), checkNotNull(columnKey), 121 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( 173 Comparator<? super C> columnComparator) { 174 this.columnComparator = checkNotNull(columnComparator); 175 return this; 176 } 177 178 /** 179 * Associates the ({@code rowKey}, {@code columnKey}) pair with {@code 180 * value} in the built table. Duplicate key pairs are not allowed and will 181 * cause {@link #build} to fail. 182 */ 183 public Builder<R, C, V> put(R rowKey, C columnKey, V value) { 184 cells.add(cellOf(rowKey, columnKey, value)); 185 return this; 186 } 187 188 /** 189 * Adds the given {@code cell} to the table, making it immutable if 190 * necessary. Duplicate key pairs are not allowed and will cause {@link 191 * #build} to fail. 192 */ 193 public Builder<R, C, V> put( 194 Cell<? extends R, ? extends C, ? extends V> cell) { 195 if (cell instanceof Tables.ImmutableCell) { 196 checkNotNull(cell.getRowKey()); 197 checkNotNull(cell.getColumnKey()); 198 checkNotNull(cell.getValue()); 199 @SuppressWarnings("unchecked") // all supported methods are covariant 200 Cell<R, C, V> immutableCell = (Cell<R, C, V>) cell; 201 cells.add(immutableCell); 202 } else { 203 put(cell.getRowKey(), cell.getColumnKey(), cell.getValue()); 204 } 205 return this; 206 } 207 208 /** 209 * Associates all of the given table's keys and values in the built table. 210 * Duplicate row key column key pairs are not allowed, and will cause 211 * {@link #build} to fail. 212 * 213 * @throws NullPointerException if any key or value in {@code table} is null 214 */ 215 public Builder<R, C, V> putAll( 216 Table<? extends R, ? extends C, ? extends V> table) { 217 for (Cell<? extends R, ? extends C, ? extends V> cell : table.cellSet()) { 218 put(cell); 219 } 220 return this; 221 } 222 223 /** 224 * Returns a newly-created immutable table. 225 * 226 * @throws IllegalArgumentException if duplicate key pairs were added 227 */ 228 public ImmutableTable<R, C, V> build() { 229 int size = cells.size(); 230 switch (size) { 231 case 0: 232 return of(); 233 case 1: 234 return new SingletonImmutableTable<R, C, V>( 235 Iterables.getOnlyElement(cells)); 236 default: 237 return RegularImmutableTable.forCells( 238 cells, rowComparator, columnComparator); 239 } 240 } 241 } 242 243 ImmutableTable() {} 244 245 @Override public abstract ImmutableSet<Cell<R, C, V>> cellSet(); 246 247 /** 248 * {@inheritDoc} 249 * 250 * @throws NullPointerException if {@code columnKey} is {@code null} 251 */ 252 @Override public abstract ImmutableMap<R, V> column(C columnKey); 253 254 @Override public abstract ImmutableSet<C> columnKeySet(); 255 256 /** 257 * {@inheritDoc} 258 * 259 * <p>The value {@code Map<R, V>}s in the returned map are 260 * {@link ImmutableMap}s as well. 261 */ 262 @Override public abstract ImmutableMap<C, Map<R, V>> columnMap(); 263 264 /** 265 * {@inheritDoc} 266 * 267 * @throws NullPointerException if {@code rowKey} is {@code null} 268 */ 269 @Override public abstract ImmutableMap<C, V> row(R rowKey); 270 271 @Override public abstract ImmutableSet<R> rowKeySet(); 272 273 /** 274 * {@inheritDoc} 275 * 276 * <p>The value {@code Map<C, V>}s in the returned map are 277 * {@link ImmutableMap}s as well. 278 */ 279 @Override public abstract ImmutableMap<R, Map<C, V>> rowMap(); 280 281 /** 282 * Guaranteed to throw an exception and leave the table unmodified. 283 * 284 * @throws UnsupportedOperationException always 285 */ 286 @Override public final void clear() { 287 throw new UnsupportedOperationException(); 288 } 289 290 /** 291 * Guaranteed to throw an exception and leave the table unmodified. 292 * 293 * @throws UnsupportedOperationException always 294 */ 295 @Override public final V put(R rowKey, C columnKey, V value) { 296 throw new UnsupportedOperationException(); 297 } 298 299 /** 300 * Guaranteed to throw an exception and leave the table unmodified. 301 * 302 * @throws UnsupportedOperationException always 303 */ 304 @Override public final void putAll( 305 Table<? extends R, ? extends C, ? extends V> table) { 306 throw new UnsupportedOperationException(); 307 } 308 309 /** 310 * Guaranteed to throw an exception and leave the table unmodified. 311 * 312 * @throws UnsupportedOperationException always 313 */ 314 @Override public final V remove(Object rowKey, Object columnKey) { 315 throw new UnsupportedOperationException(); 316 } 317 318 @Override public boolean equals(@Nullable Object obj) { 319 if (obj == this) { 320 return true; 321 } else if (obj instanceof Table<?, ?, ?>) { 322 Table<?, ?, ?> that = (Table<?, ?, ?>) obj; 323 return this.cellSet().equals(that.cellSet()); 324 } else { 325 return false; 326 } 327 } 328 329 @Override public int hashCode() { 330 return cellSet().hashCode(); 331 } 332 333 @Override public String toString() { 334 return rowMap().toString(); 335 } 336 }