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.Beta; 022 import com.google.common.annotations.GwtCompatible; 023 024 import java.util.Comparator; 025 import java.util.List; 026 import java.util.Map; 027 028 import javax.annotation.Nullable; 029 030 /** 031 * An immutable {@link Table} with reliable user-specified iteration order. 032 * Does not permit null keys or values. 033 * 034 * <p><b>Note</b>: Although this class is not final, it cannot be subclassed as 035 * it has no public or protected constructors. Thus, instances of this class are 036 * guaranteed to be immutable. 037 * 038 * @author [email protected] (Gregory Kick) 039 * @since 11.0 040 */ 041 @Beta 042 @GwtCompatible 043 // TODO(gak): make serializable 044 public abstract class ImmutableTable<R, C, V> implements Table<R, C, V> { 045 /** Returns an empty immutable table. */ 046 @SuppressWarnings("unchecked") 047 public static final <R, C, V> ImmutableTable<R, C, V> of() { 048 return (ImmutableTable<R, C, V>) EmptyImmutableTable.INSTANCE; 049 } 050 051 /** Returns an immutable table containing a single cell. */ 052 public static final <R, C, V> ImmutableTable<R, C, V> of(R rowKey, 053 C columnKey, V value) { 054 return new SingletonImmutableTable<R, C, V>(rowKey, columnKey, value); 055 } 056 057 /** 058 * Returns an immutable copy of the provided table. 059 * 060 * <p>The {@link Table#cellSet()} iteration order of the provided table 061 * determines the iteration ordering of all views in the returned table. Note 062 * that some views of the original table and the copied table may have 063 * different iteration orders. For more control over the ordering, create a 064 * {@link Builder} and call {@link Builder#orderRowsBy}, 065 * {@link Builder#orderColumnsBy}, and {@link Builder#putAll} 066 * 067 * <p>Despite the method name, this method attempts to avoid actually copying 068 * the data when it is safe to do so. The exact circumstances under which a 069 * copy will or will not be performed are undocumented and subject to change. 070 */ 071 public static final <R, C, V> ImmutableTable<R, C, V> copyOf( 072 Table<? extends R, ? extends C, ? extends V> table) { 073 if (table instanceof ImmutableTable<?, ?, ?>) { 074 @SuppressWarnings("unchecked") 075 ImmutableTable<R, C, V> parameterizedTable 076 = (ImmutableTable<R, C, V>) table; 077 return parameterizedTable; 078 } else { 079 int size = table.size(); 080 switch (size) { 081 case 0: 082 return of(); 083 case 1: 084 Cell<? extends R, ? extends C, ? extends V> onlyCell 085 = Iterables.getOnlyElement(table.cellSet()); 086 return ImmutableTable.<R, C, V>of(onlyCell.getRowKey(), 087 onlyCell.getColumnKey(), onlyCell.getValue()); 088 default: 089 ImmutableSet.Builder<Cell<R, C, V>> cellSetBuilder 090 = ImmutableSet.builder(); 091 for (Cell<? extends R, ? extends C, ? extends V> cell : 092 table.cellSet()) { 093 /* 094 * Must cast to be able to create a Cell<R, C, V> rather than a 095 * Cell<? extends R, ? extends C, ? extends V> 096 */ 097 cellSetBuilder.add(cellOf((R) cell.getRowKey(), 098 (C) cell.getColumnKey(), (V) cell.getValue())); 099 } 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 }