diff --git a/src/main/java/fr/xephi/authme/AuthMe.java b/src/main/java/fr/xephi/authme/AuthMe.java index e10c815c5..58d791d5e 100644 --- a/src/main/java/fr/xephi/authme/AuthMe.java +++ b/src/main/java/fr/xephi/authme/AuthMe.java @@ -44,6 +44,7 @@ import fr.xephi.authme.security.PasswordSecurity; import fr.xephi.authme.security.crypts.SHA256; import fr.xephi.authme.settings.NewSetting; import fr.xephi.authme.settings.Settings; +import fr.xephi.authme.settings.SettingsMigrationService; import fr.xephi.authme.settings.SpawnLoader; import fr.xephi.authme.settings.properties.DatabaseSettings; import fr.xephi.authme.settings.properties.EmailSettings; @@ -52,6 +53,8 @@ import fr.xephi.authme.settings.properties.PluginSettings; import fr.xephi.authme.settings.properties.PurgeSettings; import fr.xephi.authme.settings.properties.RestrictionSettings; 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.CollectionUtils; import fr.xephi.authme.util.FileUtils; import fr.xephi.authme.util.GeoLiteAPI; @@ -464,8 +467,10 @@ public class AuthMe extends JavaPlugin { private NewSetting createNewSetting() { File configFile = new File(getDataFolder(), "config.yml"); + PropertyMap properties = SettingsFieldRetriever.getAllPropertyFields(); + SettingsMigrationService migrationService = new SettingsMigrationService(); return FileUtils.copyFileFromResource(configFile, "config.yml") - ? new NewSetting(configFile, getDataFolder()) + ? new NewSetting(configFile, getDataFolder(), properties, migrationService) : null; } @@ -674,7 +679,10 @@ public class AuthMe extends JavaPlugin { } String name = player.getName().toLowerCase(); if (PlayerCache.getInstance().isAuthenticated(name) && !player.isDead() && Settings.isSaveQuitLocationEnabled) { - final PlayerAuth auth = new PlayerAuth(player.getName().toLowerCase(), player.getLocation().getX(), player.getLocation().getY(), player.getLocation().getZ(), player.getWorld().getName(), player.getName()); + final PlayerAuth auth = PlayerAuth.builder() + .name(player.getName().toLowerCase()) + .realName(player.getName()) + .location(player.getLocation()).build(); database.updateQuitLoc(auth); } if (LimboCache.getInstance().hasLimboPlayer(name)) { diff --git a/src/main/java/fr/xephi/authme/cache/auth/PlayerAuth.java b/src/main/java/fr/xephi/authme/cache/auth/PlayerAuth.java index c8b4c6b5a..9bc57e20c 100644 --- a/src/main/java/fr/xephi/authme/cache/auth/PlayerAuth.java +++ b/src/main/java/fr/xephi/authme/cache/auth/PlayerAuth.java @@ -8,20 +8,24 @@ import static com.google.common.base.Preconditions.checkNotNull; /** + * AuthMe player data. */ public class PlayerAuth { + /** The player's name in lowercase, e.g. "xephi". */ private String nickname; + /** The player's name in the correct casing, e.g. "Xephi". */ + private String realName; private HashedPassword password; + private String email; private String ip; + private int groupId; private long lastLogin; + // Fields storing the player's quit location private double x; private double y; private double z; private String world; - private int groupId; - private String email; - private String realName; /** * @param serialized String @@ -31,80 +35,19 @@ public class PlayerAuth { } /** - * Constructor for PlayerAuth. + * Constructor. Instantiate objects with the {@link #builder() builder}. * - * @param nickname String - * @param x double - * @param y double - * @param z double - * @param world String - * @param realName String - */ - public PlayerAuth(String nickname, double x, double y, double z, String world, String realName) { - this(nickname, new HashedPassword(""), -1, "127.0.0.1", System.currentTimeMillis(), x, y, z, world, - "your@email.com", realName); - } - - /** - * Constructor for PlayerAuth. - * - * @param nickname String - * @param hash String - * @param ip String - * @param lastLogin long - * @param realName String - */ - public PlayerAuth(String nickname, String hash, String ip, long lastLogin, String realName) { - this(nickname, new HashedPassword(hash), -1, ip, lastLogin, 0, 0, 0, "world", "your@email.com", realName); - } - - /** - * Constructor for PlayerAuth. - * - * @param nickname String - * @param hash String - * @param ip String - * @param lastLogin long - * @param email String - * @param realName String - */ - public PlayerAuth(String nickname, String hash, String ip, long lastLogin, String email, String realName) { - this(nickname, new HashedPassword(hash), -1, ip, lastLogin, 0, 0, 0, "world", email, realName); - } - - /** - * Constructor for PlayerAuth. - * - * @param nickname String - * @param hash String - * @param ip String - * @param lastLogin long - * @param x double - * @param y double - * @param z double - * @param world String - * @param email String - * @param realName String - */ - public PlayerAuth(String nickname, String hash, String ip, long lastLogin, double x, double y, double z, - String world, String email, String realName) { - this(nickname, new HashedPassword(hash), -1, ip, lastLogin, x, y, z, world, email, realName); - } - - /** - * Constructor for PlayerAuth. - * - * @param nickname String - * @param password String - * @param groupId int - * @param ip String - * @param lastLogin long - * @param x double - * @param y double - * @param z double - * @param world String - * @param email String - * @param realName String + * @param nickname all lowercase name of the player + * @param password password + * @param groupId the group id + * @param ip the associated ip address + * @param lastLogin player's last login (timestamp) + * @param x quit location: x coordinate + * @param y quit location: y coordinate + * @param z quit location: z coordinate + * @param world quit location: world name + * @param email the associated email + * @param realName the player's name with proper casing */ private PlayerAuth(String nickname, HashedPassword password, int groupId, String ip, long lastLogin, double x, double y, double z, String world, String email, String realName) { @@ -121,24 +64,6 @@ public class PlayerAuth { this.realName = realName; } - /** - * Method set. - * - * @param auth PlayerAuth - */ - public void set(PlayerAuth auth) { - this.setEmail(auth.getEmail()); - this.setPassword(auth.getPassword()); - this.setIp(auth.getIp()); - this.setLastLogin(auth.getLastLogin()); - this.setNickname(auth.getNickname()); - this.setQuitLocX(auth.getQuitLocX()); - this.setQuitLocY(auth.getQuitLocY()); - this.setQuitLocZ(auth.getQuitLocZ()); - this.setWorld(auth.getWorld()); - this.setRealName(auth.getRealName()); - } - public void setNickname(String nickname) { this.nickname = nickname.toLowerCase(); diff --git a/src/main/java/fr/xephi/authme/converter/CrazyLoginConverter.java b/src/main/java/fr/xephi/authme/converter/CrazyLoginConverter.java index 8b4ee564d..c617ccad2 100644 --- a/src/main/java/fr/xephi/authme/converter/CrazyLoginConverter.java +++ b/src/main/java/fr/xephi/authme/converter/CrazyLoginConverter.java @@ -31,27 +31,13 @@ public class CrazyLoginConverter implements Converter { this.sender = sender; } - /** - * Method getInstance. - * - * @return CrazyLoginConverter - */ - public CrazyLoginConverter getInstance() { - return this; - } - - /** - * Method run. - * - * @see java.lang.Runnable#run() - */ @Override public void run() { String fileName = Settings.crazyloginFileName; try { File source = new File(AuthMe.getInstance().getDataFolder() + File.separator + fileName); if (!source.exists()) { - sender.sendMessage("Error while trying to import datas, please put " + fileName + " in AuthMe folder!"); + sender.sendMessage("Error while trying to import data, please put " + fileName + " in AuthMe folder!"); return; } String line; @@ -59,14 +45,17 @@ public class CrazyLoginConverter implements Converter { while ((line = users.readLine()) != null) { if (line.contains("|")) { String[] args = line.split("\\|"); - if (args.length < 2) + if (args.length < 2 || "name".equalsIgnoreCase(args[0])) { continue; - if (args[0].equalsIgnoreCase("name")) - continue; - String playerName = args[0].toLowerCase(); + } + String playerName = args[0]; String psw = args[1]; if (psw != null) { - PlayerAuth auth = new PlayerAuth(playerName, psw, "127.0.0.1", System.currentTimeMillis(), playerName); + PlayerAuth auth = PlayerAuth.builder() + .name(playerName.toLowerCase()) + .realName(playerName) + .password(psw, null) + .build(); database.saveAuth(auth); } } diff --git a/src/main/java/fr/xephi/authme/converter/RoyalAuthConverter.java b/src/main/java/fr/xephi/authme/converter/RoyalAuthConverter.java index 42bd681df..b79657e28 100644 --- a/src/main/java/fr/xephi/authme/converter/RoyalAuthConverter.java +++ b/src/main/java/fr/xephi/authme/converter/RoyalAuthConverter.java @@ -5,35 +5,46 @@ import fr.xephi.authme.ConsoleLogger; import fr.xephi.authme.cache.auth.PlayerAuth; import fr.xephi.authme.datasource.DataSource; import org.bukkit.OfflinePlayer; +import org.bukkit.configuration.file.FileConfiguration; +import org.bukkit.configuration.file.YamlConfiguration; import java.io.File; +import static fr.xephi.authme.util.StringUtils.makePath; + public class RoyalAuthConverter implements Converter { + private static final String LAST_LOGIN_PATH = "timestamps.quit"; + private static final String PASSWORD_PATH = "login.password"; private final AuthMe plugin; - private final DataSource data; + private final DataSource dataSource; public RoyalAuthConverter(AuthMe plugin) { this.plugin = plugin; - this.data = plugin.getDataSource(); + this.dataSource = plugin.getDataSource(); } @Override public void run() { - for (OfflinePlayer o : plugin.getServer().getOfflinePlayers()) { + for (OfflinePlayer player : plugin.getServer().getOfflinePlayers()) { try { - String name = o.getName().toLowerCase(); - String sp = File.separator; - File file = new File("." + sp + "plugins" + sp + "RoyalAuth" + sp + "userdata" + sp + name + ".yml"); - if (data.isAuthAvailable(name)) + String name = player.getName().toLowerCase(); + File file = new File(makePath(".", "plugins", "RoyalAuth", "userdata", name + ".yml")); + + if (dataSource.isAuthAvailable(name) || !file.exists()) { continue; - if (!file.exists()) - continue; - RoyalAuthYamlReader ra = new RoyalAuthYamlReader(file); - PlayerAuth auth = new PlayerAuth(name, ra.getHash(), "127.0.0.1", ra.getLastLogin(), "your@email.com", o.getName()); - data.saveAuth(auth); + } + FileConfiguration configuration = YamlConfiguration.loadConfiguration(file); + PlayerAuth auth = PlayerAuth.builder() + .name(name) + .password(configuration.getString(PASSWORD_PATH), null) + .lastLogin(configuration.getLong(LAST_LOGIN_PATH)) + .realName(player.getName()) + .build(); + + dataSource.saveAuth(auth); } catch (Exception e) { - ConsoleLogger.logException("Error while trying to import " + o.getName() + " RoyalAuth data", e); + ConsoleLogger.logException("Error while trying to import " + player.getName() + " RoyalAuth data", e); } } } diff --git a/src/main/java/fr/xephi/authme/converter/RoyalAuthYamlReader.java b/src/main/java/fr/xephi/authme/converter/RoyalAuthYamlReader.java deleted file mode 100644 index c0437cb45..000000000 --- a/src/main/java/fr/xephi/authme/converter/RoyalAuthYamlReader.java +++ /dev/null @@ -1,22 +0,0 @@ -package fr.xephi.authme.converter; - -import fr.xephi.authme.settings.CustomConfiguration; - -import java.io.File; - -class RoyalAuthYamlReader extends CustomConfiguration { - - public RoyalAuthYamlReader(File file) { - super(file); - load(); - save(); - } - - public long getLastLogin() { - return getLong("timestamps.quit"); - } - - public String getHash() { - return getString("login.password"); - } -} diff --git a/src/main/java/fr/xephi/authme/converter/vAuthFileReader.java b/src/main/java/fr/xephi/authme/converter/vAuthFileReader.java index bb25b04bd..4c5b3b4f2 100644 --- a/src/main/java/fr/xephi/authme/converter/vAuthFileReader.java +++ b/src/main/java/fr/xephi/authme/converter/vAuthFileReader.java @@ -12,6 +12,8 @@ import java.io.IOException; import java.util.Scanner; import java.util.UUID; +import static fr.xephi.authme.util.StringUtils.makePath; + class vAuthFileReader { private final AuthMe plugin; @@ -28,7 +30,7 @@ class vAuthFileReader { } public void convert() { - final File file = new File(plugin.getDataFolder().getParent() + File.separator + "vAuth" + File.separator + "passwords.yml"); + final File file = new File(plugin.getDataFolder().getParent(), makePath("vAuth", "passwords.yml")); Scanner scanner; try { scanner = new Scanner(file); @@ -46,9 +48,15 @@ class vAuthFileReader { } if (pname == null) continue; - auth = new PlayerAuth(pname.toLowerCase(), password, "127.0.0.1", System.currentTimeMillis(), "your@email.com", pname); + auth = PlayerAuth.builder() + .name(pname.toLowerCase()) + .realName(pname) + .password(password, null).build(); } else { - auth = new PlayerAuth(name.toLowerCase(), password, "127.0.0.1", System.currentTimeMillis(), "your@email.com", name); + auth = PlayerAuth.builder() + .name(name.toLowerCase()) + .realName(name) + .password(password, null).build(); } database.saveAuth(auth); } diff --git a/src/main/java/fr/xephi/authme/converter/xAuthToFlat.java b/src/main/java/fr/xephi/authme/converter/xAuthToFlat.java index 8d9bca702..34ec2a5ee 100644 --- a/src/main/java/fr/xephi/authme/converter/xAuthToFlat.java +++ b/src/main/java/fr/xephi/authme/converter/xAuthToFlat.java @@ -50,7 +50,10 @@ class xAuthToFlat { String pl = getIdPlayer(id); String psw = getPassword(id); if (psw != null && !psw.isEmpty() && pl != null) { - PlayerAuth auth = new PlayerAuth(pl, psw, "192.168.0.1", 0, "your@email.com", pl); + PlayerAuth auth = PlayerAuth.builder() + .name(pl.toLowerCase()) + .realName(pl) + .password(psw, null).build(); database.saveAuth(auth); } } @@ -69,7 +72,8 @@ class xAuthToFlat { PreparedStatement ps = null; ResultSet rs = null; try { - String sql = String.format("SELECT `playername` FROM `%s` WHERE `id` = ?", xAuth.getPlugin().getDatabaseController().getTable(DatabaseTables.ACCOUNT)); + String sql = String.format("SELECT `playername` FROM `%s` WHERE `id` = ?", + xAuth.getPlugin().getDatabaseController().getTable(DatabaseTables.ACCOUNT)); ps = conn.prepareStatement(sql); ps.setInt(1, id); rs = ps.executeQuery(); @@ -91,7 +95,8 @@ class xAuthToFlat { PreparedStatement ps = null; ResultSet rs = null; try { - String sql = String.format("SELECT * FROM `%s`", xAuth.getPlugin().getDatabaseController().getTable(DatabaseTables.ACCOUNT)); + String sql = String.format("SELECT * FROM `%s`", + xAuth.getPlugin().getDatabaseController().getTable(DatabaseTables.ACCOUNT)); ps = conn.prepareStatement(sql); rs = ps.executeQuery(); while (rs.next()) { @@ -112,7 +117,8 @@ class xAuthToFlat { PreparedStatement ps = null; ResultSet rs = null; try { - String sql = String.format("SELECT `password`, `pwtype` FROM `%s` WHERE `id` = ?", xAuth.getPlugin().getDatabaseController().getTable(DatabaseTables.ACCOUNT)); + String sql = String.format("SELECT `password`, `pwtype` FROM `%s` WHERE `id` = ?", + xAuth.getPlugin().getDatabaseController().getTable(DatabaseTables.ACCOUNT)); ps = conn.prepareStatement(sql); ps.setInt(1, accountId); rs = ps.executeQuery(); diff --git a/src/main/java/fr/xephi/authme/datasource/FlatFile.java b/src/main/java/fr/xephi/authme/datasource/FlatFile.java index 0765c7874..288012dae 100644 --- a/src/main/java/fr/xephi/authme/datasource/FlatFile.java +++ b/src/main/java/fr/xephi/authme/datasource/FlatFile.java @@ -10,6 +10,7 @@ import fr.xephi.authme.settings.Settings; import java.io.BufferedReader; import java.io.BufferedWriter; +import java.io.Closeable; import java.io.File; import java.io.FileNotFoundException; import java.io.FileReader; @@ -19,6 +20,8 @@ import java.util.ArrayList; import java.util.List; /** + * Deprecated flat file datasource. The only method guaranteed to work is {@link FlatFile#getAllAuths()} + * as to migrate the entries to {@link SQLite} when AuthMe starts. */ @Deprecated public class FlatFile implements DataSource { @@ -76,19 +79,11 @@ public class FlatFile implements DataSource { return true; } } - } catch (FileNotFoundException ex) { - ConsoleLogger.showError(ex.getMessage()); - return false; } catch (IOException ex) { ConsoleLogger.showError(ex.getMessage()); return false; } finally { - if (br != null) { - try { - br.close(); - } catch (IOException ignored) { - } - } + silentClose(br); } return false; } @@ -110,17 +105,12 @@ public class FlatFile implements DataSource { BufferedWriter bw = null; try { bw = new BufferedWriter(new FileWriter(source, true)); - bw.write(auth.getNickname() + ":" + auth.getPassword() + ":" + auth.getIp() + ":" + auth.getLastLogin() + ":" + auth.getQuitLocX() + ":" + auth.getQuitLocY() + ":" + auth.getQuitLocZ() + ":" + auth.getWorld() + ":" + auth.getEmail() + "\n"); + bw.write(auth.getNickname() + ":" + auth.getPassword().getHash() + ":" + auth.getIp() + ":" + auth.getLastLogin() + ":" + auth.getQuitLocX() + ":" + auth.getQuitLocY() + ":" + auth.getQuitLocZ() + ":" + auth.getWorld() + ":" + auth.getEmail() + "\n"); } catch (IOException ex) { ConsoleLogger.showError(ex.getMessage()); return false; } finally { - if (bw != null) { - try { - bw.close(); - } catch (IOException ignored) { - } - } + silentClose(bw); } return true; } @@ -131,6 +121,7 @@ public class FlatFile implements DataSource { } @Override + // Note ljacqu 20151230: This does not persist the salt; it is not supported in flat file. public boolean updatePassword(String user, HashedPassword password) { user = user.toLowerCase(); if (!isAuthAvailable(user)) { @@ -144,45 +135,18 @@ public class FlatFile implements DataSource { while ((line = br.readLine()) != null) { String[] args = line.split(":"); if (args[0].equals(user)) { - // Note ljacqu 20151230: This does not persist the salt; it is not supported in flat file. - switch (args.length) { - case 4: { - newAuth = new PlayerAuth(args[0], password.getHash(), args[2], Long.parseLong(args[3]), 0, 0, 0, "world", "your@email.com", args[0]); - break; - } - case 7: { - newAuth = new PlayerAuth(args[0], password.getHash(), args[2], Long.parseLong(args[3]), Double.parseDouble(args[4]), Double.parseDouble(args[5]), Double.parseDouble(args[6]), "world", "your@email.com", args[0]); - break; - } - case 8: { - newAuth = new PlayerAuth(args[0], password.getHash(), args[2], Long.parseLong(args[3]), Double.parseDouble(args[4]), Double.parseDouble(args[5]), Double.parseDouble(args[6]), args[7], "your@email.com", args[0]); - break; - } - case 9: { - newAuth = new PlayerAuth(args[0], password.getHash(), args[2], Long.parseLong(args[3]), Double.parseDouble(args[4]), Double.parseDouble(args[5]), Double.parseDouble(args[6]), args[7], args[8], args[0]); - break; - } - default: { - newAuth = new PlayerAuth(args[0], password.getHash(), args[2], 0, 0, 0, 0, "world", "your@email.com", args[0]); - break; - } + newAuth = buildAuthFromArray(args); + if (newAuth != null) { + newAuth.setPassword(password); } break; } } - } catch (FileNotFoundException ex) { - ConsoleLogger.showError(ex.getMessage()); - return false; } catch (IOException ex) { ConsoleLogger.showError(ex.getMessage()); return false; } finally { - if (br != null) { - try { - br.close(); - } catch (IOException ignored) { - } - } + silentClose(br); } if (newAuth != null) { removeAuth(user); @@ -204,44 +168,19 @@ public class FlatFile implements DataSource { while ((line = br.readLine()) != null) { String[] args = line.split(":"); if (args[0].equalsIgnoreCase(auth.getNickname())) { - switch (args.length) { - case 4: { - newAuth = new PlayerAuth(args[0], args[1], auth.getIp(), auth.getLastLogin(), 0, 0, 0, "world", "your@email.com", args[0]); - break; - } - case 7: { - newAuth = new PlayerAuth(args[0], args[1], auth.getIp(), auth.getLastLogin(), Double.parseDouble(args[4]), Double.parseDouble(args[5]), Double.parseDouble(args[6]), "world", "your@email.com", args[0]); - break; - } - case 8: { - newAuth = new PlayerAuth(args[0], args[1], auth.getIp(), auth.getLastLogin(), Double.parseDouble(args[4]), Double.parseDouble(args[5]), Double.parseDouble(args[6]), args[7], "your@email.com", args[0]); - break; - } - case 9: { - newAuth = new PlayerAuth(args[0], args[1], auth.getIp(), auth.getLastLogin(), Double.parseDouble(args[4]), Double.parseDouble(args[5]), Double.parseDouble(args[6]), args[7], args[8], args[0]); - break; - } - default: { - newAuth = new PlayerAuth(args[0], args[1], auth.getIp(), auth.getLastLogin(), 0, 0, 0, "world", "your@email.com", args[0]); - break; - } + newAuth = buildAuthFromArray(args); + if (newAuth != null) { + newAuth.setLastLogin(auth.getLastLogin()); + newAuth.setIp(auth.getIp()); } break; } } - } catch (FileNotFoundException ex) { - ConsoleLogger.showError(ex.getMessage()); - return false; } catch (IOException ex) { ConsoleLogger.showError(ex.getMessage()); return false; } finally { - if (br != null) { - try { - br.close(); - } catch (IOException ignored) { - } - } + silentClose(br); } if (newAuth != null) { removeAuth(auth.getNickname()); @@ -263,23 +202,22 @@ public class FlatFile implements DataSource { while ((line = br.readLine()) != null) { String[] args = line.split(":"); if (args[0].equalsIgnoreCase(auth.getNickname())) { - newAuth = new PlayerAuth(args[0], args[1], args[2], Long.parseLong(args[3]), auth.getQuitLocX(), auth.getQuitLocY(), auth.getQuitLocZ(), auth.getWorld(), auth.getEmail(), args[0]); + newAuth = buildAuthFromArray(args); + if (newAuth != null) { + newAuth.setQuitLocX(auth.getQuitLocX()); + newAuth.setQuitLocY(auth.getQuitLocY()); + newAuth.setQuitLocZ(auth.getQuitLocZ()); + newAuth.setWorld(auth.getWorld()); + newAuth.setEmail(auth.getEmail()); + } break; } } - } catch (FileNotFoundException ex) { - ConsoleLogger.showError(ex.getMessage()); - return false; } catch (IOException ex) { ConsoleLogger.showError(ex.getMessage()); return false; } finally { - if (br != null) { - try { - br.close(); - } catch (IOException ignored) { - } - } + silentClose(br); } if (newAuth != null) { removeAuth(auth.getNickname()); @@ -311,25 +249,12 @@ public class FlatFile implements DataSource { for (String l : lines) { bw.write(l + "\n"); } - } catch (FileNotFoundException ex) { - ConsoleLogger.showError(ex.getMessage()); - return cleared; } catch (IOException ex) { ConsoleLogger.showError(ex.getMessage()); return cleared; } finally { - if (br != null) { - try { - br.close(); - } catch (IOException ignored) { - } - } - if (bw != null) { - try { - bw.close(); - } catch (IOException ignored) { - } - } + silentClose(br); + silentClose(bw); } return cleared; } @@ -355,25 +280,12 @@ public class FlatFile implements DataSource { for (String l : lines) { bw.write(l + "\n"); } - } catch (FileNotFoundException ex) { - ConsoleLogger.showError(ex.getMessage()); - return false; } catch (IOException ex) { ConsoleLogger.showError(ex.getMessage()); return false; } finally { - if (br != null) { - try { - br.close(); - } catch (IOException ignored) { - } - } - if (bw != null) { - try { - bw.close(); - } catch (IOException ignored) { - } - } + silentClose(br); + silentClose(bw); } return true; } @@ -387,35 +299,14 @@ public class FlatFile implements DataSource { while ((line = br.readLine()) != null) { String[] args = line.split(":"); if (args[0].equalsIgnoreCase(user)) { - switch (args.length) { - case 2: - return new PlayerAuth(args[0], args[1], "192.168.0.1", 0, "your@email.com", args[0]); - case 3: - return new PlayerAuth(args[0], args[1], args[2], 0, "your@email.com", args[0]); - case 4: - return new PlayerAuth(args[0], args[1], args[2], Long.parseLong(args[3]), "your@email.com", args[0]); - case 7: - return new PlayerAuth(args[0], args[1], args[2], Long.parseLong(args[3]), Double.parseDouble(args[4]), Double.parseDouble(args[5]), Double.parseDouble(args[6]), "unavailableworld", "your@email.com", args[0]); - case 8: - return new PlayerAuth(args[0], args[1], args[2], Long.parseLong(args[3]), Double.parseDouble(args[4]), Double.parseDouble(args[5]), Double.parseDouble(args[6]), args[7], "your@email.com", args[0]); - case 9: - return new PlayerAuth(args[0], args[1], args[2], Long.parseLong(args[3]), Double.parseDouble(args[4]), Double.parseDouble(args[5]), Double.parseDouble(args[6]), args[7], args[8], args[0]); - } + return buildAuthFromArray(args); } } - } catch (FileNotFoundException ex) { - ConsoleLogger.showError(ex.getMessage()); - return null; } catch (IOException ex) { ConsoleLogger.showError(ex.getMessage()); return null; } finally { - if (br != null) { - try { - br.close(); - } catch (IOException ignored) { - } - } + silentClose(br); } return null; } @@ -437,7 +328,10 @@ public class FlatFile implements DataSource { while ((line = br.readLine()) != null) { String[] args = line.split(":"); if (args[0].equals(auth.getNickname())) { - newAuth = new PlayerAuth(args[0], args[1], args[2], Long.parseLong(args[3]), Double.parseDouble(args[4]), Double.parseDouble(args[5]), Double.parseDouble(args[6]), args[7], auth.getEmail(), args[0]); + newAuth = buildAuthFromArray(args); + if (newAuth != null) { + newAuth.setEmail(auth.getEmail()); + } break; } } @@ -545,18 +439,8 @@ public class FlatFile implements DataSource { ConsoleLogger.showError(ex.getMessage()); } finally { - if (br != null) { - try { - br.close(); - } catch (IOException ignored) { - } - } - if (bw != null) { - try { - bw.close(); - } catch (IOException ignored) { - } - } + silentClose(br); + silentClose(bw); } } @@ -595,12 +479,7 @@ public class FlatFile implements DataSource { ConsoleLogger.showError(ex.getMessage()); return result; } finally { - if (br != null) { - try { - br.close(); - } catch (IOException ignored) { - } - } + silentClose(br); } return result; } @@ -624,32 +503,15 @@ public class FlatFile implements DataSource { String line; while ((line = br.readLine()) != null) { String[] args = line.split(":"); - // We expect to encounter 2, 3, 4, 7, 8 or 9 fields. Ignore the line otherwise - if (args.length >= 2 && args.length != 5 && args.length != 6 && args.length <= 9) { - PlayerAuth.Builder builder = PlayerAuth.builder() - .name(args[0]).realName(args[0]) - .password(args[1], null); - if (args.length >= 3) builder.ip(args[2]); - if (args.length >= 4) builder.lastLogin(Long.parseLong(args[3])); - if (args.length >= 7) { - builder.locX(Double.parseDouble(args[4])) - .locY(Double.parseDouble(args[5])) - .locZ(Double.parseDouble(args[6])); - } - if (args.length >= 8) builder.locWorld(args[7]); - if (args.length >= 9) builder.email(args[8]); - auths.add(builder.build()); + PlayerAuth auth = buildAuthFromArray(args); + if (auth != null) { + auths.add(auth); } } } catch (IOException ex) { ConsoleLogger.logException("Error while getting auths from flatfile:", ex); } finally { - if (br != null) { - try { - br.close(); - } catch (IOException ignored) { - } - } + silentClose(br); } return auths; } @@ -663,4 +525,34 @@ public class FlatFile implements DataSource { public boolean isEmailStored(String email) { throw new UnsupportedOperationException("Flat file no longer supported"); } + + private static PlayerAuth buildAuthFromArray(String[] args) { + // Format allows 2, 3, 4, 7, 8, 9 fields. Anything else is unknown + if (args.length >= 2 && args.length <= 9 && args.length != 5 && args.length != 6) { + PlayerAuth.Builder builder = PlayerAuth.builder() + .name(args[0]).realName(args[0]).password(args[1], null); + + if (args.length >= 3) builder.ip(args[2]); + if (args.length >= 4) builder.lastLogin(Long.parseLong(args[3])); + if (args.length >= 7) { + builder.locX(Double.parseDouble(args[4])) + .locY(Double.parseDouble(args[5])) + .locZ(Double.parseDouble(args[6])); + } + if (args.length >= 8) builder.locWorld(args[7]); + if (args.length >= 9) builder.email(args[8]); + return builder.build(); + } + return null; + } + + private static void silentClose(Closeable closeable) { + if (closeable != null) { + try { + closeable.close(); + } catch (IOException ignored) { + // silent close + } + } + } } diff --git a/src/main/java/fr/xephi/authme/settings/CustomConfiguration.java b/src/main/java/fr/xephi/authme/settings/CustomConfiguration.java deleted file mode 100644 index 3defffe44..000000000 --- a/src/main/java/fr/xephi/authme/settings/CustomConfiguration.java +++ /dev/null @@ -1,94 +0,0 @@ -package fr.xephi.authme.settings; - -import fr.xephi.authme.AuthMe; -import fr.xephi.authme.ConsoleLogger; -import org.bukkit.configuration.InvalidConfigurationException; -import org.bukkit.configuration.file.YamlConfiguration; - -import java.io.File; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.io.InputStream; -import java.nio.file.Files; -import java.nio.file.StandardCopyOption; - -/** - */ -public abstract class CustomConfiguration extends YamlConfiguration { - - private final File configFile; - - /** - * Constructor for CustomConfiguration. - * - * @param file the config file - */ - public CustomConfiguration(File file) { - this.configFile = file; - load(); - } - - public void load() { - try { - super.load(configFile); - } catch (FileNotFoundException e) { - ConsoleLogger.showError("Could not find " + configFile.getName() + ", creating new one..."); - reLoad(); - } catch (IOException e) { - ConsoleLogger.showError("Could not load " + configFile.getName()); - } catch (InvalidConfigurationException e) { - ConsoleLogger.showError(configFile.getName() + " is no valid configuration file"); - } - } - - public boolean reLoad() { - boolean out = true; - if (!configFile.exists()) { - out = loadResource(configFile); - } - if (out) - load(); - return out; - } - - public void save() { - try { - super.save(configFile); - } catch (IOException ex) { - ConsoleLogger.showError("Could not save config to " + configFile.getName()); - } - } - - public File getConfigFile() { - return configFile; - } - - private boolean loadResource(File file) { - if (!file.exists()) { - try { - if (!file.getParentFile().exists() && !file.getParentFile().mkdirs()) { - return false; - } - int i = file.getPath().indexOf("AuthMe"); - if (i > -1) { - String path = file.getPath().substring(i + 6).replace('\\', '/'); - InputStream is = AuthMe.class.getResourceAsStream(path); - Files.copy(is, file.toPath(), StandardCopyOption.REPLACE_EXISTING); - return true; - } - } catch (Exception e) { - ConsoleLogger.logException("Failed to load config from JAR", e); - } - } - return false; - } - - public boolean containsAll(String... paths) { - for (String path : paths) { - if (!contains(path)) { - return false; - } - } - return true; - } -} diff --git a/src/main/java/fr/xephi/authme/settings/NewSetting.java b/src/main/java/fr/xephi/authme/settings/NewSetting.java index 00a597249..89717a8c6 100644 --- a/src/main/java/fr/xephi/authme/settings/NewSetting.java +++ b/src/main/java/fr/xephi/authme/settings/NewSetting.java @@ -6,7 +6,6 @@ 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.RegistrationSettings; -import fr.xephi.authme.settings.properties.SettingsFieldRetriever; import fr.xephi.authme.settings.propertymap.PropertyMap; import fr.xephi.authme.util.CollectionUtils; import fr.xephi.authme.util.StringUtils; @@ -33,6 +32,8 @@ public class NewSetting { 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}. */ private File messagesFile; @@ -44,11 +45,16 @@ public class NewSetting { * * @param configFile The configuration file * @param pluginFolder The AuthMe plugin folder + * @param propertyMap Collection of all available settings + * @param migrationService Migration service to check the settings file with */ - public NewSetting(File configFile, File pluginFolder) { + public NewSetting(File configFile, File pluginFolder, PropertyMap propertyMap, + SettingsMigrationService migrationService) { this.configuration = YamlConfiguration.loadConfiguration(configFile); this.configFile = configFile; this.pluginFolder = pluginFolder; + this.propertyMap = propertyMap; + this.migrationService = migrationService; validateAndLoadOptions(); } @@ -57,16 +63,21 @@ public class NewSetting { * * @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 - NewSetting(FileConfiguration configuration, File configFile, PropertyMap propertyMap) { + NewSetting(FileConfiguration configuration, File configFile, File pluginFolder, PropertyMap propertyMap, + SettingsMigrationService migrationService) { this.configuration = configuration; this.configFile = configFile; - this.pluginFolder = new File(""); + this.pluginFolder = pluginFolder; + this.propertyMap = propertyMap; + this.migrationService = migrationService; - if (propertyMap != null && SettingsMigrationService.checkAndMigrate(configuration, propertyMap, pluginFolder)) { - save(propertyMap); + if (propertyMap != null && migrationService != null) { + validateAndLoadOptions(); } } @@ -92,13 +103,6 @@ public class NewSetting { configuration.set(property.getPath(), value); } - /** - * Save the config file. Use after migrating one or more settings. - */ - public void save() { - save(SettingsFieldRetriever.getAllPropertyFields()); - } - /** * Return the messages file based on the messages language config. * @@ -133,7 +137,10 @@ public class NewSetting { validateAndLoadOptions(); } - private void save(PropertyMap propertyMap) { + /** + * 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); @@ -186,11 +193,10 @@ public class NewSetting { } private void validateAndLoadOptions() { - PropertyMap propertyMap = SettingsFieldRetriever.getAllPropertyFields(); - if (SettingsMigrationService.checkAndMigrate(configuration, propertyMap, pluginFolder)) { + if (migrationService.checkAndMigrate(configuration, propertyMap, pluginFolder)) { ConsoleLogger.info("Merged new config options"); ConsoleLogger.info("Please check your config.yml file for new settings!"); - save(propertyMap); + save(); } messagesFile = buildMessagesFile(); @@ -205,18 +211,20 @@ public class NewSetting { private File buildMessagesFile() { String languageCode = getProperty(PluginSettings.MESSAGES_LANGUAGE); - File messagesFile = buildMessagesFileFromCode(languageCode); - if (messagesFile.exists()) { + + String filePath = buildMessagesFilePathFromCode(languageCode); + File messagesFile = new File(pluginFolder, filePath); + if (copyFileFromResource(messagesFile, filePath)) { return messagesFile; } - return copyFileFromResource(messagesFile, buildMessagesFilePathFromCode(languageCode)) - ? messagesFile - : buildMessagesFileFromCode("en"); - } + // File doesn't exist or couldn't be copied - try again with default, "en" + String defaultFilePath = buildMessagesFilePathFromCode("en"); + File defaultFile = new File(pluginFolder, defaultFilePath); + copyFileFromResource(defaultFile, defaultFilePath); - private File buildMessagesFileFromCode(String language) { - return new File(pluginFolder, buildMessagesFilePathFromCode(language)); + // No matter the result, need to return a file + return defaultFile; } private static String buildMessagesFilePathFromCode(String language) { @@ -243,7 +251,7 @@ public class NewSetting { final Charset charset = Charset.forName("UTF-8"); if (copyFileFromResource(emailFile, "email.html")) { try { - return StringUtils.join("", Files.readLines(emailFile, charset)); + return StringUtils.join("\n", Files.readLines(emailFile, charset)); } catch (IOException e) { ConsoleLogger.logException("Failed to read file '" + emailFile.getPath() + "':", e); } diff --git a/src/main/java/fr/xephi/authme/settings/SettingsMigrationService.java b/src/main/java/fr/xephi/authme/settings/SettingsMigrationService.java index 72806a8ba..a3d70dd16 100644 --- a/src/main/java/fr/xephi/authme/settings/SettingsMigrationService.java +++ b/src/main/java/fr/xephi/authme/settings/SettingsMigrationService.java @@ -1,6 +1,5 @@ package fr.xephi.authme.settings; -import com.google.common.annotations.VisibleForTesting; import fr.xephi.authme.ConsoleLogger; import fr.xephi.authme.settings.domain.Property; import fr.xephi.authme.settings.propertymap.PropertyMap; @@ -18,25 +17,23 @@ import static fr.xephi.authme.settings.properties.RestrictionSettings.ALLOWED_NI /** * Service for verifying that the configuration is up-to-date. */ -public final class SettingsMigrationService { - - private SettingsMigrationService() { - } +public class SettingsMigrationService { /** - * Checks the config file and does any necessary migrations. + * Checks the config file and performs any necessary migrations. * * @param configuration The file configuration to check and migrate * @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 static boolean checkAndMigrate(FileConfiguration configuration, PropertyMap propertyMap, File pluginFolder) { - return performMigrations(configuration, pluginFolder) || hasDeprecatedProperties(configuration) + public boolean checkAndMigrate(FileConfiguration configuration, PropertyMap propertyMap, File pluginFolder) { + return performMigrations(configuration, pluginFolder) + || hasDeprecatedProperties(configuration) || !containsAllSettings(configuration, propertyMap); } - private static boolean performMigrations(FileConfiguration configuration, File pluginFolder) { + private boolean performMigrations(FileConfiguration configuration, File pluginFolder) { boolean changes = false; if ("[a-zA-Z0-9_?]*".equals(configuration.getString(ALLOWED_NICKNAME_CHARACTERS.getPath()))) { configuration.set(ALLOWED_NICKNAME_CHARACTERS.getPath(), "[a-zA-Z0-9_]*"); @@ -50,8 +47,7 @@ public final class SettingsMigrationService { | migrateJoinLeaveMessages(configuration); } - @VisibleForTesting - static boolean containsAllSettings(FileConfiguration configuration, PropertyMap propertyMap) { + public boolean containsAllSettings(FileConfiguration configuration, PropertyMap propertyMap) { for (Property property : propertyMap.keySet()) { if (!property.isPresent(configuration)) { return false; @@ -80,16 +76,16 @@ public final class SettingsMigrationService { * 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 dataFolder The plugin data folder + * @param pluginFolder The plugin data folder * @return True if a migration has been completed, false otherwise */ - private static boolean performMailTextToFileMigration(FileConfiguration configuration, File dataFolder) { + private static boolean performMailTextToFileMigration(FileConfiguration configuration, File pluginFolder) { final String oldSettingPath = "Email.mailText"; if (!configuration.contains(oldSettingPath)) { return false; } - final File emailFile = new File(dataFolder, "email.html"); + final File emailFile = new File(pluginFolder, "email.html"); final String mailText = configuration.getString(oldSettingPath) .replace("", "") .replace("", "") diff --git a/src/main/java/fr/xephi/authme/settings/domain/Comment.java b/src/main/java/fr/xephi/authme/settings/domain/Comment.java index 07f20e257..0e69ec97a 100644 --- a/src/main/java/fr/xephi/authme/settings/domain/Comment.java +++ b/src/main/java/fr/xephi/authme/settings/domain/Comment.java @@ -12,6 +12,6 @@ import java.lang.annotation.Target; @Target(ElementType.FIELD) public @interface Comment { - String[] value(); + String[] value(); } diff --git a/src/main/java/fr/xephi/authme/settings/domain/EnumProperty.java b/src/main/java/fr/xephi/authme/settings/domain/EnumProperty.java new file mode 100644 index 000000000..14d09329f --- /dev/null +++ b/src/main/java/fr/xephi/authme/settings/domain/EnumProperty.java @@ -0,0 +1,49 @@ +package fr.xephi.authme.settings.domain; + +import org.bukkit.configuration.file.FileConfiguration; +import org.yaml.snakeyaml.Yaml; + +/** + * Enum property. + * + * @param The enum class + */ +class EnumProperty> extends Property { + + private Class clazz; + + public EnumProperty(Class 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 ? mappedValue : getDefaultValue(); + } + + @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; + } +} diff --git a/src/main/java/fr/xephi/authme/settings/domain/EnumPropertyType.java b/src/main/java/fr/xephi/authme/settings/domain/EnumPropertyType.java deleted file mode 100644 index d38d0649d..000000000 --- a/src/main/java/fr/xephi/authme/settings/domain/EnumPropertyType.java +++ /dev/null @@ -1,48 +0,0 @@ -package fr.xephi.authme.settings.domain; - -import org.bukkit.configuration.file.FileConfiguration; -import org.yaml.snakeyaml.Yaml; - -/** - * Enum property type. - * - * @param The enum class - */ -class EnumPropertyType> extends PropertyType { - - private Class clazz; - - public EnumPropertyType(Class clazz) { - this.clazz = clazz; - } - - @Override - public E getFromFile(Property property, FileConfiguration configuration) { - String textValue = configuration.getString(property.getPath()); - if (textValue == null) { - return property.getDefaultValue(); - } - E mappedValue = mapToEnum(textValue); - return mappedValue != null ? mappedValue : property.getDefaultValue(); - } - - @Override - public boolean contains(Property property, FileConfiguration configuration) { - return super.contains(property, configuration) - && mapToEnum(configuration.getString(property.getPath())) != null; - } - - @Override - public String toYaml(E value, Yaml simpleYaml, Yaml singleQuoteYaml) { - return singleQuoteYaml.dump(value.name()); - } - - private E mapToEnum(String value) { - for (E entry : clazz.getEnumConstants()) { - if (entry.name().equalsIgnoreCase(value)) { - return entry; - } - } - return null; - } -} diff --git a/src/main/java/fr/xephi/authme/settings/domain/Property.java b/src/main/java/fr/xephi/authme/settings/domain/Property.java index f9637a7be..1d5100aff 100644 --- a/src/main/java/fr/xephi/authme/settings/domain/Property.java +++ b/src/main/java/fr/xephi/authme/settings/domain/Property.java @@ -10,45 +10,27 @@ import java.util.Objects; /** * Property class, representing a setting that is read from the config.yml file. */ -public class Property { +public abstract class Property { - private final PropertyType type; private final String path; private final T defaultValue; - private Property(PropertyType type, String path, T defaultValue) { + protected Property(String path, T defaultValue) { Objects.requireNonNull(defaultValue); - this.type = type; this.path = path; this.defaultValue = defaultValue; } /** - * Create a new property. See also {@link #newProperty(PropertyType, String, Object[])} for lists and - * {@link #newProperty(Class, String, Enum)}. + * Create a new string list property. * - * @param type The property type * @param path The property's path - * @param defaultValue The default value - * @param The type of the property - * @return The created property - */ - public static Property newProperty(PropertyType type, String path, T defaultValue) { - return new Property<>(type, path, defaultValue); - } - - /** - * Create a new list property. - * - * @param type The list type of the property - * @param path The property's path - * @param defaultValues The default value's items - * @param The list type + * @param defaultValues The items in the default list * @return The created list property */ - @SafeVarargs - public static Property> newProperty(PropertyType> type, String path, U... defaultValues) { - return new Property<>(type, path, Arrays.asList(defaultValues)); + public static Property> newListProperty(String path, String... defaultValues) { + // does not have the same name as not to clash with #newProperty(String, String) + return new StringListProperty(path, defaultValues); } /** @@ -61,36 +43,28 @@ public class Property { * @return The created enum property */ public static > Property newProperty(Class clazz, String path, E defaultValue) { - return new Property<>(new EnumPropertyType<>(clazz), path, defaultValue); + return new EnumProperty<>(clazz, path, defaultValue); } - // ----- - // Overloaded convenience methods for specific types - // ----- public static Property newProperty(String path, boolean defaultValue) { - return new Property<>(PropertyType.BOOLEAN, path, defaultValue); + return new BooleanProperty(path, defaultValue); } public static Property newProperty(String path, int defaultValue) { - return new Property<>(PropertyType.INTEGER, path, defaultValue); + return new IntegerProperty(path, defaultValue); } public static Property newProperty(String path, String defaultValue) { - return new Property<>(PropertyType.STRING, path, defaultValue); + return new StringProperty(path, defaultValue); } - // ----- - // Hooks to the PropertyType methods - // ----- /** * Get the property value from the given configuration – guaranteed to never return null. * * @param configuration The configuration to read the value from * @return The value, or default if not present */ - public T getFromFile(FileConfiguration configuration) { - return type.getFromFile(this, configuration); - } + public abstract T getFromFile(FileConfiguration configuration); /** * Return whether or not the given configuration file contains the property. @@ -99,7 +73,7 @@ public class Property { * @return True if the property is present, false otherwise */ public boolean isPresent(FileConfiguration configuration) { - return type.contains(this, configuration); + return configuration.contains(path); } /** @@ -111,12 +85,9 @@ public class Property { * @return The generated YAML */ public String toYaml(FileConfiguration configuration, Yaml simpleYaml, Yaml singleQuoteYaml) { - return type.toYaml(getFromFile(configuration), simpleYaml, singleQuoteYaml); + return simpleYaml.dump(getFromFile(configuration)); } - // ----- - // Trivial getters - // ----- /** * Return the default value of the property. * @@ -140,4 +111,89 @@ public class Property { return "Property '" + path + "'"; } + + /** + * Boolean property. + */ + private static final class BooleanProperty extends Property { + + 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 { + + 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 { + + 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 final class StringListProperty extends Property> { + + public StringListProperty(String path, String[] defaultValues) { + super(path, Arrays.asList(defaultValues)); + } + + @Override + public List 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 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; + } + } + } diff --git a/src/main/java/fr/xephi/authme/settings/domain/PropertyType.java b/src/main/java/fr/xephi/authme/settings/domain/PropertyType.java deleted file mode 100644 index 28a505cfb..000000000 --- a/src/main/java/fr/xephi/authme/settings/domain/PropertyType.java +++ /dev/null @@ -1,116 +0,0 @@ -package fr.xephi.authme.settings.domain; - -import org.bukkit.configuration.file.FileConfiguration; -import org.yaml.snakeyaml.Yaml; - -import java.util.List; - -/** - * Handles a certain property type and provides type-specific functionality. - * - * @param The value of the property - * @see Property - */ -public abstract class PropertyType { - - public static final PropertyType BOOLEAN = new BooleanProperty(); - public static final PropertyType INTEGER = new IntegerProperty(); - public static final PropertyType STRING = new StringProperty(); - public static final PropertyType> STRING_LIST = new StringListProperty(); - - /** - * Get the property's value from the given YAML configuration. - * - * @param property The property to retrieve - * @param configuration The YAML configuration to read from - * @return The read value, or the default value if absent - */ - public abstract T getFromFile(Property property, FileConfiguration configuration); - - /** - * Return whether the property is present in the given configuration. - * - * @param property The property to search for - * @param configuration The configuration to verify - * @return True if the property is present, false otherwise - */ - public boolean contains(Property property, FileConfiguration configuration) { - return configuration.contains(property.getPath()); - } - - /** - * Format the value as YAML. - * - * @param value The value to export - * @param simpleYaml YAML object (default) - * @param singleQuoteYaml YAML object set to use single quotes - * @return The generated YAML - */ - public String toYaml(T value, Yaml simpleYaml, Yaml singleQuoteYaml) { - return simpleYaml.dump(value); - } - - - /** - * Boolean property. - */ - private static final class BooleanProperty extends PropertyType { - @Override - public Boolean getFromFile(Property property, FileConfiguration configuration) { - return configuration.getBoolean(property.getPath(), property.getDefaultValue()); - } - } - - /** - * Integer property. - */ - private static final class IntegerProperty extends PropertyType { - @Override - public Integer getFromFile(Property property, FileConfiguration configuration) { - return configuration.getInt(property.getPath(), property.getDefaultValue()); - } - } - - /** - * String property. - */ - private static final class StringProperty extends PropertyType { - @Override - public String getFromFile(Property property, FileConfiguration configuration) { - return configuration.getString(property.getPath(), property.getDefaultValue()); - } - @Override - public String toYaml(String value, Yaml simpleYaml, Yaml singleQuoteYaml) { - return singleQuoteYaml.dump(value); - } - } - - /** - * String list property. - */ - private static final class StringListProperty extends PropertyType> { - @Override - public List getFromFile(Property> property, FileConfiguration configuration) { - if (!configuration.isList(property.getPath())) { - return property.getDefaultValue(); - } - return configuration.getStringList(property.getPath()); - } - - @Override - public boolean contains(Property> property, FileConfiguration configuration) { - return configuration.contains(property.getPath()) && configuration.isList(property.getPath()); - } - - @Override - public String toYaml(List value, Yaml simpleYaml, Yaml singleQuoteYaml) { - 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; - } - } - -} diff --git a/src/main/java/fr/xephi/authme/settings/properties/ConverterSettings.java b/src/main/java/fr/xephi/authme/settings/properties/ConverterSettings.java index 9dc4ad60a..35e9cef96 100644 --- a/src/main/java/fr/xephi/authme/settings/properties/ConverterSettings.java +++ b/src/main/java/fr/xephi/authme/settings/properties/ConverterSettings.java @@ -5,26 +5,24 @@ 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 fr.xephi.authme.settings.domain.PropertyType.BOOLEAN; -import static fr.xephi.authme.settings.domain.PropertyType.STRING; public class ConverterSettings implements SettingsClass { @Comment("Rakamak file name") public static final Property RAKAMAK_FILE_NAME = - newProperty(STRING, "Converter.Rakamak.fileName", "users.rak"); + newProperty("Converter.Rakamak.fileName", "users.rak"); @Comment("Rakamak use IP?") public static final Property RAKAMAK_USE_IP = - newProperty(BOOLEAN, "Converter.Rakamak.useIP", false); + newProperty("Converter.Rakamak.useIP", false); @Comment("Rakamak IP file name") public static final Property RAKAMAK_IP_FILE_NAME = - newProperty(STRING, "Converter.Rakamak.ipFileName", "UsersIp.rak"); + newProperty("Converter.Rakamak.ipFileName", "UsersIp.rak"); @Comment("CrazyLogin database file name") public static final Property CRAZYLOGIN_FILE_NAME = - newProperty(STRING, "Converter.CrazyLogin.fileName", "accounts.db"); + newProperty("Converter.CrazyLogin.fileName", "accounts.db"); private ConverterSettings() { } diff --git a/src/main/java/fr/xephi/authme/settings/properties/EmailSettings.java b/src/main/java/fr/xephi/authme/settings/properties/EmailSettings.java index 7b7ee3cef..c6b6959d7 100644 --- a/src/main/java/fr/xephi/authme/settings/properties/EmailSettings.java +++ b/src/main/java/fr/xephi/authme/settings/properties/EmailSettings.java @@ -6,29 +6,26 @@ import fr.xephi.authme.settings.domain.SettingsClass; import java.util.List; +import static fr.xephi.authme.settings.domain.Property.newListProperty; import static fr.xephi.authme.settings.domain.Property.newProperty; -import static fr.xephi.authme.settings.domain.PropertyType.BOOLEAN; -import static fr.xephi.authme.settings.domain.PropertyType.INTEGER; -import static fr.xephi.authme.settings.domain.PropertyType.STRING; -import static fr.xephi.authme.settings.domain.PropertyType.STRING_LIST; public class EmailSettings implements SettingsClass { @Comment("Email SMTP server host") public static final Property SMTP_HOST = - newProperty(STRING, "Email.mailSMTP", "smtp.gmail.com"); + newProperty("Email.mailSMTP", "smtp.gmail.com"); @Comment("Email SMTP server port") public static final Property SMTP_PORT = - newProperty(INTEGER, "Email.mailPort", 465); + newProperty("Email.mailPort", 465); @Comment("Email account which sends the mails") public static final Property MAIL_ACCOUNT = - newProperty(STRING, "Email.mailAccount", ""); + newProperty("Email.mailAccount", ""); @Comment("Email account password") public static final Property MAIL_PASSWORD = - newProperty(STRING, "Email.mailPassword", ""); + newProperty("Email.mailPassword", ""); @Comment("Custom sender name, replacing the mailAccount name in the email") public static final Property MAIL_SENDER_NAME = @@ -36,39 +33,39 @@ public class EmailSettings implements SettingsClass { @Comment("Recovery password length") public static final Property RECOVERY_PASSWORD_LENGTH = - newProperty(INTEGER, "Email.RecoveryPasswordLength", 8); + newProperty("Email.RecoveryPasswordLength", 8); @Comment("Mail Subject") public static final Property RECOVERY_MAIL_SUBJECT = - newProperty(STRING, "Email.mailSubject", "Your new AuthMe password"); + newProperty("Email.mailSubject", "Your new AuthMe password"); @Comment("Like maxRegPerIP but with email") public static final Property MAX_REG_PER_EMAIL = - newProperty(INTEGER, "Email.maxRegPerEmail", 1); + newProperty("Email.maxRegPerEmail", 1); @Comment("Recall players to add an email?") public static final Property RECALL_PLAYERS = - newProperty(BOOLEAN, "Email.recallPlayers", false); + newProperty("Email.recallPlayers", false); @Comment("Delay in minute for the recall scheduler") public static final Property DELAY_RECALL = - newProperty(INTEGER, "Email.delayRecall", 5); + newProperty("Email.delayRecall", 5); @Comment("Blacklist these domains for emails") public static final Property> DOMAIN_BLACKLIST = - newProperty(STRING_LIST, "Email.emailBlacklisted", "10minutemail.com"); + newListProperty("Email.emailBlacklisted", "10minutemail.com"); @Comment("Whitelist ONLY these domains for emails") public static final Property> DOMAIN_WHITELIST = - newProperty(STRING_LIST, "Email.emailWhitelisted"); + newListProperty("Email.emailWhitelisted"); @Comment("Send the new password drawn in an image?") public static final Property PASSWORD_AS_IMAGE = - newProperty(BOOLEAN, "Email.generateImage", false); + newProperty("Email.generateImage", false); @Comment("The OAuth2 token") public static final Property OAUTH2_TOKEN = - newProperty(STRING, "Email.emailOauth2Token", ""); + newProperty("Email.emailOauth2Token", ""); private EmailSettings() { } diff --git a/src/main/java/fr/xephi/authme/settings/properties/HooksSettings.java b/src/main/java/fr/xephi/authme/settings/properties/HooksSettings.java index b20d28686..95d91f85d 100644 --- a/src/main/java/fr/xephi/authme/settings/properties/HooksSettings.java +++ b/src/main/java/fr/xephi/authme/settings/properties/HooksSettings.java @@ -2,11 +2,11 @@ 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.PropertyType; import fr.xephi.authme.settings.domain.SettingsClass; import java.util.List; +import static fr.xephi.authme.settings.domain.Property.newListProperty; import static fr.xephi.authme.settings.domain.Property.newProperty; public class HooksSettings implements SettingsClass { @@ -48,7 +48,7 @@ public class HooksSettings implements SettingsClass { @Comment("Other MySQL columns where we need to put the username (case-sensitive)") public static final Property> MYSQL_OTHER_USERNAME_COLS = - newProperty(PropertyType.STRING_LIST, "ExternalBoardOptions.mySQLOtherUsernameColumns"); + newListProperty("ExternalBoardOptions.mySQLOtherUsernameColumns"); @Comment("How much log2 rounds needed in BCrypt (do not change if you do not know what it does)") public static final Property BCRYPT_LOG2_ROUND = diff --git a/src/main/java/fr/xephi/authme/settings/properties/ProtectionSettings.java b/src/main/java/fr/xephi/authme/settings/properties/ProtectionSettings.java index f5a51215e..d4b5f00fc 100644 --- a/src/main/java/fr/xephi/authme/settings/properties/ProtectionSettings.java +++ b/src/main/java/fr/xephi/authme/settings/properties/ProtectionSettings.java @@ -6,39 +6,37 @@ import fr.xephi.authme.settings.domain.SettingsClass; import java.util.List; +import static fr.xephi.authme.settings.domain.Property.newListProperty; import static fr.xephi.authme.settings.domain.Property.newProperty; -import static fr.xephi.authme.settings.domain.PropertyType.BOOLEAN; -import static fr.xephi.authme.settings.domain.PropertyType.INTEGER; -import static fr.xephi.authme.settings.domain.PropertyType.STRING_LIST; public class ProtectionSettings implements SettingsClass { @Comment("Enable some servers protection (country based login, antibot)") public static final Property ENABLE_PROTECTION = - newProperty(BOOLEAN, "Protection.enableProtection", false); + newProperty("Protection.enableProtection", false); @Comment({"Countries allowed to join the server and register, see http://dev.bukkit.org/bukkit-plugins/authme-reloaded/pages/countries-codes/ for countries' codes", "PLEASE USE QUOTES!"}) public static final Property> COUNTRIES_WHITELIST = - newProperty(STRING_LIST, "Protection.countries", "US", "GB", "A1"); + newListProperty("Protection.countries", "US", "GB", "A1"); @Comment({"Countries not allowed to join the server and register", "PLEASE USE QUOTES!"}) public static final Property> COUNTRIES_BLACKLIST = - newProperty(STRING_LIST, "Protection.countriesBlacklist"); + newListProperty("Protection.countriesBlacklist"); @Comment("Do we need to enable automatic antibot system?") public static final Property ENABLE_ANTIBOT = - newProperty(BOOLEAN, "Protection.enableAntiBot", false); + newProperty("Protection.enableAntiBot", false); @Comment("Max number of player allowed to login in 5 secs before enable AntiBot system automatically") public static final Property ANTIBOT_SENSIBILITY = - newProperty(INTEGER, "Protection.antiBotSensibility", 5); + newProperty("Protection.antiBotSensibility", 5); @Comment("Duration in minutes of the antibot automatic system") public static final Property ANTIBOT_DURATION = - newProperty(INTEGER, "Protection.antiBotDuration", 10); + newProperty("Protection.antiBotDuration", 10); private ProtectionSettings() { } diff --git a/src/main/java/fr/xephi/authme/settings/properties/RegistrationSettings.java b/src/main/java/fr/xephi/authme/settings/properties/RegistrationSettings.java index 14ef825e0..c2c2bbe3a 100644 --- a/src/main/java/fr/xephi/authme/settings/properties/RegistrationSettings.java +++ b/src/main/java/fr/xephi/authme/settings/properties/RegistrationSettings.java @@ -2,11 +2,11 @@ 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.PropertyType; import fr.xephi.authme.settings.domain.SettingsClass; import java.util.List; +import static fr.xephi.authme.settings.domain.Property.newListProperty; import static fr.xephi.authme.settings.domain.Property.newProperty; public class RegistrationSettings implements SettingsClass { @@ -50,21 +50,21 @@ public class RegistrationSettings implements SettingsClass { @Comment("Force these commands after /login, without any '/', use %p to replace with player name") public static final Property> FORCE_COMMANDS = - newProperty(PropertyType.STRING_LIST, "settings.forceCommands"); + newListProperty("settings.forceCommands"); @Comment("Force these commands after /login as service console, without any '/'. " + "Use %p to replace with player name") public static final Property> FORCE_COMMANDS_AS_CONSOLE = - newProperty(PropertyType.STRING_LIST, "settings.forceCommandsAsConsole"); + newListProperty("settings.forceCommandsAsConsole"); @Comment("Force these commands after /register, without any '/', use %p to replace with player name") public static final Property> FORCE_REGISTER_COMMANDS = - newProperty(PropertyType.STRING_LIST, "settings.forceRegisterCommands"); + newListProperty("settings.forceRegisterCommands"); @Comment("Force these commands after /register as a server console, without any '/'. " + "Use %p to replace with player name") public static final Property> FORCE_REGISTER_COMMANDS_AS_CONSOLE = - newProperty(PropertyType.STRING_LIST, "settings.forceRegisterCommandsAsConsole"); + newListProperty("settings.forceRegisterCommandsAsConsole"); @Comment({ "Enable to display the welcome message (welcome.txt) after a registration or a login", diff --git a/src/main/java/fr/xephi/authme/settings/properties/RestrictionSettings.java b/src/main/java/fr/xephi/authme/settings/properties/RestrictionSettings.java index dd26b9e7a..c2b2cb687 100644 --- a/src/main/java/fr/xephi/authme/settings/properties/RestrictionSettings.java +++ b/src/main/java/fr/xephi/authme/settings/properties/RestrictionSettings.java @@ -2,11 +2,11 @@ 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.PropertyType; import fr.xephi.authme.settings.domain.SettingsClass; import java.util.List; +import static fr.xephi.authme.settings.domain.Property.newListProperty; import static fr.xephi.authme.settings.domain.Property.newProperty; public class RestrictionSettings implements SettingsClass { @@ -26,7 +26,7 @@ public class RestrictionSettings implements SettingsClass { @Comment("Allowed commands for unauthenticated players") public static final Property> ALLOW_COMMANDS = - newProperty(PropertyType.STRING_LIST, "settings.restrictions.allowCommands", + newListProperty("settings.restrictions.allowCommands", "login", "register", "l", "reg", "email", "captcha"); @Comment("Max number of allowed registrations per IP") @@ -75,7 +75,7 @@ public class RestrictionSettings implements SettingsClass { " AllowedRestrictedUser:", " - playername;127.0.0.1"}) public static final Property> ALLOWED_RESTRICTED_USERS = - newProperty(PropertyType.STRING_LIST, "settings.restrictions.AllowedRestrictedUser"); + newListProperty("settings.restrictions.AllowedRestrictedUser"); @Comment("Should unregistered players be kicked immediately?") public static final Property KICK_NON_REGISTERED = @@ -148,7 +148,7 @@ public class RestrictionSettings implements SettingsClass { "WorldNames where we need to force the spawn location for ForceSpawnLocOnJoinEnabled", "Case-sensitive!"}) public static final Property> FORCE_SPAWN_ON_WORLDS = - newProperty(PropertyType.STRING_LIST, "settings.restrictions.ForceSpawnOnTheseWorlds", + newListProperty("settings.restrictions.ForceSpawnOnTheseWorlds", "world", "world_nether", "world_the_end"); @Comment("Ban ip when the ip is not the ip registered in database") @@ -189,7 +189,7 @@ public class RestrictionSettings implements SettingsClass { "It is case-sensitive!" }) public static final Property> UNRESTRICTED_NAMES = - newProperty(PropertyType.STRING_LIST, "settings.unrestrictions.UnrestrictedName"); + newListProperty("settings.unrestrictions.UnrestrictedName"); private RestrictionSettings() { diff --git a/src/main/java/fr/xephi/authme/settings/properties/SecuritySettings.java b/src/main/java/fr/xephi/authme/settings/properties/SecuritySettings.java index 6a2ace715..9a52ca829 100644 --- a/src/main/java/fr/xephi/authme/settings/properties/SecuritySettings.java +++ b/src/main/java/fr/xephi/authme/settings/properties/SecuritySettings.java @@ -7,8 +7,8 @@ import fr.xephi.authme.settings.domain.SettingsClass; import java.util.List; +import static fr.xephi.authme.settings.domain.Property.newListProperty; import static fr.xephi.authme.settings.domain.Property.newProperty; -import static fr.xephi.authme.settings.domain.PropertyType.STRING_LIST; public class SecuritySettings implements SettingsClass { @@ -98,8 +98,7 @@ public class SecuritySettings implements SettingsClass { "- '123456'", "- 'password'"}) public static final Property> UNSAFE_PASSWORDS = - newProperty(STRING_LIST, "settings.security.unsafePasswords", - "123456", "password", "qwerty", "12345", "54321"); + newListProperty("settings.security.unsafePasswords", "123456", "password", "qwerty", "12345", "54321"); private SecuritySettings() { } diff --git a/src/main/java/fr/xephi/authme/settings/propertymap/Node.java b/src/main/java/fr/xephi/authme/settings/propertymap/Node.java index 70d8ce239..5586a578f 100644 --- a/src/main/java/fr/xephi/authme/settings/propertymap/Node.java +++ b/src/main/java/fr/xephi/authme/settings/propertymap/Node.java @@ -1,5 +1,7 @@ package fr.xephi.authme.settings.propertymap; +import fr.xephi.authme.ConsoleLogger; + import java.util.ArrayList; import java.util.List; @@ -37,14 +39,13 @@ final class Node { } /** - * Add a node to the root, creating any intermediary children that don't exist. + * Add a child node, creating any intermediary children that don't exist. * - * @param root The root to add the path to * @param fullPath The entire path of the node to add, separate by periods */ - public static void addNode(Node root, String fullPath) { + public void addNode(String fullPath) { String[] pathParts = fullPath.split("\\."); - Node parent = root; + Node parent = this; for (String part : pathParts) { Node child = parent.getChild(part); if (child == null) { @@ -59,17 +60,16 @@ final class Node { * Compare two nodes by this class' sorting behavior (insertion order). * Note that this method assumes that both supplied paths exist in the tree. * - * @param root The root of 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 static int compare(Node root, String fullPath1, String fullPath2) { + public int compare(String fullPath1, String fullPath2) { String[] path1 = fullPath1.split("\\."); String[] path2 = fullPath2.split("\\."); int commonCount = 0; - Node commonNode = root; + Node commonNode = this; while (commonCount < path1.length && commonCount < path2.length && path1[commonCount].equals(path2[commonCount]) && commonNode != null) { commonNode = commonNode.getChild(path1[commonCount]); @@ -77,7 +77,7 @@ final class Node { } if (commonNode == null) { - System.err.println("Could not find common node for '" + fullPath1 + "' at index " + commonCount); + ConsoleLogger.showError("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); diff --git a/src/main/java/fr/xephi/authme/settings/propertymap/PropertyMapComparator.java b/src/main/java/fr/xephi/authme/settings/propertymap/PropertyMapComparator.java index 77f5f60ee..a7ec1cb3b 100644 --- a/src/main/java/fr/xephi/authme/settings/propertymap/PropertyMapComparator.java +++ b/src/main/java/fr/xephi/authme/settings/propertymap/PropertyMapComparator.java @@ -23,12 +23,12 @@ final class PropertyMapComparator implements Comparator { * @param property The property that is being added */ public void add(Property property) { - Node.addNode(parent, property.getPath()); + parent.addNode(property.getPath()); } @Override public int compare(Property p1, Property p2) { - return Node.compare(parent, p1.getPath(), p2.getPath()); + return parent.compare(p1.getPath(), p2.getPath()); } } diff --git a/src/test/java/fr/xephi/authme/output/MessagesFileYamlCheckerTest.java b/src/test/java/fr/xephi/authme/output/MessagesFileYamlCheckerTest.java new file mode 100644 index 000000000..fc7b74e5e --- /dev/null +++ b/src/test/java/fr/xephi/authme/output/MessagesFileYamlCheckerTest.java @@ -0,0 +1,75 @@ +package fr.xephi.authme.output; + +import fr.xephi.authme.TestHelper; +import fr.xephi.authme.util.StringUtils; +import org.bukkit.configuration.file.YamlConfiguration; +import org.junit.Test; + +import java.io.File; +import java.util.ArrayList; +import java.util.List; +import java.util.regex.Pattern; + +import static org.junit.Assert.fail; + +/** + * Tests that all YML message files can be loaded. + */ +public class MessagesFileYamlCheckerTest { + + /** Path in the resources folder where the message files are located. */ + private static final String MESSAGES_FOLDER = "/messages/"; + /** Pattern of the message file names. */ + private static final Pattern MESSAGE_FILE_PATTERN = Pattern.compile("messages_\\w+\\.yml"); + /** Message key that is present in all files. Used to make sure that text is returned. */ + private static final MessageKey MESSAGE_KEY = MessageKey.LOGIN_MESSAGE; + + @Test + public void shouldAllBeValidYaml() { + // given + List messageFiles = getMessageFiles(); + + // when + List errors = new ArrayList<>(); + for (File file : messageFiles) { + String error = null; + try { + YamlConfiguration configuration = YamlConfiguration.loadConfiguration(file); + if (StringUtils.isEmpty(configuration.getString(MESSAGE_KEY.getKey()))) { + error = "Message for '" + MESSAGE_KEY + "' is empty"; + } + } catch (Exception e) { + error = "Could not load file: " + StringUtils.formatException(e); + } + if (!StringUtils.isEmpty(error)) { + errors.add(file.getName() + ": " + error); + } + } + + // then + if (!errors.isEmpty()) { + fail("Errors during verification of message files:\n-" + StringUtils.join("\n-", errors)); + } + } + + + private List getMessageFiles() { + File folder = TestHelper.getJarFile(MESSAGES_FOLDER); + File[] files = folder.listFiles(); + if (files == null) { + throw new IllegalStateException("Could not read folder '" + folder.getName() + "'"); + } + + List messageFiles = new ArrayList<>(); + for (File file : files) { + if (MESSAGE_FILE_PATTERN.matcher(file.getName()).matches()) { + messageFiles.add(file); + } + } + if (messageFiles.isEmpty()) { + throw new IllegalStateException("Error getting message files: list of files is empty"); + } + return messageFiles; + } + +} diff --git a/src/test/java/fr/xephi/authme/settings/ConfigFileConsistencyTest.java b/src/test/java/fr/xephi/authme/settings/ConfigFileConsistencyTest.java index 7c9170f39..6a7d7fe1c 100644 --- a/src/test/java/fr/xephi/authme/settings/ConfigFileConsistencyTest.java +++ b/src/test/java/fr/xephi/authme/settings/ConfigFileConsistencyTest.java @@ -35,10 +35,10 @@ public class ConfigFileConsistencyTest { // given File configFile = TestHelper.getJarFile(CONFIG_FILE); FileConfiguration configuration = YamlConfiguration.loadConfiguration(configFile); + SettingsMigrationService migration = new SettingsMigrationService(); // when - boolean result = SettingsMigrationService.containsAllSettings( - configuration, SettingsFieldRetriever.getAllPropertyFields()); + boolean result = migration.containsAllSettings(configuration, SettingsFieldRetriever.getAllPropertyFields()); // then if (!result) { diff --git a/src/test/java/fr/xephi/authme/settings/NewSettingIntegrationTest.java b/src/test/java/fr/xephi/authme/settings/NewSettingIntegrationTest.java index 2cb0197de..81ad29803 100644 --- a/src/test/java/fr/xephi/authme/settings/NewSettingIntegrationTest.java +++ b/src/test/java/fr/xephi/authme/settings/NewSettingIntegrationTest.java @@ -1,27 +1,31 @@ package fr.xephi.authme.settings; import com.google.common.collect.ImmutableMap; -import fr.xephi.authme.ReflectionTestUtils; +import com.google.common.io.Files; +import fr.xephi.authme.ConsoleLoggerTestInitializer; +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.TestEnum; import fr.xephi.authme.settings.propertymap.PropertyMap; -import fr.xephi.authme.util.WrapperMock; import org.bukkit.configuration.file.YamlConfiguration; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Rule; import org.junit.Test; +import org.junit.rules.TemporaryFolder; import java.io.File; -import java.lang.reflect.Field; +import java.io.IOException; import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Map; -import static fr.xephi.authme.TestHelper.getJarFile; +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.junit.Assert.assertThat; -import static org.junit.Assume.assumeThat; /** * Integration test for {@link NewSetting}. @@ -35,16 +39,33 @@ public class NewSettingIntegrationTest { /** File name for testing difficult values. */ private static final String DIFFICULT_FILE = "/config-difficult-values.yml"; - private static PropertyMap propertyMap = generatePropertyMap(); + private static PropertyMap propertyMap = TestConfiguration.generatePropertyMap(); + + @Rule + public TemporaryFolder temporaryFolder = new TemporaryFolder(); + + private File testPluginFolder; + + @BeforeClass + public static void setUpLogger() { + ConsoleLoggerTestInitializer.setupLogger(); + } + + @Before + public void setUpTestPluginFolder() throws IOException { + testPluginFolder = temporaryFolder.newFolder(); + } @Test - public void shouldLoadAndReadAllProperties() { + public void shouldLoadAndReadAllProperties() throws IOException { // given - YamlConfiguration configuration = YamlConfiguration.loadConfiguration(getJarFile(COMPLETE_FILE)); - File file = new File("unused"); + YamlConfiguration configuration = YamlConfiguration.loadConfiguration(copyFileFromResources(COMPLETE_FILE)); + // Pass another, non-existent file to check if the settings had to be rewritten + File newFile = temporaryFolder.newFile(); // when / then - NewSetting settings = new NewSetting(configuration, file, propertyMap); + NewSetting settings = new NewSetting(configuration, newFile, testPluginFolder, propertyMap, + checkAllPropertiesPresent()); Map, Object> expectedValues = ImmutableMap., Object>builder() .put(TestConfiguration.DURATION_IN_SECONDS, 22) .put(TestConfiguration.SYSTEM_NAME, "Custom sys name") @@ -61,23 +82,23 @@ public class NewSettingIntegrationTest { assertThat("Property '" + entry.getKey().getPath() + "' has expected value", settings.getProperty(entry.getKey()), equalTo(entry.getValue())); } - assertThat(file.exists(), equalTo(false)); + assertThat(newFile.length(), equalTo(0L)); } @Test public void shouldWriteMissingProperties() { // given/when - File file = getJarFile(INCOMPLETE_FILE); + File file = copyFileFromResources(INCOMPLETE_FILE); YamlConfiguration configuration = YamlConfiguration.loadConfiguration(file); - assumeThat(configuration.contains(TestConfiguration.BORING_COLORS.getPath()), equalTo(false)); // Expectation: File is rewritten to since it does not have all configurations - new NewSetting(configuration, file, propertyMap); + new NewSetting(configuration, file, testPluginFolder, propertyMap, checkAllPropertiesPresent()); // Load the settings again -> checks that what we wrote can be loaded again configuration = YamlConfiguration.loadConfiguration(file); // then - NewSetting settings = new NewSetting(configuration, file, propertyMap); + NewSetting settings = new NewSetting(configuration, file, testPluginFolder, propertyMap, + checkAllPropertiesPresent()); Map, Object> expectedValues = ImmutableMap., Object>builder() .put(TestConfiguration.DURATION_IN_SECONDS, 22) .put(TestConfiguration.SYSTEM_NAME, "[TestDefaultValue]") @@ -100,29 +121,28 @@ public class NewSettingIntegrationTest { @Test public void shouldProperlyExportAnyValues() { // given - File file = getJarFile(DIFFICULT_FILE); + File file = copyFileFromResources(DIFFICULT_FILE); YamlConfiguration configuration = YamlConfiguration.loadConfiguration(file); - assumeThat(configuration.contains(TestConfiguration.DUST_LEVEL.getPath()), equalTo(false)); // Additional string properties List> additionalProperties = Arrays.asList( newProperty("more.string1", "it's a text with some \\'apostrophes'"), newProperty("more.string2", "\tthis one\nhas some\nnew '' lines-test") ); - PropertyMap propertyMap = generatePropertyMap(); for (Property property : additionalProperties) { propertyMap.put(property, new String[0]); } // when - new NewSetting(configuration, file, propertyMap); - // reload the file as settings should hav been rewritten + new NewSetting(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"); - NewSetting settings = new NewSetting(configuration, unusedFile, propertyMap); + NewSetting settings = new NewSetting(configuration, unusedFile, testPluginFolder, propertyMap, + checkAllPropertiesPresent()); assertThat(unusedFile.exists(), equalTo(false)); assertThat(configuration.contains(TestConfiguration.DUST_LEVEL.getPath()), equalTo(true)); @@ -147,23 +167,32 @@ public class NewSettingIntegrationTest { } } - /** - * Generate a property map with all properties in {@link TestConfiguration}. - * - * @return The generated property map - */ - private static PropertyMap generatePropertyMap() { - WrapperMock.createInstance(); - PropertyMap propertyMap = new PropertyMap(); - for (Field field : TestConfiguration.class.getDeclaredFields()) { - Object fieldValue = ReflectionTestUtils.getFieldValue(TestConfiguration.class, null, field.getName()); - if (fieldValue instanceof Property) { - Property property = (Property) fieldValue; - String[] comments = new String[]{"Comment for '" + property.getPath() + "'"}; - propertyMap.put(property, comments); - } + @Test + public void shouldReloadSettings() throws IOException { + // given + YamlConfiguration configuration = YamlConfiguration.loadConfiguration(temporaryFolder.newFile()); + File fullConfigFile = copyFileFromResources(COMPLETE_FILE); + NewSetting settings = new NewSetting(configuration, fullConfigFile, testPluginFolder, propertyMap, + TestSettingsMigrationServices.alwaysFulfilled()); + + // when + assertThat(settings.getProperty(TestConfiguration.RATIO_ORDER), + equalTo(TestConfiguration.RATIO_ORDER.getDefaultValue())); + settings.reload(); + + // then + assertThat(settings.getProperty(TestConfiguration.RATIO_ORDER), equalTo(TestEnum.FIRST)); + } + + private File copyFileFromResources(String path) { + try { + File source = TestHelper.getJarFile(path); + File destination = temporaryFolder.newFile(); + Files.copy(source, destination); + return destination; + } catch (IOException e) { + throw new IllegalStateException("Could not copy test file", e); } - return propertyMap; } } diff --git a/src/test/java/fr/xephi/authme/settings/NewSettingTest.java b/src/test/java/fr/xephi/authme/settings/NewSettingTest.java index 710698e5a..0c69919c1 100644 --- a/src/test/java/fr/xephi/authme/settings/NewSettingTest.java +++ b/src/test/java/fr/xephi/authme/settings/NewSettingTest.java @@ -1,17 +1,30 @@ package fr.xephi.authme.settings; +import fr.xephi.authme.ConsoleLoggerTestInitializer; import fr.xephi.authme.settings.domain.Property; +import fr.xephi.authme.settings.properties.RegistrationSettings; 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.BeforeClass; +import org.junit.Rule; import org.junit.Test; +import org.junit.rules.TemporaryFolder; import org.mockito.internal.stubbing.answers.ReturnsArgumentAt; +import java.io.File; import java.io.IOException; import java.io.InputStream; +import java.nio.file.Files; +import java.util.List; +import static fr.xephi.authme.settings.properties.PluginSettings.MESSAGES_LANGUAGE; +import static fr.xephi.authme.util.StringUtils.makePath; import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.endsWith; import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.hasSize; import static org.hamcrest.Matchers.not; import static org.hamcrest.Matchers.nullValue; import static org.mockito.BDDMockito.given; @@ -21,13 +34,28 @@ 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.verify; import static org.mockito.Mockito.when; /** - * Test for {@link NewSetting}. + * Unit tests for {@link NewSetting}. */ public class NewSettingTest { + @Rule + public TemporaryFolder temporaryFolder = new TemporaryFolder(); + private File testPluginFolder; + + @BeforeClass + public static void setUpLogger() { + ConsoleLoggerTestInitializer.setupLogger(); + } + + @Before + public void setUpTestPluginFolder() throws IOException { + testPluginFolder = temporaryFolder.newFolder(); + } + @Test public void shouldLoadAllConfigs() { // given @@ -43,7 +71,7 @@ public class NewSettingTest { setReturnValue(configuration, TestConfiguration.SYSTEM_NAME, "myTestSys"); // when / then - NewSetting settings = new NewSetting(configuration, null, null); + NewSetting settings = new NewSetting(configuration, null, null, null, null); assertThat(settings.getProperty(TestConfiguration.VERSION_NUMBER), equalTo(20)); assertThat(settings.getProperty(TestConfiguration.SKIP_BORING_FEATURES), equalTo(true)); @@ -59,7 +87,7 @@ public class NewSettingTest { public void shouldReturnDefaultFile() throws IOException { // given YamlConfiguration configuration = mock(YamlConfiguration.class); - NewSetting settings = new NewSetting(configuration, null, null); + NewSetting settings = new NewSetting(configuration, null, null, null, null); // when String defaultFile = settings.getDefaultMessagesFile(); @@ -71,6 +99,99 @@ public class NewSettingTest { assertThat(stream.read(), not(equalTo(0))); } + @Test + public void shouldSetProperty() { + // given + YamlConfiguration configuration = mock(YamlConfiguration.class); + NewSetting settings = new NewSetting(configuration, null, null, null, null); + + // when + settings.setProperty(TestConfiguration.DUST_LEVEL, -4); + + // then + verify(configuration).set(TestConfiguration.DUST_LEVEL.getPath(), -4); + } + + @Test + public void shouldReturnMessagesFile() { + // given + // Use some code that is for sure not present in our JAR + String languageCode = "notinjar"; + File file = new File(testPluginFolder, makePath("messages", "messages_" + languageCode + ".yml")); + createFile(file); + + YamlConfiguration configuration = mock(YamlConfiguration.class); + given(configuration.contains(anyString())).willReturn(true); + setReturnValue(configuration, MESSAGES_LANGUAGE, languageCode); + NewSetting settings = new NewSetting(configuration, null, testPluginFolder, + TestConfiguration.generatePropertyMap(), TestSettingsMigrationServices.alwaysFulfilled()); + + // when + File messagesFile = settings.getMessagesFile(); + + // then + assertThat(messagesFile.getPath(), endsWith("messages_" + languageCode + ".yml")); + assertThat(messagesFile.exists(), equalTo(true)); + } + + @Test + public void shouldCopyDefaultForUnknownLanguageCode() { + // given + YamlConfiguration configuration = mock(YamlConfiguration.class); + given(configuration.contains(anyString())).willReturn(true); + setReturnValue(configuration, MESSAGES_LANGUAGE, "doesntexist"); + NewSetting settings = new NewSetting(configuration, null, testPluginFolder, + TestConfiguration.generatePropertyMap(), TestSettingsMigrationServices.alwaysFulfilled()); + + // when + File messagesFile = settings.getMessagesFile(); + + // then + assertThat(messagesFile.getPath(), endsWith("messages_en.yml")); + assertThat(messagesFile.exists(), equalTo(true)); + } + + @Test + public void shouldLoadWelcomeMessage() throws IOException { + // given + String welcomeMessage = "This is my welcome message for testing\nBye!"; + File welcomeFile = new File(testPluginFolder, "welcome.txt"); + createFile(welcomeFile); + Files.write(welcomeFile.toPath(), welcomeMessage.getBytes()); + + YamlConfiguration configuration = mock(YamlConfiguration.class); + setReturnValue(configuration, RegistrationSettings.USE_WELCOME_MESSAGE, true); + NewSetting settings = new NewSetting(configuration, null, testPluginFolder, + TestConfiguration.generatePropertyMap(), TestSettingsMigrationServices.alwaysFulfilled()); + + // when + List result = settings.getWelcomeMessage(); + + // then + assertThat(result, hasSize(2)); + assertThat(result.get(0), equalTo(welcomeMessage.split("\\n")[0])); + assertThat(result.get(1), equalTo(welcomeMessage.split("\\n")[1])); + } + + @Test + public void shouldLoadEmailMessage() throws IOException { + // given + String emailMessage = "Sample email message\nThat's all!"; + File emailFile = new File(testPluginFolder, "email.html"); + createFile(emailFile); + Files.write(emailFile.toPath(), emailMessage.getBytes()); + + YamlConfiguration configuration = mock(YamlConfiguration.class); + NewSetting settings = new NewSetting(configuration, null, testPluginFolder, + TestConfiguration.generatePropertyMap(), TestSettingsMigrationServices.alwaysFulfilled()); + + // when + String result = settings.getEmailMessage(); + + // then + assertThat(result, equalTo(emailMessage)); + } + private static void setReturnValue(YamlConfiguration config, Property property, T value) { if (value instanceof String) { when(config.getString(eq(property.getPath()), anyString())).thenReturn((String) value); @@ -91,4 +212,13 @@ public class NewSettingTest { setting.getProperty(property).equals(property.getDefaultValue()), equalTo(true)); } + private static void createFile(File file) { + try { + file.getParentFile().mkdirs(); + file.createNewFile(); + } catch (IOException e) { + throw new IllegalStateException(e); + } + } + } diff --git a/src/test/java/fr/xephi/authme/settings/SettingsMigrationServiceTest.java b/src/test/java/fr/xephi/authme/settings/SettingsMigrationServiceTest.java index cc89a463a..a85f86e1b 100644 --- a/src/test/java/fr/xephi/authme/settings/SettingsMigrationServiceTest.java +++ b/src/test/java/fr/xephi/authme/settings/SettingsMigrationServiceTest.java @@ -42,9 +42,10 @@ public class SettingsMigrationServiceTest { FileConfiguration configuration = YamlConfiguration.loadConfiguration(configTestFile); PropertyMap propertyMap = SettingsFieldRetriever.getAllPropertyFields(); assumeThat(testFolder.listFiles(), arrayWithSize(1)); + SettingsMigrationService migrationService = new SettingsMigrationService(); // when - boolean result = SettingsMigrationService.checkAndMigrate(configuration, propertyMap, testFolder); + boolean result = migrationService.checkAndMigrate(configuration, propertyMap, testFolder); // then assertThat(result, equalTo(false)); diff --git a/src/test/java/fr/xephi/authme/settings/TestSettingsMigrationServices.java b/src/test/java/fr/xephi/authme/settings/TestSettingsMigrationServices.java new file mode 100644 index 000000000..b3c7dd085 --- /dev/null +++ b/src/test/java/fr/xephi/authme/settings/TestSettingsMigrationServices.java @@ -0,0 +1,49 @@ +package fr.xephi.authme.settings; + +import fr.xephi.authme.settings.propertymap.PropertyMap; +import org.bukkit.configuration.file.FileConfiguration; + +import java.io.File; + +/** + * Provides {@link SettingsMigrationService} implementations for testing. + */ +public final class TestSettingsMigrationServices { + + private TestSettingsMigrationServices() { + } + + /** + * Returns a settings migration service which always answers that all data is up-to-date. + * + * @return test settings migration service + */ + public static SettingsMigrationService alwaysFulfilled() { + return new SettingsMigrationService() { + @Override + public boolean checkAndMigrate(FileConfiguration configuration, PropertyMap propertyMap, File pluginFolder) { + 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); + } + }; + } + +} diff --git a/src/test/java/fr/xephi/authme/settings/domain/EnumPropertyTypeTest.java b/src/test/java/fr/xephi/authme/settings/domain/EnumPropertyTest.java similarity index 75% rename from src/test/java/fr/xephi/authme/settings/domain/EnumPropertyTypeTest.java rename to src/test/java/fr/xephi/authme/settings/domain/EnumPropertyTest.java index 461314e8d..5fe553623 100644 --- a/src/test/java/fr/xephi/authme/settings/domain/EnumPropertyTypeTest.java +++ b/src/test/java/fr/xephi/authme/settings/domain/EnumPropertyTest.java @@ -9,20 +9,19 @@ import static org.mockito.BDDMockito.given; import static org.mockito.Mockito.mock; /** - * Test for {@link EnumPropertyType}. + * Test for {@link EnumProperty}. */ -public class EnumPropertyTypeTest { +public class EnumPropertyTest { @Test public void shouldReturnCorrectEnumValue() { // given - PropertyType propertyType = new EnumPropertyType<>(TestEnum.class); Property 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 = propertyType.getFromFile(property, configuration); + TestEnum result = property.getFromFile(configuration); // then assertThat(result, equalTo(TestEnum.ENTRY_B)); @@ -31,13 +30,12 @@ public class EnumPropertyTypeTest { @Test public void shouldFallBackToDefaultForInvalidValue() { // given - PropertyType propertyType = new EnumPropertyType<>(TestEnum.class); Property 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 = propertyType.getFromFile(property, configuration); + TestEnum result = property.getFromFile(configuration); // then assertThat(result, equalTo(TestEnum.ENTRY_C)); @@ -46,13 +44,12 @@ public class EnumPropertyTypeTest { @Test public void shouldFallBackToDefaultForNonExistentValue() { // given - PropertyType propertyType = new EnumPropertyType<>(TestEnum.class); Property 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 = propertyType.getFromFile(property, configuration); + TestEnum result = property.getFromFile(configuration); // then assertThat(result, equalTo(TestEnum.ENTRY_C)); @@ -61,14 +58,13 @@ public class EnumPropertyTypeTest { @Test public void shouldReturnTrueForContainsCheck() { // given - PropertyType propertyType = new EnumPropertyType<>(TestEnum.class); Property 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 = propertyType.contains(property, configuration); + boolean result = property.isPresent(configuration); // then assertThat(result, equalTo(true)); @@ -77,13 +73,12 @@ public class EnumPropertyTypeTest { @Test public void shouldReturnFalseForFileWithoutConfig() { // given - PropertyType propertyType = new EnumPropertyType<>(TestEnum.class); Property 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 = propertyType.contains(property, configuration); + boolean result = property.isPresent(configuration); // then assertThat(result, equalTo(false)); @@ -92,14 +87,13 @@ public class EnumPropertyTypeTest { @Test public void shouldReturnFalseForUnknownValue() { // given - PropertyType propertyType = new EnumPropertyType<>(TestEnum.class); Property 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 = propertyType.contains(property, configuration); + boolean result = property.isPresent(configuration); // then assertThat(result, equalTo(false)); diff --git a/src/test/java/fr/xephi/authme/settings/domain/PropertyTypeTest.java b/src/test/java/fr/xephi/authme/settings/domain/PropertyTest.java similarity index 82% rename from src/test/java/fr/xephi/authme/settings/domain/PropertyTypeTest.java rename to src/test/java/fr/xephi/authme/settings/domain/PropertyTest.java index df0120631..de33c53ca 100644 --- a/src/test/java/fr/xephi/authme/settings/domain/PropertyTypeTest.java +++ b/src/test/java/fr/xephi/authme/settings/domain/PropertyTest.java @@ -3,8 +3,7 @@ package fr.xephi.authme.settings.domain; import org.bukkit.configuration.file.YamlConfiguration; import org.junit.BeforeClass; import org.junit.Test; -import org.mockito.invocation.InvocationOnMock; -import org.mockito.stubbing.Answer; +import org.mockito.internal.stubbing.answers.ReturnsArgumentAt; import java.util.Arrays; import java.util.List; @@ -20,9 +19,9 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; /** - * Test for {@link PropertyType} and the contained subtypes. + * Test for {@link Property} and the contained subtypes. */ -public class PropertyTypeTest { +public class PropertyTest { private static YamlConfiguration configuration; @@ -31,11 +30,11 @@ public class PropertyTypeTest { configuration = mock(YamlConfiguration.class); when(configuration.getBoolean(eq("bool.path.test"), anyBoolean())).thenReturn(true); - when(configuration.getBoolean(eq("bool.path.wrong"), anyBoolean())).thenAnswer(secondParameter()); + 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(secondParameter()); + 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(secondParameter()); + 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); @@ -120,7 +119,7 @@ public class PropertyTypeTest { @Test public void shouldGetStringListValue() { // given - Property> property = Property.newProperty(PropertyType.STRING_LIST, "list.path.test", "1", "b"); + Property> property = Property.newListProperty("list.path.test", "1", "b"); // when List result = property.getFromFile(configuration); @@ -133,7 +132,7 @@ public class PropertyTypeTest { public void shouldGetStringListDefault() { // given Property> property = - Property.newProperty(PropertyType.STRING_LIST, "list.path.wrong", "default", "list", "elements"); + Property.newListProperty("list.path.wrong", "default", "list", "elements"); // when List result = property.getFromFile(configuration); @@ -142,13 +141,4 @@ public class PropertyTypeTest { assertThat(result, contains("default", "list", "elements")); } - private static Answer secondParameter() { - return new Answer() { - @Override - public T answer(InvocationOnMock invocation) throws Throwable { - // Return the second parameter -> the default - return (T) invocation.getArguments()[1]; - } - }; - } } diff --git a/src/test/java/fr/xephi/authme/settings/properties/SettingsClassConsistencyTest.java b/src/test/java/fr/xephi/authme/settings/properties/SettingsClassConsistencyTest.java index 075103345..c98dd7d0e 100644 --- a/src/test/java/fr/xephi/authme/settings/properties/SettingsClassConsistencyTest.java +++ b/src/test/java/fr/xephi/authme/settings/properties/SettingsClassConsistencyTest.java @@ -9,6 +9,7 @@ import org.junit.Test; import java.io.File; import java.lang.reflect.Constructor; import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.HashSet; @@ -92,6 +93,17 @@ public class SettingsClassConsistencyTest { constructors, arrayWithSize(1)); assertThat("Constructor of " + clazz + " is private", Modifier.isPrivate(constructors[0].getModifiers()), equalTo(true)); + + // Ugly hack to get coverage on the private constructors + // http://stackoverflow.com/questions/14077842/how-to-test-a-private-constructor-in-java-application + try { + Constructor constructor = clazz.getDeclaredConstructor(); + constructor.setAccessible(true); + constructor.newInstance(); + } catch (NoSuchMethodException | InstantiationException + | IllegalAccessException | InvocationTargetException e) { + e.printStackTrace(); + } } } diff --git a/src/test/java/fr/xephi/authme/settings/properties/TestConfiguration.java b/src/test/java/fr/xephi/authme/settings/properties/TestConfiguration.java index 72960f6c4..0f16e1e8e 100644 --- a/src/test/java/fr/xephi/authme/settings/properties/TestConfiguration.java +++ b/src/test/java/fr/xephi/authme/settings/properties/TestConfiguration.java @@ -1,11 +1,14 @@ package fr.xephi.authme.settings.properties; +import fr.xephi.authme.ReflectionTestUtils; import fr.xephi.authme.settings.domain.Property; -import fr.xephi.authme.settings.domain.PropertyType; import fr.xephi.authme.settings.domain.SettingsClass; +import fr.xephi.authme.settings.propertymap.PropertyMap; +import java.lang.reflect.Field; import java.util.List; +import static fr.xephi.authme.settings.domain.Property.newListProperty; import static fr.xephi.authme.settings.domain.Property.newProperty; /** @@ -23,7 +26,7 @@ public final class TestConfiguration implements SettingsClass { newProperty(TestEnum.class, "sample.ratio.order", TestEnum.SECOND); public static final Property> RATIO_FIELDS = - newProperty(PropertyType.STRING_LIST, "sample.ratio.fields", "a", "b", "c"); + newListProperty("sample.ratio.fields", "a", "b", "c"); public static final Property VERSION_NUMBER = newProperty("version", 32046); @@ -32,7 +35,7 @@ public final class TestConfiguration implements SettingsClass { newProperty("features.boring.skip", false); public static final Property> BORING_COLORS = - newProperty(PropertyType.STRING_LIST, "features.boring.colors"); + newListProperty("features.boring.colors"); public static final Property DUST_LEVEL = newProperty("features.boring.dustLevel", -1); @@ -41,10 +44,28 @@ public final class TestConfiguration implements SettingsClass { newProperty("features.cool.enabled", false); public static final Property> COOL_OPTIONS = - newProperty(PropertyType.STRING_LIST, "features.cool.options", "Sparks", "Sprinkles"); + newListProperty("features.cool.options", "Sparks", "Sprinkles"); private TestConfiguration() { } + /** + * Generate a property map with all properties in {@link TestConfiguration}. + * + * @return The generated property map + */ + public static PropertyMap generatePropertyMap() { + PropertyMap propertyMap = new PropertyMap(); + for (Field field : TestConfiguration.class.getDeclaredFields()) { + Object fieldValue = ReflectionTestUtils.getFieldValue(TestConfiguration.class, null, field.getName()); + if (fieldValue instanceof Property) { + Property property = (Property) fieldValue; + String[] comments = new String[]{"Comment for '" + property.getPath() + "'"}; + propertyMap.put(property, comments); + } + } + return propertyMap; + } + }