diff --git a/Plugin/src/main/java/com/gmail/filoghost/chestcommands/SimpleUpdater.java b/Plugin/src/main/java/com/gmail/filoghost/chestcommands/SimpleUpdater.java index 0d684ed..02467d4 100644 --- a/Plugin/src/main/java/com/gmail/filoghost/chestcommands/SimpleUpdater.java +++ b/Plugin/src/main/java/com/gmail/filoghost/chestcommands/SimpleUpdater.java @@ -29,23 +29,19 @@ import org.json.simple.JSONArray; import org.json.simple.JSONObject; import org.json.simple.JSONValue; +import com.google.common.primitives.Ints; + /** - * A very simple and lightweight updater. + * A very simple and lightweight updater, without download features. + * @author filoghost */ public final class SimpleUpdater { - - public interface ResponseHandler { - - /** - * Called when the updater finds a new version. - * @param newVersion - the new version - */ - public void onUpdateFound(final String newVersion); - - } + private static Pattern VERSION_PATTERN = Pattern.compile("v?([0-9\\.]+)"); + private Plugin plugin; private int projectId; + public SimpleUpdater(Plugin plugin, int projectId) { if (plugin == null) { @@ -56,11 +52,14 @@ public final class SimpleUpdater { this.projectId = projectId; } + /** * This method creates a new async thread to check for updates. + * @param responseHandler the response handler */ public void checkForUpdates(final ResponseHandler responseHandler) { Thread updaterThread = new Thread(new Runnable() { + @Override public void run() { @@ -68,46 +67,43 @@ public final class SimpleUpdater { JSONArray filesArray = (JSONArray) readJson("https://api.curseforge.com/servermods/files?projectIds=" + projectId); if (filesArray.size() == 0) { - // The array cannot be empty, there must be at least one file. The project ID is not valid - plugin.getLogger().warning("The project ID (" + projectId + ") provided for updating is invalid or curse had a problem."); - plugin.getLogger().warning("If the error persists, please inform the author."); + // The array cannot be empty, there must be at least one file. + // The project ID is not valid or curse returned a wrong response. return; } - String updateName = (String) ((JSONObject) filesArray.get(filesArray.size() - 1)).get("name"); - final String newVersion = extractVersion(updateName); + String updateName = (String) ((JSONObject) filesArray.get(filesArray.size() - 1)).get("name"); + final PluginVersion remoteVersion = new PluginVersion(updateName); + PluginVersion localVersion = new PluginVersion(plugin.getDescription().getVersion()); - if (newVersion == null) { - throw new NumberFormatException(); - } - - if (isNewerVersion(newVersion)) { + if (remoteVersion.isNewerThan(localVersion)) { Bukkit.getScheduler().scheduleSyncDelayedTask(plugin, new Runnable() { + @Override public void run() { - responseHandler.onUpdateFound(newVersion); + responseHandler.onUpdateFound(remoteVersion.toString()); } }); } } catch (IOException e) { plugin.getLogger().warning("Could not contact BukkitDev to check for updates."); - } catch (NumberFormatException e) { - plugin.getLogger().warning("The author of this plugin has misconfigured the Updater system."); - plugin.getLogger().warning("File versions should follow the format 'PluginName vVERSION'"); + } catch (InvalidVersionException e) { + plugin.getLogger().warning("Could not check for updates because of a version format error: " + e.getMessage() + "."); plugin.getLogger().warning("Please notify the author of this error."); } catch (Exception e) { e.printStackTrace(); plugin.getLogger().warning("Unable to check for updates: unhandled exception."); } - } + } }); + updaterThread.start(); } + private Object readJson(String url) throws MalformedURLException, IOException { - URLConnection conn = new URL(url).openConnection(); conn.setConnectTimeout(5000); conn.setReadTimeout(8000); @@ -117,64 +113,107 @@ public final class SimpleUpdater { return JSONValue.parse(new BufferedReader(new InputStreamReader(conn.getInputStream()))); } - /** - * Compare the version found with the plugin's version, from an array of integer separated by full stops. - * Examples: - * v1.2 > v1.12 - * v2.1 = v2.01 - */ - private boolean isNewerVersion(String remoteVersion) { - String pluginVersion = plugin.getDescription().getVersion(); + + private static class PluginVersion { - if (pluginVersion == null) { - // Do not throw exceptions, just consider it as v0 - pluginVersion = "0"; - } + // The version extracted from a string, e.g. "Chest Commands v1.3" becomes [1, 3] + private int[] versionNumbers; + private boolean isDevBuild; - if (!remoteVersion.matches("v?[0-9\\.]+")) { - // Should always be checked before by this class - throw new IllegalArgumentException("fetched version's format is incorrect"); - } - // Remove all the "v" from the versions, replace multiple full stops with a single full stop, and split them - String[] pluginVersionSplit = pluginVersion.replace("v", "").replaceAll("[\\.]{2,}", ".").split("\\."); - String[] remoteVersionSplit = remoteVersion.replace("v", "").replaceAll("[\\.]{2,}", ".").split("\\."); - - int longest = Math.max(pluginVersionSplit.length, remoteVersionSplit.length); - - int[] pluginVersionArray = new int[longest]; - int[] remoteVersionArray = new int[longest]; - - for (int i = 0; i < pluginVersionSplit.length; i++) { - pluginVersionArray[i] = Integer.parseInt(pluginVersionSplit[i]); - } - - for (int i = 0; i < remoteVersionSplit.length; i++) { - remoteVersionArray[i] = Integer.parseInt(remoteVersionSplit[i]); - } - - for (int i = 0; i < longest; i++) { - int diff = remoteVersionArray[i] - pluginVersionArray[i]; - if (diff > 0) { - return true; - } else if (diff < 0) { - return false; + public PluginVersion(String input) throws InvalidVersionException { + if (input == null) { + throw new InvalidVersionException("input was null"); } - // Continue the loop + Matcher matcher = VERSION_PATTERN.matcher(input); + + if (!matcher.find()) { + throw new InvalidVersionException("version pattern not found in \"" + input + "\""); + } + + // Get the first group of the matcher (without the "v") + String version = matcher.group(1); + + // Replace multiple full stops (probably typos) with a single full stop, and split the version with them + String[] versionParts = version.replaceAll("[\\.]{2,}", ".").split("\\."); + + // Convert the strings to integers in order to compare them + this.versionNumbers = new int[versionParts.length]; + for (int i = 0; i < versionParts.length; i++) { + try { + this.versionNumbers[i] = Integer.parseInt(versionParts[i]); + } catch (NumberFormatException e) { + throw new InvalidVersionException("invalid number in \"" + input + "\""); + } + } + + this.isDevBuild = input.contains("SNAPSHOT"); + } + + + /** + * Compares this version with another version, using the array "versionNumbers". + * Examples: + * v1.12 is newer than v1.2 ([1, 12] is newer than [1, 2]) + * v2.01 is equal to v2.1 ([2, 1] is equal to [2, 1]) + * + * @return true if this version is newer than the other, false if equal or older + */ + public boolean isNewerThan(PluginVersion other) { + int longest = Math.max(this.versionNumbers.length, other.versionNumbers.length); + + for (int i = 0; i < longest; i++) { + int thisVersionPart = i < this.versionNumbers.length ? this.versionNumbers[i] : 0; + int otherVersionPart = i < other.versionNumbers.length ? other.versionNumbers[i] : 0; + int diff = thisVersionPart - otherVersionPart; + + if (diff > 0) { + return true; + } else if (diff < 0) { + return false; + } + + // Continue the loop until diff = 0 + } + + // If we get here, they're the same version, check dev builds. + // This version is newer only if it's not a dev build and the other is. + if (other.isDevBuild && !this.isDevBuild) { + return true; + } + + return false; + } + + + @Override + public String toString() { + return "v" + Ints.join(".", versionNumbers); } - return false; } - private String extractVersion(String input) { - Matcher matcher = Pattern.compile("v[0-9\\.]+").matcher(input); - - String result = null; - if (matcher.find()) { - result = matcher.group(); + + private static class InvalidVersionException extends Exception { + + private static final long serialVersionUID = 1L; + + public InvalidVersionException(String message) { + super(message); } - return result; } + + + public interface ResponseHandler { + + /** + * Called when the updater finds a new version. + * @param newVersion - the new version + */ + public void onUpdateFound(final String newVersion); + + } + }