1
0
mirror of https://github.com/lucko/LuckPerms.git synced 2025-09-03 03:12:46 +02:00

Refactor contexts, expose cached data in the API & release 2.13

This commit is contained in:
Luck
2016-10-23 16:46:53 +01:00
parent 33c78e4a17
commit 383276f47a
52 changed files with 1263 additions and 517 deletions

View File

@@ -5,7 +5,7 @@
<parent>
<artifactId>luckperms</artifactId>
<groupId>me.lucko.luckperms</groupId>
<version>2.12-SNAPSHOT</version>
<version>2.13-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@@ -22,9 +22,8 @@
package me.lucko.luckperms.api;
import com.google.common.collect.ImmutableMap;
import me.lucko.luckperms.api.context.ContextSet;
import java.util.Collections;
import java.util.Map;
/**
@@ -41,23 +40,19 @@ public class Contexts {
* @return a context that will not apply any filters
*/
public static Contexts allowAll() {
return new Contexts(Collections.emptyMap(), true, true, true, true, true, true);
return new Contexts(ContextSet.empty(), true, true, true, true, true, true);
}
public static Contexts of(Map<String, String> context, boolean includeGlobal, boolean includeGlobalWorld, boolean applyGroups, boolean applyGlobalGroups, boolean applyGlobalWorldGroups) {
return new Contexts(context, includeGlobal, includeGlobalWorld, applyGroups, applyGlobalGroups, applyGlobalWorldGroups);
}
public static Contexts of(Map<String, String> context, boolean includeGlobal, boolean includeGlobalWorld, boolean applyGroups, boolean applyGlobalGroups, boolean applyGlobalWorldGroups, boolean op) {
public static Contexts of(ContextSet context, boolean includeGlobal, boolean includeGlobalWorld, boolean applyGroups, boolean applyGlobalGroups, boolean applyGlobalWorldGroups, boolean op) {
return new Contexts(context, includeGlobal, includeGlobalWorld, applyGroups, applyGlobalGroups, applyGlobalWorldGroups, op);
}
public Contexts(Map<String, String> context, boolean includeGlobal, boolean includeGlobalWorld, boolean applyGroups, boolean applyGlobalGroups, boolean applyGlobalWorldGroups, boolean op) {
public Contexts(ContextSet context, boolean includeGlobal, boolean includeGlobalWorld, boolean applyGroups, boolean applyGlobalGroups, boolean applyGlobalWorldGroups, boolean op) {
if (context == null) {
throw new NullPointerException("context");
}
this.context = ImmutableMap.copyOf(context);
this.context = context.makeImmutable();
this.includeGlobal = includeGlobal;
this.includeGlobalWorld = includeGlobalWorld;
this.applyGroups = applyGroups;
@@ -66,16 +61,34 @@ public class Contexts {
this.op = op;
}
@SuppressWarnings("deprecation")
@Deprecated
public static Contexts of(Map<String, String> context, boolean includeGlobal, boolean includeGlobalWorld, boolean applyGroups, boolean applyGlobalGroups, boolean applyGlobalWorldGroups) {
return new Contexts(context, includeGlobal, includeGlobalWorld, applyGroups, applyGlobalGroups, applyGlobalWorldGroups);
}
@SuppressWarnings("deprecation")
@Deprecated
public static Contexts of(Map<String, String> context, boolean includeGlobal, boolean includeGlobalWorld, boolean applyGroups, boolean applyGlobalGroups, boolean applyGlobalWorldGroups, boolean op) {
return new Contexts(context, includeGlobal, includeGlobalWorld, applyGroups, applyGlobalGroups, applyGlobalWorldGroups, op);
}
@Deprecated
public Contexts(Map<String, String> context, boolean includeGlobal, boolean includeGlobalWorld, boolean applyGroups, boolean applyGlobalGroups, boolean applyGlobalWorldGroups, boolean op) {
this(context == null ? null : ContextSet.fromMap(context), includeGlobal, includeGlobalWorld, applyGroups, applyGlobalGroups, applyGlobalWorldGroups, op);
}
@SuppressWarnings("deprecation")
@Deprecated
public Contexts(Map<String, String> context, boolean includeGlobal, boolean includeGlobalWorld, boolean applyGroups, boolean applyGlobalGroups, boolean applyGlobalWorldGroups) {
this(context, includeGlobal, includeGlobalWorld, applyGroups, applyGlobalGroups, applyGlobalWorldGroups, false);
}
/**
* The contexts that apply for this lookup
*
* The keys for servers and worlds are defined as static values.
*/
private final Map<String, String> context;
private final ContextSet context;
/**
* The mode to parse defaults on Bukkit
@@ -110,12 +123,23 @@ public class Contexts {
/**
* Gets the contexts that apply for this lookup
* @return an immutable map of context key value pairs
* @return an immutable set of context key value pairs
* @since 2.13
*/
public Map<String, String> getContext() {
public ContextSet getContexts() {
return this.context;
}
/**
* Gets the contexts that apply for this lookup
* @return an immutable map of context key value pairs
* @deprecated in favour of {@link #getContexts()}
*/
@Deprecated
public Map<String, String> getContext() {
return this.context.toMap();
}
/**
* Gets if OP defaults should be included
* @return true if op defaults should be included
@@ -166,7 +190,7 @@ public class Contexts {
public String toString() {
return "Contexts(" +
"context=" + this.getContext() + ", " +
"context=" + this.getContexts() + ", " +
"op=" + this.isOp() + ", " +
"includeGlobal=" + this.isIncludeGlobal() + ", " +
"includeGlobalWorld=" + this.isIncludeGlobalWorld() + ", " +
@@ -190,8 +214,8 @@ public class Contexts {
if (o == this) return true;
if (!(o instanceof Contexts)) return false;
final Contexts other = (Contexts) o;
final Object this$context = this.getContext();
final Object other$context = other.getContext();
final Object this$context = this.getContexts();
final Object other$context = other.getContexts();
if (this$context == null ? other$context != null : !this$context.equals(other$context)) return false;
if (this.isOp() != other.isOp()) return false;
if (this.isIncludeGlobal() != other.isIncludeGlobal()) return false;
@@ -210,7 +234,7 @@ public class Contexts {
public int hashCode() {
final int PRIME = 59;
int result = 1;
final Object $context = this.getContext();
final Object $context = this.getContexts();
result = result * PRIME + ($context == null ? 43 : $context.hashCode());
result = result * PRIME + (this.isOp() ? 79 : 97);
result = result * PRIME + (this.isIncludeGlobal() ? 79 : 97);

View File

@@ -258,7 +258,7 @@ public class MetaUtils {
int priority = Integer.MIN_VALUE;
String meta = null;
for (Node n : holder.getAllNodes()) {
for (Node n : holder.getAllNodes(Contexts.allowAll())) {
if (!n.getValue()) {
continue;
}

View File

@@ -22,10 +22,9 @@
package me.lucko.luckperms.api;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import me.lucko.luckperms.api.context.ContextSet;
import java.util.*;
/**
* Represents an immutable node object
@@ -109,15 +108,40 @@ public interface Node extends Map.Entry<String, Boolean> {
* @param context the context key value pairs
* @param worldAndServer if world and server contexts should be checked
* @return true if the node should apply
* @since 2.13
*/
boolean shouldApplyWithContext(Map<String, String> context, boolean worldAndServer);
boolean shouldApplyWithContext(ContextSet context, boolean worldAndServer);
/**
* If this node should apply in the given context
* @param context the context key value pairs
* @return true if the node should apply
* @since 2.13
*/
boolean shouldApplyWithContext(Map<String, String> context);
boolean shouldApplyWithContext(ContextSet context);
/**
* If this node should apply in the given context
* @param context the context key value pairs
* @param worldAndServer if world and server contexts should be checked
* @return true if the node should apply
* @deprecated in favour of {@link #shouldApplyWithContext(ContextSet, boolean)}
*/
@Deprecated
default boolean shouldApplyWithContext(Map<String, String> context, boolean worldAndServer) {
return shouldApplyWithContext(ContextSet.fromMap(context), worldAndServer);
}
/**
* If this node should apply in the given context
* @param context the context key value pairs
* @return true if the node should apply
* @deprecated in favour of {@link #shouldApplyWithContext(ContextSet)}
*/
@Deprecated
default boolean shouldApplyWithContext(Map<String, String> context) {
return shouldApplyWithContext(ContextSet.fromMap(context));
}
/**
* Similar to {@link #shouldApplyOnServer(String, boolean, boolean)}, except this method accepts a List
@@ -186,8 +210,18 @@ public interface Node extends Map.Entry<String, Boolean> {
/**
* @return the extra contexts required for this node to apply
* @deprecated in favour of {@link #getContexts()}
*/
Map<String, String> getExtraContexts();
@Deprecated
default Map<String, String> getExtraContexts() {
return getContexts().toMap();
}
/**
* @return the extra contexts required for this node to apply
* @since 2.13
*/
ContextSet getContexts();
/**
* Converts this node into a serialized form
@@ -302,7 +336,9 @@ public interface Node extends Map.Entry<String, Boolean> {
Builder setServer(String server) throws IllegalArgumentException;
Builder withExtraContext(String key, String value);
Builder withExtraContext(Map<String, String> map);
Builder withExtraContext(Set<Map.Entry<String, String>> context);
Builder withExtraContext(Map.Entry<String, String> entry);
Builder withExtraContext(ContextSet set);
Node build();
}

View File

@@ -22,10 +22,12 @@
package me.lucko.luckperms.api;
import me.lucko.luckperms.api.caching.UserData;
import me.lucko.luckperms.exceptions.ObjectAlreadyHasException;
import me.lucko.luckperms.exceptions.ObjectLacksException;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
/**
@@ -64,6 +66,13 @@ public interface User extends PermissionHolder {
*/
void refreshPermissions();
/**
* Gets the user's {@link UserData} cache, if they have one setup.
* @return an optional, possibly containing the user's cached lookup data.
* @since 2.13
*/
Optional<UserData> getUserDataCache();
/**
* Check to see if the user is a member of a group
* @param group The group to check membership of

View File

@@ -0,0 +1,64 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
*
* 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.caching;
import java.util.Map;
import java.util.SortedMap;
/**
* Holds cached Meta lookup data for a specific set of contexts
* @since 2.13
*/
public interface MetaData {
/**
* Gets an immutable copy of the meta this user has
* @return an immutable map of meta
*/
Map<String, String> getMeta();
/**
* Gets an immutable sorted map of all of the prefixes the user has, whereby the first value is the highest priority prefix.
* @return a sorted map of prefixes
*/
SortedMap<Integer, String> getPrefixes();
/**
* Gets an immutable sorted map of all of the suffixes the user has, whereby the first value is the highest priority suffix.
* @return a sorted map of suffixes
*/
SortedMap<Integer, String> getSuffixes();
/**
* Gets the user's highest priority prefix, or null if the user has no prefixes
* @return a prefix string, or null
*/
String getPrefix();
/**
* Gets the user's highest priority suffix, or null if the user has no suffixes
* @return a suffix string, or null
*/
String getSuffix();
}

View File

@@ -0,0 +1,55 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
*
* 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.caching;
import me.lucko.luckperms.api.Tristate;
import java.util.Map;
/**
* Holds cached Permission lookup data for a specific set of contexts
* @since 2.13
*/
public interface PermissionData {
/**
* Gets a permission value for the given permission node
* @param permission the permission node
* @return a tristate result
* @throws NullPointerException if permission is null
*/
Tristate getPermissionValue(String permission);
/**
* Invalidates the underlying permission calculator cache.
* Can be called to allow for an update in defaults.
*/
void invalidateCache();
/**
* Gets an immutable copy of the permission map backing the permission calculator
* @return an immutable set of permissions
*/
Map<String, Boolean> getImmutableBacking();
}

View File

@@ -0,0 +1,119 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
*
* 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.caching;
import me.lucko.luckperms.api.Contexts;
import java.util.Set;
/**
* Holds cached permission and meta lookup data for a {@link me.lucko.luckperms.api.User}.
* Data is only likely to be available for online users. All calls will account for inheritance, as well as any
* default data provided by the platform. This calls are heavily cached and are therefore fast.
*
* @since 2.13
*/
public interface UserData {
/**
* Gets PermissionData from the cache, given a specified context.
* If the data is not cached, it is calculated. Therefore, this call could be costly.
* @param contexts the contexts to get the permission data in
* @return a permission data instance
* @throws NullPointerException if contexts is null
*/
PermissionData getPermissionData(Contexts contexts);
/**
* Gets MetaData from the cache, given a specified context.
* If the data is not cached, it is calculated. Therefore, this call could be costly.
* @param contexts the contexts to get the permission data in
* @return a meta data instance
* @throws NullPointerException if contexts is null
*/
MetaData getMetaData(Contexts contexts);
/**
* Calculates permission data, bypassing the cache.
* @param contexts the contexts to get permission data in
* @return a permission data instance
* @throws NullPointerException if contexts is null
*/
PermissionData calculatePermissions(Contexts contexts);
/**
* Calculates meta data, bypassing the cache.
* @param contexts the contexts to get meta data in
* @return a meta data instance
* @throws NullPointerException if contexts is null
*/
MetaData calculateMeta(Contexts contexts);
/**
* Calculates permission data and stores it in the cache. If there is already data cached for the given contexts,
* and if the resultant output is different, the cached value is updated.
* @param contexts the contexts to recalculate in.
* @throws NullPointerException if contexts is null
*/
void recalculatePermissions(Contexts contexts);
/**
* Calculates meta data and stores it in the cache. If there is already data cached for the given contexts,
* and if the resultant output is different, the cached value is updated.
* @param contexts the contexts to recalculate in.
* @throws NullPointerException if contexts is null
*/
void recalculateMeta(Contexts contexts);
/**
* Calls {@link #recalculatePermissions(Contexts)} for all current loaded contexts
*/
void recalculatePermissions();
/**
* Calls {@link #recalculateMeta(Contexts)} for all current loaded contexts
*/
void recalculateMeta();
/**
* Calls {@link #preCalculate(Contexts)} for the given contexts
* @param contexts a set of contexts
* @throws NullPointerException if contexts is null
*/
void preCalculate(Set<Contexts> contexts);
/**
* Ensures that PermissionData and MetaData is cached for a context. If the cache does not contain any data for the
* context, it will be calculated and saved.
* @param contexts the contexts to pre-calculate for
* @throws NullPointerException if contexts is null
*/
void preCalculate(Contexts contexts);
/**
* Invalidates all of the underlying Permission calculators.
* Can be called to allow for an update in defaults.
*/
void invalidatePermissionCalculators();
}

View File

@@ -0,0 +1,306 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
*
* 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.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
/**
* Holds contexts.
* All contained contexts are immutable, and unlike {@link MutableContextSet}, contexts cannot be added or removed.
*
* @since 2.13
*/
public class ContextSet {
/**
* Make a singleton ContextSet from a context pair
* @param key the key
* @param value the value
* @return a new ContextSet containing one KV pair
* @throws NullPointerException if key or value is null
*/
public static ContextSet singleton(String key, String value) {
if (key == null) {
throw new NullPointerException("key");
}
if (value == null) {
throw new NullPointerException("value");
}
MutableContextSet set = new MutableContextSet();
set.add(key, value);
return set.immutableCopy();
}
/**
* Creates a ContextSet from an existing map
* @param map the map to copy from
* @return a new ContextSet representing the pairs from the map
* @throws NullPointerException if the map is null
*/
public static ContextSet fromMap(Map<String, String> map) {
if (map == null) {
throw new NullPointerException("map");
}
MutableContextSet set = new MutableContextSet();
set.addAll(map);
return set.immutableCopy();
}
/**
* Creates a ContextSet from an existing iterable of Map Entries
* @param iterable the iterable to copy from
* @return a new ContextSet representing the pairs in the iterable
* @throws NullPointerException if the iterable is null
*/
public static ContextSet fromEntries(Iterable<Map.Entry<String, String>> iterable) {
if (iterable == null) {
throw new NullPointerException("iterable");
}
MutableContextSet set = new MutableContextSet();
set.addAll(iterable);
return set.immutableCopy();
}
/**
* Creates a new ContextSet from an existing set.
* Only really useful for converting between mutable and immutable types.
* @param contextSet the context set to copy from
* @return a new ContextSet with the same content and the one provided
* @throws NullPointerException if contextSet is null
*/
public static ContextSet fromSet(ContextSet contextSet) {
if (contextSet == null) {
throw new NullPointerException("contextSet");
}
MutableContextSet set = new MutableContextSet();
set.addAll(contextSet.toSet());
return set.immutableCopy();
}
/**
* Creates a new empty ContextSet.
* @return a new ContextSet
*/
public static ContextSet empty() {
return new ContextSet();
}
final Set<Map.Entry<String, String>> contexts;
public ContextSet() {
this.contexts = new HashSet<>();
}
protected ContextSet(Set<Map.Entry<String, String>> contexts) {
this.contexts = contexts;
}
/**
* Check to see if this set is in an immutable form
* @return true if the set is immutable
*/
public boolean isImmutable() {
return true;
}
/**
* If the set is mutable, this method will return an immutable copy. Otherwise just returns itself.
* @return an immutable ContextSet
*/
public ContextSet makeImmutable() {
return this;
}
/**
* Converts this ContextSet to an immutable {@link Set} of {@link Map.Entry}s.
* @return an immutable set
*/
public Set<Map.Entry<String, String>> toSet() {
synchronized (contexts) {
return ImmutableSet.copyOf(contexts);
}
}
/**
* Converts this ContextSet to an immutable {@link Map}
*
* <b>NOTE: Use of this method may result in data being lost. ContextSets can contain lots of different values for
* one key.</b>
*
* @return an immutable map
*/
public Map<String, String> toMap() {
synchronized (contexts) {
return ImmutableMap.copyOf(contexts.stream().collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)));
}
}
/**
* Check if the set contains at least one value for the given key.
* @param key the key to check for
* @return true if the set contains a value for the key
* @throws NullPointerException if the key is null
*/
public boolean containsKey(String key) {
if (key == null) {
throw new NullPointerException("key");
}
synchronized (contexts) {
for (Map.Entry<String, String> e : contexts) {
if (e.getKey().equalsIgnoreCase(key)) {
return true;
}
}
}
return false;
}
/**
* Gets a set of all of the values mapped to the given key
* @param key the key to find values for
* @return a set of values
* @throws NullPointerException if the key is null
*/
public Set<String> getValues(String key) {
if (key == null) {
throw new NullPointerException("key");
}
synchronized (contexts) {
return ImmutableSet.copyOf(contexts.stream()
.filter(e -> e.getKey().equalsIgnoreCase(key))
.map(Map.Entry::getValue)
.collect(Collectors.toSet())
);
}
}
/**
* Check if thr set contains a given key mapped to a given value
* @param key the key to look for
* @param value the value to look for (case sensitive)
* @return true if the set contains the KV pair
* @throws NullPointerException if the key or value is null
*/
public boolean has(String key, String value) {
if (key == null) {
throw new NullPointerException("key");
}
if (value == null) {
throw new NullPointerException("value");
}
synchronized (contexts) {
for (Map.Entry<String, String> e : contexts) {
if (!e.getKey().equalsIgnoreCase(key)) {
continue;
}
if (!e.getValue().equals(value)) {
continue;
}
return true;
}
}
return false;
}
/**
* Same as {@link #has(String, String)}, except ignores the case of the value.
* @param key the key to look for
* @param value the value to look for
* @return true if the set contains the KV pair
* @throws NullPointerException if the key or value is null
*/
public boolean hasIgnoreCase(String key, String value) {
if (key == null) {
throw new NullPointerException("key");
}
if (value == null) {
throw new NullPointerException("value");
}
synchronized (contexts) {
for (Map.Entry<String, String> e : contexts) {
if (!e.getKey().equalsIgnoreCase(key)) {
continue;
}
if (!e.getValue().equalsIgnoreCase(value)) {
continue;
}
return true;
}
}
return false;
}
/**
* Check if the set is empty
* @return true if the set is empty
*/
public boolean isEmpty() {
synchronized (contexts) {
return contexts.isEmpty();
}
}
/**
* Gets the number of key-value context pairs in the set
* @return the size of the set
*/
public int size() {
synchronized (contexts) {
return contexts.size();
}
}
@Override
public boolean equals(Object o) {
if (o == this) return true;
if (!(o instanceof ContextSet)) return false;
final ContextSet other = (ContextSet) o;
final Object thisContexts = this.contexts;
final Object otherContexts = other.contexts;
return thisContexts == null ? otherContexts == null : thisContexts.equals(otherContexts);
}
@Override
public int hashCode() {
return 59 + (this.contexts == null ? 43 : this.contexts.hashCode());
}
}

View File

@@ -22,6 +22,7 @@
package me.lucko.luckperms.api.context;
import java.util.HashMap;
import java.util.Map;
/**
@@ -34,11 +35,40 @@ public interface IContextCalculator<T> {
/**
* Gives the subject all of the applicable contexts they meet
* @param subject the subject to add contexts tp
* @param subject the subject to add contexts to
* @param accumulator a map of contexts to add to
* @return the map
* @deprecated in favour of {@link #giveApplicableContext(Object, MutableContextSet)}. Older implementations of this interface
* will still work, as the replacement method is given as a default, and falls back to using this method.
*/
Map<String, String> giveApplicableContext(T subject, Map<String, String> accumulator);
@Deprecated
default Map<String, String> giveApplicableContext(T subject, Map<String, String> accumulator) {
MutableContextSet acc = new MutableContextSet();
giveApplicableContext(subject, acc);
accumulator.putAll(acc.toMap());
return accumulator;
}
/**
* Gives the subject all of the applicable contexts they meet
*
* <p><b>You MUST implement this method. The default is only provided for backwards compatibility with
* {@link #giveApplicableContext(Object, Map)}.</b>
*
* @param subject the subject to add contexts to
* @param accumulator a map of contexts to add to
* @return the map
* @since 2.13
*/
@SuppressWarnings("deprecation")
default MutableContextSet giveApplicableContext(T subject, MutableContextSet accumulator) {
Map<String, String> acc = new HashMap<>();
giveApplicableContext(subject, acc);
accumulator.addAll(acc.entrySet());
return accumulator;
}
/**
* Checks to see if a context is applicable to a subject

View File

@@ -0,0 +1,272 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
*
* 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.Maps;
import java.util.HashSet;
import java.util.Map;
/**
* Holds contexts
* All contained contexts are immutable, but contexts can be added or removed from the set.
*
* @since 2.13
*/
public class MutableContextSet extends ContextSet {
/**
* Make a singleton MutableContextSet from a context pair
* @param key the key
* @param value the value
* @return a new MutableContextSet containing one KV pair
* @throws NullPointerException if key or value is null
*/
public static MutableContextSet singleton(String key, String value) {
MutableContextSet set = new MutableContextSet();
set.add(key, value);
return set;
}
/**
* Creates a MutableContextSet from an existing map
* @param map the map to copy from
* @return a new MutableContextSet representing the pairs from the map
* @throws NullPointerException if the map is null
*/
public static MutableContextSet fromMap(Map<String, String> map) {
MutableContextSet set = new MutableContextSet();
set.addAll(map);
return set;
}
/**
* Creates a MutableContextSet from an existing iterable of Map Entries
* @param iterable the iterable to copy from
* @return a new MutableContextSet representing the pairs in the iterable
* @throws NullPointerException if the iterable is null
*/
public static MutableContextSet fromEntries(Iterable<Map.Entry<String, String>> iterable) {
MutableContextSet set = new MutableContextSet();
set.addAll(iterable);
return set;
}
/**
* Creates a new MutableContextSet from an existing set.
* Only really useful for converting between mutable and immutable types.
* @param contextSet the context set to copy from
* @return a new MutableContextSet with the same content and the one provided
* @throws NullPointerException if contextSet is null
*/
public static MutableContextSet fromSet(ContextSet contextSet) {
MutableContextSet set = new MutableContextSet();
set.addAll(contextSet.toSet());
return set;
}
/**
* Creates a new empty MutableContextSet.
* @return a new MutableContextSet
*/
public static MutableContextSet empty() {
return new MutableContextSet();
}
@Override
public boolean isImmutable() {
return false;
}
@Override
public ContextSet makeImmutable() {
return immutableCopy();
}
/**
* Returns an immutable copy of this set.
* @return an immutable copy of this set
*/
public ContextSet immutableCopy() {
synchronized (contexts) {
return new ContextSet(new HashSet<>(contexts));
}
}
/**
* 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
*/
public void add(String key, String value) {
if (key == null) {
throw new NullPointerException("key");
}
if (value == null) {
throw new NullPointerException("value");
}
synchronized (contexts) {
contexts.add(Maps.immutableEntry(key, value));
}
}
/**
* Adds a new key value pair to the set
* @param entry the entry to add
* @throws NullPointerException if the entry is null
*/
public void add(Map.Entry<String, String> entry) {
if (entry == null) {
throw new NullPointerException("context");
}
synchronized (contexts) {
contexts.add(Maps.immutableEntry(entry.getKey(), entry.getValue()));
}
}
/**
* Adds an iterable containing contexts to the set
* @param iterable an iterable of key value context pairs
* @throws NullPointerException if iterable is null
*/
public void addAll(Iterable<Map.Entry<String, String>> iterable) {
if (iterable == null) {
throw new NullPointerException("contexts");
}
synchronized (this.contexts) {
for (Map.Entry<String, String> e : iterable) {
this.contexts.add(Maps.immutableEntry(e.getKey(), e.getValue()));
}
}
}
/**
* Adds the entry set of a map to the set
* @param map the map to add from
* @throws NullPointerException if the map is null
*/
public void addAll(Map<String, String> map) {
if (map == null) {
throw new NullPointerException("contexts");
}
addAll(map.entrySet());
}
/**
* 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
*/
public void addAll(ContextSet contextSet) {
if (contextSet == null) {
throw new NullPointerException("contextSet");
}
synchronized (this.contexts) {
this.contexts.addAll(contextSet.toSet());
}
}
/**
* Remove a key value pair from this set
* @param key the key to remove
* @param value the value to remove (case sensitive)
* @throws NullPointerException if the key or value is null
*/
public void remove(String key, String value) {
if (key == null) {
throw new NullPointerException("key");
}
if (value == null) {
throw new NullPointerException("value");
}
synchronized (contexts) {
contexts.removeIf(e -> e.getKey().equalsIgnoreCase(key) && e.getValue().equals(value));
}
}
/**
* Same as {@link #remove(String, String)}, except ignores the case of the value
* @param key the key to remove
* @param value the value to remove
* @throws NullPointerException if the key or value is null
*/
public void removeIgnoreCase(String key, String value) {
if (key == null) {
throw new NullPointerException("key");
}
if (value == null) {
throw new NullPointerException("value");
}
synchronized (contexts) {
contexts.removeIf(e -> e.getKey().equalsIgnoreCase(key) && e.getValue().equalsIgnoreCase(value));
}
}
/**
* Removes all pairs with the given key
* @param key the key to remove
* @throws NullPointerException if the key is null
*/
public void removeAll(String key) {
if (key == null) {
throw new NullPointerException("key");
}
synchronized (contexts) {
contexts.removeIf(e -> e.getKey().equalsIgnoreCase(key));
}
}
/**
* Clears the set
*/
public void clear() {
synchronized (contexts) {
contexts.clear();
}
}
@Override
public boolean equals(Object o) {
if (o == this) return true;
if (!(o instanceof ContextSet)) return false;
final ContextSet other = (ContextSet) o;
final Object thisContexts = this.contexts;
final Object otherContexts = other.contexts;
return thisContexts == null ? otherContexts == null : thisContexts.equals(otherContexts);
}
@Override
public int hashCode() {
return 59 + (this.contexts == null ? 43 : this.contexts.hashCode());
}
}