All Downloads are FREE. Search and download functionalities are using the official Maven repository.

co.aikar.util.Table Maven / Gradle / Ivy

The newest version!
/*
 * 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);
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy