• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2009 The Guava Authors
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 
17 package com.google.common.collect;
18 
19 import static com.google.common.base.Preconditions.checkNotNull;
20 
21 import com.google.common.annotations.GwtCompatible;
22 import com.google.common.collect.Tables.AbstractCell;
23 import java.util.ArrayList;
24 import java.util.List;
25 import java.util.function.BinaryOperator;
26 import java.util.function.Function;
27 import java.util.function.Supplier;
28 import java.util.stream.Collector;
29 import org.checkerframework.checker.nullness.qual.Nullable;
30 
31 /** Collectors utilities for {@code common.collect.Table} internals. */
32 @GwtCompatible
33 @ElementTypesAreNonnullByDefault
34 final class TableCollectors {
35 
36   static <T extends @Nullable Object, R, C, V>
toImmutableTable( Function<? super T, ? extends R> rowFunction, Function<? super T, ? extends C> columnFunction, Function<? super T, ? extends V> valueFunction)37       Collector<T, ?, ImmutableTable<R, C, V>> toImmutableTable(
38           Function<? super T, ? extends R> rowFunction,
39           Function<? super T, ? extends C> columnFunction,
40           Function<? super T, ? extends V> valueFunction) {
41     checkNotNull(rowFunction, "rowFunction");
42     checkNotNull(columnFunction, "columnFunction");
43     checkNotNull(valueFunction, "valueFunction");
44     return Collector.of(
45         (Supplier<ImmutableTable.Builder<R, C, V>>) ImmutableTable.Builder::new,
46         (builder, t) ->
47             builder.put(rowFunction.apply(t), columnFunction.apply(t), valueFunction.apply(t)),
48         ImmutableTable.Builder::combine,
49         ImmutableTable.Builder::build);
50   }
51 
52   static <T extends @Nullable Object, R, C, V>
toImmutableTable( Function<? super T, ? extends R> rowFunction, Function<? super T, ? extends C> columnFunction, Function<? super T, ? extends V> valueFunction, BinaryOperator<V> mergeFunction)53       Collector<T, ?, ImmutableTable<R, C, V>> toImmutableTable(
54           Function<? super T, ? extends R> rowFunction,
55           Function<? super T, ? extends C> columnFunction,
56           Function<? super T, ? extends V> valueFunction,
57           BinaryOperator<V> mergeFunction) {
58 
59     checkNotNull(rowFunction, "rowFunction");
60     checkNotNull(columnFunction, "columnFunction");
61     checkNotNull(valueFunction, "valueFunction");
62     checkNotNull(mergeFunction, "mergeFunction");
63 
64     /*
65      * No mutable Table exactly matches the insertion order behavior of ImmutableTable.Builder, but
66      * the Builder can't efficiently support merging of duplicate values.  Getting around this
67      * requires some work.
68      */
69 
70     return Collector.of(
71         () -> new ImmutableTableCollectorState<R, C, V>()
72         /* GWT isn't currently playing nicely with constructor references? */ ,
73         (state, input) ->
74             state.put(
75                 rowFunction.apply(input),
76                 columnFunction.apply(input),
77                 valueFunction.apply(input),
78                 mergeFunction),
79         (s1, s2) -> s1.combine(s2, mergeFunction),
80         state -> state.toTable());
81   }
82 
83   static <
84           T extends @Nullable Object,
85           R extends @Nullable Object,
86           C extends @Nullable Object,
87           V extends @Nullable Object,
88           I extends Table<R, C, V>>
toTable( java.util.function.Function<? super T, ? extends R> rowFunction, java.util.function.Function<? super T, ? extends C> columnFunction, java.util.function.Function<? super T, ? extends V> valueFunction, java.util.function.Supplier<I> tableSupplier)89       Collector<T, ?, I> toTable(
90           java.util.function.Function<? super T, ? extends R> rowFunction,
91           java.util.function.Function<? super T, ? extends C> columnFunction,
92           java.util.function.Function<? super T, ? extends V> valueFunction,
93           java.util.function.Supplier<I> tableSupplier) {
94     return toTable(
95         rowFunction,
96         columnFunction,
97         valueFunction,
98         (v1, v2) -> {
99           throw new IllegalStateException("Conflicting values " + v1 + " and " + v2);
100         },
101         tableSupplier);
102   }
103 
104   static <
105           T extends @Nullable Object,
106           R extends @Nullable Object,
107           C extends @Nullable Object,
108           V extends @Nullable Object,
109           I extends Table<R, C, V>>
toTable( java.util.function.Function<? super T, ? extends R> rowFunction, java.util.function.Function<? super T, ? extends C> columnFunction, java.util.function.Function<? super T, ? extends V> valueFunction, BinaryOperator<V> mergeFunction, java.util.function.Supplier<I> tableSupplier)110       Collector<T, ?, I> toTable(
111           java.util.function.Function<? super T, ? extends R> rowFunction,
112           java.util.function.Function<? super T, ? extends C> columnFunction,
113           java.util.function.Function<? super T, ? extends V> valueFunction,
114           BinaryOperator<V> mergeFunction,
115           java.util.function.Supplier<I> tableSupplier) {
116     checkNotNull(rowFunction);
117     checkNotNull(columnFunction);
118     checkNotNull(valueFunction);
119     checkNotNull(mergeFunction);
120     checkNotNull(tableSupplier);
121     return Collector.of(
122         tableSupplier,
123         (table, input) ->
124             mergeTables(
125                 table,
126                 rowFunction.apply(input),
127                 columnFunction.apply(input),
128                 valueFunction.apply(input),
129                 mergeFunction),
130         (table1, table2) -> {
131           for (Table.Cell<R, C, V> cell2 : table2.cellSet()) {
132             mergeTables(
133                 table1, cell2.getRowKey(), cell2.getColumnKey(), cell2.getValue(), mergeFunction);
134           }
135           return table1;
136         });
137   }
138 
139   private static final class ImmutableTableCollectorState<R, C, V> {
140     final List<MutableCell<R, C, V>> insertionOrder = new ArrayList<>();
141     final Table<R, C, MutableCell<R, C, V>> table = HashBasedTable.create();
142 
143     void put(R row, C column, V value, BinaryOperator<V> merger) {
144       MutableCell<R, C, V> oldCell = table.get(row, column);
145       if (oldCell == null) {
146         MutableCell<R, C, V> cell = new MutableCell<>(row, column, value);
147         insertionOrder.add(cell);
148         table.put(row, column, cell);
149       } else {
150         oldCell.merge(value, merger);
151       }
152     }
153 
154     ImmutableTableCollectorState<R, C, V> combine(
155         ImmutableTableCollectorState<R, C, V> other, BinaryOperator<V> merger) {
156       for (MutableCell<R, C, V> cell : other.insertionOrder) {
157         put(cell.getRowKey(), cell.getColumnKey(), cell.getValue(), merger);
158       }
159       return this;
160     }
161 
162     ImmutableTable<R, C, V> toTable() {
163       return ImmutableTable.copyOf(insertionOrder);
164     }
165   }
166 
167   private static final class MutableCell<R, C, V> extends AbstractCell<R, C, V> {
168     private final R row;
169     private final C column;
170     private V value;
171 
172     MutableCell(R row, C column, V value) {
173       this.row = checkNotNull(row, "row");
174       this.column = checkNotNull(column, "column");
175       this.value = checkNotNull(value, "value");
176     }
177 
178     @Override
179     public R getRowKey() {
180       return row;
181     }
182 
183     @Override
184     public C getColumnKey() {
185       return column;
186     }
187 
188     @Override
189     public V getValue() {
190       return value;
191     }
192 
193     void merge(V value, BinaryOperator<V> mergeFunction) {
194       checkNotNull(value, "value");
195       this.value = checkNotNull(mergeFunction.apply(this.value, value), "mergeFunction.apply");
196     }
197   }
198 
199   private static <
200           R extends @Nullable Object, C extends @Nullable Object, V extends @Nullable Object>
201       void mergeTables(
202           Table<R, C, V> table,
203           @ParametricNullness R row,
204           @ParametricNullness C column,
205           @ParametricNullness V value,
206           BinaryOperator<V> mergeFunction) {
207     checkNotNull(value);
208     V oldValue = table.get(row, column);
209     if (oldValue == null) {
210       table.put(row, column, value);
211     } else {
212       V newValue = mergeFunction.apply(oldValue, value);
213       if (newValue == null) {
214         table.remove(row, column);
215       } else {
216         table.put(row, column, newValue);
217       }
218     }
219   }
220 
221   private TableCollectors() {}
222 }
223