From 0bec93ab1c1a0bc1fcc53ca95d7780bf926a51f9 Mon Sep 17 00:00:00 2001 From: Luck Date: Mon, 11 Dec 2017 19:02:02 +0000 Subject: [PATCH] refactor context sets --- .../api/context/AbstractContextSet.java | 101 +++++++++ .../api/context/ImmutableContextSet.java | 205 ++++++++++++------ .../api/context/MutableContextSet.java | 93 +++----- 3 files changed, 266 insertions(+), 133 deletions(-) create mode 100644 api/src/main/java/me/lucko/luckperms/api/context/AbstractContextSet.java diff --git a/api/src/main/java/me/lucko/luckperms/api/context/AbstractContextSet.java b/api/src/main/java/me/lucko/luckperms/api/context/AbstractContextSet.java new file mode 100644 index 000000000..69e2b557a --- /dev/null +++ b/api/src/main/java/me/lucko/luckperms/api/context/AbstractContextSet.java @@ -0,0 +1,101 @@ +/* + * This file is part of LuckPerms, licensed under the MIT License. + * + * Copyright (c) lucko (Luck) + * Copyright (c) contributors + * + * 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 me.lucko.luckperms.api.context; + +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Multimap; + +import java.util.Collection; +import java.util.Set; + +import javax.annotation.Nonnull; + +import static com.google.common.base.Preconditions.checkNotNull; + +abstract class AbstractContextSet implements ContextSet { + + protected abstract Multimap backing(); + + @Nonnull + @Override + public boolean containsKey(@Nonnull String key) { + return backing().containsKey(sanitizeKey(key)); + } + + @Nonnull + @Override + public Set getValues(@Nonnull String key) { + Collection values = backing().asMap().get(sanitizeKey(key)); + return values != null ? ImmutableSet.copyOf(values) : ImmutableSet.of(); + } + + @Nonnull + @Override + public boolean has(@Nonnull String key, @Nonnull String value) { + return backing().containsEntry(sanitizeKey(key), sanitizeValue(value)); + } + + @Nonnull + @Override + public boolean hasIgnoreCase(@Nonnull String key, @Nonnull String value) { + String v = sanitizeValue(value); + + Collection values = backing().asMap().get(sanitizeKey(key)); + if (values == null || values.isEmpty()) { + return false; + } + + if (values.contains(v)) { + return true; + } + + for (String val : values) { + if (val.equalsIgnoreCase(v)) { + return true; + } + } + return false; + } + + @Override + public boolean isEmpty() { + return backing().isEmpty(); + } + + @Override + public int size() { + return backing().size(); + } + + static String sanitizeKey(String key) { + return checkNotNull(key, "key is null").toLowerCase().intern(); + } + + static String sanitizeValue(String value) { + return checkNotNull(value, "value is null").intern(); + } + +} diff --git a/api/src/main/java/me/lucko/luckperms/api/context/ImmutableContextSet.java b/api/src/main/java/me/lucko/luckperms/api/context/ImmutableContextSet.java index c350f65f6..d6b4e10b0 100644 --- a/api/src/main/java/me/lucko/luckperms/api/context/ImmutableContextSet.java +++ b/api/src/main/java/me/lucko/luckperms/api/context/ImmutableContextSet.java @@ -26,11 +26,10 @@ package me.lucko.luckperms.api.context; 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 java.util.Collection; +import java.util.Iterator; import java.util.Map; import java.util.Set; @@ -46,9 +45,19 @@ import static com.google.common.base.Preconditions.checkNotNull; * * @since 2.16 */ -public final class ImmutableContextSet implements ContextSet { +public final class ImmutableContextSet extends AbstractContextSet implements ContextSet { private static final ImmutableContextSet EMPTY = new ImmutableContextSet(ImmutableSetMultimap.of()); + /** + * Creates a builder + * + * @return a new ImmutableContextSet builder + */ + @Nonnull + public static Builder builder() { + return new Builder(); + } + /** * Creates an ImmutableContextSet from a context pair * @@ -59,10 +68,7 @@ public final class ImmutableContextSet implements ContextSet { */ @Nonnull public static ImmutableContextSet singleton(@Nonnull String key, @Nonnull String value) { - return new ImmutableContextSet(ImmutableSetMultimap.of( - checkNotNull(key, "key").toLowerCase().intern(), - checkNotNull(value, "value").intern() - )); + return new ImmutableContextSet(ImmutableSetMultimap.of(sanitizeKey(key), sanitizeValue(value))); } /** @@ -79,10 +85,10 @@ public final class ImmutableContextSet implements ContextSet { @Nonnull public static ImmutableContextSet of(@Nonnull String key1, @Nonnull String value1, @Nonnull String key2, @Nonnull String value2) { return new ImmutableContextSet(ImmutableSetMultimap.of( - checkNotNull(key1, "key1").toLowerCase().intern(), - checkNotNull(value1, "value1").intern(), - checkNotNull(key2, "key2").toLowerCase().intern(), - checkNotNull(value2, "value2").intern() + sanitizeKey(key1), + sanitizeValue(value1), + sanitizeKey(key2), + sanitizeValue(value2) )); } @@ -97,11 +103,16 @@ public final class ImmutableContextSet implements ContextSet { public static ImmutableContextSet fromEntries(@Nonnull Iterable> iterable) { checkNotNull(iterable, "iterable"); - ImmutableSetMultimap.Builder b = ImmutableSetMultimap.builder(); - for (Map.Entry e : iterable) { - b.put(checkNotNull(e.getKey()).toLowerCase().intern(), checkNotNull(e.getValue()).intern()); + Iterator> iterator = iterable.iterator(); + if (!iterator.hasNext()) { + return empty(); } + ImmutableSetMultimap.Builder b = ImmutableSetMultimap.builder(); + while (iterator.hasNext()) { + Map.Entry e = checkNotNull(iterator.next(), "entry"); + b.put(sanitizeKey(e.getKey()), sanitizeValue(e.getValue())); + } return new ImmutableContextSet(b.build()); } @@ -158,6 +169,11 @@ public final class ImmutableContextSet implements ContextSet { this.map = contexts; } + @Override + protected Multimap backing() { + return map; + } + @Override public boolean isImmutable() { return true; @@ -200,67 +216,21 @@ public final class ImmutableContextSet implements ContextSet { return map; } - @Nonnull - @Override - public boolean containsKey(@Nonnull String key) { - return map.containsKey(checkNotNull(key, "key").toLowerCase().intern()); - } - - @Nonnull - @Override - public Set getValues(@Nonnull String key) { - Collection values = map.get(checkNotNull(key, "key").toLowerCase().intern()); - return values != null ? ImmutableSet.copyOf(values) : ImmutableSet.of(); - } - - @Nonnull - @Override - public boolean has(@Nonnull String key, @Nonnull String value) { - return map.containsEntry(checkNotNull(key, "key").toLowerCase().intern(), checkNotNull(value, "value").intern()); - } - - @Nonnull - @Override - public boolean hasIgnoreCase(@Nonnull String key, @Nonnull String value) { - value = checkNotNull(value, "value").intern(); - Collection values = map.get(checkNotNull(key, "key").toLowerCase().intern()); - - if (values == null || values.isEmpty()) { - return false; - } - - for (String val : values) { - if (val.equalsIgnoreCase(value)) { - return true; - } - } - return false; - } - - @Override - public boolean isEmpty() { - return map.isEmpty(); - } - - @Override - public int size() { - return map.size(); - } - @Override public boolean equals(Object o) { if (o == this) return true; if (!(o instanceof ContextSet)) return false; final ContextSet other = (ContextSet) o; - // saves on copying the multimap + final Multimap otherContexts; + if (other instanceof MutableContextSet) { - return other.equals(this); + otherContexts = ((MutableContextSet) other).backing(); + } else { + otherContexts = other.toMultimap(); } - final Multimap thisContexts = this.toMultimap(); - final Multimap otherContexts = other.toMultimap(); - return thisContexts.equals(otherContexts); + return this.map.equals(otherContexts); } @Override @@ -272,4 +242,107 @@ public final class ImmutableContextSet implements ContextSet { public String toString() { return "ImmutableContextSet(contexts=" + this.map + ")"; } + + /** + * A builder for {@link ImmutableContextSet} + */ + public static final class Builder { + private final ImmutableSetMultimap.Builder builder = ImmutableSetMultimap.builder(); + + private Builder() { + + } + + /** + * Adds a new key value pair to the set + * + * @param key the key to add + * @param value the value to add + * @throws NullPointerException if the key or value is null + */ + @Nonnull + public Builder add(@Nonnull String key, @Nonnull String value) { + builder.put(sanitizeKey(key), sanitizeValue(value)); + return this; + } + + /** + * Adds a new key value pair to the set + * + * @param entry the entry to add + * @throws NullPointerException if the entry is null + */ + @Nonnull + public Builder add(@Nonnull Map.Entry entry) { + checkNotNull(entry, "entry"); + add(entry.getKey(), entry.getValue()); + return this; + } + + /** + * Adds an iterable containing contexts to the set + * + * @param iterable an iterable of key value context pairs + * @throws NullPointerException if iterable is null + */ + @Nonnull + public Builder addAll(@Nonnull Iterable> iterable) { + for (Map.Entry e : checkNotNull(iterable, "iterable")) { + add(e); + } + return this; + } + + /** + * Adds the entry set of a map to the set + * + * @param map the map to add from + * @throws NullPointerException if the map is null + */ + @Nonnull + public Builder addAll(@Nonnull Map map) { + addAll(checkNotNull(map, "map").entrySet()); + return this; + } + + /** + * Adds the entries of a multimap to the set + * + * @param multimap the multimap to add from + * @throws NullPointerException if the map is null + * @since 3.4 + */ + @Nonnull + public Builder addAll(@Nonnull Multimap multimap) { + addAll(checkNotNull(multimap, "multimap").entries()); + return this; + } + + /** + * Adds of of the values in another ContextSet to this set + * + * @param contextSet the set to add from + * @throws NullPointerException if the contextSet is null + */ + @Nonnull + public Builder addAll(@Nonnull ContextSet contextSet) { + checkNotNull(contextSet, "contextSet"); + if (contextSet instanceof MutableContextSet) { + MutableContextSet other = ((MutableContextSet) contextSet); + builder.putAll(other.backing()); + } else if (contextSet instanceof ImmutableContextSet) { + ImmutableContextSet other = ((ImmutableContextSet) contextSet); + builder.putAll(other.backing()); + } else { + addAll(contextSet.toMultimap()); + } + return this; + } + + @Nonnull + public ImmutableContextSet build() { + return new ImmutableContextSet(builder.build()); + } + + } } diff --git a/api/src/main/java/me/lucko/luckperms/api/context/MutableContextSet.java b/api/src/main/java/me/lucko/luckperms/api/context/MutableContextSet.java index 740b67c9b..bccf6f247 100644 --- a/api/src/main/java/me/lucko/luckperms/api/context/MutableContextSet.java +++ b/api/src/main/java/me/lucko/luckperms/api/context/MutableContextSet.java @@ -50,7 +50,7 @@ import static com.google.common.base.Preconditions.checkNotNull; * * @since 2.16 */ -public final class MutableContextSet implements ContextSet { +public final class MutableContextSet extends AbstractContextSet implements ContextSet { /** * Make a singleton MutableContextSet from a context pair @@ -86,7 +86,7 @@ public final class MutableContextSet implements ContextSet { checkNotNull(value1, "value1"); checkNotNull(key2, "key2"); checkNotNull(value2, "value2"); - MutableContextSet set = MutableContextSet.create(); + MutableContextSet set = create(); set.add(key1, value1); set.add(key2, value2); return set; @@ -102,7 +102,7 @@ public final class MutableContextSet implements ContextSet { @Nonnull public static MutableContextSet fromEntries(@Nonnull Iterable> iterable) { checkNotNull(iterable, "iterable"); - MutableContextSet set = MutableContextSet.create(); + MutableContextSet set = create(); set.addAll(iterable); return set; } @@ -117,7 +117,7 @@ public final class MutableContextSet implements ContextSet { @Nonnull public static MutableContextSet fromMap(@Nonnull Map map) { checkNotNull(map, "map"); - MutableContextSet set = MutableContextSet.create(); + MutableContextSet set = create(); set.addAll(map); return set; } @@ -132,7 +132,7 @@ public final class MutableContextSet implements ContextSet { @Nonnull public static MutableContextSet fromMultimap(@Nonnull Multimap multimap) { checkNotNull(multimap, "multimap"); - MutableContextSet set = MutableContextSet.create(); + MutableContextSet set = create(); set.addAll(multimap); return set; } @@ -148,8 +148,8 @@ public final class MutableContextSet implements ContextSet { @Nonnull public static MutableContextSet fromSet(@Nonnull ContextSet contextSet) { Preconditions.checkNotNull(contextSet, "contextSet"); - MutableContextSet set = new MutableContextSet(); - set.addAll(contextSet.toSet()); + MutableContextSet set = create(); + set.addAll(contextSet); return set; } @@ -173,6 +173,11 @@ public final class MutableContextSet implements ContextSet { this.map = Multimaps.synchronizedSetMultimap(HashMultimap.create(other.map)); } + @Override + protected Multimap backing() { + return map; + } + @Override public boolean isImmutable() { return false; @@ -181,6 +186,9 @@ public final class MutableContextSet implements ContextSet { @Nonnull @Override public ImmutableContextSet makeImmutable() { + if (map.isEmpty()) { + return ImmutableContextSet.empty(); + } return new ImmutableContextSet(ImmutableSetMultimap.copyOf(map)); } @@ -204,7 +212,6 @@ public final class MutableContextSet implements ContextSet { for (Map.Entry e : map.entries()) { m.put(e.getKey(), e.getValue()); } - return m.build(); } @@ -214,53 +221,6 @@ public final class MutableContextSet implements ContextSet { return ImmutableSetMultimap.copyOf(map); } - @Nonnull - @Override - public boolean containsKey(@Nonnull String key) { - return map.containsKey(checkNotNull(key, "key").toLowerCase().intern()); - } - - @Nonnull - @Override - public Set getValues(@Nonnull String key) { - Collection values = map.get(checkNotNull(key, "key").toLowerCase().intern()); - return values != null ? ImmutableSet.copyOf(values) : ImmutableSet.of(); - } - - @Nonnull - @Override - public boolean has(@Nonnull String key, @Nonnull String value) { - return map.containsEntry(checkNotNull(key, "key").toLowerCase().intern(), checkNotNull(value, "value").intern()); - } - - @Nonnull - @Override - public boolean hasIgnoreCase(@Nonnull String key, @Nonnull String value) { - value = checkNotNull(value, "value").intern(); - Collection values = map.get(checkNotNull(key, "key").toLowerCase().intern()); - - if (values == null || values.isEmpty()) { - return false; - } - - for (String val : values) { - if (val.equalsIgnoreCase(value)) { - return true; - } - } - return false; - } - - @Override - public boolean isEmpty() { - return map.isEmpty(); - } - - @Override - public int size() { - return map.size(); - } - /** * Adds a new key value pair to the set * @@ -269,7 +229,7 @@ public final class MutableContextSet implements ContextSet { * @throws NullPointerException if the key or value is null */ public void add(@Nonnull String key, @Nonnull String value) { - map.put(checkNotNull(key, "key").toLowerCase().intern(), checkNotNull(value, "value").intern()); + map.put(sanitizeKey(key), sanitizeValue(value)); } /** @@ -327,6 +287,9 @@ public final class MutableContextSet implements ContextSet { if (contextSet instanceof MutableContextSet) { MutableContextSet other = ((MutableContextSet) contextSet); this.map.putAll(other.map); + } else if (contextSet instanceof ImmutableContextSet) { + ImmutableContextSet other = ((ImmutableContextSet) contextSet); + this.map.putAll(other.backing()); } else { addAll(contextSet.toMultimap()); } @@ -340,11 +303,7 @@ public final class MutableContextSet implements ContextSet { * @throws NullPointerException if the key or value is null */ public void remove(@Nonnull String key, @Nonnull String value) { - String k = checkNotNull(key, "key").toLowerCase().intern(); - String v = checkNotNull(value, "value").intern(); - - //noinspection StringEquality - map.entries().removeIf(entry -> entry.getKey() == k && entry.getValue() == v); + map.remove(sanitizeKey(key), sanitizeValue(value)); } /** @@ -355,11 +314,11 @@ public final class MutableContextSet implements ContextSet { * @throws NullPointerException if the key or value is null */ public void removeIgnoreCase(@Nonnull String key, @Nonnull String value) { - String k = checkNotNull(key, "key").toLowerCase().intern(); - String v = checkNotNull(value, "value").intern(); - - //noinspection StringEquality - map.entries().removeIf(e -> e.getKey() == k && e.getValue().equalsIgnoreCase(v)); + String v = sanitizeValue(value); + Collection strings = map.asMap().get(sanitizeKey(key)); + if (strings != null) { + strings.removeIf(e -> e.equalsIgnoreCase(v)); + } } /** @@ -369,7 +328,7 @@ public final class MutableContextSet implements ContextSet { * @throws NullPointerException if the key is null */ public void removeAll(@Nonnull String key) { - map.removeAll(checkNotNull(key, "key").toLowerCase()); + map.removeAll(sanitizeKey(key)); } /**