diff --git a/Core/src/main/java/com/github/intellectualsites/plotsquared/plot/flags/FlagContainer.java b/Core/src/main/java/com/github/intellectualsites/plotsquared/plot/flags/FlagContainer.java index b64291d5e..1fde1eb04 100644 --- a/Core/src/main/java/com/github/intellectualsites/plotsquared/plot/flags/FlagContainer.java +++ b/Core/src/main/java/com/github/intellectualsites/plotsquared/plot/flags/FlagContainer.java @@ -8,15 +8,30 @@ import java.util.Collection; import java.util.HashMap; import java.util.Map; -@EqualsAndHashCode(of = "flagMap") public class FlagContainer { +/** + * Container type for {@link PlotFlag plot flags}. + */ +@EqualsAndHashCode(of = "flagMap") @SuppressWarnings("unused") public class FlagContainer { private final FlagContainer parentContainer; private final Map, PlotFlag> flagMap = new HashMap<>(); + private final PlotFlagUpdateHandler plotFlagUpdateHandler; - public FlagContainer(@Nullable final FlagContainer parentContainer) { + public FlagContainer(@Nullable final FlagContainer parentContainer, + @Nullable PlotFlagUpdateHandler plotFlagUpdateHandler) { this.parentContainer = parentContainer; + this.plotFlagUpdateHandler = plotFlagUpdateHandler; } + public FlagContainer(@Nullable final FlagContainer parentContainer) { + this(parentContainer, null); + } + + /** + * Return the parent container (if the container has a parent) + * + * @return Parent container + */ public FlagContainer getParentContainer() { return this.parentContainer; } @@ -25,24 +40,68 @@ import java.util.Map; return this.flagMap; } + /** + * Get an immutable view of the underlying flag map + * + * @return Immutable flag map + */ public Map, PlotFlag> getFlagMap() { return ImmutableMap., PlotFlag>builder().putAll(this.flagMap).build(); } - public void addFlag(final PlotFlag> flag) { + /** + * Add a flag to the container + * + * @param flag Flag to add + * @see #addAll(Collection) to add multiple flags + */ + public > void addFlag(final T flag) { this.flagMap.put(flag.getClass(), flag); + if (this.plotFlagUpdateHandler != null) { + this.plotFlagUpdateHandler.handle(flag, PlotFlagUpdateType.FLAG_ADDED); + } } + /** + * Remove a flag from the container + * + * @param flag Flag to remove + */ + public > void removeFlag(final T flag) { + this.flagMap.remove(flag.getClass()); + if (this.plotFlagUpdateHandler != null) { + this.plotFlagUpdateHandler.handle(flag, PlotFlagUpdateType.FLAG_REMOVED); + } + } + + /** + * Add all flags to the container + * + * @param flags Flags to add + * @see #addFlag(PlotFlag) to add a single flagg + */ public void addAll(final Collection> flags) { for (final PlotFlag flag : flags) { this.addFlag(flag); } } + /** + * Get a collection of all recognized plot flags. Will by + * default use the values contained in {@link GlobalFlagContainer}. + * + * @return All recognized flag types + */ public Collection> getRecognizedPlotFlags() { return this.getHighestClassContainer().getFlagMap().values(); } + /** + * Recursively seek for the highest order flag container. + * This will by default return {@link GlobalFlagContainer}. + * + * @return Highest order class container. + */ public final FlagContainer getHighestClassContainer() { if (this.getParentContainer() != null) { return this.getParentContainer(); @@ -50,6 +109,10 @@ import java.util.Map; return this; } + /** + * Has the same functionality as {@link #getFlag(Class)}, but + * with erased generic types. + */ public PlotFlag getFlagErased(Class flagClass) { final PlotFlag flag = this.flagMap.get(flagClass); if (flag != null) { @@ -62,10 +125,20 @@ import java.util.Map; return null; } - public PlotFlag getFlag(final Class> flagClass) { + /** + * Query all levels of flag containers for a flag. This guarantees that a flag + * instance is returned, as long as it is registered in the + * {@link GlobalFlagContainer global flag container}. + * + * @param flagClass Flag class to query for + * @param Flag value type + * @param Flag type + * @return Flag instance + */ + public > T getFlag(final Class flagClass) { final PlotFlag flag = this.flagMap.get(flagClass); if (flag != null) { - return (PlotFlag) flag; + return castUnsafe(flag); } else { if (getParentContainer() != null) { return getParentContainer().getFlag(flagClass); @@ -74,4 +147,55 @@ import java.util.Map; return null; } + /** + * Check for flag existence in this flag container instance. + * + * @param flagClass Flag class to query for + * @param Flag value type + * @param Flag type + * @return The flag instance, if it exists in this container, else null. + */ + @Nullable public > T queryLocal(final Class flagClass) { + final PlotFlag localFlag = this.flagMap.get(flagClass); + if (localFlag == null) { + return null; + } else { + return castUnsafe(localFlag); + } + } + + @SuppressWarnings("ALL") + protected static > T castUnsafe(final PlotFlag flag) { + return (T) flag; + } + + /** + * Handler for update events in {@link FlagContainer flag containers}. + */ + @FunctionalInterface public interface PlotFlagUpdateHandler { + + /** + * Act on the flag update event + * @param plotFlag Plot flag + * @param type Update type + */ + void handle(PlotFlag plotFlag, PlotFlagUpdateType type); + + } + + + /** + * Update event types used in {@link PlotFlagUpdateHandler}. + */ + public enum PlotFlagUpdateType { + /** + * A flag was added to a plot container + */ + FLAG_ADDED, + /** + * A flag was removed from a plot container + */ + FLAG_REMOVED + } + } diff --git a/Core/src/main/java/com/github/intellectualsites/plotsquared/plot/flags/GlobalFlagContainer.java b/Core/src/main/java/com/github/intellectualsites/plotsquared/plot/flags/GlobalFlagContainer.java index 2e57a9081..de8b38ba0 100644 --- a/Core/src/main/java/com/github/intellectualsites/plotsquared/plot/flags/GlobalFlagContainer.java +++ b/Core/src/main/java/com/github/intellectualsites/plotsquared/plot/flags/GlobalFlagContainer.java @@ -15,8 +15,21 @@ import java.util.Map; public final class GlobalFlagContainer extends FlagContainer { @Getter private static final GlobalFlagContainer instance = new GlobalFlagContainer(); + private static Map> stringClassMap = new HashMap<>(); - private final Map> stringClassMap = new HashMap<>(); + private GlobalFlagContainer() { + super(null, (flag, type) -> { + if (type == PlotFlagUpdateType.FLAG_ADDED) { + stringClassMap.put(flag.getName().toLowerCase(Locale.ENGLISH), flag.getClass()); + } + }); + // Register all default flags here + this.addFlag(ExplosionFlag.EXPLOSION_FALSE); + this.addFlag(MusicFlag.MUSIC_FLAG_NONE); + this.addFlag(FlightFlag.FLIGHT_FLAG_FALSE); + this.addFlag(UntrustedVisitFlag.UNTRUSTED_VISIT_FLAG_TRUE); + this.addFlag(DenyExitFlag.DENY_EXIT_FLAG_TRUE); + } @Override public PlotFlag getFlagErased(Class flagClass) { final PlotFlag flag = super.getFlagErased(flagClass); @@ -28,33 +41,19 @@ public final class GlobalFlagContainer extends FlagContainer { } } - @Nonnull @Override public PlotFlag getFlag(Class> flagClass) { + @Nonnull @Override + public > T getFlag(Class flagClass) { final PlotFlag flag = super.getFlag(flagClass); if (flag != null) { - return (PlotFlag) flag; + return castUnsafe(flag); } else { throw new IllegalStateException(String.format("Unrecognized flag '%s'. All flag types" + " must be present in the global flag container.", flagClass.getSimpleName())); } } - private GlobalFlagContainer() { - super(null); - // Register all default flags here - this.addFlag(ExplosionFlag.EXPLOSION_FALSE); - this.addFlag(MusicFlag.MUSIC_FLAG_NONE); - this.addFlag(FlightFlag.FLIGHT_FLAG_FALSE); - this.addFlag(UntrustedVisitFlag.UNTRUSTED_VISIT_FLAG_TRUE); - this.addFlag(DenyExitFlag.DENY_EXIT_FLAG_TRUE); - } - - @Override public void addFlag(PlotFlag flag) { - super.addFlag(flag); - this.stringClassMap.put(flag.getName().toLowerCase(Locale.ENGLISH), flag.getClass()); - } - public Class getFlagClassFromString(final String name) { - return this.stringClassMap.get(name.toLowerCase(Locale.ENGLISH)); + return stringClassMap.get(name.toLowerCase(Locale.ENGLISH)); } public PlotFlag getFlagFromString(final String name) {