From 1a690dabd7f647c47a538fad159f90c5319084fc Mon Sep 17 00:00:00 2001 From: Luck Date: Mon, 4 Feb 2019 20:35:20 +0000 Subject: [PATCH] Implement NodeMap without using Guava's Multimap util (#1416) --- .../lucko/luckperms/common/model/NodeMap.java | 155 +++++++++++++----- 1 file changed, 112 insertions(+), 43 deletions(-) diff --git a/common/src/main/java/me/lucko/luckperms/common/model/NodeMap.java b/common/src/main/java/me/lucko/luckperms/common/model/NodeMap.java index a36a92cd..92fb99ea 100644 --- a/common/src/main/java/me/lucko/luckperms/common/model/NodeMap.java +++ b/common/src/main/java/me/lucko/luckperms/common/model/NodeMap.java @@ -25,11 +25,10 @@ package me.lucko.luckperms.common.model; -import com.google.common.base.Supplier; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSetMultimap; import com.google.common.collect.Multimap; -import com.google.common.collect.Multimaps; -import com.google.common.collect.SortedSetMultimap; import me.lucko.luckperms.api.LocalizedNode; import me.lucko.luckperms.api.Node; @@ -45,17 +44,22 @@ import me.lucko.luckperms.common.node.model.ImmutableLocalizedNode; import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; import java.util.Collection; +import java.util.Comparator; import java.util.Iterator; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.SortedMap; import java.util.SortedSet; import java.util.TreeSet; import java.util.concurrent.ConcurrentSkipListMap; import java.util.concurrent.ConcurrentSkipListSet; +import java.util.function.Function; import java.util.function.Predicate; /** @@ -68,8 +72,7 @@ import java.util.function.Predicate; *

Each holder has two of these maps, one for enduring and transient nodes.

*/ public final class NodeMap { - @SuppressWarnings("Guava") - private static final Supplier> VALUE_SET_SUPPLIER = () -> new ConcurrentSkipListSet<>(NodeComparator.reverse()); + private static final Function> VALUE_SET_SUPPLIER = k -> new ConcurrentSkipListSet<>(NodeComparator.reverse()); /** * The holder which this map is for @@ -84,23 +87,13 @@ public final class NodeMap { * key, and finally by the overall size of the set. Nodes are ordered according to the priority rules * defined in {@link NodeComparator}.

*/ - private final SortedSetMultimap map = Multimaps.synchronizedSortedSetMultimap( - Multimaps.newSortedSetMultimap( - new ConcurrentSkipListMap<>(ContextSetComparator.reverse()), - VALUE_SET_SUPPLIER - ) - ); + private final SortedMap> map = new ConcurrentSkipListMap<>(ContextSetComparator.reverse()); /** * Copy of {@link #map} which only contains group nodes * @see Node#isGroupNode() */ - private final SortedSetMultimap inheritanceMap = Multimaps.synchronizedSortedSetMultimap( - Multimaps.newSortedSetMultimap( - new ConcurrentSkipListMap<>(ContextSetComparator.reverse()), - VALUE_SET_SUPPLIER - ) - ); + private final SortedMap> inheritanceMap = new ConcurrentSkipListMap<>(ContextSetComparator.reverse()); /** * A cache which holds an immutable copy of the backing map @@ -112,11 +105,15 @@ public final class NodeMap { } public List asList() { - return new ArrayList<>(this.map.values()); + List list = new ArrayList<>(); + copyTo(list); + return list; } public LinkedHashSet asSet() { - return new LinkedHashSet<>(this.map.values()); + LinkedHashSet set = new LinkedHashSet<>(); + copyTo(set); + return set; } public SortedSet asSortedSet() { @@ -126,11 +123,13 @@ public final class NodeMap { } public void copyTo(Collection collection) { - collection.addAll(this.map.values()); + for (SortedSet nodes : this.map.values()) { + collection.addAll(nodes); + } } public void copyTo(Collection collection, ContextSet filter) { - for (Map.Entry> e : this.map.asMap().entrySet()) { + for (Map.Entry> e : this.map.entrySet()) { if (e.getKey().isSatisfiedBy(filter)) { collection.addAll(e.getValue()); } @@ -138,11 +137,13 @@ public final class NodeMap { } public void copyGroupNodesTo(Collection collection) { - collection.addAll(this.inheritanceMap.values()); + for (SortedSet nodes : this.inheritanceMap.values()) { + collection.addAll(nodes); + } } public void copyGroupNodesTo(Collection collection, ContextSet filter) { - for (Map.Entry> e : this.inheritanceMap.asMap().entrySet()) { + for (Map.Entry> e : this.inheritanceMap.entrySet()) { if (e.getKey().isSatisfiedBy(filter)) { collection.addAll(e.getValue()); } @@ -183,12 +184,12 @@ public final class NodeMap { ImmutableContextSet context = node.getFullContexts().makeImmutable(); LocalizedNode n = localise(node); - SortedSet nodesInContext = this.map.get(context); + SortedSet nodesInContext = this.map.computeIfAbsent(context, VALUE_SET_SUPPLIER); nodesInContext.removeIf(e -> e.equals(node, StandardNodeEquality.IGNORE_EXPIRY_TIME_AND_VALUE)); nodesInContext.add(n); if (node.isGroupNode()) { - SortedSet groupNodesInContext = this.inheritanceMap.get(context); + SortedSet groupNodesInContext = this.inheritanceMap.computeIfAbsent(context, VALUE_SET_SUPPLIER); groupNodesInContext.removeIf(e -> e.equals(node, StandardNodeEquality.IGNORE_EXPIRY_TIME_AND_VALUE)); if (node.getValue()) { groupNodesInContext.add(n); @@ -198,17 +199,33 @@ public final class NodeMap { void remove(Node node) { ImmutableContextSet context = node.getFullContexts().makeImmutable(); - this.map.get(context).removeIf(e -> e.equals(node, StandardNodeEquality.IGNORE_EXPIRY_TIME_AND_VALUE)); + SortedSet nodesInContext = this.map.get(context); + if (nodesInContext != null) { + nodesInContext.removeIf(e -> e.equals(node, StandardNodeEquality.IGNORE_EXPIRY_TIME_AND_VALUE)); + } + if (node.isGroupNode()) { - this.inheritanceMap.get(context).removeIf(e -> e.equals(node, StandardNodeEquality.IGNORE_EXPIRY_TIME_AND_VALUE)); + SortedSet groupNodesInContext = this.inheritanceMap.get(context); + if (groupNodesInContext != null) { + groupNodesInContext.removeIf(e -> e.equals(node, StandardNodeEquality.IGNORE_EXPIRY_TIME_AND_VALUE)); + } } } private void removeExact(Node node) { ImmutableContextSet context = node.getFullContexts().makeImmutable(); - this.map.remove(context, node); + SortedSet nodesInContext = this.map.get(context); + if (nodesInContext != null) { + //noinspection SuspiciousMethodCalls + nodesInContext.remove(node); + } + if (node.isGroupNode() && node.getValue()) { - this.inheritanceMap.remove(context, node); + SortedSet groupNodesInContext = this.inheritanceMap.get(context); + if (groupNodesInContext != null) { + //noinspection SuspiciousMethodCalls + groupNodesInContext.remove(node); + } } } @@ -224,8 +241,8 @@ public final class NodeMap { void clear(ContextSet contextSet) { ImmutableContextSet context = contextSet.makeImmutable(); - this.map.removeAll(context); - this.inheritanceMap.removeAll(context); + this.map.remove(context); + this.inheritanceMap.remove(context); } void setContent(Collection set) { @@ -241,34 +258,59 @@ public final class NodeMap { } boolean removeIf(Predicate predicate) { - boolean ret = this.map.values().removeIf(predicate); - this.inheritanceMap.values().removeIf(predicate); + boolean ret = false; + for (SortedSet valueSet : this.map.values()) { + if (valueSet.removeIf(predicate)) { + ret = true; + } + } + for (SortedSet valueSet : this.inheritanceMap.values()) { + valueSet.removeIf(predicate); + } return ret; } boolean removeIf(ContextSet contextSet, Predicate predicate) { ImmutableContextSet context = contextSet.makeImmutable(); - SortedSet nodes = this.map.get(context); - boolean ret = nodes.removeIf(predicate); - this.inheritanceMap.get(context).removeIf(predicate); + + boolean ret = false; + + SortedSet nodesInContext = this.map.get(context); + if (nodesInContext != null) { + ret = nodesInContext.removeIf(predicate); + } + + SortedSet groupNodesInContext = this.inheritanceMap.get(context); + if (groupNodesInContext != null) { + groupNodesInContext.removeIf(predicate); + } + return ret; } boolean auditTemporaryNodes(@Nullable Set removed) { boolean work = false; - Iterator it = this.map.values().iterator(); - while (it.hasNext()) { - LocalizedNode entry = it.next(); - if (entry.hasExpired()) { + for (SortedSet valueSet : this.map.values()) { + Iterator it = valueSet.iterator(); + while (it.hasNext()) { + LocalizedNode entry = it.next(); + if (!entry.hasExpired()) { + continue; + } + + // remove if (removed != null) { removed.add(entry); } if (entry.isGroupNode() && entry.getValue()) { - this.inheritanceMap.remove(entry.getFullContexts().makeImmutable(), entry); + SortedSet groupNodesInContext = this.inheritanceMap.get(entry.getFullContexts().makeImmutable()); + if (groupNodesInContext != null) { + groupNodesInContext.remove(entry); + } } - work = true; it.remove(); + work = true; } } @@ -276,6 +318,16 @@ public final class NodeMap { } private static final class NodeMapCache extends Cache> { + private static final Constructor IMMUTABLE_SET_MULTIMAP_CONSTRUCTOR; + static { + try { + IMMUTABLE_SET_MULTIMAP_CONSTRUCTOR = ImmutableSetMultimap.class.getDeclaredConstructor(ImmutableMap.class, int.class, Comparator.class); + IMMUTABLE_SET_MULTIMAP_CONSTRUCTOR.setAccessible(true); + } catch (NoSuchMethodException e) { + throw new ExceptionInInitializerError(e); + } + } + private final NodeMap handle; private NodeMapCache(NodeMap handle) { @@ -284,7 +336,24 @@ public final class NodeMap { @Override protected @NonNull ImmutableSetMultimap supply() { - return ImmutableSetMultimap.copyOf(this.handle.map); + ImmutableMap.Builder> builder = ImmutableMap.builder(); + int size = 0; + + for (Map.Entry> entry : this.handle.map.entrySet()) { + ImmutableContextSet key = entry.getKey(); + ImmutableSet values = ImmutableSet.copyOf(entry.getValue()); + if (!values.isEmpty()) { + builder.put(key, values); + size += values.size(); + } + } + + try { + //noinspection unchecked + return IMMUTABLE_SET_MULTIMAP_CONSTRUCTOR.newInstance(builder.build(), size, null); + } catch (InstantiationException | IllegalAccessException | InvocationTargetException e) { + throw new RuntimeException(e); + } } }