Update Flag containers. Add javadocs. Add missing methods.

This commit is contained in:
Alexander Söderberg 2020-02-16 15:22:31 +01:00
parent e9c69dc80c
commit 83a33d2b41
2 changed files with 147 additions and 24 deletions

View File

@ -8,15 +8,30 @@ import java.util.Collection;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; 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 FlagContainer parentContainer;
private final Map<Class<?>, PlotFlag<?, ?>> flagMap = new HashMap<>(); private final Map<Class<?>, 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.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() { public FlagContainer getParentContainer() {
return this.parentContainer; return this.parentContainer;
} }
@ -25,24 +40,68 @@ import java.util.Map;
return this.flagMap; return this.flagMap;
} }
/**
* Get an immutable view of the underlying flag map
*
* @return Immutable flag map
*/
public Map<Class<?>, PlotFlag<?, ?>> getFlagMap() { public Map<Class<?>, PlotFlag<?, ?>> getFlagMap() {
return ImmutableMap.<Class<?>, PlotFlag<?, ?>>builder().putAll(this.flagMap).build(); return ImmutableMap.<Class<?>, PlotFlag<?, ?>>builder().putAll(this.flagMap).build();
} }
public void addFlag(final PlotFlag<?, ? extends PlotFlag<?, ?>> flag) { /**
* Add a flag to the container
*
* @param flag Flag to add
* @see #addAll(Collection) to add multiple flags
*/
public <V, T extends PlotFlag<V, ?>> void addFlag(final T flag) {
this.flagMap.put(flag.getClass(), 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 <V, T extends PlotFlag<V, ?>> 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<PlotFlag<?, ?>> flags) { public void addAll(final Collection<PlotFlag<?, ?>> flags) {
for (final PlotFlag<?, ?> flag : flags) { for (final PlotFlag<?, ?> flag : flags) {
this.addFlag(flag); 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<PlotFlag<?, ?>> getRecognizedPlotFlags() { public Collection<PlotFlag<?, ?>> getRecognizedPlotFlags() {
return this.getHighestClassContainer().getFlagMap().values(); 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() { public final FlagContainer getHighestClassContainer() {
if (this.getParentContainer() != null) { if (this.getParentContainer() != null) {
return this.getParentContainer(); return this.getParentContainer();
@ -50,6 +109,10 @@ import java.util.Map;
return this; return this;
} }
/**
* Has the same functionality as {@link #getFlag(Class)}, but
* with erased generic types.
*/
public PlotFlag<?, ?> getFlagErased(Class<?> flagClass) { public PlotFlag<?, ?> getFlagErased(Class<?> flagClass) {
final PlotFlag<?, ?> flag = this.flagMap.get(flagClass); final PlotFlag<?, ?> flag = this.flagMap.get(flagClass);
if (flag != null) { if (flag != null) {
@ -62,10 +125,20 @@ import java.util.Map;
return null; return null;
} }
public <T> PlotFlag<T, ?> getFlag(final Class<? extends PlotFlag<T, ?>> 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 <V> Flag value type
* @param <T> Flag type
* @return Flag instance
*/
public <V, T extends PlotFlag<V, ?>> T getFlag(final Class<? extends T> flagClass) {
final PlotFlag<?, ?> flag = this.flagMap.get(flagClass); final PlotFlag<?, ?> flag = this.flagMap.get(flagClass);
if (flag != null) { if (flag != null) {
return (PlotFlag<T, ?>) flag; return castUnsafe(flag);
} else { } else {
if (getParentContainer() != null) { if (getParentContainer() != null) {
return getParentContainer().getFlag(flagClass); return getParentContainer().getFlag(flagClass);
@ -74,4 +147,55 @@ import java.util.Map;
return null; return null;
} }
/**
* Check for flag existence in this flag container instance.
*
* @param flagClass Flag class to query for
* @param <V> Flag value type
* @param <T> Flag type
* @return The flag instance, if it exists in this container, else null.
*/
@Nullable public <V, T extends PlotFlag<V, ?>> T queryLocal(final Class<? extends T> flagClass) {
final PlotFlag<?, ?> localFlag = this.flagMap.get(flagClass);
if (localFlag == null) {
return null;
} else {
return castUnsafe(localFlag);
}
}
@SuppressWarnings("ALL")
protected static <V, T extends PlotFlag<V, ?>> 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
}
} }

View File

@ -15,8 +15,21 @@ import java.util.Map;
public final class GlobalFlagContainer extends FlagContainer { public final class GlobalFlagContainer extends FlagContainer {
@Getter private static final GlobalFlagContainer instance = new GlobalFlagContainer(); @Getter private static final GlobalFlagContainer instance = new GlobalFlagContainer();
private static Map<String, Class<?>> stringClassMap = new HashMap<>();
private final Map<String, Class<?>> 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) { @Override public PlotFlag<?, ?> getFlagErased(Class<?> flagClass) {
final PlotFlag<?, ?> flag = super.getFlagErased(flagClass); final PlotFlag<?, ?> flag = super.getFlagErased(flagClass);
@ -28,33 +41,19 @@ public final class GlobalFlagContainer extends FlagContainer {
} }
} }
@Nonnull @Override public <T> PlotFlag<T, ?> getFlag(Class<? extends PlotFlag<T, ?>> flagClass) { @Nonnull @Override
public <V, T extends PlotFlag<V, ?>> T getFlag(Class<? extends T> flagClass) {
final PlotFlag<?, ?> flag = super.getFlag(flagClass); final PlotFlag<?, ?> flag = super.getFlag(flagClass);
if (flag != null) { if (flag != null) {
return (PlotFlag<T, ?>) flag; return castUnsafe(flag);
} else { } else {
throw new IllegalStateException(String.format("Unrecognized flag '%s'. All flag types" throw new IllegalStateException(String.format("Unrecognized flag '%s'. All flag types"
+ " must be present in the global flag container.", flagClass.getSimpleName())); + " 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) { 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) { public PlotFlag<?, ?> getFlagFromString(final String name) {