mirror of
https://github.com/NoCheatPlus/NoCheatPlus.git
synced 2024-12-26 10:28:05 +01:00
Rework PathUtils.
* Add processing for a new @Moved annotation for moving content to other paths. * Remove deprecated paths. * Changed visibility of some static methods. * Moderate renaming. * Remove unused set. * Ensure only string fields are processed (potential future issue).
This commit is contained in:
parent
b84235d31f
commit
be91af4e51
@ -172,7 +172,7 @@ public class ConfigManager {
|
||||
worldsMap.clear();
|
||||
// Try to obtain and parse the global configuration file.
|
||||
final File globalFile = new File(plugin.getDataFolder(), "config.yml");
|
||||
PathUtils.warnPaths(globalFile, "global config", false);
|
||||
PathUtils.processPaths(globalFile, "global config", false);
|
||||
final ConfigFile globalConfig = new ConfigFile();
|
||||
globalConfig.setDefaults(new DefaultConfig());
|
||||
globalConfig.options().copyDefaults(true);
|
||||
@ -223,7 +223,7 @@ public class ConfigManager {
|
||||
}
|
||||
for (final Entry<String, File> worldEntry : worldFiles.entrySet()) {
|
||||
final File worldFile = worldEntry.getValue();
|
||||
PathUtils.warnPaths(worldFile, worldEntry.getKey(), true);
|
||||
PathUtils.processPaths(worldFile, "world " + worldEntry.getKey(), true);
|
||||
final ConfigFile worldConfig = new ConfigFile();
|
||||
worldConfig.setDefaults(worldDefaults);
|
||||
worldConfig.options().copyDefaults(true);
|
||||
|
@ -0,0 +1,20 @@
|
||||
package fr.neatmonster.nocheatplus.config;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* A configuration path that has been relocated. newPath may be empty (relocated "somewhere").<br>
|
||||
* Note that only individual entries can be relocated at present, not sections.
|
||||
* @author mc_dev
|
||||
*
|
||||
*/
|
||||
@Documented
|
||||
@Target(ElementType.FIELD)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface Moved{
|
||||
public String newPath() default "";
|
||||
}
|
@ -3,8 +3,11 @@ package fr.neatmonster.nocheatplus.config;
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
@ -16,100 +19,142 @@ import org.bukkit.configuration.ConfigurationSection;
|
||||
import org.bukkit.configuration.InvalidConfigurationException;
|
||||
import org.bukkit.configuration.MemoryConfiguration;
|
||||
|
||||
import fr.neatmonster.nocheatplus.logging.LogUtil;
|
||||
import fr.neatmonster.nocheatplus.utilities.ds.prefixtree.CharPrefixTree;
|
||||
import fr.neatmonster.nocheatplus.utilities.ds.prefixtree.SimpleCharPrefixTree;
|
||||
|
||||
public class PathUtils {
|
||||
|
||||
// Deprecated paths.
|
||||
private static final Set<String> deprecatedFields = new LinkedHashSet<String>();
|
||||
private static final SimpleCharPrefixTree deprecatedPrefixes = new SimpleCharPrefixTree();
|
||||
|
||||
/** Field names of ConfPaths. */
|
||||
// Paths only for the global config.
|
||||
private static final Set<String> globalOnlyFields = new HashSet<String>();
|
||||
|
||||
/** Paths of ConfPaths (field values). */
|
||||
private static final Set<String> globalOnlyPaths = new LinkedHashSet<String>();
|
||||
|
||||
private static final SimpleCharPrefixTree globalOnlyPrefixes = new SimpleCharPrefixTree();
|
||||
|
||||
// Paths moved to other paths.
|
||||
private static final Map<String, String> movedPaths = new LinkedHashMap<String, String>();
|
||||
|
||||
static{
|
||||
initGlobalOnlyPaths();
|
||||
initPaths();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test/evaluation of using annotations to confine config paths to the global config.
|
||||
* Initialize annotation-based path properties.
|
||||
* @return
|
||||
*/
|
||||
private static void initGlobalOnlyPaths(){
|
||||
private static void initPaths(){
|
||||
deprecatedFields.clear();
|
||||
deprecatedPrefixes.clear();
|
||||
globalOnlyFields.clear();
|
||||
globalOnlyPaths.clear();
|
||||
globalOnlyPrefixes.clear();
|
||||
movedPaths.clear();
|
||||
for (final Field field : ConfPaths.class.getDeclaredFields()){
|
||||
final String name = field.getName();
|
||||
if (field.isAnnotationPresent(GlobalConfig.class)){
|
||||
globalOnlyFields.add(name);
|
||||
addGlobalOnlyPath(field);
|
||||
if (field.getType() != String.class){
|
||||
// Only process strings.
|
||||
continue;
|
||||
}
|
||||
else{
|
||||
for (final String refName : globalOnlyFields){
|
||||
if (name.startsWith(refName) && field.getType() == String.class){
|
||||
addGlobalOnlyPath(field);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (field.isAnnotationPresent(Deprecated.class)){
|
||||
deprecatedFields.add(name);
|
||||
addDeprecated(field);
|
||||
final String fieldName = field.getName();
|
||||
|
||||
checkAddPrefixes(field, fieldName, GlobalConfig.class, globalOnlyFields, globalOnlyPrefixes);
|
||||
checkAddPrefixes(field, fieldName, Deprecated.class, deprecatedFields, deprecatedPrefixes);
|
||||
if (field.isAnnotationPresent(Moved.class)){
|
||||
// TODO: Prefixes: Might later support relocating entire sections with one annotation?
|
||||
addMoved(field, field.getAnnotation(Moved.class));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void addDeprecated(final Field field) {
|
||||
try {
|
||||
private static void checkAddPrefixes(Field field, String fieldName, Class<? extends Annotation> annotation, Set<String> fieldNames, SimpleCharPrefixTree pathPrefixes) {
|
||||
if (field.isAnnotationPresent(annotation)){
|
||||
fieldNames.add(fieldName);
|
||||
addPrefixesField(field, pathPrefixes);
|
||||
}
|
||||
else{
|
||||
for (final String refName : fieldNames){
|
||||
if (fieldName.startsWith(refName)){
|
||||
addPrefixesField(field, pathPrefixes);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void addPrefixesField(Field field, SimpleCharPrefixTree pathPrefixes) {
|
||||
try {
|
||||
final String path = field.get(null).toString();
|
||||
deprecatedPrefixes.feed(path);
|
||||
if (path != null){
|
||||
pathPrefixes.feed(path);
|
||||
}
|
||||
} catch (IllegalArgumentException e) {
|
||||
} catch (IllegalAccessException e) {
|
||||
}
|
||||
}
|
||||
|
||||
private static void addGlobalOnlyPath(final Field field) {
|
||||
private static void addMoved(final Field field, final Moved rel) {
|
||||
try {
|
||||
final String path = field.get(null).toString();
|
||||
globalOnlyPaths.add(path);
|
||||
globalOnlyPrefixes.feed(path);
|
||||
movedPaths.put(path, rel.newPath());
|
||||
} catch (IllegalArgumentException e) {
|
||||
} catch (IllegalAccessException e) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Warn on the console if paths are used.
|
||||
* @param config
|
||||
* @param paths
|
||||
* @param msgHeader
|
||||
* @param warnedPaths Paths which were found, can be null.
|
||||
*/
|
||||
public static void warnPaths(ConfigFile config, CharPrefixTree<?, ?> paths, String msgPrefix){
|
||||
final Logger log = Bukkit.getLogger();
|
||||
protected static void warnPaths(final ConfigFile config, final CharPrefixTree<?, ?> paths, final String msgPrefix, final Set<String> warnedPaths){
|
||||
final Logger logger = Bukkit.getLogger();
|
||||
for (final String path : config.getKeys(true)){
|
||||
if (paths.hasPrefix(path))
|
||||
log.warning("[NoCheatPlus] Config path '" + path + "'" + msgPrefix);
|
||||
if (paths.hasPrefix(path)){
|
||||
logger.warning("[NoCheatPlus] Config path '" + path + "'" + msgPrefix);
|
||||
if (warnedPaths != null){
|
||||
warnedPaths.add(path);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Run all warning checks (GlobalConfig, deprecated, ...).
|
||||
* Run all warning checks and alter config if necessary (GlobalConfig, Deprecated, Moved).
|
||||
* @param file
|
||||
* @param configName
|
||||
*/
|
||||
public static void warnPaths(File file, String configName, boolean isWorldConfig){
|
||||
final ConfigFile config = new ConfigFile();
|
||||
public static void processPaths(File file, String configName, boolean isWorldConfig){
|
||||
ConfigFile config = new ConfigFile();
|
||||
try {
|
||||
config.load(file);
|
||||
final Set<String> removePaths = new LinkedHashSet<String>();
|
||||
final Map<String, Object> addPaths = new LinkedHashMap<String, Object>();
|
||||
if (isWorldConfig){
|
||||
warnGlobalOnlyPaths(config, configName);
|
||||
// TODO: might remove these [though some global only paths might actually work].
|
||||
processGlobalOnlyPaths(config, configName, null);
|
||||
}
|
||||
processDeprecatedPaths(config, configName, removePaths);
|
||||
processMovedPaths(config, configName, removePaths, addPaths);
|
||||
boolean changed = false;
|
||||
if (!removePaths.isEmpty()){
|
||||
config = removePaths(config, removePaths);
|
||||
changed = true;
|
||||
}
|
||||
if (!addPaths.isEmpty()){
|
||||
setPaths(config, addPaths);
|
||||
changed = true;
|
||||
}
|
||||
if (changed){
|
||||
try{
|
||||
config.save(file);
|
||||
}
|
||||
catch(Throwable t){
|
||||
// Do log this one.
|
||||
LogUtil.logSevere("[NoCheatPlus] Failed to save configuration (" + configName + ") with changes: " + t.getClass().getSimpleName());
|
||||
LogUtil.logSevere(t);
|
||||
}
|
||||
}
|
||||
warnDeprecatedPaths(config, configName);
|
||||
} catch (FileNotFoundException e) {
|
||||
} catch (IOException e) {
|
||||
} catch (InvalidConfigurationException e) {
|
||||
@ -117,29 +162,79 @@ public class PathUtils {
|
||||
}
|
||||
|
||||
/**
|
||||
* Set paths.
|
||||
* @param config
|
||||
* @param addPaths
|
||||
*/
|
||||
public static void setPaths(final ConfigFile config, final Map<String, Object> setPaths) {
|
||||
for (final Entry<String, Object> entry : setPaths.entrySet()){
|
||||
config.set(entry.getKey(), entry.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a new ConfigFile instance with all paths removed (recursively by prefix).
|
||||
* @param config
|
||||
* @param removePaths
|
||||
* @return
|
||||
*/
|
||||
public static ConfigFile removePaths(final ConfigFile config, final Collection<String> removePaths) {
|
||||
final SimpleCharPrefixTree prefixes = new SimpleCharPrefixTree();
|
||||
for (final String path : removePaths){
|
||||
prefixes.feed(path);
|
||||
}
|
||||
final ConfigFile newConfig = new ConfigFile();
|
||||
for (final Entry<String, Object> entry : config.getValues(true).entrySet()){
|
||||
final String path = entry.getKey();
|
||||
final Object value = entry.getValue();
|
||||
if (value instanceof ConfigurationSection){
|
||||
continue;
|
||||
}
|
||||
if (!prefixes.hasPrefix(path)){
|
||||
newConfig.set(path, value);
|
||||
}
|
||||
}
|
||||
return newConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param config
|
||||
* @param configName
|
||||
* @param removePaths
|
||||
* @param addPaths
|
||||
* @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) {
|
||||
final Logger logger = Bukkit.getLogger();
|
||||
for (final Entry<String, String> entry : movedPaths.entrySet()){
|
||||
final String path = entry.getKey();
|
||||
if (config.contains(path)){
|
||||
final String newPath = entry.getValue();
|
||||
final String to;
|
||||
if (newPath == null | newPath.isEmpty()){
|
||||
to = ".";
|
||||
}
|
||||
else{
|
||||
to = " to '" + newPath + "'.";
|
||||
final Object value = config.get(path);
|
||||
config.set(newPath, value);
|
||||
addPaths.put(newPath, value);
|
||||
removePaths.add(path);
|
||||
}
|
||||
logger.warning("[NoCheatPlus] Config path '" + path + "' (" + configName + ") has been moved" + to);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Warn about paths that are deprecated (not in use).
|
||||
* @param config
|
||||
* @param paths
|
||||
* @param configName
|
||||
*/
|
||||
public static void warnDeprecatedPaths(ConfigFile config, String configName){
|
||||
warnPaths(config, deprecatedPrefixes, " (" + configName + ") is not in use anymore.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Warn about paths that are deprecated (not in use).
|
||||
* @param file
|
||||
* @param configName
|
||||
*/
|
||||
public static void warnDeprecatedPaths(File file, String configName){
|
||||
final ConfigFile config = new ConfigFile();
|
||||
try {
|
||||
config.load(file);
|
||||
warnDeprecatedPaths(config, configName);
|
||||
} catch (FileNotFoundException e) {
|
||||
} catch (IOException e) {
|
||||
} catch (InvalidConfigurationException e) {
|
||||
}
|
||||
protected static void processDeprecatedPaths(ConfigFile config, String configName, final Set<String> removePaths){
|
||||
warnPaths(config, deprecatedPrefixes, " (" + configName + ") is not in use anymore.", removePaths);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -148,19 +243,8 @@ public class PathUtils {
|
||||
* @param paths
|
||||
* @param configName
|
||||
*/
|
||||
public static void warnGlobalOnlyPaths(ConfigFile config, String configName){
|
||||
warnPaths(config, globalOnlyPrefixes, " (" + configName + ") should only be set in the global configuration.");
|
||||
}
|
||||
|
||||
public static void warnGlobalOnlyPaths(File file, String configName){
|
||||
final ConfigFile config = new ConfigFile();
|
||||
try {
|
||||
config.load(file);
|
||||
warnGlobalOnlyPaths(config, configName);
|
||||
} catch (FileNotFoundException e) {
|
||||
} catch (IOException e) {
|
||||
} catch (InvalidConfigurationException e) {
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -175,22 +259,34 @@ public class PathUtils {
|
||||
final Map<String, Object> defaults = defaultConfig.getValues(false);
|
||||
for (final Entry<String, Object> entry : defaults.entrySet()){
|
||||
final String part = entry.getKey();
|
||||
if (!part.isEmpty() && globalOnlyPrefixes.hasPrefix(part)) continue;
|
||||
if (!part.isEmpty() && !mayBeInWorldConfig(part)) continue;
|
||||
final Object value = entry.getValue();
|
||||
if (value instanceof ConfigurationSection) addSection(config, (ConfigurationSection) value, part, sep);
|
||||
if (value instanceof ConfigurationSection) addWorldConfigSection(config, (ConfigurationSection) value, part, sep);
|
||||
else config.set(part, value);
|
||||
}
|
||||
return config;
|
||||
}
|
||||
|
||||
public static void addSection(final MemoryConfiguration config, final ConfigurationSection section, final String path, final char sep) {
|
||||
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 (globalOnlyPrefixes.hasPrefix(fullPath)) continue;
|
||||
if (!mayBeInWorldConfig(fullPath)) continue;
|
||||
final Object value = entry.getValue();
|
||||
if (value instanceof ConfigurationSection) addSection(config, (ConfigurationSection) value, fullPath, sep);
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user