Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
co.aikar.util.Table Maven / Gradle / Ivy
/*
* Copyright (c) 2016-2018 Daniel Ennis (Aikar) - MIT License
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package co.aikar.util;
import org.jetbrains.annotations.Nullable;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Objects;
import java.util.Spliterators;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
public class Table implements Iterable>{
private final Map> rowMap;
private final Function> colMapSupplier;
public Table() {
this(new HashMap<>(), (Supplier>) HashMap::new);
}
public Table(Supplier> columnMapSupplier) {
this(new HashMap<>(), columnMapSupplier);
}
public Table(Map> backingRowMap, Supplier> columnMapSupplier) {
this(backingRowMap, (r) -> columnMapSupplier.get());
}
public Table(Map> backingRowMap, Function> columnMapSupplier) {
this.rowMap = backingRowMap;
this.colMapSupplier = columnMapSupplier;
}
public V get(R row, C col) {
return getIfExists(row, col);
}
public V getOrDefault(R row, C col, V def) {
Map colMap = getColMapIfExists(row);
if (colMap == null) {
return def;
}
V v = colMap.get(col);
if (v != null || colMap.containsKey(col)) {
return v;
}
return def;
}
public boolean containsKey(R row, C col) {
Map colMap = getColMapIfExists(row);
if (colMap == null) {
return false;
}
return colMap.containsKey(col);
}
@Nullable
public V put(R row, C col, V val) {
return getColMapForWrite(row).put(col, val);
}
public void forEach(TableConsumer consumer) {
for (Iterator> it = this.iterator(); it.hasNext(); ) {
Entry entry = it.next();
consumer.accept(entry.getRow(), entry.getCol(), entry.getValue());
}
}
public void forEach(TablePredicate predicate) {
for (Iterator> it = this.iterator(); it.hasNext(); ) {
Entry entry = it.next();
if (!predicate.test(entry.getRow(), entry.getCol(), entry.getValue())) {
return;
}
}
}
public void removeIf(TablePredicate predicate) {
for (Iterator> it = this.iterator(); it.hasNext(); ) {
Entry entry = it.next();
if (predicate.test(entry.getRow(), entry.getCol(), entry.getValue())) {
it.remove();
}
}
}
public Stream> stream() {
return stream(false);
}
public Stream> stream(boolean parallel) {
return StreamSupport.stream(Spliterators.spliteratorUnknownSize(iterator(), 0), parallel);
}
public Iterator> iterator() {
return new Iterator>() {
Iterator>> rowIter = rowMap.entrySet().iterator();
Iterator> colIter = null;
private Map.Entry> rowEntry;
private Map.Entry colEntry;
private Entry next = getNext();
private Entry getNext() {
if (colIter == null || !colIter.hasNext()) {
if (!rowIter.hasNext()) {
return null;
}
rowEntry = rowIter.next();
colIter = rowEntry.getValue().entrySet().iterator();
}
if (!colIter.hasNext()) {
return null;
}
colEntry = colIter.next();
return new Node(rowEntry, colEntry);
}
@Override
public boolean hasNext() {
return this.next != null;
}
@Override
public Entry next() {
Entry entry = this.next;
this.next = getNext();
return entry;
}
@Override
public void remove() {
this.colIter.remove();
}
};
}
public void replaceAll(TableFunction function) {
for (Iterator> it = this.iterator(); it.hasNext(); ) {
Entry entry = it.next();
entry.setValue(function.compose(entry.getRow(), entry.getCol(), entry.getValue()));
}
}
public V remove(R row, C col) {
Map rowMap = this.rowMap.get(row);
if (rowMap == null) {
return null;
}
return rowMap.remove(col);
}
@Nullable
public V replace(R row, C col, V val) {
Map rowMap = getColMapIfExists(row);
if (rowMap == null) {
return null;
}
if (rowMap.get(col) != null || rowMap.containsKey(col)) {
return rowMap.put(col, val);
}
return null;
}
@Nullable
public boolean replace(R row, C col, V old, V val) {
Map rowMap = getColMapIfExists(row);
if (rowMap == null) {
return false;
}
if (Objects.equals(rowMap.get(col), old)) {
rowMap.put(col, val);
return true;
}
return false;
}
public V computeIfAbsent(R row, C col, BiFunction function) {
return getColMapForWrite(row).computeIfAbsent(col, c -> function.apply(row, col));
}
public V computeIfPresent(R row, C col, TableFunction function) {
Map colMap = getColMapForWrite(row);
V v = colMap.computeIfPresent(col, (c, old) -> function.compose(row, col, old));
removeIfEmpty(row, colMap);
return v;
}
public V compute(R row, C col, TableFunction function) {
Map colMap = getColMapForWrite(row);
V v = colMap.compute(col, (c, old) -> function.compose(row, col, old));
removeIfEmpty(row, colMap);
return v;
}
public V merge(R row, C col, V val, TableFunction function) {
Map colMap = getColMapForWrite(row);
V v = colMap.merge(col, val, (c, old) -> function.compose(row, col, old));
removeIfEmpty(row, colMap);
return v;
}
public Map row(R row) {
Map EMPTY = new HashMap<>(0);
return new DelegatingMap() {
@Override
public Map delegate(boolean readOnly) {
if (readOnly) {
return Table.this.rowMap.getOrDefault(row, EMPTY);
}
return getColMapForWrite(row);
}
@Override
public V remove(Object key) {
Map delegate = delegate(false);
V remove = delegate.remove(key);
removeIfEmpty(row, delegate);
return remove;
}
// iterators may leave us empty, but the next get will remove it.
};
}
// Other stuff
public interface TablePredicate {
boolean test(R row, C col, V val);
}
public interface TableFunction {
RETURN compose(R row, C col, V val);
}
public interface TableConsumer {
void accept(R row, C col, V val);
}
private V getIfExists(R row, C col) {
Map colMap = getColMapIfExists(row);
if (colMap == null) {
return null;
}
return colMap.get(col);
}
private Map getColMapIfExists(R row) {
Map colMap = this.rowMap.get(row);
if (colMap != null && colMap.isEmpty()) {
rowMap.remove(row);
colMap = null;
}
return colMap;
}
private Map getColMapForWrite(R row) {
return this.rowMap.computeIfAbsent(row, this.colMapSupplier);
}
private void removeIfEmpty(R row, Map colMap) {
if (colMap.isEmpty()) {
this.rowMap.remove(row);
}
}
public interface Entry {
R getRow();
C getCol();
V getValue();
V setValue(V value);
}
private class Node implements Entry {
private final Map.Entry> rowEntry;
private final Map.Entry colEntry;
Node(Map.Entry> rowEntry, Map.Entry entry) {
this.rowEntry = rowEntry;
this.colEntry = entry;
}
@Override
public final R getRow() {
return rowEntry.getKey();
}
@Override
public final C getCol() {
return colEntry.getKey();
}
@Override
public final V getValue() {
return colEntry.getValue();
}
@Override
public final V setValue(V value) {
return colEntry.setValue(value);
}
}
}