package com.intellectualcrafters.plot.flag;
import com.google.common.base.Optional;
import com.google.common.collect.ImmutableSet;
import com.intellectualcrafters.plot.PS;
import com.intellectualcrafters.plot.database.DBFunc;
import com.intellectualcrafters.plot.object.Plot;
import com.intellectualcrafters.plot.object.PlotArea;
import com.intellectualcrafters.plot.object.PlotCluster;
import com.intellectualcrafters.plot.object.PlotPlayer;
import com.intellectualcrafters.plot.object.PlotSettings;
import com.intellectualcrafters.plot.util.EventUtil;
import com.intellectualcrafters.plot.util.Permissions;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* Flag Manager Utility.
*/
public class FlagManager {
/**
* Some events can be called millions of times each second (e.g. physics)
* and reusing is a lot faster.
*/
private static final Optional MUTABLE_OPTIONAL;
private static Field MUTABLE_OPTIONAL_FIELD;
static {
MUTABLE_OPTIONAL = Optional.of(new Object());
try {
MUTABLE_OPTIONAL_FIELD = MUTABLE_OPTIONAL.getClass().getDeclaredField("reference");
MUTABLE_OPTIONAL_FIELD.setAccessible(true);
} catch (NoSuchFieldException e) {
e.printStackTrace();
}
}
public static <V> Optional<V> getPlotFlag(Plot plot, Flag<V> key) {
V value = FlagManager.getPlotFlagRaw(plot, key);
if (value != null) {
if (PS.get().isMainThread(Thread.currentThread())) {
try {
MUTABLE_OPTIONAL_FIELD.set(MUTABLE_OPTIONAL, value);
return MUTABLE_OPTIONAL;
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
return Optional.of(value);
}
return Optional.absent();
}
/**
* Reserve a flag so that it cannot be set by players.
* @param flag the flag to reserve
* @return false if the flag was already reserved, otherwise true
*/
public static boolean reserveFlag(Flag<?> flag) {
if (flag.isReserved()) {
return false;
}
flag.reserve();
return true;
}
/**
* Check if a flag is reserved.
* @param flag the flag to check
* @return true if the flag is reserved, false otherwise
*/
public static boolean isReserved(Flag<?> flag) {
return flag.isReserved();
}
/**
* Get an immutable set of reserved flags.
* @return a set of reserved flags
*/
public static Set<Flag<?>> getReservedFlags() {
ImmutableSet.Builder<Flag<?>> reserved = ImmutableSet.builder();
for (Flag flag : Flags.getFlags()) {
if (flag.isReserved()) {
reserved.add(flag);
}
}
return reserved.build();
}
/**
* Unreserve a flag.
* @param flag the flag to unreserve
* @return true if the flag was unreserved
*/
public static boolean unreserveFlag(Flag<?> flag) {
if (flag.isReserved()) {
flag.unreserve();
return true;
}
return false;
}
public static String toString(HashMap<Flag<?>, Object> flags) {
StringBuilder flag_string = new StringBuilder();
int i = 0;
for (Map.Entry<Flag<?>, Object> entry : flags.entrySet()) {
try {
Flag flag = entry.getKey();
if (i != 0) {
flag_string.append(',');
}
flag_string.append(flag.getName() + ':' + flag.valueToString(entry.getValue()).replaceAll(":", "¯").replaceAll(",", "´"));
i++;
} catch (Exception e) {
PS.debug("Failed to parse flag: " + entry.getKey() + "->" + entry.getValue());
e.printStackTrace();
}
}
return flag_string.toString();
}
public static <V> V getSettingFlag(PlotArea area, PlotSettings settings, Flag<V> id) {
Object value;
if (settings.flags.isEmpty() || ((value = settings.flags.get(id)) == null)) {
if (area == null || area.DEFAULT_FLAGS.isEmpty()) {
return null;
}
return (V) area.DEFAULT_FLAGS.get(id);
}
return (V) value;
}
/**
* Returns the raw flag<br>
* - Faster
* - You should not modify the flag
* @param plot
* @param flag
* @return
*/
public static <V> V getPlotFlagRaw(Plot plot, Flag<V> flag) {
if (plot.owner == null) {
return null;
}
return getSettingFlag(plot.getArea(), plot.getSettings(), flag);
}
/**
* Add a flag to a plot.
* @param origin
* @param flag
* @param value
*/
public static <V> boolean addPlotFlag(Plot origin, Flag<V> flag, Object value) {
boolean result = EventUtil.manager.callFlagAdd(flag, origin);
if (!result) {
return false;
}
for (Plot plot : origin.getConnectedPlots()) {
plot.getFlags().put(flag, value);
plot.reEnter(); //TODO fix this so FlagTest will run during compile
DBFunc.setFlags(plot, plot.getFlags());
}
return true;
}
public static <V> boolean addClusterFlag(PlotCluster cluster, Flag<V> flag, V value) {
getSettingFlag(cluster.area, cluster.settings, flag);
cluster.settings.flags.put(flag, value);
DBFunc.setFlags(cluster, cluster.settings.flags);
return true;
}
/**
* Returns a map of the {@link Flag}s and their values for the specified plot.
* @param plot the plot
* @return a map of the flags and values for the plot, returns an empty map for unowned plots
*/
public static Map<Flag<?>, Object> getPlotFlags(Plot plot) {
if (!plot.hasOwner()) {
return Collections.emptyMap();
}
return getSettingFlags(plot.getArea(), plot.getSettings());
}
public static HashMap<Flag<?>, Object> getPlotFlags(PlotArea area, PlotSettings settings, boolean ignorePluginflags) {
HashMap<Flag<?>, Object> flags = null;
if (area != null && !area.DEFAULT_FLAGS.isEmpty()) {
flags = new HashMap<>(area.DEFAULT_FLAGS.size());
flags.putAll(area.DEFAULT_FLAGS);
}
if (ignorePluginflags) {
if (flags == null) {
flags = new HashMap<>(settings.flags.size());
}
for (Map.Entry<Flag<?>, Object> flag : settings.flags.entrySet()) {
if (flag.getKey().isReserved()) {
continue;
}
flags.put(flag.getKey(), flag.getValue());
}
return flags;
} else if (flags == null) {
return settings.flags;
} else {
flags.putAll(settings.flags);
}
return flags;
}
public static Map<Flag<?>, Object> getSettingFlags(PlotArea area, PlotSettings settings) {
return getPlotFlags(area, settings, false);
}
/**
* Removes a flag from a certain plot.
* @param origin the plot to remove the flag from
* @param id the flag to remove
* @return true if the plot contained the flag and was removed successfully
*/
public static boolean removePlotFlag(Plot origin, Flag<?> id) {
for (Plot plot : origin.getConnectedPlots()) {
Object value = plot.getFlags().remove(id);
if (value == null) {
return false;
}
if (plot == origin) {
boolean result = EventUtil.manager.callFlagRemove(id, plot, value);
if (!result) {
plot.getFlags().put(id, value);
return false;
}
}
plot.reEnter();
DBFunc.setFlags(plot, plot.getFlags());
}
return true;
}
public static boolean removeClusterFlag(PlotCluster cluster, Flag id) {
Object object = cluster.settings.flags.remove(id);
if (object == null) {
return false;
}
boolean result = EventUtil.manager.callFlagRemove(id, object, cluster);
if (!result) {
cluster.settings.flags.put(id, object);
return false;
}
DBFunc.setFlags(cluster, cluster.settings.flags);
return true;
}
public static void setPlotFlags(Plot origin, HashMap<Flag<?>, Object> flags) {
for (Plot plot : origin.getConnectedPlots()) {
if (flags != null && !flags.isEmpty()) {
plot.getFlags().clear();
for (Map.Entry<Flag<?>, Object> flag : flags.entrySet()) {
plot.getFlags().put(flag.getKey(), flag.getValue());
}
} else if (plot.getFlags().isEmpty()) {
return;
} else {
plot.getFlags().clear();
}
plot.reEnter();
DBFunc.setFlags(plot, plot.getFlags());
}
}
public static void setClusterFlags(PlotCluster cluster, Set<Flag> flags) {
if (flags != null && !flags.isEmpty()) {
cluster.settings.flags.clear();
for (Flag flag : flags) {
cluster.settings.flags.put(flag, flag);
}
} else if (cluster.settings.flags.isEmpty()) {
return;
} else {
cluster.settings.flags.clear();
}
DBFunc.setFlags(cluster, cluster.settings.flags);
}
/**
* Get a list of registered {@link Flag} objects based on player permissions.
*
* @param player the player
*
* @return a list of flags the specified player can use
*/
public static List<Flag> getFlags(PlotPlayer player) {
List<Flag> returnFlags = new ArrayList<>();
for (Flag flag : Flags.getFlags()) {
if (Permissions.hasPermission(player, "plots.set.flag." + flag.getName().toLowerCase())) {
returnFlags.add(flag);
}
}
return returnFlags;
}
/**
* Get a {@link Flag} specified by the specified {@code String}.
*
* @param string the flag name
*
* @return the {@code Flag} object defined by the {@code String}
*/
public static Flag<?> getFlag(String string) {
return Flags.getFlag(string);
}
public static Flag<?> getFlag(String string, boolean ignoreReserved) {
Flag<?> flag = Flags.getFlag(string);
if (!ignoreReserved && flag != null && flag.isReserved()) {
return null;
}
return flag;
}
public static Flag<?> getOrCreateFlag(String string) {
Flag<?> flag = Flags.getFlag(string);
if (flag == null) {
flag = new StringFlag(string) {
@Override public String getValueDescription() {
return "Generic Filler Flag";
}
};
flag.register();
}
return flag;
}
public static Map<Flag<?>, Object> parseFlags(List<String> flagStrings) {
HashMap<Flag<?>, Object> map = new HashMap<>();
for (String key : flagStrings) {
String[] split;
if (key.contains(";")) {
split = key.split(";");
} else {
split = key.split(":");
}
Flag<?> flag = getOrCreateFlag(split[0]);
Object value = flag.parseValue(split[1]);
map.put(flag, value);
}
return map;
}
}