001/*
002 * Copyright (c) 2016-2018 Daniel Ennis (Aikar) - MIT License
003 *
004 *  Permission is hereby granted, free of charge, to any person obtaining
005 *  a copy of this software and associated documentation files (the
006 *  "Software"), to deal in the Software without restriction, including
007 *  without limitation the rights to use, copy, modify, merge, publish,
008 *  distribute, sublicense, and/or sell copies of the Software, and to
009 *  permit persons to whom the Software is furnished to do so, subject to
010 *  the following conditions:
011 *
012 *  The above copyright notice and this permission notice shall be
013 *  included in all copies or substantial portions of the Software.
014 *
015 *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
016 *  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
017 *  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
018 *  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
019 *  LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
020 *  OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
021 *  WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
022 */
023
024package co.aikar.util;
025
026import org.jetbrains.annotations.Nullable;
027
028import java.util.HashMap;
029import java.util.Iterator;
030import java.util.Map;
031import java.util.Objects;
032import java.util.Spliterators;
033import java.util.function.BiFunction;
034import java.util.function.Function;
035import java.util.function.Supplier;
036import java.util.stream.Stream;
037import java.util.stream.StreamSupport;
038
039public class Table <R, C, V> implements Iterable<Table.Entry<R, C, V>>{
040
041    private final Map<R, Map<C, V>> rowMap;
042    private final Function<R, Map<C, V>> colMapSupplier;
043
044    public Table() {
045        this(new HashMap<>(), (Supplier<Map<C, V>>) HashMap::new);
046    }
047
048    public Table(Supplier<Map<C, V>> columnMapSupplier) {
049        this(new HashMap<>(), columnMapSupplier);
050    }
051
052    public Table(Map<R, Map<C, V>> backingRowMap, Supplier<Map<C, V>> columnMapSupplier) {
053        this(backingRowMap, (r) -> columnMapSupplier.get());
054    }
055
056    public Table(Map<R, Map<C, V>> backingRowMap, Function<R, Map<C, V>> columnMapSupplier) {
057        this.rowMap = backingRowMap;
058        this.colMapSupplier = columnMapSupplier;
059    }
060
061
062    public V get(R row, C col) {
063        return getIfExists(row, col);
064    }
065
066    public V getOrDefault(R row, C col, V def) {
067        Map<C, V> colMap = getColMapIfExists(row);
068        if (colMap == null) {
069            return def;
070        }
071
072        V v = colMap.get(col);
073        if (v != null || colMap.containsKey(col)) {
074            return v;
075        }
076        return def;
077    }
078
079    public boolean containsKey(R row, C col) {
080        Map<C, V> colMap = getColMapIfExists(row);
081        if (colMap == null) {
082            return false;
083        }
084        return colMap.containsKey(col);
085    }
086
087    @Nullable
088    public V put(R row, C col, V val) {
089        return getColMapForWrite(row).put(col, val);
090    }
091
092    public void forEach(TableConsumer<R, C, V> consumer) {
093        for (Iterator<Entry<R, C, V>> it = this.iterator(); it.hasNext(); ) {
094            Entry<R, C, V> entry = it.next();
095            consumer.accept(entry.getRow(), entry.getCol(), entry.getValue());
096        }
097    }
098
099    public void forEach(TablePredicate<R, C, V> predicate) {
100        for (Iterator<Entry<R, C, V>> it = this.iterator(); it.hasNext(); ) {
101            Entry<R, C, V> entry = it.next();
102            if (!predicate.test(entry.getRow(), entry.getCol(), entry.getValue())) {
103                return;
104            }
105        }
106    }
107
108    public void removeIf(TablePredicate<R, C, V> predicate) {
109        for (Iterator<Entry<R, C, V>> it = this.iterator(); it.hasNext(); ) {
110            Entry<R, C, V> entry = it.next();
111            if (predicate.test(entry.getRow(), entry.getCol(), entry.getValue())) {
112                it.remove();
113            }
114        }
115    }
116
117    public Stream<Entry<R, C, V>> stream() {
118        return stream(false);
119    }
120
121    public Stream<Entry<R, C, V>> stream(boolean parallel) {
122        return StreamSupport.stream(Spliterators.spliteratorUnknownSize(iterator(), 0), parallel);
123    }
124
125    public Iterator<Entry<R, C, V>> iterator() {
126        return new Iterator<Entry<R, C, V>>() {
127            Iterator<Map.Entry<R, Map<C, V>>> rowIter = rowMap.entrySet().iterator();
128            Iterator<Map.Entry<C, V>> colIter = null;
129
130            private Map.Entry<R, Map<C, V>> rowEntry;
131            private Map.Entry<C, V> colEntry;
132
133            private Entry<R, C, V> next = getNext();
134
135            private Entry<R, C, V> getNext() {
136                if (colIter == null || !colIter.hasNext()) {
137                    if (!rowIter.hasNext()) {
138                        return null;
139                    }
140                    rowEntry = rowIter.next();
141                    colIter = rowEntry.getValue().entrySet().iterator();
142                }
143
144                if (!colIter.hasNext()) {
145                    return null;
146                }
147
148                colEntry = colIter.next();
149
150                return new Node(rowEntry, colEntry);
151            }
152
153            @Override
154            public boolean hasNext() {
155                return this.next != null;
156            }
157
158            @Override
159            public Entry<R, C, V> next() {
160                Entry<R, C, V> entry = this.next;
161                this.next = getNext();
162                return entry;
163            }
164
165            @Override
166            public void remove() {
167                this.colIter.remove();
168            }
169        };
170    }
171
172    public void replaceAll(TableFunction<R, C, V, V> function) {
173        for (Iterator<Entry<R, C, V>> it = this.iterator(); it.hasNext(); ) {
174            Entry<R, C, V> entry = it.next();
175            entry.setValue(function.compose(entry.getRow(), entry.getCol(), entry.getValue()));
176        }
177    }
178
179    public V remove(R row, C col) {
180        Map<C, V> rowMap = this.rowMap.get(row);
181        if (rowMap == null) {
182            return null;
183        }
184        return rowMap.remove(col);
185    }
186
187    @Nullable
188    public V replace(R row, C col, V val) {
189        Map<C, V> rowMap = getColMapIfExists(row);
190        if (rowMap == null) {
191            return null;
192        }
193        if (rowMap.get(col) != null || rowMap.containsKey(col)) {
194            return rowMap.put(col, val);
195        }
196        return null;
197    }
198
199
200    @Nullable
201    public boolean replace(R row, C col, V old, V val) {
202        Map<C, V> rowMap = getColMapIfExists(row);
203        if (rowMap == null) {
204            return false;
205        }
206        if (Objects.equals(rowMap.get(col), old)) {
207            rowMap.put(col, val);
208            return true;
209        }
210        return false;
211    }
212
213    public V computeIfAbsent(R row, C col, BiFunction<R, C, V> function) {
214        return getColMapForWrite(row).computeIfAbsent(col, c -> function.apply(row, col));
215    }
216
217    public V computeIfPresent(R row, C col, TableFunction<R, C, V, V> function) {
218        Map<C, V> colMap = getColMapForWrite(row);
219        V v = colMap.computeIfPresent(col, (c, old) -> function.compose(row, col, old));
220        removeIfEmpty(row, colMap);
221        return v;
222    }
223
224    public V compute(R row, C col, TableFunction<R, C, V, V> function) {
225        Map<C, V> colMap = getColMapForWrite(row);
226        V v = colMap.compute(col, (c, old) -> function.compose(row, col, old));
227        removeIfEmpty(row, colMap);
228        return v;
229    }
230
231    public V merge(R row, C col, V val, TableFunction<R, C, V, V> function) {
232        Map<C, V> colMap = getColMapForWrite(row);
233        V v = colMap.merge(col, val, (c, old) -> function.compose(row, col, old));
234        removeIfEmpty(row, colMap);
235        return v;
236    }
237
238    public Map<C, V> row(R row) {
239        Map<C, V> EMPTY = new HashMap<>(0);
240        return new DelegatingMap<C, V>() {
241            @Override
242            public Map<C, V> delegate(boolean readOnly) {
243                if (readOnly) {
244                    return Table.this.rowMap.getOrDefault(row, EMPTY);
245                }
246                return getColMapForWrite(row);
247            }
248
249            @Override
250            public V remove(Object key) {
251                Map<C, V> delegate = delegate(false);
252                V remove = delegate.remove(key);
253                removeIfEmpty(row, delegate);
254                return remove;
255            }
256            // iterators may leave us empty, but the next get will remove it.
257        };
258    }
259    // Other stuff
260
261    public interface TablePredicate<R, C, V> {
262        boolean test(R row, C col, V val);
263    }
264    public interface TableFunction<R, C, V, RETURN> {
265        RETURN compose(R row, C col, V val);
266    }
267    public interface TableConsumer<R, C, V> {
268        void accept(R row, C col, V val);
269    }
270
271    private V getIfExists(R row, C col) {
272        Map<C, V> colMap = getColMapIfExists(row);
273        if (colMap == null) {
274            return null;
275        }
276
277        return colMap.get(col);
278    }
279
280    private Map<C, V> getColMapIfExists(R row) {
281        Map<C, V> colMap = this.rowMap.get(row);
282        if (colMap != null && colMap.isEmpty()) {
283            rowMap.remove(row);
284            colMap = null;
285        }
286        return colMap;
287    }
288
289    private Map<C, V> getColMapForWrite(R row) {
290        return this.rowMap.computeIfAbsent(row, this.colMapSupplier);
291    }
292
293    private void removeIfEmpty(R row, Map<C, V> colMap) {
294        if (colMap.isEmpty()) {
295            this.rowMap.remove(row);
296        }
297    }
298
299    public interface Entry <R, C, V> {
300        R getRow();
301        C getCol();
302        V getValue();
303        V setValue(V value);
304    }
305
306    private class Node implements Entry <R, C, V> {
307
308        private final Map.Entry<R, Map<C, V>> rowEntry;
309        private final Map.Entry<C, V> colEntry;
310
311        Node(Map.Entry<R, Map<C, V>> rowEntry, Map.Entry<C, V> entry) {
312            this.rowEntry = rowEntry;
313            this.colEntry = entry;
314        }
315
316        @Override
317        public final R getRow() {
318            return rowEntry.getKey();
319        }
320
321        @Override
322        public final C getCol() {
323            return colEntry.getKey();
324        }
325
326        @Override
327        public final V getValue() {
328            return colEntry.getValue();
329        }
330
331        @Override
332        public final V setValue(V value) {
333            return colEntry.setValue(value);
334        }
335    }
336}