This commit is contained in:
asofold 2015-01-05 22:02:00 +01:00
parent 41e9d89efa
commit bd5794d0bd
4 changed files with 501 additions and 499 deletions

View File

@ -16,160 +16,162 @@ import java.util.Set;
* *
*/ */
public class LinkedHashMapCOW<K, V> implements Map<K, V> { public class LinkedHashMapCOW<K, V> implements Map<K, V> {
private LinkedHashMap<K, V> map;
private final int initialCapacity;
private final float loadFactor;
/**
* Uses: 16, 0.75f, false (default settings, insertion ordered).
*/
public LinkedHashMapCOW() {
this(16, 0.75f);
}
/**
* Uses extra: 0.75f, false (default settings, insertion ordered).
* @param initialCapacity
*/
public LinkedHashMapCOW(int initialCapacity) {
this(initialCapacity, 0.75f);
}
/**
* Uses extra: false (default settings, insertion ordered).
* @param initialCapacity
* @param loadFactor
*/
public LinkedHashMapCOW(int initialCapacity, float loadFactor) {
this.initialCapacity = initialCapacity;
this.loadFactor = loadFactor;
this.map = new LinkedHashMap<K, V>(initialCapacity, loadFactor, false);
}
/**
* Uses: 16, 0.75f, false (default settings, insertion ordered).
* @param map
*/
public LinkedHashMapCOW(Map<K, V> map) {
this();
this.map.putAll(map);
}
/**
* Not synchronized: return a copy of the internal map.
* @return
*/
private LinkedHashMap<K, V> copyMap() {
final LinkedHashMap<K, V> newMap = new LinkedHashMap<K, V>(initialCapacity, loadFactor, false);
newMap.putAll(this.map);
return newMap;
}
@Override
public void clear() {
synchronized (this) {
this.map.clear();
}
}
@Override // TODO: Consider a) add removeEldest... b) add option: copyMap(K) -> only copy if needed.
public boolean containsKey(Object key) {
return this.map.containsKey(key);
}
@Override private LinkedHashMap<K, V> map;
public boolean containsValue(Object value) {
return this.map.containsValue(value);
}
/**
* Unmodifiable version of the EntrySet. Entry.setValue might be possible, but dangerous :p
*/
@Override
public Set<java.util.Map.Entry<K, V>> entrySet() {
return Collections.unmodifiableSet(map.entrySet());
}
@Override private final int initialCapacity;
public V get(Object key) { private final float loadFactor;
// NOTE: If accessOrder can be true, there needs to be synchronization here, defeating any purpose, better use Collections.synchronizedMap(LinkedHashMap...) for that case.
return map.get(key);
}
@Override /**
public boolean isEmpty() { * Uses: 16, 0.75f, false (default settings, insertion ordered).
return map.isEmpty(); */
} public LinkedHashMapCOW() {
this(16, 0.75f);
/** }
* Unmodifiable version of the KeySet.
*/
@Override
public Set<K> keySet() {
return Collections.unmodifiableSet(map.keySet());
}
@Override /**
public V put(final K key, final V value) { * Uses extra: 0.75f, false (default settings, insertion ordered).
final V out; * @param initialCapacity
synchronized (this) { */
final LinkedHashMap<K, V> newMap = copyMap(); public LinkedHashMapCOW(int initialCapacity) {
out = newMap.put(key, value); this(initialCapacity, 0.75f);
this.map = newMap; }
}
return out;
}
@Override /**
public void putAll(final Map<? extends K, ? extends V> m) { * Uses extra: false (default settings, insertion ordered).
synchronized (this) { * @param initialCapacity
final LinkedHashMap<K, V> newMap = copyMap(); * @param loadFactor
newMap.putAll(m); */
this.map = newMap; public LinkedHashMapCOW(int initialCapacity, float loadFactor) {
} this.initialCapacity = initialCapacity;
} this.loadFactor = loadFactor;
this.map = new LinkedHashMap<K, V>(initialCapacity, loadFactor, false);
}
@Override /**
public V remove(final Object key) { * Uses: 16, 0.75f, false (default settings, insertion ordered).
final V out; * @param map
synchronized (this) { */
final LinkedHashMap<K, V> newMap = copyMap(); public LinkedHashMapCOW(Map<K, V> map) {
out = newMap.remove(key); this();
this.map = newMap; this.map.putAll(map);
} }
return out;
}
/**
* Remove all given keys.<br>
* Not the most efficient implementation, copying the map and then removing
* keys, but still better than iterating remove(key).
*
* @param keys
*/
public void removeAll(final Collection<K> keys) {
synchronized (this) {
final LinkedHashMap<K, V> newMap = copyMap();
for (final K key : keys) {
newMap.remove(key);
}
this.map = newMap;
}
}
@Override /**
public int size() { * Not synchronized: return a copy of the internal map.
return map.size(); * @return
} */
private LinkedHashMap<K, V> copyMap() {
/** final LinkedHashMap<K, V> newMap = new LinkedHashMap<K, V>(initialCapacity, loadFactor, false);
* Unmodifiable version of the values (Collection). newMap.putAll(this.map);
*/ return newMap;
@Override }
public Collection<V> values() {
return Collections.unmodifiableCollection(map.values()); @Override
} public void clear() {
synchronized (this) {
this.map.clear();
}
}
@Override
public boolean containsKey(Object key) {
return this.map.containsKey(key);
}
@Override
public boolean containsValue(Object value) {
return this.map.containsValue(value);
}
/**
* Unmodifiable version of the EntrySet. Entry.setValue might be possible, but dangerous :p
*/
@Override
public Set<java.util.Map.Entry<K, V>> entrySet() {
return Collections.unmodifiableSet(map.entrySet());
}
@Override
public V get(Object key) {
// NOTE: If accessOrder can be true, there needs to be synchronization here, defeating any purpose, better use Collections.synchronizedMap(LinkedHashMap...) for that case.
return map.get(key);
}
@Override
public boolean isEmpty() {
return map.isEmpty();
}
/**
* Unmodifiable version of the KeySet.
*/
@Override
public Set<K> keySet() {
return Collections.unmodifiableSet(map.keySet());
}
@Override
public V put(final K key, final V value) {
final V out;
synchronized (this) {
final LinkedHashMap<K, V> newMap = copyMap();
out = newMap.put(key, value);
this.map = newMap;
}
return out;
}
@Override
public void putAll(final Map<? extends K, ? extends V> m) {
synchronized (this) {
final LinkedHashMap<K, V> newMap = copyMap();
newMap.putAll(m);
this.map = newMap;
}
}
@Override
public V remove(final Object key) {
final V out;
synchronized (this) {
final LinkedHashMap<K, V> newMap = copyMap();
out = newMap.remove(key);
this.map = newMap;
}
return out;
}
/**
* Remove all given keys.<br>
* Not the most efficient implementation, copying the map and then removing
* keys, but still better than iterating remove(key).
*
* @param keys
*/
public void removeAll(final Collection<K> keys) {
synchronized (this) {
final LinkedHashMap<K, V> newMap = copyMap();
for (final K key : keys) {
newMap.remove(key);
}
this.map = newMap;
}
}
@Override
public int size() {
return map.size();
}
/**
* Unmodifiable version of the values (Collection).
*/
@Override
public Collection<V> values() {
return Collections.unmodifiableCollection(map.values());
}
} }

View File

@ -18,42 +18,42 @@ import fr.neatmonster.nocheatplus.logging.StaticLog;
* The synchronized methods are to ensure that changing the configurations won't lead to trouble for the asynchronous checks. * The synchronized methods are to ensure that changing the configurations won't lead to trouble for the asynchronous checks.
*/ */
public class ConfigManager { public class ConfigManager {
public static interface ActionFactoryFactory{ public static interface ActionFactoryFactory{
public ActionFactory newActionFactory(Map<String, Object> library); public ActionFactory newActionFactory(Map<String, Object> library);
} }
private static ActionFactoryFactory actionFactoryFactory = new ActionFactoryFactory() { private static ActionFactoryFactory actionFactoryFactory = new ActionFactoryFactory() {
@Override @Override
public final ActionFactory newActionFactory(final Map<String, Object> library) { public final ActionFactory newActionFactory(final Map<String, Object> library) {
return new ActionFactory(library); return new ActionFactory(library);
} }
}; };
/** The map containing the configuration files per world. */ /** The map containing the configuration files per world. */
private static Map<String, ConfigFile> worldsMap = new LinkedHashMap<String, ConfigFile>(); private static Map<String, ConfigFile> worldsMap = new LinkedHashMap<String, ConfigFile>();
private static final WorldConfigProvider<ConfigFile> worldConfigProvider = new WorldConfigProvider<ConfigFile>() { private static final WorldConfigProvider<ConfigFile> worldConfigProvider = new WorldConfigProvider<ConfigFile>() {
@Override @Override
public ConfigFile getDefaultConfig() { public ConfigFile getDefaultConfig() {
return ConfigManager.getConfigFile(); return ConfigManager.getConfigFile();
} }
@Override @Override
public ConfigFile getConfig(String worldName) { public ConfigFile getConfig(String worldName) {
return ConfigManager.getConfigFile(worldName); return ConfigManager.getConfigFile(worldName);
} }
@Override @Override
public Collection<ConfigFile> getAllConfigs() { public Collection<ConfigFile> getAllConfigs() {
return ConfigManager.worldsMap.values(); return ConfigManager.worldsMap.values();
} }
}; };
private static boolean isInitialized = false; private static boolean isInitialized = false;
/** /**
* Factory method. * Factory method.
* @param library * @param library
@ -62,7 +62,7 @@ public class ConfigManager {
public static ActionFactory getActionFactory(final Map<String, Object> library){ public static ActionFactory getActionFactory(final Map<String, Object> library){
return actionFactoryFactory.newActionFactory(library); return actionFactoryFactory.newActionFactory(library);
} }
/** /**
* Set the factory to get actions from. * Set the factory to get actions from.
* This will reset all ActionFactories to null (lazy initialization), * This will reset all ActionFactories to null (lazy initialization),
@ -75,42 +75,42 @@ public class ConfigManager {
*/ */
public static void setActionFactoryFactory(ActionFactoryFactory factory){ public static void setActionFactoryFactory(ActionFactoryFactory factory){
if (factory != null){ if (factory != null){
actionFactoryFactory = factory; actionFactoryFactory = factory;
} }
else{ else{
actionFactoryFactory = new ActionFactoryFactory() { actionFactoryFactory = new ActionFactoryFactory() {
@Override @Override
public final ActionFactory newActionFactory(final Map<String, Object> library) { public final ActionFactory newActionFactory(final Map<String, Object> library) {
return new ActionFactory(library); return new ActionFactory(library);
} }
}; };
} }
// Use lazy resetting. // Use lazy resetting.
for (final ConfigFile config : worldsMap.values()){ for (final ConfigFile config : worldsMap.values()){
config.setActionFactory(null); config.setActionFactory(null);
} }
} }
public static ActionFactoryFactory getActionFactoryFactory(){ public static ActionFactoryFactory getActionFactoryFactory(){
return actionFactoryFactory; return actionFactoryFactory;
} }
/** /**
* Force setting up all configs action factories. * Force setting up all configs action factories.
*/ */
public static void setAllActionFactories(){ public static void setAllActionFactories(){
for (final ConfigFile config : worldsMap.values()){ for (final ConfigFile config : worldsMap.values()){
config.setActionFactory(); config.setActionFactory();
} }
} }
/** /**
* Get the WorldConfigProvider in use. * Get the WorldConfigProvider in use.
* @return * @return
*/ */
public static WorldConfigProvider<ConfigFile> getWorldConfigProvider() { public static WorldConfigProvider<ConfigFile> getWorldConfigProvider() {
return worldConfigProvider; return worldConfigProvider;
} }
/** /**
* Cleanup. * Cleanup.
@ -129,7 +129,7 @@ public class ConfigManager {
public static ConfigFile getConfigFile() { public static ConfigFile getConfigFile() {
return worldsMap.get(null); return worldsMap.get(null);
} }
/** /**
* (Synchronized version). * (Synchronized version).
* @return * @return
@ -137,7 +137,7 @@ public class ConfigManager {
*/ */
@Deprecated @Deprecated
public static synchronized ConfigFile getConfigFileSync() { public static synchronized ConfigFile getConfigFileSync() {
return getConfigFile(); return getConfigFile();
} }
/** /**
@ -148,25 +148,25 @@ public class ConfigManager {
* @return the configuration file * @return the configuration file
*/ */
public static ConfigFile getConfigFile(final String worldName) { public static ConfigFile getConfigFile(final String worldName) {
final ConfigFile configFile = worldsMap.get(worldName); final ConfigFile configFile = worldsMap.get(worldName);
if (configFile != null){ if (configFile != null){
return configFile; return configFile;
} }
// Expensive only once per world, for the rest of the runtime the file is returned fast. // Expensive only once per world, for the rest of the runtime the file is returned fast.
synchronized(ConfigManager.class){ synchronized(ConfigManager.class){
// Need to check again. // Need to check again.
if (worldsMap.containsKey(worldName)){ if (worldsMap.containsKey(worldName)){
return worldsMap.get(worldName); return worldsMap.get(worldName);
} }
// Copy the whole map with the default configuration set for this world. // Copy the whole map with the default configuration set for this world.
final Map<String, ConfigFile> newWorldsMap = new LinkedHashMap<String, ConfigFile>(ConfigManager.worldsMap); final Map<String, ConfigFile> newWorldsMap = new LinkedHashMap<String, ConfigFile>(ConfigManager.worldsMap);
final ConfigFile globalConfig = newWorldsMap.get(null); final ConfigFile globalConfig = newWorldsMap.get(null);
newWorldsMap.put(worldName, globalConfig); newWorldsMap.put(worldName, globalConfig);
ConfigManager.worldsMap = newWorldsMap; ConfigManager.worldsMap = newWorldsMap;
return globalConfig; return globalConfig;
} }
} }
/** /**
* (Synchronized version). * (Synchronized version).
* @param worldName * @param worldName
@ -175,7 +175,7 @@ public class ConfigManager {
*/ */
@Deprecated @Deprecated
public static synchronized ConfigFile getConfigFileSync(final String worldName) { public static synchronized ConfigFile getConfigFileSync(final String worldName) {
return getConfigFile(worldName); return getConfigFile(worldName);
} }
/** /**
@ -185,8 +185,8 @@ public class ConfigManager {
* the instance of NoCheatPlus * the instance of NoCheatPlus
*/ */
public static synchronized void init(final Plugin plugin) { public static synchronized void init(final Plugin plugin) {
// (This can lead to minor problems with async checks during reloading.) // (This can lead to minor problems with async checks during reloading.)
LinkedHashMap<String, ConfigFile> newWorldsMap = new LinkedHashMap<String, ConfigFile>(); LinkedHashMap<String, ConfigFile> newWorldsMap = new LinkedHashMap<String, ConfigFile>();
// Try to obtain and parse the global configuration file. // Try to obtain and parse the global configuration file.
final File globalFile = new File(plugin.getDataFolder(), "config.yml"); final File globalFile = new File(plugin.getDataFolder(), "config.yml");
PathUtils.processPaths(globalFile, "global config", false); PathUtils.processPaths(globalFile, "global config", false);
@ -194,49 +194,49 @@ public class ConfigManager {
globalConfig.setDefaults(new DefaultConfig()); globalConfig.setDefaults(new DefaultConfig());
globalConfig.options().copyDefaults(true); globalConfig.options().copyDefaults(true);
if (globalFile.exists()){ if (globalFile.exists()){
try { try {
globalConfig.load(globalFile); globalConfig.load(globalFile);
// Quick shallow ugly fix: only save back if loading was successful. // Quick shallow ugly fix: only save back if loading was successful.
try { try {
if (globalConfig.getBoolean(ConfPaths.SAVEBACKCONFIG)){ if (globalConfig.getBoolean(ConfPaths.SAVEBACKCONFIG)){
if (!globalConfig.contains(ConfPaths.CONFIGVERSION_CREATED)){ if (!globalConfig.contains(ConfPaths.CONFIGVERSION_CREATED)){
// Workaround. // Workaround.
globalConfig.set(ConfPaths.CONFIGVERSION_CREATED, DefaultConfig.buildNumber); globalConfig.set(ConfPaths.CONFIGVERSION_CREATED, DefaultConfig.buildNumber);
} }
globalConfig.set(ConfPaths.CONFIGVERSION_SAVED, DefaultConfig.buildNumber); globalConfig.set(ConfPaths.CONFIGVERSION_SAVED, DefaultConfig.buildNumber);
globalConfig.save(globalFile); globalConfig.save(globalFile);
} }
} catch (final Exception e) { } catch (final Exception e) {
StaticLog.logSevere("[NoCheatPlus] Could not save back config.yml (see exception below)."); StaticLog.logSevere("[NoCheatPlus] Could not save back config.yml (see exception below).");
StaticLog.logSevere(e); StaticLog.logSevere(e);
} }
} catch (final Exception e) { } catch (final Exception e) {
StaticLog.logSevere("[NoCheatPlus] Could not load config.yml (see exception below). Continue with default settings..."); StaticLog.logSevere("[NoCheatPlus] Could not load config.yml (see exception below). Continue with default settings...");
StaticLog.logSevere(e); StaticLog.logSevere(e);
} }
} }
else { else {
globalConfig.options().header("This configuration was auto-generated by NoCheatPlus."); globalConfig.options().header("This configuration was auto-generated by NoCheatPlus.");
globalConfig.options().copyHeader(true); globalConfig.options().copyHeader(true);
try { try {
globalConfig.set(ConfPaths.CONFIGVERSION_CREATED, DefaultConfig.buildNumber); globalConfig.set(ConfPaths.CONFIGVERSION_CREATED, DefaultConfig.buildNumber);
globalConfig.set(ConfPaths.CONFIGVERSION_SAVED, DefaultConfig.buildNumber); globalConfig.set(ConfPaths.CONFIGVERSION_SAVED, DefaultConfig.buildNumber);
globalConfig.save(globalFile); globalConfig.save(globalFile);
} catch (final Exception e) { } catch (final Exception e) {
StaticLog.logSevere(e); StaticLog.logSevere(e);
} }
} }
// globalConfig.setActionFactory(); // globalConfig.setActionFactory();
newWorldsMap.put(null, globalConfig); newWorldsMap.put(null, globalConfig);
final MemoryConfiguration worldDefaults = PathUtils.getWorldsDefaultConfig(globalConfig); final MemoryConfiguration worldDefaults = PathUtils.getWorldsDefaultConfig(globalConfig);
// Try to obtain and parse the world-specific configuration files. // Try to obtain and parse the world-specific configuration files.
final HashMap<String, File> worldFiles = new HashMap<String, File>(); final HashMap<String, File> worldFiles = new HashMap<String, File>();
if (plugin.getDataFolder().isDirectory()){ if (plugin.getDataFolder().isDirectory()){
for (final File file : plugin.getDataFolder().listFiles()){ for (final File file : plugin.getDataFolder().listFiles()){
if (file.isFile()) { if (file.isFile()) {
final String fileName = file.getName(); final String fileName = file.getName();
if (fileName.matches(".+_config.yml$")) { if (fileName.matches(".+_config.yml$")) {
final String worldname = fileName.substring(0, fileName.length() - 11); final String worldname = fileName.substring(0, fileName.length() - 11);
@ -252,26 +252,26 @@ public class ConfigManager {
worldConfig.setDefaults(worldDefaults); worldConfig.setDefaults(worldDefaults);
worldConfig.options().copyDefaults(true); worldConfig.options().copyDefaults(true);
try { try {
worldConfig.load(worldFile); worldConfig.load(worldFile);
newWorldsMap.put(worldEntry.getKey(), worldConfig); newWorldsMap.put(worldEntry.getKey(), worldConfig);
try{ try{
if (worldConfig.getBoolean(ConfPaths.SAVEBACKCONFIG)) worldConfig.save(worldFile); if (worldConfig.getBoolean(ConfPaths.SAVEBACKCONFIG)) worldConfig.save(worldFile);
} catch (final Exception e){ } catch (final Exception e){
StaticLog.logSevere("[NoCheatPlus] Couldn't save back world-specific configuration for " + worldEntry.getKey() + " (see exception below)."); StaticLog.logSevere("[NoCheatPlus] Couldn't save back world-specific configuration for " + worldEntry.getKey() + " (see exception below).");
StaticLog.logSevere(e); StaticLog.logSevere(e);
} }
} catch (final Exception e) { } catch (final Exception e) {
StaticLog.logSevere("[NoCheatPlus] Couldn't load world-specific configuration for " + worldEntry.getKey() + " (see exception below). Continue with global default settings..."); StaticLog.logSevere("[NoCheatPlus] Couldn't load world-specific configuration for " + worldEntry.getKey() + " (see exception below). Continue with global default settings...");
StaticLog.logSevere(e); StaticLog.logSevere(e);
} }
worldConfig.setDefaults(globalConfig); worldConfig.setDefaults(globalConfig);
worldConfig.options().copyDefaults(true); worldConfig.options().copyDefaults(true);
// worldConfig.setActionFactory(); // worldConfig.setActionFactory();
} }
ConfigManager.worldsMap = newWorldsMap; ConfigManager.worldsMap = newWorldsMap;
isInitialized = true; isInitialized = true;
} }
/** /**
* Informal test if the init method completed (no details are reflected). * Informal test if the init method completed (no details are reflected).
* @return * @return
@ -279,34 +279,34 @@ public class ConfigManager {
public static boolean isInitialized() { public static boolean isInitialized() {
return isInitialized; return isInitialized;
} }
/** /**
* Set a property for all configurations. Might use with DataManager.clearConfigs if check-configurations might already be in use. * Set a property for all configurations. Might use with DataManager.clearConfigs if check-configurations might already be in use.
* @param path * @param path
* @param value * @param value
*/ */
public static synchronized void setForAllConfigs(String path, Object value){ public static synchronized void setForAllConfigs(String path, Object value){
final Map<String, ConfigFile> newWorldsMap = new LinkedHashMap<String, ConfigFile>(ConfigManager.worldsMap); final Map<String, ConfigFile> newWorldsMap = new LinkedHashMap<String, ConfigFile>(ConfigManager.worldsMap);
for (final ConfigFile cfg : newWorldsMap.values()){ for (final ConfigFile cfg : newWorldsMap.values()){
cfg.set(path, value); cfg.set(path, value);
} }
ConfigManager.worldsMap = newWorldsMap; ConfigManager.worldsMap = newWorldsMap;
} }
/** /**
* Check if any config has a boolean set to true for the given path. * Check if any config has a boolean set to true for the given path.
* @param path * @param path
* @return True if any config has a boolean set to true for the given path. * @return True if any config has a boolean set to true for the given path.
*/ */
public static boolean isTrueForAnyConfig(String path) { public static boolean isTrueForAnyConfig(String path) {
for (final ConfigFile cfg : worldsMap.values()){ for (final ConfigFile cfg : worldsMap.values()){
if (cfg.getBoolean(path, false)) { if (cfg.getBoolean(path, false)) {
return true; return true;
} }
} }
return false; return false;
} }
/** /**
* Get the maximally found number for the given config path. This does not throw errors. It will return null, if nothing is found or all lookups failed otherwise. * Get the maximally found number for the given config path. This does not throw errors. It will return null, if nothing is found or all lookups failed otherwise.
* <br> * <br>
@ -315,24 +315,24 @@ public class ConfigManager {
* @return Value or null. * @return Value or null.
*/ */
public static Double getMaxNumberForAllConfigs(final String path){ public static Double getMaxNumberForAllConfigs(final String path){
Number max = null; Number max = null;
for (final ConfigFile config : worldsMap.values()){ for (final ConfigFile config : worldsMap.values()){
try{ try{
final Object obj = config.get(path); final Object obj = config.get(path);
if (obj instanceof Number){ if (obj instanceof Number){
final Number num = (Number) obj; final Number num = (Number) obj;
if (max == null || num.doubleValue() > max.doubleValue()){ if (max == null || num.doubleValue() > max.doubleValue()){
max = num; max = num;
} }
} }
} }
catch (Throwable t){ catch (Throwable t){
// Holzhammer // Holzhammer
} }
} }
return max.doubleValue(); return max.doubleValue();
} }
/** /**
* Get the minimally found number for the given config path. This does not throw errors. It will return null, if nothing is found or all lookups failed otherwise. * Get the minimally found number for the given config path. This does not throw errors. It will return null, if nothing is found or all lookups failed otherwise.
* <br> * <br>
@ -341,24 +341,24 @@ public class ConfigManager {
* @return Value or null. * @return Value or null.
*/ */
public static Double getMinNumberForAllConfigs(final String path){ public static Double getMinNumberForAllConfigs(final String path){
Number min = null; Number min = null;
for (final ConfigFile config : worldsMap.values()){ for (final ConfigFile config : worldsMap.values()){
try{ try{
final Object obj = config.get(path); final Object obj = config.get(path);
if (obj instanceof Number){ if (obj instanceof Number){
final Number num = (Number) obj; final Number num = (Number) obj;
if (min == null || num.doubleValue() < min.doubleValue()){ if (min == null || num.doubleValue() < min.doubleValue()){
min = num; min = num;
} }
} }
} }
catch (Throwable t){ catch (Throwable t){
// Holzhammer // Holzhammer
} }
} }
return min.doubleValue(); return min.doubleValue();
} }
// TODO: consider: filter(Max|Min)NumberForAllConfigs(String path, String filerPath, boolean filterPreset) // TODO: consider: filter(Max|Min)NumberForAllConfigs(String path, String filerPath, boolean filterPreset)
} }

View File

@ -24,83 +24,83 @@ import fr.neatmonster.nocheatplus.utilities.ds.prefixtree.CharPrefixTree;
import fr.neatmonster.nocheatplus.utilities.ds.prefixtree.SimpleCharPrefixTree; import fr.neatmonster.nocheatplus.utilities.ds.prefixtree.SimpleCharPrefixTree;
public class PathUtils { public class PathUtils {
// Deprecated paths. // Deprecated paths.
private static final Set<String> deprecatedFields = new LinkedHashSet<String>(); private static final Set<String> deprecatedFields = new LinkedHashSet<String>();
private static final SimpleCharPrefixTree deprecatedPrefixes = new SimpleCharPrefixTree(); private static final SimpleCharPrefixTree deprecatedPrefixes = new SimpleCharPrefixTree();
// Paths only for the global config. // Paths only for the global config.
private static final Set<String> globalOnlyFields = new HashSet<String>(); private static final Set<String> globalOnlyFields = new HashSet<String>();
private static final SimpleCharPrefixTree globalOnlyPrefixes = new SimpleCharPrefixTree(); private static final SimpleCharPrefixTree globalOnlyPrefixes = new SimpleCharPrefixTree();
// Paths moved to other paths. // Paths moved to other paths.
private static final Map<String, String> movedPaths = new LinkedHashMap<String, String>(); private static final Map<String, String> movedPaths = new LinkedHashMap<String, String>();
static{ static{
initPaths(); initPaths();
} }
/** /**
* Initialize annotation-based path properties. * Initialize annotation-based path properties.
* @return * @return
*/ */
private static void initPaths(){ private static void initPaths(){
deprecatedFields.clear(); deprecatedFields.clear();
deprecatedPrefixes.clear(); deprecatedPrefixes.clear();
globalOnlyFields.clear(); globalOnlyFields.clear();
globalOnlyPrefixes.clear(); globalOnlyPrefixes.clear();
movedPaths.clear(); movedPaths.clear();
for (final Field field : ConfPaths.class.getDeclaredFields()){ for (final Field field : ConfPaths.class.getDeclaredFields()){
if (field.getType() != String.class){ if (field.getType() != String.class){
// Only process strings. // Only process strings.
continue; continue;
} }
final String fieldName = field.getName(); final String fieldName = field.getName();
checkAddPrefixes(field, fieldName, GlobalConfig.class, globalOnlyFields, globalOnlyPrefixes); checkAddPrefixes(field, fieldName, GlobalConfig.class, globalOnlyFields, globalOnlyPrefixes);
checkAddPrefixes(field, fieldName, Deprecated.class, deprecatedFields, deprecatedPrefixes); checkAddPrefixes(field, fieldName, Deprecated.class, deprecatedFields, deprecatedPrefixes);
if (field.isAnnotationPresent(Moved.class)){ if (field.isAnnotationPresent(Moved.class)){
// TODO: Prefixes: Might later support relocating entire sections with one annotation? // TODO: Prefixes: Might later support relocating entire sections with one annotation?
addMoved(field, field.getAnnotation(Moved.class)); addMoved(field, field.getAnnotation(Moved.class));
} }
} }
} }
private static void checkAddPrefixes(Field field, String fieldName, Class<? extends Annotation> annotation, Set<String> fieldNames, SimpleCharPrefixTree pathPrefixes) { private static void checkAddPrefixes(Field field, String fieldName, Class<? extends Annotation> annotation, Set<String> fieldNames, SimpleCharPrefixTree pathPrefixes) {
if (field.isAnnotationPresent(annotation)){ if (field.isAnnotationPresent(annotation)){
fieldNames.add(fieldName); fieldNames.add(fieldName);
addPrefixesField(field, pathPrefixes); addPrefixesField(field, pathPrefixes);
} }
else{ else{
for (final String refName : fieldNames){ for (final String refName : fieldNames){
if (fieldName.startsWith(refName)){ if (fieldName.startsWith(refName)){
addPrefixesField(field, pathPrefixes); addPrefixesField(field, pathPrefixes);
} }
} }
} }
} }
private static void addPrefixesField(Field field, SimpleCharPrefixTree pathPrefixes) { private static void addPrefixesField(Field field, SimpleCharPrefixTree pathPrefixes) {
try { try {
final String path = field.get(null).toString(); final String path = field.get(null).toString();
if (path != null){ if (path != null){
pathPrefixes.feed(path); pathPrefixes.feed(path);
} }
} catch (IllegalArgumentException e) { } catch (IllegalArgumentException e) {
} catch (IllegalAccessException e) { } catch (IllegalAccessException e) {
} }
} }
private static void addMoved(final Field field, final Moved rel) { private static void addMoved(final Field field, final Moved rel) {
try { try {
final String path = field.get(null).toString(); final String path = field.get(null).toString();
movedPaths.put(path, rel.newPath()); movedPaths.put(path, rel.newPath());
} catch (IllegalArgumentException e) { } catch (IllegalArgumentException e) {
} catch (IllegalAccessException e) { } catch (IllegalAccessException e) {
} }
} }
/** /**
* Warn on the console if paths are used. * Warn on the console if paths are used.
* @param config * @param config
* @param paths * @param paths
@ -108,135 +108,135 @@ public class PathUtils {
* @param warnedPaths Paths which were found, can be null. * @param warnedPaths Paths which were found, can be null.
*/ */
protected static void warnPaths(final ConfigFile config, final CharPrefixTree<?, ?> paths, final String msgPrefix, final Set<String> warnedPaths){ protected static void warnPaths(final ConfigFile config, final CharPrefixTree<?, ?> paths, final String msgPrefix, final Set<String> warnedPaths){
final Logger logger = Bukkit.getLogger(); final Logger logger = Bukkit.getLogger();
for (final String path : config.getKeys(true)){ for (final String path : config.getKeys(true)){
if (paths.hasPrefix(path)){ if (paths.hasPrefix(path)){
logger.warning("[NoCheatPlus] Config path '" + path + "'" + msgPrefix); logger.warning("[NoCheatPlus] Config path '" + path + "'" + msgPrefix);
if (warnedPaths != null){ if (warnedPaths != null){
warnedPaths.add(path); warnedPaths.add(path);
} }
} }
} }
} }
/** /**
* Run all warning checks and alter config if necessary (GlobalConfig, Deprecated, Moved). * Run all warning checks and alter config if necessary (GlobalConfig, Deprecated, Moved).
* @param file * @param file
* @param configName * @param configName
*/ */
public static void processPaths(File file, String configName, boolean isWorldConfig){ public static void processPaths(File file, String configName, boolean isWorldConfig){
ConfigFile config = new ConfigFile(); ConfigFile config = new ConfigFile();
try { try {
config.load(file); config.load(file);
final Set<String> removePaths = new LinkedHashSet<String>(); final Set<String> removePaths = new LinkedHashSet<String>();
final Map<String, Object> addPaths = new LinkedHashMap<String, Object>(); final Map<String, Object> addPaths = new LinkedHashMap<String, Object>();
if (isWorldConfig){ if (isWorldConfig){
// TODO: might remove these [though some global only paths might actually work]. // TODO: might remove these [though some global only paths might actually work].
processGlobalOnlyPaths(config, configName, null); processGlobalOnlyPaths(config, configName, null);
} }
processDeprecatedPaths(config, configName, removePaths); processDeprecatedPaths(config, configName, removePaths);
processMovedPaths(config, configName, removePaths, addPaths); processMovedPaths(config, configName, removePaths, addPaths);
boolean changed = false; boolean changed = false;
if (!removePaths.isEmpty()){ if (!removePaths.isEmpty()){
config = removePaths(config, removePaths); config = removePaths(config, removePaths);
changed = true; changed = true;
} }
if (!addPaths.isEmpty()){ if (!addPaths.isEmpty()){
setPaths(config, addPaths); setPaths(config, addPaths);
changed = true; changed = true;
} }
if (changed){ if (changed){
try{ try{
config.save(file); config.save(file);
} }
catch(Throwable t){ catch(Throwable t){
// Do log this one. // Do log this one.
StaticLog.logSevere("[NoCheatPlus] Failed to save configuration (" + configName + ") with changes: " + t.getClass().getSimpleName()); StaticLog.logSevere("[NoCheatPlus] Failed to save configuration (" + configName + ") with changes: " + t.getClass().getSimpleName());
StaticLog.logSevere(t); StaticLog.logSevere(t);
} }
} }
} catch (FileNotFoundException e) { } catch (FileNotFoundException e) {
} catch (IOException e) { } catch (IOException e) {
} catch (InvalidConfigurationException e) { } catch (InvalidConfigurationException e) {
} }
} }
/** /**
* Set paths. * Set paths.
* @param config * @param config
* @param addPaths * @param addPaths
*/ */
public static void setPaths(final ConfigFile config, final Map<String, Object> setPaths) { public static void setPaths(final ConfigFile config, final Map<String, Object> setPaths) {
for (final Entry<String, Object> entry : setPaths.entrySet()){ for (final Entry<String, Object> entry : setPaths.entrySet()){
config.set(entry.getKey(), entry.getValue()); config.set(entry.getKey(), entry.getValue());
} }
} }
/** /**
* Get a new ConfigFile instance with all paths removed (recursively by prefix). * Get a new ConfigFile instance with all paths removed (recursively by prefix).
* @param config * @param config
* @param removePaths * @param removePaths
* @return * @return
*/ */
public static ConfigFile removePaths(final ConfigFile config, final Collection<String> removePaths) { public static ConfigFile removePaths(final ConfigFile config, final Collection<String> removePaths) {
final SimpleCharPrefixTree prefixes = new SimpleCharPrefixTree(); final SimpleCharPrefixTree prefixes = new SimpleCharPrefixTree();
for (final String path : removePaths){ for (final String path : removePaths){
prefixes.feed(path); prefixes.feed(path);
} }
final ConfigFile newConfig = new ConfigFile(); final ConfigFile newConfig = new ConfigFile();
for (final Entry<String, Object> entry : config.getValues(true).entrySet()){ for (final Entry<String, Object> entry : config.getValues(true).entrySet()){
final String path = entry.getKey(); final String path = entry.getKey();
final Object value = entry.getValue(); final Object value = entry.getValue();
if (value instanceof ConfigurationSection){ if (value instanceof ConfigurationSection){
continue; continue;
} }
if (!prefixes.hasPrefix(path)){ if (!prefixes.hasPrefix(path)){
newConfig.set(path, value); newConfig.set(path, value);
} }
} }
return newConfig; return newConfig;
} }
/** /**
* *
* @param config * @param config
* @param configName * @param configName
* @param removePaths * @param removePaths
* @param addPaths * @param addPaths
* @return If entries were added (paths to be removed are processed later). * @return If entries were added (paths to be removed are processed later).
*/ */
protected static void processMovedPaths(final ConfigFile config, final String configName, final Set<String> removePaths, final Map<String, Object> addPaths) { protected static void processMovedPaths(final ConfigFile config, final String configName, final Set<String> removePaths, final Map<String, Object> addPaths) {
final Logger logger = Bukkit.getLogger(); final Logger logger = Bukkit.getLogger();
for (final Entry<String, String> entry : movedPaths.entrySet()){ for (final Entry<String, String> entry : movedPaths.entrySet()){
final String path = entry.getKey(); final String path = entry.getKey();
if (config.contains(path)){ if (config.contains(path)){
final String newPath = entry.getValue(); final String newPath = entry.getValue();
final String to; final String to;
if (newPath == null | newPath.isEmpty()){ if (newPath == null | newPath.isEmpty()){
to = "."; to = ".";
} }
else{ else{
to = " to '" + newPath + "'."; to = " to '" + newPath + "'.";
final Object value = config.get(path); final Object value = config.get(path);
config.set(newPath, value); config.set(newPath, value);
addPaths.put(newPath, value); addPaths.put(newPath, value);
removePaths.add(path); removePaths.add(path);
} }
logger.warning("[NoCheatPlus] Config path '" + path + "' (" + configName + ") has been moved" + to); logger.warning("[NoCheatPlus] Config path '" + path + "' (" + configName + ") has been moved" + to);
} }
} }
} }
/** /**
* Warn about paths that are deprecated (not in use). * Warn about paths that are deprecated (not in use).
* @param config * @param config
* @param paths * @param paths
* @param configName * @param configName
*/ */
protected static void processDeprecatedPaths(ConfigFile config, String configName, final Set<String> removePaths){ protected static void processDeprecatedPaths(ConfigFile config, String configName, final Set<String> removePaths){
warnPaths(config, deprecatedPrefixes, " (" + configName + ") is not in use anymore.", removePaths); warnPaths(config, deprecatedPrefixes, " (" + configName + ") is not in use anymore.", removePaths);
} }
/** /**
* Warn about paths that are only to be set in the global config. * Warn about paths that are only to be set in the global config.
* @param config * @param config
@ -244,49 +244,49 @@ public class PathUtils {
* @param configName * @param configName
*/ */
protected static void processGlobalOnlyPaths(ConfigFile config, String configName, final Set<String> removePaths){ protected static void processGlobalOnlyPaths(ConfigFile config, String configName, final Set<String> removePaths){
warnPaths(config, globalOnlyPrefixes, " (" + configName + ") should only be set in the global configuration.", removePaths); warnPaths(config, globalOnlyPrefixes, " (" + configName + ") should only be set in the global configuration.", removePaths);
} }
/** /**
* A config file only containing the entries that are not set as global only. * A config file only containing the entries that are not set as global only.
* @param defaultConfig * @param defaultConfig
* @return * @return
*/ */
public static MemoryConfiguration getWorldsDefaultConfig(final ConfigFile defaultConfig){ public static MemoryConfiguration getWorldsDefaultConfig(final ConfigFile defaultConfig){
final char sep = defaultConfig.options().pathSeparator(); final char sep = defaultConfig.options().pathSeparator();
final MemoryConfiguration config = new ConfigFile(); final MemoryConfiguration config = new ConfigFile();
config.options().pathSeparator(sep); config.options().pathSeparator(sep);
final Map<String, Object> defaults = defaultConfig.getValues(false); final Map<String, Object> defaults = defaultConfig.getValues(false);
for (final Entry<String, Object> entry : defaults.entrySet()){ for (final Entry<String, Object> entry : defaults.entrySet()){
final String part = entry.getKey(); final String part = entry.getKey();
if (!part.isEmpty() && !mayBeInWorldConfig(part)) continue; if (!part.isEmpty() && !mayBeInWorldConfig(part)) continue;
final Object value = entry.getValue(); final Object value = entry.getValue();
if (value instanceof ConfigurationSection) addWorldConfigSection(config, (ConfigurationSection) value, part, sep); if (value instanceof ConfigurationSection) addWorldConfigSection(config, (ConfigurationSection) value, part, sep);
else config.set(part, value); else config.set(part, value);
} }
return config; return config;
}
protected static void addWorldConfigSection(final MemoryConfiguration config, final ConfigurationSection section, final String path, final char sep) {
final Map<String, Object> values = section.getValues(false);
for (final Entry<String, Object> entry : values.entrySet()){
final String fullPath = path + sep + entry.getKey();
if (!mayBeInWorldConfig(fullPath)) continue;
final Object value = entry.getValue();
if (value instanceof ConfigurationSection) addWorldConfigSection(config, (ConfigurationSection) value, fullPath, sep);
else config.set(fullPath, value);
}
}
public static boolean mayBeInWorldConfig(final String path){
if (globalOnlyPrefixes.hasPrefix(path)) return false;
return mayBeInConfig(path);
}
public static boolean mayBeInConfig(final String path){
if (deprecatedPrefixes.hasPrefix(path)) return false;
if (movedPaths.containsKey(path)) return false;
return true;
} }
protected static void addWorldConfigSection(final MemoryConfiguration config, final ConfigurationSection section, final String path, final char sep) {
final Map<String, Object> values = section.getValues(false);
for (final Entry<String, Object> entry : values.entrySet()){
final String fullPath = path + sep + entry.getKey();
if (!mayBeInWorldConfig(fullPath)) continue;
final Object value = entry.getValue();
if (value instanceof ConfigurationSection) addWorldConfigSection(config, (ConfigurationSection) value, fullPath, sep);
else config.set(fullPath, value);
}
}
public static boolean mayBeInWorldConfig(final String path){
if (globalOnlyPrefixes.hasPrefix(path)) return false;
return mayBeInConfig(path);
}
public static boolean mayBeInConfig(final String path){
if (deprecatedPrefixes.hasPrefix(path)) return false;
if (movedPaths.containsKey(path)) return false;
return true;
}
} }

View File

@ -11,25 +11,25 @@ import java.util.Collection;
* *
*/ */
public interface WorldConfigProvider <C extends RawConfigFile>{ public interface WorldConfigProvider <C extends RawConfigFile>{
/** /**
* Get the default configuration. * Get the default configuration.
* @return * @return
*/ */
public C getDefaultConfig(); public C getDefaultConfig();
/** /**
* Get the world configuration. * Get the world configuration.
* @param worldName The default config has null as world. The default config is returned, if the world is not known. * @param worldName The default config has null as world. The default config is returned, if the world is not known.
* @return * @return
*/ */
public C getConfig(String worldName); public C getConfig(String worldName);
/** /**
* Get a Collection-view of all worlds config files, including the default configuration. * Get a Collection-view of all worlds config files, including the default configuration.
* @return * @return
*/ */
public Collection<C> getAllConfigs(); public Collection<C> getAllConfigs();
// TODO: Add operations for all configs, like setForAllConfigs, get(Max|min)NumberForAllConfigs, .... // TODO: Add operations for all configs, like setForAllConfigs, get(Max|min)NumberForAllConfigs, ....
} }