Java tutorial
/* * This file is part of HeavySpleef. * Copyright (c) 2014-2016 Matthias Werning * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package de.xaniox.heavyspleef.core.flag; import com.google.common.collect.*; import de.xaniox.heavyspleef.core.game.Game; import de.xaniox.heavyspleef.core.game.GameProperty; import org.apache.commons.lang.Validate; import org.bukkit.Bukkit; import org.bukkit.event.HandlerList; import org.bukkit.plugin.java.JavaPlugin; import java.lang.reflect.Method; import java.util.*; import java.util.Map.Entry; public class FlagManager { private final JavaPlugin plugin; private BiMap<String, AbstractFlag<?>> flags; private List<UnloadedFlag> unloadedFlags; private Set<String> disabledFlags; private Set<GamePropertyBundle> propertyBundles; private DefaultGamePropertyBundle requestedProperties; private GamePropertyBundle defaults; private boolean migrateManager; public FlagManager(JavaPlugin plugin, GamePropertyBundle defaults) { this(plugin, defaults, false); } public FlagManager(JavaPlugin plugin, GamePropertyBundle defaults, boolean migrateManager) { this.plugin = plugin; this.defaults = defaults; this.flags = HashBiMap.create(); this.disabledFlags = Sets.newHashSet(); this.unloadedFlags = Lists.newArrayList(); this.propertyBundles = Sets.newTreeSet(); this.requestedProperties = new DefaultGamePropertyBundle(Maps.newEnumMap(GameProperty.class)); this.migrateManager = migrateManager; } public void addFlag(AbstractFlag<?> flag) { addFlag(flag, false); } public void addFlag(AbstractFlag<?> flag, boolean disable) { Class<?> clazz = flag.getClass(); String path; if (flag instanceof UnloadedFlag) { UnloadedFlag unloadedFlag = (UnloadedFlag) flag; path = unloadedFlag.getFlagName(); for (UnloadedFlag unloaded : unloadedFlags) { if (unloaded.getFlagName().equals(path)) { throw new IllegalStateException("Unloaded flag with name " + path + " already registered"); } } if (flags.containsKey(path) || disabledFlags.contains(path)) { return; } unloadedFlags.add(unloadedFlag); } else { Validate.isTrue(clazz.isAnnotationPresent(Flag.class), "Flag class " + clazz.getCanonicalName() + " must annotate " + Flag.class.getCanonicalName()); Flag flagAnnotation = clazz.getAnnotation(Flag.class); path = generatePath(flagAnnotation); if (flags.containsKey(path) || disabledFlags.contains(path)) { return; } if (disable) { disabledFlags.add(path); } else { flags.put(path, flag); if (clazz.isAnnotationPresent(BukkitListener.class) && !migrateManager) { Bukkit.getPluginManager().registerEvents(flag, plugin); } if (flagAnnotation.hasGameProperties()) { Map<GameProperty, Object> flagGamePropertiesMap = new EnumMap<GameProperty, Object>( GameProperty.class); flag.defineGameProperties(flagGamePropertiesMap); if (!flagGamePropertiesMap.isEmpty()) { GamePropertyBundle properties = new GamePropertyBundle(flag, flagGamePropertiesMap); propertyBundles.add(properties); } } if (flagAnnotation.parent() != NullFlag.class) { AbstractFlag<?> parent = getFlag(flagAnnotation.parent()); flag.setParent(parent); } } } } public void revalidateParents() { for (AbstractFlag<?> flag : flags.values()) { Flag annotation = flag.getClass().getAnnotation(Flag.class); if (annotation.parent() == NullFlag.class || flag.getParent() != null) { continue; } AbstractFlag<?> parent = getFlag(annotation.parent()); if (parent == null) { throw new IllegalStateException( "Parent of flag " + flag.getClass().getSimpleName() + " is not available!"); } flag.setParent(parent); } } public static String generatePath(Flag flagAnnotation) { //Generate the full path StringBuilder pathBuilder = new StringBuilder(); Flag lastParentFlagData = flagAnnotation; while (lastParentFlagData != null) { pathBuilder.insert(0, lastParentFlagData.name()); Class<? extends AbstractFlag<?>> parentClass = lastParentFlagData.parent(); lastParentFlagData = parentClass.getAnnotation(Flag.class); if (lastParentFlagData != null) { pathBuilder.insert(0, ":"); } } String path = pathBuilder.toString(); return path; } public AbstractFlag<?> removeFlag(String path) { AbstractFlag<?> flag = flags.remove(path); if (flag == null) { disabledFlags.remove(path); UnloadedFlag removed = null; Iterator<UnloadedFlag> iterator = unloadedFlags.iterator(); while (iterator.hasNext()) { UnloadedFlag unloaded = iterator.next(); if (!unloaded.getFlagName().equals(path)) { continue; } iterator.remove(); removed = unloaded; } return removed; } if (flag.getClass().isAnnotationPresent(BukkitListener.class)) { HandlerList.unregisterAll(flag); } Iterator<GamePropertyBundle> iterator = propertyBundles.iterator(); while (iterator.hasNext()) { GamePropertyBundle bundle = iterator.next(); if (bundle.getRelatingFlag() == null || bundle.getRelatingFlag() != flag) { continue; } iterator.remove(); } return flag; } public AbstractFlag<?> removeFlag(Class<? extends AbstractFlag<?>> flagClass) { AbstractFlag<?> recent = null; if (UnloadedFlag.class.isAssignableFrom(flagClass)) { for (UnloadedFlag other : Lists.newArrayList(unloadedFlags)) { if (other.getClass() != flagClass) { continue; } recent = removeFlag(other.getFlagName()); } } else { for (Entry<String, AbstractFlag<?>> entry : Sets.newHashSet(flags.entrySet())) { AbstractFlag<?> flag = entry.getValue(); if (flag.getClass() != flagClass) { continue; } recent = removeFlag(entry.getKey()); } } return recent; } public boolean isFlagPresent(String path) { return isFlagPresent(path, false); } public boolean isFlagPresent(String path, boolean checkDisabled) { boolean present = flags.containsKey(path); if (checkDisabled && !present) { present = disabledFlags.contains(path); } return present; } public boolean isFlagPresent(Class<? extends AbstractFlag<?>> clazz) { for (AbstractFlag<?> val : flags.values()) { if (clazz.isInstance(val)) { return true; } } return false; } public Set<AbstractFlag<?>> getFlags() { Set<AbstractFlag<?>> set = Sets.newHashSet(); set.addAll(flags.values()); set.addAll(unloadedFlags); return set; } @SuppressWarnings("rawtypes") public List<Conflict> computeConflicts(Class<? extends AbstractFlag<?>> flagClass, Flag flagAnnotation) { List<Conflict> conflicts = Lists.newArrayList(); for (AbstractFlag<?> otherFlag : flags.values()) { Class<? extends AbstractFlag> otherFlagClass = otherFlag.getClass(); Flag otherFlagAnnotation = otherFlagClass.getAnnotation(Flag.class); for (Class<? extends AbstractFlag<?>> flagConflictClass : flagAnnotation.conflictsWith()) { if (flagConflictClass != otherFlagClass) { continue; } conflicts.add(new Conflict(flagClass, flagAnnotation, otherFlagClass, otherFlagAnnotation)); } for (Class<? extends AbstractFlag<?>> flagConflictClass : otherFlagAnnotation.conflictsWith()) { if (flagConflictClass != flagClass) { continue; } conflicts.add(new Conflict(otherFlagClass, otherFlagAnnotation, flagClass, flagAnnotation)); } } return conflicts; } @SuppressWarnings("unchecked") public void disableFlag(String path) { Validate.isTrue(flags.containsKey(path), "Flag is not registered or already disabled"); AbstractFlag<?> flag = flags.get(path); disableFlag((Class<? extends AbstractFlag<?>>) flag.getClass()); } public void disableFlag(Class<? extends AbstractFlag<?>> clazz) { AbstractFlag<?> flag = getFlag(clazz); Validate.notNull(flag, "Flag is not registered or already disabled"); String path = flags.inverse().remove(flag); if (clazz.isAnnotationPresent(BukkitListener.class)) { HandlerList.unregisterAll(flag); } Iterator<GamePropertyBundle> iterator = propertyBundles.iterator(); while (iterator.hasNext()) { GamePropertyBundle bundle = iterator.next(); if (bundle.getRelatingFlag() == null || bundle.getRelatingFlag() != flag) { continue; } iterator.remove(); } disabledFlags.add(path); } @SuppressWarnings("unchecked") public void enableFlag(String path, FlagRegistry registry, Game game) { Validate.isTrue(disabledFlags.contains(path), "Flag is not disabled"); AbstractFlag<?> flag = flags.get(path); enableFlag((Class<? extends AbstractFlag<?>>) flag.getClass(), registry, game); } public void enableFlag(Class<? extends AbstractFlag<?>> clazz, FlagRegistry registry, Game game) { Validate.isTrue(!isFlagPresent(clazz)); Validate.isTrue(clazz.isAnnotationPresent(Flag.class)); Flag flagAnnotation = clazz.getAnnotation(Flag.class); String path = generatePath(flagAnnotation); Validate.isTrue(disabledFlags.contains(path), "Flag is not disabled"); AbstractFlag<?> flag = registry.newFlagInstance(path, AbstractFlag.class, game); if (clazz.isAnnotationPresent(BukkitListener.class)) { Bukkit.getPluginManager().registerEvents(flag, plugin); } if (flagAnnotation.hasGameProperties()) { Map<GameProperty, Object> flagGamePropertiesMap = new EnumMap<GameProperty, Object>(GameProperty.class); flag.defineGameProperties(flagGamePropertiesMap); if (!flagGamePropertiesMap.isEmpty()) { GamePropertyBundle properties = new GamePropertyBundle(flag, flagGamePropertiesMap); propertyBundles.add(properties); } } if (flagAnnotation.parent() != NullFlag.class) { AbstractFlag<?> parent = getFlag(flagAnnotation.parent()); flag.setParent(parent); } flags.put(path, flag); disabledFlags.remove(path); } public Map<String, AbstractFlag<?>> getPresentFlags() { Map<String, AbstractFlag<?>> unloadedFlags = Maps.newHashMap(); for (UnloadedFlag unloaded : this.unloadedFlags) { unloadedFlags.put(unloaded.getFlagName(), unloaded); } ImmutableMap.Builder<String, AbstractFlag<?>> builder = ImmutableMap.builder(); return builder.putAll(flags).putAll(unloadedFlags).build(); } public Set<String> getDisabledFlags() { return ImmutableSet.copyOf(disabledFlags); } @SuppressWarnings("unchecked") public <T extends AbstractFlag<?>> T getFlag(Class<T> clazz) { for (AbstractFlag<?> flag : flags.values()) { if (flag.getClass() == clazz) { return (T) flag; } } return null; } public AbstractFlag<?> getFlag(String path) { AbstractFlag<?> flag = flags.get(path); return flag; } public Object getProperty(GameProperty property) { Object value = null; for (GamePropertyBundle bundle : propertyBundles) { Object candidateValue = bundle.get(property); if (candidateValue != null) { value = candidateValue; } } if (value == null) { // Requested properties have the lowest priority value = requestedProperties.get(property); } if (value == null) { // There is no requested property, just use the config default value = defaults.get(property); } if (value == null) { value = property.getDefaultValue(); } return value; } public void requestProperty(GameProperty property, Object value) { requestedProperties.put(property, value); } public GamePropertyBundle getDefaultPropertyBundle() { return requestedProperties; } public static class GamePropertyBundle extends ForwardingMap<GameProperty, Object> implements Comparable<GamePropertyBundle> { private AbstractFlag<?> relatingFlag; private Map<GameProperty, Object> delegate; private GamePropertyPriority.Priority priority; public GamePropertyBundle(AbstractFlag<?> flag, Map<GameProperty, Object> propertyMap) { this.delegate = propertyMap; this.relatingFlag = flag; try { // Doing it the ugly way... Method method = flag.getClass().getMethod("defineGameProperties", Map.class); if (!method.isAnnotationPresent(GamePropertyPriority.class)) { priority = GamePropertyPriority.Priority.NORMAL; } else { GamePropertyPriority priorityAnnotation = method.getAnnotation(GamePropertyPriority.class); priority = priorityAnnotation.value(); } } catch (Exception e) { //Could not get priority priority = GamePropertyPriority.Priority.NORMAL; } } public GamePropertyBundle(GamePropertyPriority.Priority priority, Map<GameProperty, Object> propertyMap) { this.priority = priority; this.delegate = propertyMap; } @Override protected Map<GameProperty, Object> delegate() { return delegate; } public GamePropertyPriority.Priority getPriority() { return priority; } public AbstractFlag<?> getRelatingFlag() { return relatingFlag; } @Override public int compareTo(GamePropertyBundle other) { return Integer.valueOf(priority.getSortInt()).compareTo(other.getPriority().getSortInt()); } } public static class DefaultGamePropertyBundle extends GamePropertyBundle { public DefaultGamePropertyBundle(Map<GameProperty, Object> propertyMap) { super(GamePropertyPriority.Priority.REQUESTED, propertyMap); } } public static class Conflict { private Class<?> conflictSource; private Flag conflictSourceAnnotation; private Class<?> conflictWith; private Flag conflictWithAnnotation; public Conflict(Class<?> conflictSource, Flag conflictSourceAnnotation, Class<?> conflictWith, Flag conflictWithAnnotation) { this.conflictSource = conflictSource; this.conflictSourceAnnotation = conflictSourceAnnotation; this.conflictWith = conflictWith; this.conflictWithAnnotation = conflictWithAnnotation; } public Class<?> getConflictSource() { return conflictSource; } public Flag getConflictSourceAnnotation() { return conflictSourceAnnotation; } public Class<?> getConflictWith() { return conflictWith; } public Flag getConflictWithAnnotation() { return conflictWithAnnotation; } } }