#927 Integrate ConfigMe into AuthMe (work in progress)

- Replace own code with ConfigMe
This commit is contained in:
ljacqu 2016-08-30 15:28:07 +02:00
parent 33eab1df21
commit c7bb7b460e
44 changed files with 290 additions and 1482 deletions

View File

@ -852,6 +852,13 @@
<optional>true</optional> <optional>true</optional>
</dependency> </dependency>
<!-- ConfigMe -->
<dependency>
<groupId>com.github.authme</groupId>
<artifactId>configme</artifactId>
<version>0.1-SNAPSHOT</version>
</dependency>
<!-- Unit Testing Libraries --> <!-- Unit Testing Libraries -->
<dependency> <dependency>
<groupId>junit</groupId> <groupId>junit</groupId>

View File

@ -1,9 +1,9 @@
package fr.xephi.authme.command; package fr.xephi.authme.command;
import com.github.authme.configme.properties.Property;
import fr.xephi.authme.output.MessageKey; import fr.xephi.authme.output.MessageKey;
import fr.xephi.authme.output.Messages; import fr.xephi.authme.output.Messages;
import fr.xephi.authme.settings.Settings; import fr.xephi.authme.settings.Settings;
import fr.xephi.authme.settings.domain.Property;
import fr.xephi.authme.util.ValidationService; import fr.xephi.authme.util.ValidationService;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;

View File

@ -1,5 +1,8 @@
package fr.xephi.authme.initialization; package fr.xephi.authme.initialization;
import com.github.authme.configme.propertymap.PropertyEntry;
import com.github.authme.configme.resource.PropertyResource;
import com.github.authme.configme.resource.YamlFileResource;
import fr.xephi.authme.AuthMe; import fr.xephi.authme.AuthMe;
import fr.xephi.authme.ConsoleLogger; import fr.xephi.authme.ConsoleLogger;
import fr.xephi.authme.cache.auth.PlayerAuth; import fr.xephi.authme.cache.auth.PlayerAuth;
@ -15,11 +18,10 @@ import fr.xephi.authme.output.MessageKey;
import fr.xephi.authme.output.Messages; import fr.xephi.authme.output.Messages;
import fr.xephi.authme.settings.Settings; import fr.xephi.authme.settings.Settings;
import fr.xephi.authme.settings.SettingsMigrationService; import fr.xephi.authme.settings.SettingsMigrationService;
import fr.xephi.authme.settings.properties.AuthMeSettingsRetriever;
import fr.xephi.authme.settings.properties.DatabaseSettings; import fr.xephi.authme.settings.properties.DatabaseSettings;
import fr.xephi.authme.settings.properties.EmailSettings; import fr.xephi.authme.settings.properties.EmailSettings;
import fr.xephi.authme.settings.properties.SecuritySettings; import fr.xephi.authme.settings.properties.SecuritySettings;
import fr.xephi.authme.settings.properties.SettingsFieldRetriever;
import fr.xephi.authme.settings.propertymap.PropertyMap;
import fr.xephi.authme.util.BukkitService; import fr.xephi.authme.util.BukkitService;
import fr.xephi.authme.util.FileUtils; import fr.xephi.authme.util.FileUtils;
import fr.xephi.authme.util.MigrationService; import fr.xephi.authme.util.MigrationService;
@ -31,6 +33,7 @@ import org.bukkit.entity.Player;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.sql.SQLException; import java.sql.SQLException;
import java.util.List;
import java.util.logging.Logger; import java.util.logging.Logger;
import static fr.xephi.authme.settings.properties.EmailSettings.RECALL_PLAYERS; import static fr.xephi.authme.settings.properties.EmailSettings.RECALL_PLAYERS;
@ -59,10 +62,12 @@ public class Initializer {
*/ */
public Settings createSettings() throws Exception { public Settings createSettings() throws Exception {
File configFile = new File(authMe.getDataFolder(), "config.yml"); File configFile = new File(authMe.getDataFolder(), "config.yml");
PropertyMap properties = SettingsFieldRetriever.getAllPropertyFields(); PropertyResource resource = new YamlFileResource(configFile);
SettingsMigrationService migrationService = new SettingsMigrationService(); SettingsMigrationService migrationService = new SettingsMigrationService(authMe.getDataFolder());
List<PropertyEntry> knownProperties = AuthMeSettingsRetriever.getAllPropertyFields();
if (FileUtils.copyFileFromResource(configFile, "config.yml")) { if (FileUtils.copyFileFromResource(configFile, "config.yml")) {
return new Settings(configFile, authMe.getDataFolder(), properties, migrationService); return new Settings(authMe.getDataFolder(), knownProperties, resource, migrationService);
} }
throw new Exception("Could not copy config.yml from JAR to plugin folder"); throw new Exception("Could not copy config.yml from JAR to plugin folder");
} }
@ -71,6 +76,7 @@ public class Initializer {
* Sets up the data source. * Sets up the data source.
* *
* @param settings the settings * @param settings the settings
* @return the constructed datasource
* @throws ClassNotFoundException if no driver could be found for the datasource * @throws ClassNotFoundException if no driver could be found for the datasource
* @throws SQLException when initialization of a SQL datasource failed * @throws SQLException when initialization of a SQL datasource failed
* @throws IOException if flat file cannot be read * @throws IOException if flat file cannot be read

View File

@ -1,5 +1,6 @@
package fr.xephi.authme.process; package fr.xephi.authme.process;
import com.github.authme.configme.properties.Property;
import fr.xephi.authme.output.MessageKey; import fr.xephi.authme.output.MessageKey;
import fr.xephi.authme.output.Messages; import fr.xephi.authme.output.Messages;
import fr.xephi.authme.permission.AuthGroupHandler; import fr.xephi.authme.permission.AuthGroupHandler;
@ -7,7 +8,6 @@ import fr.xephi.authme.permission.AuthGroupType;
import fr.xephi.authme.permission.PermissionNode; import fr.xephi.authme.permission.PermissionNode;
import fr.xephi.authme.permission.PermissionsManager; import fr.xephi.authme.permission.PermissionsManager;
import fr.xephi.authme.settings.Settings; import fr.xephi.authme.settings.Settings;
import fr.xephi.authme.settings.domain.Property;
import fr.xephi.authme.util.ValidationService; import fr.xephi.authme.util.ValidationService;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;

View File

@ -1,108 +1,46 @@
package fr.xephi.authme.settings; package fr.xephi.authme.settings;
import com.google.common.annotations.VisibleForTesting; import com.github.authme.configme.SettingsManager;
import com.google.common.base.Joiner; import com.github.authme.configme.migration.MigrationService;
import com.google.common.base.Strings; import com.github.authme.configme.propertymap.PropertyEntry;
import com.github.authme.configme.resource.PropertyResource;
import com.google.common.io.Files; import com.google.common.io.Files;
import fr.xephi.authme.ConsoleLogger; import fr.xephi.authme.ConsoleLogger;
import fr.xephi.authme.settings.domain.Property;
import fr.xephi.authme.settings.properties.PluginSettings; import fr.xephi.authme.settings.properties.PluginSettings;
import fr.xephi.authme.settings.properties.RegistrationSettings; import fr.xephi.authme.settings.properties.RegistrationSettings;
import fr.xephi.authme.settings.propertymap.PropertyMap;
import fr.xephi.authme.util.CollectionUtils;
import fr.xephi.authme.util.StringUtils; import fr.xephi.authme.util.StringUtils;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.configuration.file.YamlConfiguration;
import org.yaml.snakeyaml.DumperOptions;
import org.yaml.snakeyaml.Yaml;
import java.io.File; import java.io.File;
import java.io.FileWriter;
import java.io.IOException; import java.io.IOException;
import java.nio.charset.Charset; import java.nio.charset.Charset;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.Map;
import static fr.xephi.authme.util.FileUtils.copyFileFromResource; import static fr.xephi.authme.util.FileUtils.copyFileFromResource;
/** /**
* The AuthMe settings manager. * The AuthMe settings manager.
*/ */
public class Settings { public class Settings extends SettingsManager {
private final File pluginFolder; private final File pluginFolder;
private final File configFile;
private final PropertyMap propertyMap;
private final SettingsMigrationService migrationService;
private FileConfiguration configuration;
/** The file with the localized messages based on {@link PluginSettings#MESSAGES_LANGUAGE}. */ /** The file with the localized messages based on {@link PluginSettings#MESSAGES_LANGUAGE}. */
private File messagesFile; private File messagesFile;
private List<String> welcomeMessage; private List<String> welcomeMessage;
private String emailMessage; private String emailMessage;
/** /**
* Constructor. Checks the given {@link FileConfiguration} object for completeness. * Constructor.
* *
* @param configFile The configuration file * @param pluginFolder the AuthMe plugin folder
* @param pluginFolder The AuthMe plugin folder * @param knownProperties collection of all available settings
* @param propertyMap Collection of all available settings * @param resource the property resource to read and write properties to
* @param migrationService Migration service to check the settings file with * @param migrationService migration service to check the settings file with
*/ */
public Settings(File configFile, File pluginFolder, PropertyMap propertyMap, public Settings(File pluginFolder, List<PropertyEntry> knownProperties, PropertyResource resource,
SettingsMigrationService migrationService) { MigrationService migrationService) {
this.configuration = YamlConfiguration.loadConfiguration(configFile); super(knownProperties, resource, migrationService);
this.configFile = configFile;
this.pluginFolder = pluginFolder; this.pluginFolder = pluginFolder;
this.propertyMap = propertyMap;
this.migrationService = migrationService;
validateAndLoadOptions();
}
/**
* Constructor for testing purposes, allowing more options.
*
* @param configuration The FileConfiguration object to use
* @param configFile The file to write to
* @param pluginFolder The plugin folder
* @param propertyMap The property map whose properties should be verified for presence, or null to skip this
* @param migrationService Migration service, or null to skip migration checks
*/
@VisibleForTesting
Settings(FileConfiguration configuration, File configFile, File pluginFolder, PropertyMap propertyMap,
SettingsMigrationService migrationService) {
this.configuration = configuration;
this.configFile = configFile;
this.pluginFolder = pluginFolder;
this.propertyMap = propertyMap;
this.migrationService = migrationService;
if (propertyMap != null && migrationService != null) {
validateAndLoadOptions();
}
}
/**
* Get the given property from the configuration.
*
* @param property The property to retrieve
* @param <T> The property's type
* @return The property's value
*/
public <T> T getProperty(Property<T> property) {
return property.getFromFile(configuration);
}
/**
* Set a new value for the given property.
*
* @param property The property to modify
* @param value The new value to assign to the property
* @param <T> The property's type
*/
public <T> void setProperty(Property<T> property, T value) {
configuration.set(property.getPath(), value);
} }
/** /**
@ -141,71 +79,9 @@ public class Settings {
return welcomeMessage; return welcomeMessage;
} }
/** @Override
* Reload the configuration. protected void validateAndLoadOptions() {
*/ if (migrationService.checkAndMigrate(resource, knownProperties)) {
public void reload() {
configuration = YamlConfiguration.loadConfiguration(configFile);
validateAndLoadOptions();
}
/**
* Save the config file. Use after migrating one or more settings.
*/
public void save() {
try (FileWriter writer = new FileWriter(configFile)) {
Yaml simpleYaml = newYaml(false);
Yaml singleQuoteYaml = newYaml(true);
writer.write("");
// Contains all but the last node of the setting, e.g. [DataSource, mysql] for "DataSource.mysql.username"
List<String> currentPath = new ArrayList<>();
for (Map.Entry<Property<?>, String[]> entry : propertyMap.entrySet()) {
Property<?> property = entry.getKey();
// Handle properties
List<String> propertyPath = Arrays.asList(property.getPath().split("\\."));
List<String> commonPathParts = CollectionUtils.filterCommonStart(
currentPath, propertyPath.subList(0, propertyPath.size() - 1));
List<String> newPathParts = CollectionUtils.getRange(propertyPath, commonPathParts.size());
if (commonPathParts.isEmpty()) {
writer.append("\n");
}
int indentationLevel = commonPathParts.size();
if (newPathParts.size() > 1) {
for (String path : newPathParts.subList(0, newPathParts.size() - 1)) {
writer.append("\n")
.append(indent(indentationLevel))
.append(path)
.append(": ");
++indentationLevel;
}
}
for (String comment : entry.getValue()) {
writer.append("\n")
.append(indent(indentationLevel))
.append("# ")
.append(comment);
}
writer.append("\n")
.append(indent(indentationLevel))
.append(CollectionUtils.getRange(newPathParts, newPathParts.size() - 1).get(0))
.append(": ")
.append(toYaml(property, indentationLevel, simpleYaml, singleQuoteYaml));
currentPath = propertyPath.subList(0, propertyPath.size() - 1);
}
writer.flush();
writer.close();
} catch (IOException e) {
ConsoleLogger.logException("Could not save config file:", e);
}
}
private void validateAndLoadOptions() {
if (migrationService.checkAndMigrate(configuration, propertyMap, pluginFolder)) {
ConsoleLogger.info("Merged new config options"); ConsoleLogger.info("Merged new config options");
ConsoleLogger.info("Please check your config.yml file for new settings!"); ConsoleLogger.info("Please check your config.yml file for new settings!");
save(); save();
@ -216,11 +92,6 @@ public class Settings {
emailMessage = readEmailMessage(); emailMessage = readEmailMessage();
} }
private <T> String toYaml(Property<T> property, int indent, Yaml simpleYaml, Yaml singleQuoteYaml) {
String representation = property.toYaml(configuration, simpleYaml, singleQuoteYaml);
return Joiner.on("\n" + indent(indent)).join(representation.split("\\n"));
}
private File buildMessagesFile() { private File buildMessagesFile() {
String languageCode = getProperty(PluginSettings.MESSAGES_LANGUAGE); String languageCode = getProperty(PluginSettings.MESSAGES_LANGUAGE);
@ -270,20 +141,4 @@ public class Settings {
} }
return ""; return "";
} }
private static Yaml newYaml(boolean useSingleQuotes) {
DumperOptions options = new DumperOptions();
options.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK);
options.setAllowUnicode(true);
if (useSingleQuotes) {
options.setDefaultScalarStyle(DumperOptions.ScalarStyle.SINGLE_QUOTED);
}
return new Yaml(options);
}
private static String indent(int level) {
// We use an indentation of 4 spaces
return Strings.repeat(" ", level * 4);
}
} }

View File

@ -1,17 +1,20 @@
package fr.xephi.authme.settings; package fr.xephi.authme.settings;
import com.github.authme.configme.migration.PlainMigrationService;
import com.github.authme.configme.properties.Property;
import com.github.authme.configme.propertymap.PropertyEntry;
import com.github.authme.configme.resource.PropertyResource;
import fr.xephi.authme.ConsoleLogger; import fr.xephi.authme.ConsoleLogger;
import fr.xephi.authme.output.LogLevel; import fr.xephi.authme.output.LogLevel;
import fr.xephi.authme.settings.domain.Property;
import fr.xephi.authme.settings.properties.PluginSettings; import fr.xephi.authme.settings.properties.PluginSettings;
import fr.xephi.authme.settings.propertymap.PropertyMap;
import org.bukkit.configuration.file.FileConfiguration;
import java.io.File; import java.io.File;
import java.io.FileWriter; import java.io.FileWriter;
import java.io.IOException; import java.io.IOException;
import java.util.List; import java.util.List;
import static com.github.authme.configme.properties.PropertyInitializer.newListProperty;
import static com.github.authme.configme.properties.PropertyInitializer.newProperty;
import static fr.xephi.authme.settings.properties.RegistrationSettings.DELAY_JOIN_MESSAGE; import static fr.xephi.authme.settings.properties.RegistrationSettings.DELAY_JOIN_MESSAGE;
import static fr.xephi.authme.settings.properties.RegistrationSettings.REMOVE_JOIN_MESSAGE; import static fr.xephi.authme.settings.properties.RegistrationSettings.REMOVE_JOIN_MESSAGE;
import static fr.xephi.authme.settings.properties.RegistrationSettings.REMOVE_LEAVE_MESSAGE; import static fr.xephi.authme.settings.properties.RegistrationSettings.REMOVE_LEAVE_MESSAGE;
@ -22,55 +25,40 @@ import static fr.xephi.authme.settings.properties.RestrictionSettings.FORCE_SPAW
/** /**
* Service for verifying that the configuration is up-to-date. * Service for verifying that the configuration is up-to-date.
*/ */
public class SettingsMigrationService { public class SettingsMigrationService extends PlainMigrationService {
/** private final File pluginFolder;
* Checks the config file and performs any necessary migrations.
* public SettingsMigrationService(File pluginFolder) {
* @param configuration The file configuration to check and migrate this.pluginFolder = pluginFolder;
* @param propertyMap The property map of all existing properties
* @param pluginFolder The plugin folder
* @return True if there is a change and the config must be saved, false if the config is up-to-date
*/
public boolean checkAndMigrate(FileConfiguration configuration, PropertyMap propertyMap, File pluginFolder) {
return performMigrations(configuration, pluginFolder)
|| hasDeprecatedProperties(configuration)
|| !containsAllSettings(configuration, propertyMap);
} }
private boolean performMigrations(FileConfiguration configuration, File pluginFolder) { @Override
protected boolean performMigrations(PropertyResource resource, List<PropertyEntry> knownProperties) {
boolean changes = false; boolean changes = false;
if ("[a-zA-Z0-9_?]*".equals(configuration.getString(ALLOWED_NICKNAME_CHARACTERS.getPath()))) { if ("[a-zA-Z0-9_?]*".equals(resource.getString(ALLOWED_NICKNAME_CHARACTERS.getPath()))) {
configuration.set(ALLOWED_NICKNAME_CHARACTERS.getPath(), "[a-zA-Z0-9_]*"); resource.setValue(ALLOWED_NICKNAME_CHARACTERS.getPath(), "[a-zA-Z0-9_]*");
changes = true; changes = true;
} }
// Note ljacqu 20160211: Concatenating migration methods with | instead of the usual || // Note ljacqu 20160211: Concatenating migration methods with | instead of the usual ||
// ensures that all migrations will be performed // ensures that all migrations will be performed
return changes return changes
| performMailTextToFileMigration(configuration, pluginFolder) | performMailTextToFileMigration(resource)
| migrateJoinLeaveMessages(configuration) | migrateJoinLeaveMessages(resource)
| migrateForceSpawnSettings(configuration) | migrateForceSpawnSettings(resource)
| changeBooleanSettingToLogLevelProperty(configuration); | changeBooleanSettingToLogLevelProperty(resource)
|| hasDeprecatedProperties(resource);
} }
public boolean containsAllSettings(FileConfiguration configuration, PropertyMap propertyMap) { private static boolean hasDeprecatedProperties(PropertyResource resource) {
for (Property<?> property : propertyMap.keySet()) {
if (!property.isPresent(configuration)) {
return false;
}
}
return true;
}
private static boolean hasDeprecatedProperties(FileConfiguration configuration) {
String[] deprecatedProperties = { String[] deprecatedProperties = {
"Converter.Rakamak.newPasswordHash", "Hooks.chestshop", "Hooks.legacyChestshop", "Hooks.notifications", "Converter.Rakamak.newPasswordHash", "Hooks.chestshop", "Hooks.legacyChestshop", "Hooks.notifications",
"Passpartu", "Performances", "settings.restrictions.enablePasswordVerifier", "Xenoforo.predefinedSalt", "Passpartu", "Performances", "settings.restrictions.enablePasswordVerifier", "Xenoforo.predefinedSalt",
"VeryGames", "settings.restrictions.allowAllCommandsIfRegistrationIsOptional", "DataSource.mySQLWebsite", "VeryGames", "settings.restrictions.allowAllCommandsIfRegistrationIsOptional", "DataSource.mySQLWebsite",
"Hooks.customAttributes", "Security.stop.kickPlayersBeforeStopping"}; "Hooks.customAttributes", "Security.stop.kickPlayersBeforeStopping"};
for (String deprecatedPath : deprecatedProperties) { for (String deprecatedPath : deprecatedProperties) {
if (configuration.contains(deprecatedPath)) { if (resource.contains(deprecatedPath)) {
return true; return true;
} }
} }
@ -84,18 +72,18 @@ public class SettingsMigrationService {
/** /**
* Check if {@code Email.mailText} is present and move it to the Email.html file if it doesn't exist yet. * Check if {@code Email.mailText} is present and move it to the Email.html file if it doesn't exist yet.
* *
* @param configuration The file configuration to verify * @param resource The property resource
* @param pluginFolder The plugin data folder
* @return True if a migration has been completed, false otherwise * @return True if a migration has been completed, false otherwise
*/ */
private static boolean performMailTextToFileMigration(FileConfiguration configuration, File pluginFolder) { private boolean performMailTextToFileMigration(PropertyResource resource) {
final String oldSettingPath = "Email.mailText"; final String oldSettingPath = "Email.mailText";
if (!configuration.contains(oldSettingPath)) { final String oldMailText = resource.getString(oldSettingPath);
if (oldMailText == null) {
return false; return false;
} }
final File emailFile = new File(pluginFolder, "email.html"); final File emailFile = new File(pluginFolder, "email.html");
final String mailText = configuration.getString(oldSettingPath) final String mailText = oldMailText
.replace("<playername>", "<playername />").replace("%playername%", "<playername />") .replace("<playername>", "<playername />").replace("%playername%", "<playername />")
.replace("<servername>", "<servername />").replace("%servername%", "<servername />") .replace("<servername>", "<servername />").replace("%servername%", "<servername />")
.replace("<generatedpass>", "<generatedpass />").replace("%generatedpass%", "<generatedpass />") .replace("<generatedpass>", "<generatedpass />").replace("%generatedpass%", "<generatedpass />")
@ -114,12 +102,12 @@ public class SettingsMigrationService {
* Detect deprecated {@code settings.delayJoinLeaveMessages} and inform user of new "remove join messages" * Detect deprecated {@code settings.delayJoinLeaveMessages} and inform user of new "remove join messages"
* and "remove leave messages" settings. * and "remove leave messages" settings.
* *
* @param configuration The file configuration * @param resource The property resource
* @return True if the configuration has changed, false otherwise * @return True if the configuration has changed, false otherwise
*/ */
private static boolean migrateJoinLeaveMessages(FileConfiguration configuration) { private static boolean migrateJoinLeaveMessages(PropertyResource resource) {
Property<Boolean> oldDelayJoinProperty = Property.newProperty("settings.delayJoinLeaveMessages", false); Property<Boolean> oldDelayJoinProperty = newProperty("settings.delayJoinLeaveMessages", false);
boolean hasMigrated = moveProperty(oldDelayJoinProperty, DELAY_JOIN_MESSAGE, configuration); boolean hasMigrated = moveProperty(oldDelayJoinProperty, DELAY_JOIN_MESSAGE, resource);
if (hasMigrated) { if (hasMigrated) {
ConsoleLogger.info(String.format("Note that we now also have the settings %s and %s", ConsoleLogger.info(String.format("Note that we now also have the settings %s and %s",
@ -132,33 +120,33 @@ public class SettingsMigrationService {
* Detect old "force spawn loc on join" and "force spawn on these worlds" settings and moves them * Detect old "force spawn loc on join" and "force spawn on these worlds" settings and moves them
* to the new paths. * to the new paths.
* *
* @param configuration The file configuration * @param resource The property resource
* @return True if the configuration has changed, false otherwise * @return True if the configuration has changed, false otherwise
*/ */
private static boolean migrateForceSpawnSettings(FileConfiguration configuration) { private static boolean migrateForceSpawnSettings(PropertyResource resource) {
Property<Boolean> oldForceLocEnabled = Property.newProperty( Property<Boolean> oldForceLocEnabled = newProperty(
"settings.restrictions.ForceSpawnLocOnJoinEnabled", false); "settings.restrictions.ForceSpawnLocOnJoinEnabled", false);
Property<List<String>> oldForceWorlds = Property.newListProperty( Property<List<String>> oldForceWorlds = newListProperty(
"settings.restrictions.ForceSpawnOnTheseWorlds", "world", "world_nether", "world_the_ed"); "settings.restrictions.ForceSpawnOnTheseWorlds", "world", "world_nether", "world_the_ed");
return moveProperty(oldForceLocEnabled, FORCE_SPAWN_LOCATION_AFTER_LOGIN, configuration) return moveProperty(oldForceLocEnabled, FORCE_SPAWN_LOCATION_AFTER_LOGIN, resource)
| moveProperty(oldForceWorlds, FORCE_SPAWN_ON_WORLDS, configuration); | moveProperty(oldForceWorlds, FORCE_SPAWN_ON_WORLDS, resource);
} }
/** /**
* Changes the old boolean property "hide spam from console" to the new property specifying * Changes the old boolean property "hide spam from console" to the new property specifying
* the log level. * the log level.
* *
* @param configuration The file configuration * @param resource The property resource
* @return True if the configuration has changed, false otherwise * @return True if the configuration has changed, false otherwise
*/ */
private static boolean changeBooleanSettingToLogLevelProperty(FileConfiguration configuration) { private static boolean changeBooleanSettingToLogLevelProperty(PropertyResource resource) {
final String oldPath = "Security.console.noConsoleSpam"; final String oldPath = "Security.console.noConsoleSpam";
final Property<LogLevel> newProperty = PluginSettings.LOG_LEVEL; final Property<LogLevel> newProperty = PluginSettings.LOG_LEVEL;
if (!newProperty.isPresent(configuration) && configuration.contains(oldPath)) { if (!newProperty.isPresent(resource) && resource.contains(oldPath)) {
ConsoleLogger.info("Moving '" + oldPath + "' to '" + newProperty.getPath() + "'"); ConsoleLogger.info("Moving '" + oldPath + "' to '" + newProperty.getPath() + "'");
LogLevel level = configuration.getBoolean(oldPath) ? LogLevel.INFO : LogLevel.FINE; LogLevel level = Boolean.valueOf(resource.getString(oldPath)) ? LogLevel.INFO : LogLevel.FINE;
configuration.set(newProperty.getPath(), level.name()); resource.setValue(newProperty.getPath(), level.name());
return true; return true;
} }
return false; return false;
@ -169,18 +157,18 @@ public class SettingsMigrationService {
* *
* @param oldProperty The old property (create a temporary {@link Property} object with the path) * @param oldProperty The old property (create a temporary {@link Property} object with the path)
* @param newProperty The new property to move the value to * @param newProperty The new property to move the value to
* @param configuration The file configuration * @param resource The property resource
* @param <T> The type of the property * @param <T> The type of the property
* @return True if a migration has been done, false otherwise * @return True if a migration has been done, false otherwise
*/ */
private static <T> boolean moveProperty(Property<T> oldProperty, private static <T> boolean moveProperty(Property<T> oldProperty,
Property<T> newProperty, Property<T> newProperty,
FileConfiguration configuration) { PropertyResource resource) {
if (configuration.contains(oldProperty.getPath())) { if (resource.contains(oldProperty.getPath())) {
ConsoleLogger.info("Detected deprecated property " + oldProperty.getPath()); ConsoleLogger.info("Detected deprecated property " + oldProperty.getPath());
if (!configuration.contains(newProperty.getPath())) { if (!resource.contains(newProperty.getPath())) {
ConsoleLogger.info("Renamed " + oldProperty.getPath() + " to " + newProperty.getPath()); ConsoleLogger.info("Renamed " + oldProperty.getPath() + " to " + newProperty.getPath());
configuration.set(newProperty.getPath(), oldProperty.getFromFile(configuration)); resource.setValue(newProperty.getPath(), oldProperty.getValue(resource));
} }
return true; return true;
} }

View File

@ -1,17 +0,0 @@
package fr.xephi.authme.settings.domain;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Comment for properties which are also included in the YAML file upon saving.
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Comment {
String[] value();
}

View File

@ -1,49 +0,0 @@
package fr.xephi.authme.settings.domain;
import org.bukkit.configuration.file.FileConfiguration;
import org.yaml.snakeyaml.Yaml;
/**
* Enum property.
*
* @param <E> The enum class
*/
class EnumProperty<E extends Enum<E>> extends Property<E> {
private Class<E> clazz;
public EnumProperty(Class<E> clazz, String path, E defaultValue) {
super(path, defaultValue);
this.clazz = clazz;
}
@Override
public E getFromFile(FileConfiguration configuration) {
String textValue = configuration.getString(getPath());
if (textValue == null) {
return getDefaultValue();
}
E mappedValue = mapToEnum(textValue);
return mappedValue == null ? getDefaultValue() : mappedValue;
}
@Override
public boolean isPresent(FileConfiguration configuration) {
return super.isPresent(configuration) && mapToEnum(configuration.getString(getPath())) != null;
}
@Override
public String toYaml(FileConfiguration configuration, Yaml simpleYaml, Yaml singleQuoteYaml) {
E value = getFromFile(configuration);
return singleQuoteYaml.dump(value.name());
}
private E mapToEnum(String value) {
for (E entry : clazz.getEnumConstants()) {
if (entry.name().equalsIgnoreCase(value)) {
return entry;
}
}
return null;
}
}

View File

@ -1,235 +0,0 @@
package fr.xephi.authme.settings.domain;
import org.bukkit.configuration.file.FileConfiguration;
import org.yaml.snakeyaml.Yaml;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
/**
* Property class, representing a <i>setting</i> that is read from the config.yml file.
*/
public abstract class Property<T> {
private final String path;
private final T defaultValue;
protected Property(String path, T defaultValue) {
Objects.requireNonNull(defaultValue);
this.path = path;
this.defaultValue = defaultValue;
}
/**
* Create a new string list property.
*
* @param path The property's path
* @param defaultValues The items in the default list
* @return The created list property
*/
public static Property<List<String>> newListProperty(String path, String... defaultValues) {
// does not have the same name as not to clash with #newProperty(String, String)
return new StringListProperty(path, defaultValues);
}
/**
* Create a new String list property where all values are lowercase.
*
* @param path The property's path
* @param defaultValues The items in the default list
* @return The created list property
*/
public static Property<List<String>> newLowercaseListProperty(String path, String... defaultValues) {
return new LowercaseStringListProperty(path, defaultValues);
}
/**
* Create a new enum property.
*
* @param clazz The enum class
* @param path The property's path
* @param defaultValue The default value
* @param <E> The enum type
* @return The created enum property
*/
public static <E extends Enum<E>> Property<E> newProperty(Class<E> clazz, String path, E defaultValue) {
return new EnumProperty<>(clazz, path, defaultValue);
}
public static Property<Boolean> newProperty(String path, boolean defaultValue) {
return new BooleanProperty(path, defaultValue);
}
public static Property<Integer> newProperty(String path, int defaultValue) {
return new IntegerProperty(path, defaultValue);
}
public static Property<String> newProperty(String path, String defaultValue) {
return new StringProperty(path, defaultValue);
}
/**
* Get the property value from the given configuration &ndash; guaranteed to never return null.
*
* @param configuration The configuration to read the value from
* @return The value, or default if not present
*/
public abstract T getFromFile(FileConfiguration configuration);
/**
* Return whether or not the given configuration file contains the property.
*
* @param configuration The configuration file to verify
* @return True if the property is present, false otherwise
*/
public boolean isPresent(FileConfiguration configuration) {
return configuration.contains(path);
}
/**
* Format the property's value as YAML.
*
* @param configuration The file configuration
* @param simpleYaml YAML object (default)
* @param singleQuoteYaml YAML object using single quotes
* @return The generated YAML
*/
public String toYaml(FileConfiguration configuration, Yaml simpleYaml, Yaml singleQuoteYaml) {
return simpleYaml.dump(getFromFile(configuration));
}
/**
* Return the default value of the property.
*
* @return The default value
*/
public T getDefaultValue() {
return defaultValue;
}
/**
* Return the property path (i.e. the node at which this property is located in the YAML file).
*
* @return The path
*/
public String getPath() {
return path;
}
@Override
public String toString() {
return "Property '" + path + "'";
}
/**
* Boolean property.
*/
private static final class BooleanProperty extends Property<Boolean> {
public BooleanProperty(String path, Boolean defaultValue) {
super(path, defaultValue);
}
@Override
public Boolean getFromFile(FileConfiguration configuration) {
return configuration.getBoolean(getPath(), getDefaultValue());
}
}
/**
* Integer property.
*/
private static final class IntegerProperty extends Property<Integer> {
public IntegerProperty(String path, Integer defaultValue) {
super(path, defaultValue);
}
@Override
public Integer getFromFile(FileConfiguration configuration) {
return configuration.getInt(getPath(), getDefaultValue());
}
}
/**
* String property.
*/
private static final class StringProperty extends Property<String> {
public StringProperty(String path, String defaultValue) {
super(path, defaultValue);
}
@Override
public String getFromFile(FileConfiguration configuration) {
return configuration.getString(getPath(), getDefaultValue());
}
@Override
public String toYaml(FileConfiguration configuration, Yaml simpleYaml, Yaml singleQuoteYaml) {
return singleQuoteYaml.dump(getFromFile(configuration));
}
}
/**
* String list property.
*/
private static class StringListProperty extends Property<List<String>> {
public StringListProperty(String path, String[] defaultValues) {
super(path, Arrays.asList(defaultValues));
}
@Override
public List<String> getFromFile(FileConfiguration configuration) {
if (!configuration.isList(getPath())) {
return getDefaultValue();
}
return configuration.getStringList(getPath());
}
@Override
public boolean isPresent(FileConfiguration configuration) {
return configuration.isList(getPath());
}
@Override
public String toYaml(FileConfiguration configuration, Yaml simpleYaml, Yaml singleQuoteYaml) {
List<String> value = getFromFile(configuration);
String yaml = singleQuoteYaml.dump(value);
// If the property is a non-empty list we need to append a new line because it will be
// something like the following, which requires a new line:
// - 'item 1'
// - 'second item in list'
return value.isEmpty() ? yaml : "\n" + yaml;
}
}
/**
* Lowercase String list property.
*/
private static final class LowercaseStringListProperty extends StringListProperty {
public LowercaseStringListProperty(String path, String[] defaultValues) {
super(path, defaultValues);
}
@Override
public List<String> getFromFile(FileConfiguration configuration) {
if (!configuration.isList(getPath())) {
return getDefaultValue();
}
// make sure all elements are lowercase
List<String> lowercaseList = new ArrayList<>();
for (String element : configuration.getStringList(getPath())) {
lowercaseList.add(element.toLowerCase());
}
return lowercaseList;
}
}
}

View File

@ -1,7 +0,0 @@
package fr.xephi.authme.settings.domain;
/**
* Marker for classes that define {@link Property} fields.
*/
public interface SettingsClass {
}

View File

@ -0,0 +1,33 @@
package fr.xephi.authme.settings.properties;
import com.github.authme.configme.SettingsHolder;
import com.github.authme.configme.properties.Property;
import com.github.authme.configme.propertymap.PropertyEntry;
import com.github.authme.configme.propertymap.SettingsFieldRetriever;
import java.util.List;
/**
* Utility class responsible for retrieving all {@link Property} fields
* from {@link SettingsHolder} implementations via reflection.
*/
public final class AuthMeSettingsRetriever {
private AuthMeSettingsRetriever() {
}
/**
* Constructs a list with all property fields in AuthMe {@link SettingsHolder} classes.
*
* @return list of all known properties
*/
public static List<PropertyEntry> getAllPropertyFields() {
SettingsFieldRetriever retriever = new SettingsFieldRetriever(
DatabaseSettings.class, ConverterSettings.class, PluginSettings.class,
RestrictionSettings.class, EmailSettings.class, HooksSettings.class,
ProtectionSettings.class, PurgeSettings.class, SecuritySettings.class,
RegistrationSettings.class, BackupSettings.class);
return retriever.getAllPropertyFields();
}
}

View File

@ -1,12 +1,12 @@
package fr.xephi.authme.settings.properties; package fr.xephi.authme.settings.properties;
import fr.xephi.authme.settings.domain.Comment; import com.github.authme.configme.Comment;
import fr.xephi.authme.settings.domain.Property; import com.github.authme.configme.SettingsHolder;
import fr.xephi.authme.settings.domain.SettingsClass; import com.github.authme.configme.properties.Property;
import static fr.xephi.authme.settings.domain.Property.newProperty; import static com.github.authme.configme.properties.PropertyInitializer.newProperty;
public class BackupSettings implements SettingsClass { public class BackupSettings implements SettingsHolder {
@Comment("Enable or disable automatic backup") @Comment("Enable or disable automatic backup")
public static final Property<Boolean> ENABLED = public static final Property<Boolean> ENABLED =

View File

@ -1,12 +1,12 @@
package fr.xephi.authme.settings.properties; package fr.xephi.authme.settings.properties;
import fr.xephi.authme.settings.domain.Comment; import com.github.authme.configme.Comment;
import fr.xephi.authme.settings.domain.Property; import com.github.authme.configme.SettingsHolder;
import fr.xephi.authme.settings.domain.SettingsClass; import com.github.authme.configme.properties.Property;
import static fr.xephi.authme.settings.domain.Property.newProperty; import static com.github.authme.configme.properties.PropertyInitializer.newProperty;
public class ConverterSettings implements SettingsClass { public class ConverterSettings implements SettingsHolder {
@Comment("Rakamak file name") @Comment("Rakamak file name")
public static final Property<String> RAKAMAK_FILE_NAME = public static final Property<String> RAKAMAK_FILE_NAME =

View File

@ -1,13 +1,13 @@
package fr.xephi.authme.settings.properties; package fr.xephi.authme.settings.properties;
import com.github.authme.configme.Comment;
import com.github.authme.configme.SettingsHolder;
import com.github.authme.configme.properties.Property;
import fr.xephi.authme.datasource.DataSourceType; import fr.xephi.authme.datasource.DataSourceType;
import fr.xephi.authme.settings.domain.Comment;
import fr.xephi.authme.settings.domain.Property;
import fr.xephi.authme.settings.domain.SettingsClass;
import static fr.xephi.authme.settings.domain.Property.newProperty; import static com.github.authme.configme.properties.PropertyInitializer.newProperty;
public class DatabaseSettings implements SettingsClass { public class DatabaseSettings implements SettingsHolder {
@Comment({"What type of database do you want to use?", @Comment({"What type of database do you want to use?",
"Valid values: sqlite, mysql"}) "Valid values: sqlite, mysql"})

View File

@ -1,15 +1,15 @@
package fr.xephi.authme.settings.properties; package fr.xephi.authme.settings.properties;
import fr.xephi.authme.settings.domain.Comment; import com.github.authme.configme.Comment;
import fr.xephi.authme.settings.domain.Property; import com.github.authme.configme.SettingsHolder;
import fr.xephi.authme.settings.domain.SettingsClass; import com.github.authme.configme.properties.Property;
import java.util.List; import java.util.List;
import static fr.xephi.authme.settings.domain.Property.newListProperty; import static com.github.authme.configme.properties.PropertyInitializer.newListProperty;
import static fr.xephi.authme.settings.domain.Property.newProperty; import static com.github.authme.configme.properties.PropertyInitializer.newProperty;
public class EmailSettings implements SettingsClass { public class EmailSettings implements SettingsHolder {
@Comment("Email SMTP server host") @Comment("Email SMTP server host")
public static final Property<String> SMTP_HOST = public static final Property<String> SMTP_HOST =

View File

@ -1,15 +1,15 @@
package fr.xephi.authme.settings.properties; package fr.xephi.authme.settings.properties;
import fr.xephi.authme.settings.domain.Comment; import com.github.authme.configme.Comment;
import fr.xephi.authme.settings.domain.Property; import com.github.authme.configme.SettingsHolder;
import fr.xephi.authme.settings.domain.SettingsClass; import com.github.authme.configme.properties.Property;
import java.util.List; import java.util.List;
import static fr.xephi.authme.settings.domain.Property.newListProperty; import static com.github.authme.configme.properties.PropertyInitializer.newListProperty;
import static fr.xephi.authme.settings.domain.Property.newProperty; import static com.github.authme.configme.properties.PropertyInitializer.newProperty;
public class HooksSettings implements SettingsClass { public class HooksSettings implements SettingsHolder {
@Comment("Do we need to hook with multiverse for spawn checking?") @Comment("Do we need to hook with multiverse for spawn checking?")
public static final Property<Boolean> MULTIVERSE = public static final Property<Boolean> MULTIVERSE =

View File

@ -1,13 +1,13 @@
package fr.xephi.authme.settings.properties; package fr.xephi.authme.settings.properties;
import com.github.authme.configme.Comment;
import com.github.authme.configme.SettingsHolder;
import com.github.authme.configme.properties.Property;
import fr.xephi.authme.output.LogLevel; import fr.xephi.authme.output.LogLevel;
import fr.xephi.authme.settings.domain.Comment;
import fr.xephi.authme.settings.domain.Property;
import fr.xephi.authme.settings.domain.SettingsClass;
import static fr.xephi.authme.settings.domain.Property.newProperty; import static com.github.authme.configme.properties.PropertyInitializer.newProperty;
public class PluginSettings implements SettingsClass { public class PluginSettings implements SettingsHolder {
@Comment("The name shown in the help messages") @Comment("The name shown in the help messages")
public static final Property<String> HELP_HEADER = public static final Property<String> HELP_HEADER =

View File

@ -1,16 +1,16 @@
package fr.xephi.authme.settings.properties; package fr.xephi.authme.settings.properties;
import fr.xephi.authme.settings.domain.Comment; import com.github.authme.configme.Comment;
import fr.xephi.authme.settings.domain.Property; import com.github.authme.configme.SettingsHolder;
import fr.xephi.authme.settings.domain.SettingsClass; import com.github.authme.configme.properties.Property;
import java.util.List; import java.util.List;
import static fr.xephi.authme.settings.domain.Property.newListProperty; import static com.github.authme.configme.properties.PropertyInitializer.newListProperty;
import static fr.xephi.authme.settings.domain.Property.newProperty; import static com.github.authme.configme.properties.PropertyInitializer.newProperty;
public class ProtectionSettings implements SettingsClass { public class ProtectionSettings implements SettingsHolder {
@Comment("Enable some servers protection (country based login, antibot)") @Comment("Enable some servers protection (country based login, antibot)")
public static final Property<Boolean> ENABLE_PROTECTION = public static final Property<Boolean> ENABLE_PROTECTION =

View File

@ -1,12 +1,12 @@
package fr.xephi.authme.settings.properties; package fr.xephi.authme.settings.properties;
import fr.xephi.authme.settings.domain.Comment; import com.github.authme.configme.Comment;
import fr.xephi.authme.settings.domain.Property; import com.github.authme.configme.SettingsHolder;
import fr.xephi.authme.settings.domain.SettingsClass; import com.github.authme.configme.properties.Property;
import static fr.xephi.authme.settings.domain.Property.newProperty; import static com.github.authme.configme.properties.PropertyInitializer.newProperty;
public class PurgeSettings implements SettingsClass { public class PurgeSettings implements SettingsHolder {
@Comment("If enabled, AuthMe automatically purges old, unused accounts") @Comment("If enabled, AuthMe automatically purges old, unused accounts")
public static final Property<Boolean> USE_AUTO_PURGE = public static final Property<Boolean> USE_AUTO_PURGE =

View File

@ -1,15 +1,15 @@
package fr.xephi.authme.settings.properties; package fr.xephi.authme.settings.properties;
import fr.xephi.authme.settings.domain.Comment; import com.github.authme.configme.Comment;
import fr.xephi.authme.settings.domain.Property; import com.github.authme.configme.SettingsHolder;
import fr.xephi.authme.settings.domain.SettingsClass; import com.github.authme.configme.properties.Property;
import java.util.List; import java.util.List;
import static fr.xephi.authme.settings.domain.Property.newListProperty; import static com.github.authme.configme.properties.PropertyInitializer.newListProperty;
import static fr.xephi.authme.settings.domain.Property.newProperty; import static com.github.authme.configme.properties.PropertyInitializer.newProperty;
public class RegistrationSettings implements SettingsClass { public class RegistrationSettings implements SettingsHolder {
@Comment("Enable registration on the server?") @Comment("Enable registration on the server?")
public static final Property<Boolean> IS_ENABLED = public static final Property<Boolean> IS_ENABLED =

View File

@ -1,16 +1,16 @@
package fr.xephi.authme.settings.properties; package fr.xephi.authme.settings.properties;
import fr.xephi.authme.settings.domain.Comment; import com.github.authme.configme.Comment;
import fr.xephi.authme.settings.domain.Property; import com.github.authme.configme.SettingsHolder;
import fr.xephi.authme.settings.domain.SettingsClass; import com.github.authme.configme.properties.Property;
import java.util.List; import java.util.List;
import static fr.xephi.authme.settings.domain.Property.newListProperty; import static com.github.authme.configme.properties.PropertyInitializer.newListProperty;
import static fr.xephi.authme.settings.domain.Property.newLowercaseListProperty; import static com.github.authme.configme.properties.PropertyInitializer.newLowercaseListProperty;
import static fr.xephi.authme.settings.domain.Property.newProperty; import static com.github.authme.configme.properties.PropertyInitializer.newProperty;
public class RestrictionSettings implements SettingsClass { public class RestrictionSettings implements SettingsHolder {
@Comment({ @Comment({
"Can not authenticated players chat?", "Can not authenticated players chat?",

View File

@ -1,16 +1,16 @@
package fr.xephi.authme.settings.properties; package fr.xephi.authme.settings.properties;
import com.github.authme.configme.Comment;
import com.github.authme.configme.SettingsHolder;
import com.github.authme.configme.properties.Property;
import fr.xephi.authme.security.HashAlgorithm; import fr.xephi.authme.security.HashAlgorithm;
import fr.xephi.authme.settings.domain.Comment;
import fr.xephi.authme.settings.domain.Property;
import fr.xephi.authme.settings.domain.SettingsClass;
import java.util.List; import java.util.List;
import static fr.xephi.authme.settings.domain.Property.newLowercaseListProperty; import static com.github.authme.configme.properties.PropertyInitializer.newLowercaseListProperty;
import static fr.xephi.authme.settings.domain.Property.newProperty; import static com.github.authme.configme.properties.PropertyInitializer.newProperty;
public class SecuritySettings implements SettingsClass { public class SecuritySettings implements SettingsHolder {
@Comment({"Stop the server if we can't contact the sql database", @Comment({"Stop the server if we can't contact the sql database",
"Take care with this, if you set this to false,", "Take care with this, if you set this to false,",

View File

@ -1,75 +0,0 @@
package fr.xephi.authme.settings.properties;
import fr.xephi.authme.settings.domain.Comment;
import fr.xephi.authme.settings.domain.Property;
import fr.xephi.authme.settings.domain.SettingsClass;
import fr.xephi.authme.settings.propertymap.PropertyMap;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.Arrays;
import java.util.List;
/**
* Utility class responsible for retrieving all {@link Property} fields
* from {@link SettingsClass} implementations via reflection.
*/
public final class SettingsFieldRetriever {
/** The classes to scan for properties. */
private static final List<Class<? extends SettingsClass>> CONFIGURATION_CLASSES = Arrays.asList(
DatabaseSettings.class, ConverterSettings.class, PluginSettings.class,
RestrictionSettings.class, EmailSettings.class, HooksSettings.class,
ProtectionSettings.class, PurgeSettings.class, SecuritySettings.class,
RegistrationSettings.class, BackupSettings.class);
private SettingsFieldRetriever() {
}
/**
* Scan all given classes for their properties and return the generated {@link PropertyMap}.
*
* @return PropertyMap containing all found properties and their associated comments
* @see #CONFIGURATION_CLASSES
*/
public static PropertyMap getAllPropertyFields() {
PropertyMap properties = new PropertyMap();
for (Class<?> clazz : CONFIGURATION_CLASSES) {
Field[] declaredFields = clazz.getDeclaredFields();
for (Field field : declaredFields) {
Property<?> property = getPropertyField(field);
if (property != null) {
properties.put(property, getCommentsForField(field));
}
}
}
return properties;
}
private static String[] getCommentsForField(Field field) {
if (field.isAnnotationPresent(Comment.class)) {
return field.getAnnotation(Comment.class).value();
}
return new String[0];
}
/**
* Return the given field's value if it is a static {@link Property}.
*
* @param field The field's value to return
* @return The property the field defines, or null if not applicable
*/
private static Property<?> getPropertyField(Field field) {
field.setAccessible(true);
if (field.isAccessible() && Property.class.equals(field.getType()) && Modifier.isStatic(field.getModifiers())) {
try {
return (Property<?>) field.get(null);
} catch (IllegalAccessException e) {
throw new IllegalStateException("Could not fetch field '" + field.getName() + "' from class '"
+ field.getDeclaringClass().getSimpleName() + "'", e);
}
}
return null;
}
}

View File

@ -1,121 +0,0 @@
package fr.xephi.authme.settings.propertymap;
import fr.xephi.authme.ConsoleLogger;
import java.util.ArrayList;
import java.util.List;
/**
* Node class for building a tree from supplied String paths, ordered by insertion.
* <p>
* For instance, consider a tree to which the following paths are inserted (in the given order):
* "animal.bird.duck", "color.yellow", "animal.rodent.rat", "animal.rodent.rabbit", "color.red".
* For such a tree:<ul>
* <li>"animal" (or any of its children) is sorted before "color" (or any of its children)</li>
* <li>"animal.bird" or any child thereof is sorted before "animal.rodent"</li>
* <li>"animal.rodent.rat" comes before "animal.rodent.rabbit"</li>
* </ul>
*
* @see PropertyMapComparator
*/
final class Node {
private final String name;
private final List<Node> children;
private Node(String name) {
this.name = name;
this.children = new ArrayList<>();
}
/**
* Create a root node, i.e. the starting node for a new tree. Call this method to create
* a new tree and always pass this root to other methods.
*
* @return The generated root node.
*/
public static Node createRoot() {
return new Node(null);
}
/**
* Add a child node, creating any intermediary children that don't exist.
*
* @param fullPath The entire path of the node to add, separate by periods
*/
public void addNode(String fullPath) {
String[] pathParts = fullPath.split("\\.");
Node parent = this;
for (String part : pathParts) {
Node child = parent.getChild(part);
if (child == null) {
child = new Node(part);
parent.children.add(child);
}
parent = child;
}
}
/**
* Compare two nodes by this class' sorting behavior (insertion order).
* Note that this method assumes that both supplied paths exist in the tree.
*
* @param fullPath1 The full path to the first node
* @param fullPath2 The full path to the second node
* @return The comparison result, in the same format as {@link Comparable#compareTo}
*/
public int compare(String fullPath1, String fullPath2) {
String[] path1 = fullPath1.split("\\.");
String[] path2 = fullPath2.split("\\.");
int commonCount = 0;
Node commonNode = this;
while (commonCount < path1.length && commonCount < path2.length
&& path1[commonCount].equals(path2[commonCount]) && commonNode != null) {
commonNode = commonNode.getChild(path1[commonCount]);
++commonCount;
}
if (commonNode == null) {
ConsoleLogger.warning("Could not find common node for '" + fullPath1 + "' at index " + commonCount);
return fullPath1.compareTo(fullPath2); // fallback
} else if (commonCount >= path1.length || commonCount >= path2.length) {
return Integer.compare(path1.length, path2.length);
}
int child1Index = commonNode.getChildIndex(path1[commonCount]);
int child2Index = commonNode.getChildIndex(path2[commonCount]);
return Integer.compare(child1Index, child2Index);
}
private Node getChild(String name) {
for (Node child : children) {
if (child.name.equals(name)) {
return child;
}
}
return null;
}
/**
* Return the child's index, i.e. the position at which it was inserted to its parent.
*
* @param name The name of the node
* @return The insertion index
*/
private int getChildIndex(String name) {
int i = 0;
for (Node child : children) {
if (child.name.equals(name)) {
return i;
}
++i;
}
return -1;
}
@Override
public String toString() {
return "Node '" + name + "'";
}
}

View File

@ -1,66 +0,0 @@
package fr.xephi.authme.settings.propertymap;
import fr.xephi.authme.settings.domain.Property;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
/**
* Class wrapping a {@code Map<Property, String[]>} for storing properties and their associated
* comments with custom ordering.
*
* @see PropertyMapComparator for details about the map's order
*/
public class PropertyMap {
private Map<Property<?>, String[]> map;
private PropertyMapComparator comparator;
/**
* Create a new property map.
*/
public PropertyMap() {
comparator = new PropertyMapComparator();
map = new TreeMap<>(comparator);
}
/**
* Add a new property to the map.
*
* @param property The property to add
* @param comments The comments associated to the property
*/
public void put(Property<?> property, String[] comments) {
comparator.add(property);
map.put(property, comments);
}
/**
* Return the entry set of the map.
*
* @return The entry set
*/
public Set<Map.Entry<Property<?>, String[]>> entrySet() {
return map.entrySet();
}
/**
* Return the key set of the map, i.e. all property objects it holds.
*
* @return The key set
*/
public Set<Property<?>> keySet() {
return map.keySet();
}
/**
* Return the size of the map.
*
* @return The size
*/
public int size() {
return map.size();
}
}

View File

@ -1,34 +0,0 @@
package fr.xephi.authme.settings.propertymap;
import fr.xephi.authme.settings.domain.Property;
import java.util.Comparator;
/**
* Custom comparator for {@link PropertyMap}. It guarantees that the map's entries:
* <ul>
* <li>are grouped by path, e.g. all "DataSource.mysql" properties are together, and "DataSource.mysql" properties
* are within the broader "DataSource" group.</li>
* <li>are ordered by insertion, e.g. if the first "DataSource" property is inserted before the first "security"
* property, then "DataSource" properties will come before the "security" ones.</li>
* </ul>
*/
final class PropertyMapComparator implements Comparator<Property<?>> {
private Node parent = Node.createRoot();
/**
* Method to call when adding a new property to the map (as to retain its insertion time).
*
* @param property The property that is being added
*/
public void add(Property<?> property) {
parent.addNode(property.getPath());
}
@Override
public int compare(Property<?> p1, Property<?> p2) {
return parent.compare(p1.getPath(), p2.getPath());
}
}

View File

@ -1,12 +1,12 @@
package fr.xephi.authme.util; package fr.xephi.authme.util;
import com.github.authme.configme.properties.Property;
import fr.xephi.authme.datasource.DataSource; import fr.xephi.authme.datasource.DataSource;
import fr.xephi.authme.initialization.Reloadable; import fr.xephi.authme.initialization.Reloadable;
import fr.xephi.authme.output.MessageKey; import fr.xephi.authme.output.MessageKey;
import fr.xephi.authme.permission.PermissionsManager; import fr.xephi.authme.permission.PermissionsManager;
import fr.xephi.authme.permission.PlayerStatePermission; import fr.xephi.authme.permission.PlayerStatePermission;
import fr.xephi.authme.settings.Settings; import fr.xephi.authme.settings.Settings;
import fr.xephi.authme.settings.domain.Property;
import fr.xephi.authme.settings.properties.EmailSettings; import fr.xephi.authme.settings.properties.EmailSettings;
import fr.xephi.authme.settings.properties.ProtectionSettings; import fr.xephi.authme.settings.properties.ProtectionSettings;
import fr.xephi.authme.settings.properties.RestrictionSettings; import fr.xephi.authme.settings.properties.RestrictionSettings;

View File

@ -2,6 +2,7 @@ package fr.xephi.authme;
import ch.jalu.injector.Injector; import ch.jalu.injector.Injector;
import ch.jalu.injector.InjectorBuilder; import ch.jalu.injector.InjectorBuilder;
import com.github.authme.configme.resource.PropertyResource;
import com.google.common.io.Files; import com.google.common.io.Files;
import fr.xephi.authme.api.NewAPI; import fr.xephi.authme.api.NewAPI;
import fr.xephi.authme.command.CommandHandler; import fr.xephi.authme.command.CommandHandler;
@ -35,7 +36,7 @@ import java.io.IOException;
import java.util.logging.Logger; import java.util.logging.Logger;
import static fr.xephi.authme.settings.TestSettingsMigrationServices.alwaysFulfilled; import static fr.xephi.authme.settings.TestSettingsMigrationServices.alwaysFulfilled;
import static fr.xephi.authme.settings.properties.SettingsFieldRetriever.getAllPropertyFields; import static fr.xephi.authme.settings.properties.AuthMeSettingsRetriever.getAllPropertyFields;
import static org.hamcrest.Matchers.not; import static org.hamcrest.Matchers.not;
import static org.hamcrest.Matchers.nullValue; import static org.hamcrest.Matchers.nullValue;
import static org.junit.Assert.assertThat; import static org.junit.Assert.assertThat;
@ -93,7 +94,7 @@ public class AuthMeInitializationTest {
@Test @Test
public void shouldInitializeAllServices() { public void shouldInitializeAllServices() {
// given // given
Settings settings = new Settings(settingsFile, dataFolder, getAllPropertyFields(), alwaysFulfilled()); Settings settings = new Settings(dataFolder, getAllPropertyFields(), mock(PropertyResource.class), alwaysFulfilled());
Injector injector = new InjectorBuilder().addDefaultHandlers("fr.xephi.authme").create(); Injector injector = new InjectorBuilder().addDefaultHandlers("fr.xephi.authme").create();
injector.provide(DataFolder.class, dataFolder); injector.provide(DataFolder.class, dataFolder);

View File

@ -1,9 +1,9 @@
package fr.xephi.authme.command; package fr.xephi.authme.command;
import com.github.authme.configme.properties.Property;
import fr.xephi.authme.output.MessageKey; import fr.xephi.authme.output.MessageKey;
import fr.xephi.authme.output.Messages; import fr.xephi.authme.output.Messages;
import fr.xephi.authme.settings.Settings; import fr.xephi.authme.settings.Settings;
import fr.xephi.authme.settings.domain.Property;
import fr.xephi.authme.settings.properties.SecuritySettings; import fr.xephi.authme.settings.properties.SecuritySettings;
import fr.xephi.authme.util.ValidationService; import fr.xephi.authme.util.ValidationService;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;

View File

@ -1,5 +1,6 @@
package fr.xephi.authme.datasource; package fr.xephi.authme.datasource;
import com.github.authme.configme.properties.Property;
import com.google.common.base.Objects; import com.google.common.base.Objects;
import com.google.common.base.Preconditions; import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMap;
@ -9,7 +10,6 @@ import fr.xephi.authme.cache.auth.PlayerAuth;
import fr.xephi.authme.security.HashAlgorithm; import fr.xephi.authme.security.HashAlgorithm;
import fr.xephi.authme.security.crypts.HashedPassword; import fr.xephi.authme.security.crypts.HashedPassword;
import fr.xephi.authme.settings.Settings; import fr.xephi.authme.settings.Settings;
import fr.xephi.authme.settings.domain.Property;
import fr.xephi.authme.settings.properties.SecuritySettings; import fr.xephi.authme.settings.properties.SecuritySettings;
import org.junit.Before; import org.junit.Before;
import org.junit.BeforeClass; import org.junit.BeforeClass;

View File

@ -1,10 +1,10 @@
package fr.xephi.authme.datasource; package fr.xephi.authme.datasource;
import com.github.authme.configme.properties.Property;
import com.zaxxer.hikari.HikariConfig; import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource; import com.zaxxer.hikari.HikariDataSource;
import fr.xephi.authme.TestHelper; import fr.xephi.authme.TestHelper;
import fr.xephi.authme.settings.Settings; import fr.xephi.authme.settings.Settings;
import fr.xephi.authme.settings.domain.Property;
import fr.xephi.authme.settings.properties.DatabaseSettings; import fr.xephi.authme.settings.properties.DatabaseSettings;
import org.junit.After; import org.junit.After;
import org.junit.Before; import org.junit.Before;

View File

@ -1,9 +1,9 @@
package fr.xephi.authme.datasource; package fr.xephi.authme.datasource;
import com.github.authme.configme.properties.Property;
import fr.xephi.authme.TestHelper; import fr.xephi.authme.TestHelper;
import fr.xephi.authme.cache.auth.PlayerAuth; import fr.xephi.authme.cache.auth.PlayerAuth;
import fr.xephi.authme.settings.Settings; import fr.xephi.authme.settings.Settings;
import fr.xephi.authme.settings.domain.Property;
import fr.xephi.authme.settings.properties.DatabaseSettings; import fr.xephi.authme.settings.properties.DatabaseSettings;
import org.junit.After; import org.junit.After;
import org.junit.Before; import org.junit.Before;

View File

@ -1,9 +1,13 @@
package fr.xephi.authme.settings; package fr.xephi.authme.settings;
import com.github.authme.configme.migration.MigrationService;
import com.github.authme.configme.migration.PlainMigrationService;
import com.github.authme.configme.properties.Property;
import com.github.authme.configme.propertymap.PropertyEntry;
import com.github.authme.configme.resource.PropertyResource;
import com.github.authme.configme.resource.YamlFileResource;
import fr.xephi.authme.TestHelper; import fr.xephi.authme.TestHelper;
import fr.xephi.authme.settings.domain.Property; import fr.xephi.authme.settings.properties.AuthMeSettingsRetriever;
import fr.xephi.authme.settings.properties.SettingsFieldRetriever;
import fr.xephi.authme.settings.propertymap.PropertyMap;
import fr.xephi.authme.util.StringUtils; import fr.xephi.authme.util.StringUtils;
import org.bukkit.configuration.MemorySection; import org.bukkit.configuration.MemorySection;
import org.bukkit.configuration.file.FileConfiguration; import org.bukkit.configuration.file.FileConfiguration;
@ -35,18 +39,18 @@ public class ConfigFileConsistencyTest {
public void shouldHaveAllConfigs() throws IOException { public void shouldHaveAllConfigs() throws IOException {
// given // given
File configFile = TestHelper.getJarFile(CONFIG_FILE); File configFile = TestHelper.getJarFile(CONFIG_FILE);
FileConfiguration configuration = YamlConfiguration.loadConfiguration(configFile); PropertyResource resource = new YamlFileResource(configFile);
SettingsMigrationService migration = new SettingsMigrationService(); MigrationService migration = new PlainMigrationService();
// when // when
boolean result = migration.containsAllSettings(configuration, SettingsFieldRetriever.getAllPropertyFields()); boolean result = migration.checkAndMigrate(resource, AuthMeSettingsRetriever.getAllPropertyFields());
// then // then
if (!result) { if (result) {
Set<String> knownProperties = getAllKnownPropertyPaths(); Set<String> knownProperties = getAllKnownPropertyPaths();
List<String> missingProperties = new ArrayList<>(); List<String> missingProperties = new ArrayList<>();
for (String path : knownProperties) { for (String path : knownProperties) {
if (!configuration.contains(path)) { if (!resource.contains(path)) {
missingProperties.add(path); missingProperties.add(path);
} }
} }
@ -82,21 +86,22 @@ public class ConfigFileConsistencyTest {
public void shouldHaveValueCorrespondingToPropertyDefault() { public void shouldHaveValueCorrespondingToPropertyDefault() {
// given // given
File configFile = TestHelper.getJarFile(CONFIG_FILE); File configFile = TestHelper.getJarFile(CONFIG_FILE);
FileConfiguration configuration = YamlConfiguration.loadConfiguration(configFile); PropertyResource resource = new YamlFileResource(configFile);
PropertyMap propertyMap = SettingsFieldRetriever.getAllPropertyFields(); List<PropertyEntry> knownProperties = AuthMeSettingsRetriever.getAllPropertyFields();
// when / then // when / then
for (Property<?> property : propertyMap.keySet()) { for (PropertyEntry propertyEntry : knownProperties) {
Property<?> property = propertyEntry.getProperty();
assertThat("Default value of '" + property.getPath() + "' in config.yml should be the same as in Property", assertThat("Default value of '" + property.getPath() + "' in config.yml should be the same as in Property",
property.getFromFile(configuration).equals(property.getDefaultValue()), equalTo(true)); property.getValue(resource).equals(property.getDefaultValue()), equalTo(true));
} }
} }
private static Set<String> getAllKnownPropertyPaths() { private static Set<String> getAllKnownPropertyPaths() {
PropertyMap propertyMap = SettingsFieldRetriever.getAllPropertyFields(); List<PropertyEntry> knownProperties = AuthMeSettingsRetriever.getAllPropertyFields();
Set<String> paths = new HashSet<>(propertyMap.size()); Set<String> paths = new HashSet<>(knownProperties.size());
for (Property<?> property : propertyMap.keySet()) { for (PropertyEntry propertyEntry : knownProperties) {
paths.add(property.getPath()); paths.add(propertyEntry.getProperty().getPath());
} }
return paths; return paths;
} }

View File

@ -1,13 +1,15 @@
package fr.xephi.authme.settings; package fr.xephi.authme.settings;
import com.github.authme.configme.migration.PlainMigrationService;
import com.github.authme.configme.properties.Property;
import com.github.authme.configme.propertymap.PropertyEntry;
import com.github.authme.configme.resource.PropertyResource;
import com.github.authme.configme.resource.YamlFileResource;
import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMap;
import com.google.common.io.Files; import com.google.common.io.Files;
import fr.xephi.authme.TestHelper; import fr.xephi.authme.TestHelper;
import fr.xephi.authme.settings.domain.Property;
import fr.xephi.authme.settings.properties.TestConfiguration; import fr.xephi.authme.settings.properties.TestConfiguration;
import fr.xephi.authme.settings.properties.TestEnum; import fr.xephi.authme.settings.properties.TestEnum;
import fr.xephi.authme.settings.propertymap.PropertyMap;
import org.bukkit.configuration.file.YamlConfiguration;
import org.junit.Before; import org.junit.Before;
import org.junit.BeforeClass; import org.junit.BeforeClass;
import org.junit.Rule; import org.junit.Rule;
@ -21,8 +23,6 @@ import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import static fr.xephi.authme.settings.TestSettingsMigrationServices.checkAllPropertiesPresent;
import static fr.xephi.authme.settings.domain.Property.newProperty;
import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.equalTo;
import static org.junit.Assert.assertThat; import static org.junit.Assert.assertThat;
@ -35,10 +35,8 @@ public class SettingsIntegrationTest {
private static final String COMPLETE_FILE = TestHelper.PROJECT_ROOT + "settings/config-sample-values.yml"; private static final String COMPLETE_FILE = TestHelper.PROJECT_ROOT + "settings/config-sample-values.yml";
/** File name of the sample config missing certain {@link TestConfiguration} values. */ /** File name of the sample config missing certain {@link TestConfiguration} values. */
private static final String INCOMPLETE_FILE = TestHelper.PROJECT_ROOT + "settings/config-incomplete-sample.yml"; private static final String INCOMPLETE_FILE = TestHelper.PROJECT_ROOT + "settings/config-incomplete-sample.yml";
/** File name for testing difficult values. */
private static final String DIFFICULT_FILE = TestHelper.PROJECT_ROOT + "settings/config-difficult-values.yml";
private static PropertyMap propertyMap = TestConfiguration.generatePropertyMap(); private static List<PropertyEntry> propertyMap = TestConfiguration.generatePropertyMap();
@Rule @Rule
public TemporaryFolder temporaryFolder = new TemporaryFolder(); public TemporaryFolder temporaryFolder = new TemporaryFolder();
@ -58,13 +56,13 @@ public class SettingsIntegrationTest {
@Test @Test
public void shouldLoadAndReadAllProperties() throws IOException { public void shouldLoadAndReadAllProperties() throws IOException {
// given // given
YamlConfiguration configuration = YamlConfiguration.loadConfiguration(copyFileFromResources(COMPLETE_FILE)); PropertyResource resource = new YamlFileResource(copyFileFromResources(COMPLETE_FILE));
// Pass another, non-existent file to check if the settings had to be rewritten // Pass another, non-existent file to check if the settings had to be rewritten
File newFile = temporaryFolder.newFile(); File newFile = temporaryFolder.newFile();
// when / then // when / then
Settings settings = new Settings(configuration, newFile, testPluginFolder, propertyMap, Settings settings = new Settings(testPluginFolder, propertyMap, resource,
checkAllPropertiesPresent()); new PlainMigrationService());
Map<Property<?>, Object> expectedValues = ImmutableMap.<Property<?>, Object>builder() Map<Property<?>, Object> expectedValues = ImmutableMap.<Property<?>, Object>builder()
.put(TestConfiguration.DURATION_IN_SECONDS, 22) .put(TestConfiguration.DURATION_IN_SECONDS, 22)
.put(TestConfiguration.SYSTEM_NAME, "Custom sys name") .put(TestConfiguration.SYSTEM_NAME, "Custom sys name")
@ -88,16 +86,16 @@ public class SettingsIntegrationTest {
public void shouldWriteMissingProperties() { public void shouldWriteMissingProperties() {
// given/when // given/when
File file = copyFileFromResources(INCOMPLETE_FILE); File file = copyFileFromResources(INCOMPLETE_FILE);
YamlConfiguration configuration = YamlConfiguration.loadConfiguration(file); PropertyResource resource = new YamlFileResource(file);
// Expectation: File is rewritten to since it does not have all configurations // Expectation: File is rewritten to since it does not have all configurations
new Settings(configuration, file, testPluginFolder, propertyMap, checkAllPropertiesPresent()); new Settings(testPluginFolder, propertyMap, resource, new PlainMigrationService());
// Load the settings again -> checks that what we wrote can be loaded again // Load the settings again -> checks that what we wrote can be loaded again
configuration = YamlConfiguration.loadConfiguration(file); resource = new YamlFileResource(file);
// then // then
Settings settings = new Settings(configuration, file, testPluginFolder, propertyMap, Settings settings = new Settings(testPluginFolder, propertyMap, resource,
checkAllPropertiesPresent()); new PlainMigrationService());
Map<Property<?>, Object> expectedValues = ImmutableMap.<Property<?>, Object>builder() Map<Property<?>, Object> expectedValues = ImmutableMap.<Property<?>, Object>builder()
.put(TestConfiguration.DURATION_IN_SECONDS, 22) .put(TestConfiguration.DURATION_IN_SECONDS, 22)
.put(TestConfiguration.SYSTEM_NAME, "[TestDefaultValue]") .put(TestConfiguration.SYSTEM_NAME, "[TestDefaultValue]")
@ -116,62 +114,11 @@ public class SettingsIntegrationTest {
} }
} }
/** Verify that "difficult cases" such as apostrophes in strings etc. are handled properly. */
@Test
public void shouldProperlyExportAnyValues() {
// given
File file = copyFileFromResources(DIFFICULT_FILE);
YamlConfiguration configuration = YamlConfiguration.loadConfiguration(file);
// Additional string properties
List<Property<String>> additionalProperties = Arrays.asList(
newProperty("more.string1", "it's a text with some \\'apostrophes'"),
newProperty("more.string2", "\tthis one\nhas some\nnew '' lines-test")
);
for (Property<?> property : additionalProperties) {
propertyMap.put(property, new String[0]);
}
// when
new Settings(configuration, file, testPluginFolder, propertyMap, checkAllPropertiesPresent());
// reload the file as settings should have been rewritten
configuration = YamlConfiguration.loadConfiguration(file);
// then
// assert that we won't rewrite the settings again! One rewrite should produce a valid, complete configuration
File unusedFile = new File("config-difficult-values.unused.yml");
Settings settings = new Settings(configuration, unusedFile, testPluginFolder, propertyMap,
checkAllPropertiesPresent());
assertThat(unusedFile.exists(), equalTo(false));
assertThat(configuration.contains(TestConfiguration.DUST_LEVEL.getPath()), equalTo(true));
Map<Property<?>, Object> expectedValues = ImmutableMap.<Property<?>, Object>builder()
.put(TestConfiguration.DURATION_IN_SECONDS, 20)
.put(TestConfiguration.SYSTEM_NAME, "A 'test' name")
.put(TestConfiguration.RATIO_ORDER, TestEnum.FOURTH)
.put(TestConfiguration.RATIO_FIELDS, Arrays.asList("Australia\\", "\tBurundi'", "Colombia?\n''"))
.put(TestConfiguration.VERSION_NUMBER, -1337)
.put(TestConfiguration.SKIP_BORING_FEATURES, false)
.put(TestConfiguration.BORING_COLORS, Arrays.asList("it's a difficult string!", "gray\nwith new lines\n"))
.put(TestConfiguration.DUST_LEVEL, -1)
.put(TestConfiguration.USE_COOL_FEATURES, true)
.put(TestConfiguration.COOL_OPTIONS, Collections.EMPTY_LIST)
.put(additionalProperties.get(0), additionalProperties.get(0).getDefaultValue())
.put(additionalProperties.get(1), additionalProperties.get(1).getDefaultValue())
.build();
for (Map.Entry<Property<?>, Object> entry : expectedValues.entrySet()) {
assertThat("Property '" + entry.getKey().getPath() + "' has expected value"
+ entry.getValue() + " but found " + settings.getProperty(entry.getKey()),
settings.getProperty(entry.getKey()), equalTo(entry.getValue()));
}
}
@Test @Test
public void shouldReloadSettings() throws IOException { public void shouldReloadSettings() throws IOException {
// given // given
YamlConfiguration configuration = YamlConfiguration.loadConfiguration(temporaryFolder.newFile()); PropertyResource resource = new YamlFileResource(temporaryFolder.newFile());
File fullConfigFile = copyFileFromResources(COMPLETE_FILE); Settings settings = new Settings(testPluginFolder, propertyMap, resource,
Settings settings = new Settings(configuration, fullConfigFile, testPluginFolder, propertyMap,
TestSettingsMigrationServices.alwaysFulfilled()); TestSettingsMigrationServices.alwaysFulfilled());
// when // when

View File

@ -1,17 +1,18 @@
package fr.xephi.authme.settings; package fr.xephi.authme.settings;
import com.github.authme.configme.propertymap.PropertyEntry;
import com.github.authme.configme.resource.PropertyResource;
import com.github.authme.configme.resource.YamlFileResource;
import com.google.common.io.Files; import com.google.common.io.Files;
import fr.xephi.authme.TestHelper; import fr.xephi.authme.TestHelper;
import fr.xephi.authme.settings.properties.SettingsFieldRetriever; import fr.xephi.authme.settings.properties.AuthMeSettingsRetriever;
import fr.xephi.authme.settings.propertymap.PropertyMap;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.configuration.file.YamlConfiguration;
import org.junit.Rule; import org.junit.Rule;
import org.junit.Test; import org.junit.Test;
import org.junit.rules.TemporaryFolder; import org.junit.rules.TemporaryFolder;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.util.List;
import static org.hamcrest.Matchers.arrayWithSize; import static org.hamcrest.Matchers.arrayWithSize;
import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.equalTo;
@ -39,13 +40,13 @@ public class SettingsMigrationServiceTest {
public void shouldNotRewriteJarConfig() throws IOException { public void shouldNotRewriteJarConfig() throws IOException {
// given // given
copyConfigToTestFolder(); copyConfigToTestFolder();
FileConfiguration configuration = YamlConfiguration.loadConfiguration(configTestFile); PropertyResource resource = new YamlFileResource(configTestFile);
PropertyMap propertyMap = SettingsFieldRetriever.getAllPropertyFields(); List<PropertyEntry> propertyMap = AuthMeSettingsRetriever.getAllPropertyFields();
assumeThat(testFolder.listFiles(), arrayWithSize(1)); assumeThat(testFolder.listFiles(), arrayWithSize(1));
SettingsMigrationService migrationService = new SettingsMigrationService(); SettingsMigrationService migrationService = new SettingsMigrationService(testFolder);
// when // when
boolean result = migrationService.checkAndMigrate(configuration, propertyMap, testFolder); boolean result = migrationService.checkAndMigrate(resource, propertyMap);
// then // then
assertThat(result, equalTo(false)); assertThat(result, equalTo(false));

View File

@ -1,22 +1,23 @@
package fr.xephi.authme.settings; package fr.xephi.authme.settings;
import com.github.authme.configme.migration.PlainMigrationService;
import com.github.authme.configme.properties.Property;
import com.github.authme.configme.propertymap.PropertyEntry;
import com.github.authme.configme.resource.PropertyResource;
import fr.xephi.authme.TestHelper; import fr.xephi.authme.TestHelper;
import fr.xephi.authme.settings.domain.Property;
import fr.xephi.authme.settings.properties.RegistrationSettings; import fr.xephi.authme.settings.properties.RegistrationSettings;
import fr.xephi.authme.settings.properties.TestConfiguration; import fr.xephi.authme.settings.properties.TestConfiguration;
import fr.xephi.authme.settings.properties.TestEnum;
import org.bukkit.configuration.file.YamlConfiguration;
import org.junit.Before; import org.junit.Before;
import org.junit.BeforeClass; import org.junit.BeforeClass;
import org.junit.Rule; import org.junit.Rule;
import org.junit.Test; import org.junit.Test;
import org.junit.rules.TemporaryFolder; import org.junit.rules.TemporaryFolder;
import org.mockito.internal.stubbing.answers.ReturnsArgumentAt;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.nio.file.Files; import java.nio.file.Files;
import java.util.Collections;
import java.util.List; import java.util.List;
import static fr.xephi.authme.settings.properties.PluginSettings.MESSAGES_LANGUAGE; import static fr.xephi.authme.settings.properties.PluginSettings.MESSAGES_LANGUAGE;
@ -28,13 +29,9 @@ import static org.hamcrest.Matchers.not;
import static org.hamcrest.Matchers.nullValue; import static org.hamcrest.Matchers.nullValue;
import static org.junit.Assert.assertThat; import static org.junit.Assert.assertThat;
import static org.mockito.BDDMockito.given; import static org.mockito.BDDMockito.given;
import static org.mockito.Matchers.anyBoolean;
import static org.mockito.Matchers.anyDouble;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.anyString; import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.eq; import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
/** /**
@ -56,38 +53,12 @@ public class SettingsTest {
testPluginFolder = temporaryFolder.newFolder(); testPluginFolder = temporaryFolder.newFolder();
} }
@Test
public void shouldLoadAllConfigs() {
// given
YamlConfiguration configuration = mock(YamlConfiguration.class);
given(configuration.getString(anyString(), anyString())).willAnswer(new ReturnsArgumentAt(1));
given(configuration.getBoolean(anyString(), anyBoolean())).willAnswer(new ReturnsArgumentAt(1));
given(configuration.getDouble(anyString(), anyDouble())).willAnswer(new ReturnsArgumentAt(1));
given(configuration.getInt(anyString(), anyInt())).willAnswer(new ReturnsArgumentAt(1));
setReturnValue(configuration, TestConfiguration.VERSION_NUMBER, 20);
setReturnValue(configuration, TestConfiguration.SKIP_BORING_FEATURES, true);
setReturnValue(configuration, TestConfiguration.RATIO_ORDER, TestEnum.THIRD);
setReturnValue(configuration, TestConfiguration.SYSTEM_NAME, "myTestSys");
// when / then
Settings settings = new Settings(configuration, null, null, null, null);
assertThat(settings.getProperty(TestConfiguration.VERSION_NUMBER), equalTo(20));
assertThat(settings.getProperty(TestConfiguration.SKIP_BORING_FEATURES), equalTo(true));
assertThat(settings.getProperty(TestConfiguration.RATIO_ORDER), equalTo(TestEnum.THIRD));
assertThat(settings.getProperty(TestConfiguration.SYSTEM_NAME), equalTo("myTestSys"));
assertDefaultValue(TestConfiguration.DURATION_IN_SECONDS, settings);
assertDefaultValue(TestConfiguration.DUST_LEVEL, settings);
assertDefaultValue(TestConfiguration.COOL_OPTIONS, settings);
}
@Test @Test
public void shouldReturnDefaultFile() throws IOException { public void shouldReturnDefaultFile() throws IOException {
// given // given
YamlConfiguration configuration = mock(YamlConfiguration.class); PropertyResource resource = mock(PropertyResource.class);
Settings settings = new Settings(configuration, null, null, null, null); List<PropertyEntry> knownProperties = Collections.emptyList();
Settings settings = new Settings(testPluginFolder, knownProperties, resource, new PlainMigrationService());
// when // when
String defaultFile = settings.getDefaultMessagesFile(); String defaultFile = settings.getDefaultMessagesFile();
@ -99,19 +70,6 @@ public class SettingsTest {
assertThat(stream.read(), not(equalTo(0))); assertThat(stream.read(), not(equalTo(0)));
} }
@Test
public void shouldSetProperty() {
// given
YamlConfiguration configuration = mock(YamlConfiguration.class);
Settings settings = new Settings(configuration, null, null, null, null);
// when
settings.setProperty(TestConfiguration.DUST_LEVEL, -4);
// then
verify(configuration).set(TestConfiguration.DUST_LEVEL.getPath(), -4);
}
@Test @Test
public void shouldReturnMessagesFile() { public void shouldReturnMessagesFile() {
// given // given
@ -120,11 +78,11 @@ public class SettingsTest {
File file = new File(testPluginFolder, makePath("messages", "messages_" + languageCode + ".yml")); File file = new File(testPluginFolder, makePath("messages", "messages_" + languageCode + ".yml"));
createFile(file); createFile(file);
YamlConfiguration configuration = mock(YamlConfiguration.class); PropertyResource resource = mock(PropertyResource.class);
given(configuration.contains(anyString())).willReturn(true); given(resource.contains(anyString())).willReturn(true);
setReturnValue(configuration, MESSAGES_LANGUAGE, languageCode); setReturnValue(resource, MESSAGES_LANGUAGE, languageCode);
Settings settings = new Settings(configuration, null, testPluginFolder, Settings settings = new Settings(testPluginFolder, TestConfiguration.generatePropertyMap(),
TestConfiguration.generatePropertyMap(), TestSettingsMigrationServices.alwaysFulfilled()); resource, TestSettingsMigrationServices.alwaysFulfilled());
// when // when
File messagesFile = settings.getMessagesFile(); File messagesFile = settings.getMessagesFile();
@ -137,11 +95,11 @@ public class SettingsTest {
@Test @Test
public void shouldCopyDefaultForUnknownLanguageCode() { public void shouldCopyDefaultForUnknownLanguageCode() {
// given // given
YamlConfiguration configuration = mock(YamlConfiguration.class); PropertyResource resource = mock(PropertyResource.class);
given(configuration.contains(anyString())).willReturn(true); given(resource.contains(anyString())).willReturn(true);
setReturnValue(configuration, MESSAGES_LANGUAGE, "doesntexist"); setReturnValue(resource, MESSAGES_LANGUAGE, "doesntexist");
Settings settings = new Settings(configuration, null, testPluginFolder, Settings settings = new Settings(testPluginFolder, TestConfiguration.generatePropertyMap(),
TestConfiguration.generatePropertyMap(), TestSettingsMigrationServices.alwaysFulfilled()); resource, TestSettingsMigrationServices.alwaysFulfilled());
// when // when
File messagesFile = settings.getMessagesFile(); File messagesFile = settings.getMessagesFile();
@ -159,10 +117,10 @@ public class SettingsTest {
createFile(welcomeFile); createFile(welcomeFile);
Files.write(welcomeFile.toPath(), welcomeMessage.getBytes()); Files.write(welcomeFile.toPath(), welcomeMessage.getBytes());
YamlConfiguration configuration = mock(YamlConfiguration.class); PropertyResource resource = mock(PropertyResource.class);
setReturnValue(configuration, RegistrationSettings.USE_WELCOME_MESSAGE, true); setReturnValue(resource, RegistrationSettings.USE_WELCOME_MESSAGE, true);
Settings settings = new Settings(configuration, null, testPluginFolder, Settings settings = new Settings(testPluginFolder, TestConfiguration.generatePropertyMap(),
TestConfiguration.generatePropertyMap(), TestSettingsMigrationServices.alwaysFulfilled()); resource, TestSettingsMigrationServices.alwaysFulfilled());
// when // when
List<String> result = settings.getWelcomeMessage(); List<String> result = settings.getWelcomeMessage();
@ -181,9 +139,9 @@ public class SettingsTest {
createFile(emailFile); createFile(emailFile);
Files.write(emailFile.toPath(), emailMessage.getBytes()); Files.write(emailFile.toPath(), emailMessage.getBytes());
YamlConfiguration configuration = mock(YamlConfiguration.class); PropertyResource resource = mock(PropertyResource.class);
Settings settings = new Settings(configuration, null, testPluginFolder, Settings settings = new Settings(testPluginFolder, TestConfiguration.generatePropertyMap(),
TestConfiguration.generatePropertyMap(), TestSettingsMigrationServices.alwaysFulfilled()); resource, TestSettingsMigrationServices.alwaysFulfilled());
// when // when
String result = settings.getEmailMessage(); String result = settings.getEmailMessage();
@ -192,26 +150,21 @@ public class SettingsTest {
assertThat(result, equalTo(emailMessage)); assertThat(result, equalTo(emailMessage));
} }
private static <T> void setReturnValue(YamlConfiguration config, Property<T> property, T value) { private static <T> void setReturnValue(PropertyResource resource, Property<T> property, T value) {
if (value instanceof String) { if (value instanceof String) {
when(config.getString(eq(property.getPath()), anyString())).thenReturn((String) value); when(resource.getString(eq(property.getPath()))).thenReturn((String) value);
} else if (value instanceof Integer) { } else if (value instanceof Integer) {
when(config.getInt(eq(property.getPath()), anyInt())).thenReturn((Integer) value); when(resource.getInt(eq(property.getPath()))).thenReturn((Integer) value);
} else if (value instanceof Boolean) { } else if (value instanceof Boolean) {
when(config.getBoolean(eq(property.getPath()), anyBoolean())).thenReturn((Boolean) value); when(resource.getBoolean(eq(property.getPath()))).thenReturn((Boolean) value);
} else if (value instanceof Enum<?>) { } else if (value instanceof Enum<?>) {
when(config.getString(property.getPath())).thenReturn(((Enum<?>) value).name()); when(resource.getString(property.getPath())).thenReturn(((Enum<?>) value).name());
} else { } else {
throw new UnsupportedOperationException("Value has unsupported type '" throw new UnsupportedOperationException("Value has unsupported type '"
+ (value == null ? "null" : value.getClass().getSimpleName()) + "'"); + (value == null ? "null" : value.getClass().getSimpleName()) + "'");
} }
} }
private static void assertDefaultValue(Property<?> property, Settings setting) {
assertThat(property.getPath() + " has default value",
setting.getProperty(property).equals(property.getDefaultValue()), equalTo(true));
}
private static void createFile(File file) { private static void createFile(File file) {
try { try {
file.getParentFile().mkdirs(); file.getParentFile().mkdirs();

View File

@ -1,12 +1,13 @@
package fr.xephi.authme.settings; package fr.xephi.authme.settings;
import fr.xephi.authme.settings.propertymap.PropertyMap; import com.github.authme.configme.migration.MigrationService;
import org.bukkit.configuration.file.FileConfiguration; import com.github.authme.configme.propertymap.PropertyEntry;
import com.github.authme.configme.resource.PropertyResource;
import java.io.File; import java.util.List;
/** /**
* Provides {@link SettingsMigrationService} implementations for testing. * Provides {@link MigrationService} implementations for testing.
*/ */
public final class TestSettingsMigrationServices { public final class TestSettingsMigrationServices {
@ -18,32 +19,12 @@ public final class TestSettingsMigrationServices {
* *
* @return test settings migration service * @return test settings migration service
*/ */
public static SettingsMigrationService alwaysFulfilled() { public static MigrationService alwaysFulfilled() {
return new SettingsMigrationService() { return new MigrationService() {
@Override @Override
public boolean checkAndMigrate(FileConfiguration configuration, PropertyMap propertyMap, File pluginFolder) { public boolean checkAndMigrate(PropertyResource propertyResource, List<PropertyEntry> list) {
return false; return false;
} }
@Override
public boolean containsAllSettings(FileConfiguration configuration, PropertyMap propertyMap) {
return true;
}
}; };
} }
/**
* Returns a simple settings migration service which is fulfilled if all properties are present.
*
* @return test settings migration service
*/
public static SettingsMigrationService checkAllPropertiesPresent() {
return new SettingsMigrationService() {
// See parent javadoc: true = some migration had to be done, false = config file is up-to-date
@Override
public boolean checkAndMigrate(FileConfiguration configuration, PropertyMap propertyMap, File pluginFolder) {
return !super.containsAllSettings(configuration, propertyMap);
}
};
}
} }

View File

@ -1,112 +0,0 @@
package fr.xephi.authme.settings.domain;
import org.bukkit.configuration.file.YamlConfiguration;
import org.junit.Test;
import static org.hamcrest.Matchers.equalTo;
import static org.junit.Assert.assertThat;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.mock;
/**
* Test for {@link EnumProperty}.
*/
public class EnumPropertyTest {
@Test
public void shouldReturnCorrectEnumValue() {
// given
Property<TestEnum> property = Property.newProperty(TestEnum.class, "enum.path", TestEnum.ENTRY_C);
YamlConfiguration configuration = mock(YamlConfiguration.class);
given(configuration.getString(property.getPath())).willReturn("Entry_B");
// when
TestEnum result = property.getFromFile(configuration);
// then
assertThat(result, equalTo(TestEnum.ENTRY_B));
}
@Test
public void shouldFallBackToDefaultForInvalidValue() {
// given
Property<TestEnum> property = Property.newProperty(TestEnum.class, "enum.path", TestEnum.ENTRY_C);
YamlConfiguration configuration = mock(YamlConfiguration.class);
given(configuration.getString(property.getPath())).willReturn("Bogus");
// when
TestEnum result = property.getFromFile(configuration);
// then
assertThat(result, equalTo(TestEnum.ENTRY_C));
}
@Test
public void shouldFallBackToDefaultForNonExistentValue() {
// given
Property<TestEnum> property = Property.newProperty(TestEnum.class, "enum.path", TestEnum.ENTRY_C);
YamlConfiguration configuration = mock(YamlConfiguration.class);
given(configuration.getString(property.getPath())).willReturn(null);
// when
TestEnum result = property.getFromFile(configuration);
// then
assertThat(result, equalTo(TestEnum.ENTRY_C));
}
@Test
public void shouldReturnTrueForContainsCheck() {
// given
Property<TestEnum> property = Property.newProperty(TestEnum.class, "my.test.path", TestEnum.ENTRY_C);
YamlConfiguration configuration = mock(YamlConfiguration.class);
given(configuration.contains(property.getPath())).willReturn(true);
given(configuration.getString(property.getPath())).willReturn("ENTRY_B");
// when
boolean result = property.isPresent(configuration);
// then
assertThat(result, equalTo(true));
}
@Test
public void shouldReturnFalseForFileWithoutConfig() {
// given
Property<TestEnum> property = Property.newProperty(TestEnum.class, "my.test.path", TestEnum.ENTRY_C);
YamlConfiguration configuration = mock(YamlConfiguration.class);
given(configuration.contains(property.getPath())).willReturn(false);
// when
boolean result = property.isPresent(configuration);
// then
assertThat(result, equalTo(false));
}
@Test
public void shouldReturnFalseForUnknownValue() {
// given
Property<TestEnum> property = Property.newProperty(TestEnum.class, "my.test.path", TestEnum.ENTRY_C);
YamlConfiguration configuration = mock(YamlConfiguration.class);
given(configuration.contains(property.getPath())).willReturn(true);
given(configuration.getString(property.getPath())).willReturn("wrong value");
// when
boolean result = property.isPresent(configuration);
// then
assertThat(result, equalTo(false));
}
private enum TestEnum {
ENTRY_A,
ENTRY_B,
ENTRY_C
}
}

View File

@ -1,172 +0,0 @@
package fr.xephi.authme.settings.domain;
import org.bukkit.configuration.file.YamlConfiguration;
import org.junit.BeforeClass;
import org.junit.Test;
import org.mockito.internal.stubbing.answers.ReturnsArgumentAt;
import java.util.Arrays;
import java.util.List;
import static org.hamcrest.Matchers.contains;
import static org.hamcrest.Matchers.equalTo;
import static org.junit.Assert.assertThat;
import static org.mockito.Matchers.anyBoolean;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
/**
* Test for {@link Property} and the contained subtypes.
*/
public class PropertyTest {
private static YamlConfiguration configuration;
@BeforeClass
public static void setUpYamlConfigurationMock() {
configuration = mock(YamlConfiguration.class);
when(configuration.getBoolean(eq("bool.path.test"), anyBoolean())).thenReturn(true);
when(configuration.getBoolean(eq("bool.path.wrong"), anyBoolean())).thenAnswer(new ReturnsArgumentAt(1));
when(configuration.getInt(eq("int.path.test"), anyInt())).thenReturn(27);
when(configuration.getInt(eq("int.path.wrong"), anyInt())).thenAnswer(new ReturnsArgumentAt(1));
when(configuration.getString(eq("str.path.test"), anyString())).thenReturn("Test value");
when(configuration.getString(eq("str.path.wrong"), anyString())).thenAnswer(new ReturnsArgumentAt(1));
when(configuration.isList("list.path.test")).thenReturn(true);
when(configuration.getStringList("list.path.test")).thenReturn(Arrays.asList("test1", "Test2", "3rd test"));
when(configuration.isList("list.path.wrong")).thenReturn(false);
when(configuration.isList("lowercaselist.path.test")).thenReturn(true);
when(configuration.getStringList("lowercaselist.path.test")).thenReturn(Arrays.asList("test1", "Test2", "3rd test"));
when(configuration.isList("lowercaselist.path.wrong")).thenReturn(false);
}
/* Boolean */
@Test
public void shouldGetBoolValue() {
// given
Property<Boolean> property = Property.newProperty("bool.path.test", false);
// when
boolean result = property.getFromFile(configuration);
// then
assertThat(result, equalTo(true));
}
@Test
public void shouldGetBoolDefault() {
// given
Property<Boolean> property = Property.newProperty("bool.path.wrong", true);
// when
boolean result = property.getFromFile(configuration);
// then
assertThat(result, equalTo(true));
}
/* Integer */
@Test
public void shouldGetIntValue() {
// given
Property<Integer> property = Property.newProperty("int.path.test", 3);
// when
int result = property.getFromFile(configuration);
// then
assertThat(result, equalTo(27));
}
@Test
public void shouldGetIntDefault() {
// given
Property<Integer> property = Property.newProperty("int.path.wrong", -10);
// when
int result = property.getFromFile(configuration);
// then
assertThat(result, equalTo(-10));
}
/* String */
@Test
public void shouldGetStringValue() {
// given
Property<String> property = Property.newProperty("str.path.test", "unused default");
// when
String result = property.getFromFile(configuration);
// then
assertThat(result, equalTo("Test value"));
}
@Test
public void shouldGetStringDefault() {
// given
Property<String> property = Property.newProperty("str.path.wrong", "given default value");
// when
String result = property.getFromFile(configuration);
// then
assertThat(result, equalTo("given default value"));
}
/* String list */
@Test
public void shouldGetStringListValue() {
// given
Property<List<String>> property = Property.newListProperty("list.path.test", "1", "b");
// when
List<String> result = property.getFromFile(configuration);
// then
assertThat(result, contains("test1", "Test2", "3rd test"));
}
@Test
public void shouldGetStringListDefault() {
// given
Property<List<String>> property =
Property.newListProperty("list.path.wrong", "default", "list", "elements");
// when
List<String> result = property.getFromFile(configuration);
// then
assertThat(result, contains("default", "list", "elements"));
}
/* Lowercase String list */
@Test
public void shouldGetLowercaseStringListValue() {
// given
Property<List<String>> property = Property.newLowercaseListProperty("lowercaselist.path.test", "1", "b");
// when
List<String> result = property.getFromFile(configuration);
// then
assertThat(result, contains("test1", "test2", "3rd test"));
}
@Test
public void shouldGetLowercaseStringListDefault() {
// given
Property<List<String>> property =
Property.newLowercaseListProperty("lowercaselist.path.wrong", "default", "list", "elements");
// when
List<String> result = property.getFromFile(configuration);
// then
assertThat(result, contains("default", "list", "elements"));
}
}

View File

@ -1,9 +1,9 @@
package fr.xephi.authme.settings.properties; package fr.xephi.authme.settings.properties;
import com.github.authme.configme.SettingsHolder;
import com.github.authme.configme.properties.Property;
import fr.xephi.authme.ReflectionTestUtils; import fr.xephi.authme.ReflectionTestUtils;
import fr.xephi.authme.TestHelper; import fr.xephi.authme.TestHelper;
import fr.xephi.authme.settings.domain.Property;
import fr.xephi.authme.settings.domain.SettingsClass;
import org.junit.BeforeClass; import org.junit.BeforeClass;
import org.junit.Test; import org.junit.Test;
@ -20,12 +20,12 @@ import static org.junit.Assert.assertThat;
import static org.junit.Assert.fail; import static org.junit.Assert.fail;
/** /**
* Test for {@link SettingsClass} implementations. * Test for {@link SettingsHolder} implementations.
*/ */
public class SettingsClassConsistencyTest { public class SettingsClassConsistencyTest {
private static final String SETTINGS_FOLDER = "src/main/java/fr/xephi/authme/settings/properties"; private static final String SETTINGS_FOLDER = "src/main/java/fr/xephi/authme/settings/properties";
private static List<Class<? extends SettingsClass>> classes; private static List<Class<? extends SettingsHolder>> classes;
@BeforeClass @BeforeClass
public static void scanForSettingsClasses() { public static void scanForSettingsClasses() {
@ -37,7 +37,7 @@ public class SettingsClassConsistencyTest {
classes = new ArrayList<>(); classes = new ArrayList<>();
for (File file : filesInFolder) { for (File file : filesInFolder) {
Class<? extends SettingsClass> clazz = getSettingsClassFromFile(file); Class<? extends SettingsHolder> clazz = getSettingsClassFromFile(file);
if (clazz != null) { if (clazz != null) {
classes.add(clazz); classes.add(clazz);
} }
@ -95,15 +95,15 @@ public class SettingsClassConsistencyTest {
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
private static Class<? extends SettingsClass> getSettingsClassFromFile(File file) { private static Class<? extends SettingsHolder> getSettingsClassFromFile(File file) {
String fileName = file.getPath(); String fileName = file.getPath();
String className = fileName String className = fileName
.substring("src/main/java/".length(), fileName.length() - ".java".length()) .substring("src/main/java/".length(), fileName.length() - ".java".length())
.replace(File.separator, "."); .replace(File.separator, ".");
try { try {
Class<?> clazz = SettingsClassConsistencyTest.class.getClassLoader().loadClass(className); Class<?> clazz = SettingsClassConsistencyTest.class.getClassLoader().loadClass(className);
if (SettingsClass.class.isAssignableFrom(clazz)) { if (SettingsHolder.class.isAssignableFrom(clazz)) {
return (Class<? extends SettingsClass>) clazz; return (Class<? extends SettingsHolder>) clazz;
} }
return null; return null;
} catch (ClassNotFoundException e) { } catch (ClassNotFoundException e) {

View File

@ -1,20 +1,21 @@
package fr.xephi.authme.settings.properties; package fr.xephi.authme.settings.properties;
import com.github.authme.configme.SettingsHolder;
import com.github.authme.configme.properties.Property;
import com.github.authme.configme.propertymap.PropertyEntry;
import fr.xephi.authme.ReflectionTestUtils; import fr.xephi.authme.ReflectionTestUtils;
import fr.xephi.authme.settings.domain.Property;
import fr.xephi.authme.settings.domain.SettingsClass;
import fr.xephi.authme.settings.propertymap.PropertyMap;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List; import java.util.List;
import static fr.xephi.authme.settings.domain.Property.newListProperty; import static com.github.authme.configme.properties.PropertyInitializer.newListProperty;
import static fr.xephi.authme.settings.domain.Property.newProperty; import static com.github.authme.configme.properties.PropertyInitializer.newProperty;
/** /**
* Sample properties for testing purposes. * Sample properties for testing purposes.
*/ */
public final class TestConfiguration implements SettingsClass { public final class TestConfiguration implements SettingsHolder {
public static final Property<Integer> DURATION_IN_SECONDS = public static final Property<Integer> DURATION_IN_SECONDS =
newProperty("test.duration", 4); newProperty("test.duration", 4);
@ -55,17 +56,17 @@ public final class TestConfiguration implements SettingsClass {
* *
* @return The generated property map * @return The generated property map
*/ */
public static PropertyMap generatePropertyMap() { public static List<PropertyEntry> generatePropertyMap() {
PropertyMap propertyMap = new PropertyMap(); List<PropertyEntry> properties = new ArrayList<>();
for (Field field : TestConfiguration.class.getDeclaredFields()) { for (Field field : TestConfiguration.class.getDeclaredFields()) {
Object fieldValue = ReflectionTestUtils.getFieldValue(TestConfiguration.class, null, field.getName()); Object fieldValue = ReflectionTestUtils.getFieldValue(TestConfiguration.class, null, field.getName());
if (fieldValue instanceof Property<?>) { if (fieldValue instanceof Property<?>) {
Property<?> property = (Property<?>) fieldValue; Property<?> property = (Property<?>) fieldValue;
String[] comments = new String[]{"Comment for '" + property.getPath() + "'"}; String[] comments = new String[]{"Comment for '" + property.getPath() + "'"};
propertyMap.put(property, comments); properties.add(new PropertyEntry(property, comments));
} }
} }
return propertyMap; return properties;
} }
} }

View File

@ -1,53 +0,0 @@
package fr.xephi.authme.settings.propertymap;
import fr.xephi.authme.settings.domain.Property;
import org.junit.Assert;
import org.junit.Test;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Set;
import static org.hamcrest.Matchers.contains;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
/**
* Test for {@link PropertyMap}.
*/
public class PropertyMapTest {
@Test
public void shouldKeepEntriesByInsertionAndGroup() {
// given
List<String> paths = Arrays.asList("japan", "indonesia.jakarta", "japan.tokyo", "china.shanghai", "egypt.cairo",
"china.shenzhen", "china", "indonesia.jakarta.tugu", "egypt", "japan.nagoya", "japan.tokyo.taito");
PropertyMap map = new PropertyMap();
// when
for (String path : paths) {
Property<?> property = createPropertyWithPath(path);
map.put(property, new String[0]);
}
// then
Set<Map.Entry<Property<?>, String[]>> entrySet = map.entrySet();
List<String> resultPaths = new ArrayList<>(entrySet.size());
for (Map.Entry<Property<?>, String[]> entry : entrySet) {
resultPaths.add(entry.getKey().getPath());
}
Assert.assertThat(resultPaths, contains("japan", "japan.tokyo", "japan.tokyo.taito", "japan.nagoya",
"indonesia.jakarta", "indonesia.jakarta.tugu", "china", "china.shanghai", "china.shenzhen",
"egypt", "egypt.cairo"));
}
private static Property<?> createPropertyWithPath(String path) {
Property<?> property = mock(Property.class);
when(property.getPath()).thenReturn(path);
return property;
}
}

View File

@ -2,6 +2,7 @@ package tools.hashmethods;
import ch.jalu.injector.Injector; import ch.jalu.injector.Injector;
import ch.jalu.injector.InjectorBuilder; import ch.jalu.injector.InjectorBuilder;
import com.github.authme.configme.properties.Property;
import fr.xephi.authme.security.HashAlgorithm; import fr.xephi.authme.security.HashAlgorithm;
import fr.xephi.authme.security.crypts.EncryptionMethod; import fr.xephi.authme.security.crypts.EncryptionMethod;
import fr.xephi.authme.security.crypts.HexSaltedMethod; import fr.xephi.authme.security.crypts.HexSaltedMethod;
@ -9,7 +10,6 @@ import fr.xephi.authme.security.crypts.description.AsciiRestricted;
import fr.xephi.authme.security.crypts.description.HasSalt; import fr.xephi.authme.security.crypts.description.HasSalt;
import fr.xephi.authme.security.crypts.description.Recommendation; import fr.xephi.authme.security.crypts.description.Recommendation;
import fr.xephi.authme.settings.Settings; import fr.xephi.authme.settings.Settings;
import fr.xephi.authme.settings.domain.Property;
import org.mockito.invocation.InvocationOnMock; import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer; import org.mockito.stubbing.Answer;

View File

@ -1,29 +0,0 @@
# Test config file with some "difficult" values
test:
duration: 20.102
systemName: 'A ''test'' name'
sample:
ratio:
order: Fourth
fields:
- Australia\
- ' Burundi'''
- 'Colombia?
'''''
# The last element above represents "Colombia?\n''"
version: -1337
features:
boring:
# YAML allows both "yes"/"no" and "true"/"false" for expressing booleans
skip: no
colors:
- 'it''s a difficult string!'
- |
gray
with new lines
# dustLevel: 8 <-- missing property triggering rewrite
cool:
enabled: yes
options: []