refactor context sets

This commit is contained in:
Luck 2017-12-11 19:02:02 +00:00
parent dfb1b8f841
commit 0bec93ab1c
No known key found for this signature in database
GPG Key ID: EFA9B3EC5FD90F8B
3 changed files with 266 additions and 133 deletions

View File

@ -0,0 +1,101 @@
/*
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* 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<String, String> backing();
@Nonnull
@Override
public boolean containsKey(@Nonnull String key) {
return backing().containsKey(sanitizeKey(key));
}
@Nonnull
@Override
public Set<String> getValues(@Nonnull String key) {
Collection<String> 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<String> 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();
}
}

View File

@ -26,11 +26,10 @@
package me.lucko.luckperms.api.context; package me.lucko.luckperms.api.context;
import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSetMultimap; import com.google.common.collect.ImmutableSetMultimap;
import com.google.common.collect.Multimap; import com.google.common.collect.Multimap;
import java.util.Collection; import java.util.Iterator;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
@ -46,9 +45,19 @@ import static com.google.common.base.Preconditions.checkNotNull;
* *
* @since 2.16 * @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()); 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 * Creates an ImmutableContextSet from a context pair
* *
@ -59,10 +68,7 @@ public final class ImmutableContextSet implements ContextSet {
*/ */
@Nonnull @Nonnull
public static ImmutableContextSet singleton(@Nonnull String key, @Nonnull String value) { public static ImmutableContextSet singleton(@Nonnull String key, @Nonnull String value) {
return new ImmutableContextSet(ImmutableSetMultimap.of( return new ImmutableContextSet(ImmutableSetMultimap.of(sanitizeKey(key), sanitizeValue(value)));
checkNotNull(key, "key").toLowerCase().intern(),
checkNotNull(value, "value").intern()
));
} }
/** /**
@ -79,10 +85,10 @@ public final class ImmutableContextSet implements ContextSet {
@Nonnull @Nonnull
public static ImmutableContextSet of(@Nonnull String key1, @Nonnull String value1, @Nonnull String key2, @Nonnull String value2) { public static ImmutableContextSet of(@Nonnull String key1, @Nonnull String value1, @Nonnull String key2, @Nonnull String value2) {
return new ImmutableContextSet(ImmutableSetMultimap.of( return new ImmutableContextSet(ImmutableSetMultimap.of(
checkNotNull(key1, "key1").toLowerCase().intern(), sanitizeKey(key1),
checkNotNull(value1, "value1").intern(), sanitizeValue(value1),
checkNotNull(key2, "key2").toLowerCase().intern(), sanitizeKey(key2),
checkNotNull(value2, "value2").intern() sanitizeValue(value2)
)); ));
} }
@ -97,11 +103,16 @@ public final class ImmutableContextSet implements ContextSet {
public static ImmutableContextSet fromEntries(@Nonnull Iterable<? extends Map.Entry<String, String>> iterable) { public static ImmutableContextSet fromEntries(@Nonnull Iterable<? extends Map.Entry<String, String>> iterable) {
checkNotNull(iterable, "iterable"); checkNotNull(iterable, "iterable");
ImmutableSetMultimap.Builder<String, String> b = ImmutableSetMultimap.builder(); Iterator<? extends Map.Entry<String, String>> iterator = iterable.iterator();
for (Map.Entry<String, String> e : iterable) { if (!iterator.hasNext()) {
b.put(checkNotNull(e.getKey()).toLowerCase().intern(), checkNotNull(e.getValue()).intern()); return empty();
} }
ImmutableSetMultimap.Builder<String, String> b = ImmutableSetMultimap.builder();
while (iterator.hasNext()) {
Map.Entry<String, String> e = checkNotNull(iterator.next(), "entry");
b.put(sanitizeKey(e.getKey()), sanitizeValue(e.getValue()));
}
return new ImmutableContextSet(b.build()); return new ImmutableContextSet(b.build());
} }
@ -158,6 +169,11 @@ public final class ImmutableContextSet implements ContextSet {
this.map = contexts; this.map = contexts;
} }
@Override
protected Multimap<String, String> backing() {
return map;
}
@Override @Override
public boolean isImmutable() { public boolean isImmutable() {
return true; return true;
@ -200,67 +216,21 @@ public final class ImmutableContextSet implements ContextSet {
return map; return map;
} }
@Nonnull
@Override
public boolean containsKey(@Nonnull String key) {
return map.containsKey(checkNotNull(key, "key").toLowerCase().intern());
}
@Nonnull
@Override
public Set<String> getValues(@Nonnull String key) {
Collection<String> 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<String> 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 @Override
public boolean equals(Object o) { public boolean equals(Object o) {
if (o == this) return true; if (o == this) return true;
if (!(o instanceof ContextSet)) return false; if (!(o instanceof ContextSet)) return false;
final ContextSet other = (ContextSet) o; final ContextSet other = (ContextSet) o;
// saves on copying the multimap final Multimap<String, String> otherContexts;
if (other instanceof MutableContextSet) { if (other instanceof MutableContextSet) {
return other.equals(this); otherContexts = ((MutableContextSet) other).backing();
} else {
otherContexts = other.toMultimap();
} }
final Multimap<String, String> thisContexts = this.toMultimap(); return this.map.equals(otherContexts);
final Multimap<String, String> otherContexts = other.toMultimap();
return thisContexts.equals(otherContexts);
} }
@Override @Override
@ -272,4 +242,107 @@ public final class ImmutableContextSet implements ContextSet {
public String toString() { public String toString() {
return "ImmutableContextSet(contexts=" + this.map + ")"; return "ImmutableContextSet(contexts=" + this.map + ")";
} }
/**
* A builder for {@link ImmutableContextSet}
*/
public static final class Builder {
private final ImmutableSetMultimap.Builder<String, String> 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<String, String> 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<? extends Map.Entry<String, String>> iterable) {
for (Map.Entry<String, String> 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<String, String> 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<String, String> 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());
}
}
} }

View File

@ -50,7 +50,7 @@ import static com.google.common.base.Preconditions.checkNotNull;
* *
* @since 2.16 * @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 * Make a singleton MutableContextSet from a context pair
@ -86,7 +86,7 @@ public final class MutableContextSet implements ContextSet {
checkNotNull(value1, "value1"); checkNotNull(value1, "value1");
checkNotNull(key2, "key2"); checkNotNull(key2, "key2");
checkNotNull(value2, "value2"); checkNotNull(value2, "value2");
MutableContextSet set = MutableContextSet.create(); MutableContextSet set = create();
set.add(key1, value1); set.add(key1, value1);
set.add(key2, value2); set.add(key2, value2);
return set; return set;
@ -102,7 +102,7 @@ public final class MutableContextSet implements ContextSet {
@Nonnull @Nonnull
public static MutableContextSet fromEntries(@Nonnull Iterable<? extends Map.Entry<String, String>> iterable) { public static MutableContextSet fromEntries(@Nonnull Iterable<? extends Map.Entry<String, String>> iterable) {
checkNotNull(iterable, "iterable"); checkNotNull(iterable, "iterable");
MutableContextSet set = MutableContextSet.create(); MutableContextSet set = create();
set.addAll(iterable); set.addAll(iterable);
return set; return set;
} }
@ -117,7 +117,7 @@ public final class MutableContextSet implements ContextSet {
@Nonnull @Nonnull
public static MutableContextSet fromMap(@Nonnull Map<String, String> map) { public static MutableContextSet fromMap(@Nonnull Map<String, String> map) {
checkNotNull(map, "map"); checkNotNull(map, "map");
MutableContextSet set = MutableContextSet.create(); MutableContextSet set = create();
set.addAll(map); set.addAll(map);
return set; return set;
} }
@ -132,7 +132,7 @@ public final class MutableContextSet implements ContextSet {
@Nonnull @Nonnull
public static MutableContextSet fromMultimap(@Nonnull Multimap<String, String> multimap) { public static MutableContextSet fromMultimap(@Nonnull Multimap<String, String> multimap) {
checkNotNull(multimap, "multimap"); checkNotNull(multimap, "multimap");
MutableContextSet set = MutableContextSet.create(); MutableContextSet set = create();
set.addAll(multimap); set.addAll(multimap);
return set; return set;
} }
@ -148,8 +148,8 @@ public final class MutableContextSet implements ContextSet {
@Nonnull @Nonnull
public static MutableContextSet fromSet(@Nonnull ContextSet contextSet) { public static MutableContextSet fromSet(@Nonnull ContextSet contextSet) {
Preconditions.checkNotNull(contextSet, "contextSet"); Preconditions.checkNotNull(contextSet, "contextSet");
MutableContextSet set = new MutableContextSet(); MutableContextSet set = create();
set.addAll(contextSet.toSet()); set.addAll(contextSet);
return set; return set;
} }
@ -173,6 +173,11 @@ public final class MutableContextSet implements ContextSet {
this.map = Multimaps.synchronizedSetMultimap(HashMultimap.create(other.map)); this.map = Multimaps.synchronizedSetMultimap(HashMultimap.create(other.map));
} }
@Override
protected Multimap<String, String> backing() {
return map;
}
@Override @Override
public boolean isImmutable() { public boolean isImmutable() {
return false; return false;
@ -181,6 +186,9 @@ public final class MutableContextSet implements ContextSet {
@Nonnull @Nonnull
@Override @Override
public ImmutableContextSet makeImmutable() { public ImmutableContextSet makeImmutable() {
if (map.isEmpty()) {
return ImmutableContextSet.empty();
}
return new ImmutableContextSet(ImmutableSetMultimap.copyOf(map)); return new ImmutableContextSet(ImmutableSetMultimap.copyOf(map));
} }
@ -204,7 +212,6 @@ public final class MutableContextSet implements ContextSet {
for (Map.Entry<String, String> e : map.entries()) { for (Map.Entry<String, String> e : map.entries()) {
m.put(e.getKey(), e.getValue()); m.put(e.getKey(), e.getValue());
} }
return m.build(); return m.build();
} }
@ -214,53 +221,6 @@ public final class MutableContextSet implements ContextSet {
return ImmutableSetMultimap.copyOf(map); return ImmutableSetMultimap.copyOf(map);
} }
@Nonnull
@Override
public boolean containsKey(@Nonnull String key) {
return map.containsKey(checkNotNull(key, "key").toLowerCase().intern());
}
@Nonnull
@Override
public Set<String> getValues(@Nonnull String key) {
Collection<String> 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<String> 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 * 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 * @throws NullPointerException if the key or value is null
*/ */
public void add(@Nonnull String key, @Nonnull String value) { 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) { if (contextSet instanceof MutableContextSet) {
MutableContextSet other = ((MutableContextSet) contextSet); MutableContextSet other = ((MutableContextSet) contextSet);
this.map.putAll(other.map); this.map.putAll(other.map);
} else if (contextSet instanceof ImmutableContextSet) {
ImmutableContextSet other = ((ImmutableContextSet) contextSet);
this.map.putAll(other.backing());
} else { } else {
addAll(contextSet.toMultimap()); addAll(contextSet.toMultimap());
} }
@ -340,11 +303,7 @@ public final class MutableContextSet implements ContextSet {
* @throws NullPointerException if the key or value is null * @throws NullPointerException if the key or value is null
*/ */
public void remove(@Nonnull String key, @Nonnull String value) { public void remove(@Nonnull String key, @Nonnull String value) {
String k = checkNotNull(key, "key").toLowerCase().intern(); map.remove(sanitizeKey(key), sanitizeValue(value));
String v = checkNotNull(value, "value").intern();
//noinspection StringEquality
map.entries().removeIf(entry -> entry.getKey() == k && entry.getValue() == v);
} }
/** /**
@ -355,11 +314,11 @@ public final class MutableContextSet implements ContextSet {
* @throws NullPointerException if the key or value is null * @throws NullPointerException if the key or value is null
*/ */
public void removeIgnoreCase(@Nonnull String key, @Nonnull String value) { public void removeIgnoreCase(@Nonnull String key, @Nonnull String value) {
String k = checkNotNull(key, "key").toLowerCase().intern(); String v = sanitizeValue(value);
String v = checkNotNull(value, "value").intern(); Collection<String> strings = map.asMap().get(sanitizeKey(key));
if (strings != null) {
//noinspection StringEquality strings.removeIf(e -> e.equalsIgnoreCase(v));
map.entries().removeIf(e -> e.getKey() == k && e.getValue().equalsIgnoreCase(v)); }
} }
/** /**
@ -369,7 +328,7 @@ public final class MutableContextSet implements ContextSet {
* @throws NullPointerException if the key is null * @throws NullPointerException if the key is null
*/ */
public void removeAll(@Nonnull String key) { public void removeAll(@Nonnull String key) {
map.removeAll(checkNotNull(key, "key").toLowerCase()); map.removeAll(sanitizeKey(key));
} }
/** /**