de.sanandrew.core.manpack.managers.SAPUpdateManager.java Source code

Java tutorial

Introduction

Here is the source code for de.sanandrew.core.manpack.managers.SAPUpdateManager.java

Source

/*******************************************************************************************************************
 * Authors:   SanAndreasP
 * Copyright: SanAndreasP
 * License:   Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International
 *                http://creativecommons.org/licenses/by-nc-sa/4.0/
 *******************************************************************************************************************/
package de.sanandrew.core.manpack.managers;

import com.google.common.collect.Maps;
import com.google.gson.*;
import de.sanandrew.core.manpack.mod.ConfigurationManager;
import de.sanandrew.core.manpack.mod.ModCntManPack;
import de.sanandrew.core.manpack.util.MutableString;
import de.sanandrew.core.manpack.util.javatuples.Triplet;
import net.minecraft.util.EnumChatFormatting;
import org.apache.commons.lang3.mutable.MutableBoolean;
import org.apache.logging.log4j.Level;

import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.Field;
import java.lang.reflect.Type;
import java.net.MalformedURLException;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 *
 */
public class SAPUpdateManager {
    public static final List<Triplet<SAPUpdateManager, MutableBoolean, MutableString>> UPD_MANAGERS = new ArrayList<>();
    public static final Map<Integer, Boolean> IS_IN_RENDER_QUEUE = Maps.newHashMap();

    private boolean checkedForUpdate = false;
    private Version version;
    private String modName;
    private URL updURL;
    private String modInfoURL;
    private File modPackedJar;
    private UpdateFile updInfo = new UpdateFile();
    private final int mgrId;

    public UpdateDownloader downloader;

    public static synchronized void setChecked(int mgrId) {
        UPD_MANAGERS.get(mgrId).getValue1().setTrue();
    }

    public static synchronized void setHasUpdate(int mgrId, String version) {
        setChecked(mgrId);
        UPD_MANAGERS.get(mgrId).getValue2().set(version);
    }

    public static void setInRenderQueue(int mgrId) {
        IS_IN_RENDER_QUEUE.put(mgrId, true);
    }

    private SAPUpdateManager(String modName, Version version, String updateURL, String modURL, File modJar) {
        this.modName = modName;
        this.version = version.clone();
        this.modInfoURL = modURL;
        this.modPackedJar = modJar;

        try {
            this.updURL = new URL(updateURL);
            this.updURL.toURI(); // check validity
        } catch (MalformedURLException | NullPointerException | URISyntaxException e) {
            this.updURL = null;
            ModCntManPack.UPD_LOG.log(Level.WARN, "The URL to the mod version file is invalid!");
            e.printStackTrace();
        }

        this.mgrId = UPD_MANAGERS.size();
    }

    public static SAPUpdateManager createUpdateManager(String modName, Version version, String updateURL,
            String modURL, File modJar) {
        SAPUpdateManager updMgr = new SAPUpdateManager(modName, version, updateURL, modURL, modJar);

        if (updMgr.updURL != null) {
            UPD_MANAGERS.add(Triplet.with(updMgr, new MutableBoolean(false), new MutableString("")));
            IS_IN_RENDER_QUEUE.put(updMgr.mgrId, false);
        }

        return updMgr;
    }

    private void check() {
        Runnable threadProcessor = new Runnable() {
            @Override
            public void run() {
                try {
                    ModCntManPack.UPD_LOG.printf(Level.INFO, "Checking for %s update",
                            SAPUpdateManager.this.getModName());
                    if (SAPUpdateManager.this.getUpdateURL() == null) {
                        throw new MalformedURLException("[NULL]");
                    }

                    Gson gson = new GsonBuilder()
                            .registerTypeAdapter(UpdateFile.class, new AnnotatedDeserializer<UpdateFile>())
                            .create();

                    try (BufferedReader in = new BufferedReader(
                            new InputStreamReader(SAPUpdateManager.this.getUpdateURL().openStream()))) {
                        SAPUpdateManager.this.updInfo = gson.fromJson(in, UpdateFile.class);
                    } catch (IOException | JsonSyntaxException ex) {
                        ModCntManPack.UPD_LOG.printf(Level.WARN, "Check for Update failed!", ex);
                    }

                    if (SAPUpdateManager.this.updInfo.version == null
                            || SAPUpdateManager.this.updInfo.version.length() < 1) {
                        SAPUpdateManager.setChecked(SAPUpdateManager.this.getId());
                        return;
                    }

                    Version webVersion = SAPUpdateManager.this.updInfo.getVersionInst();
                    SAPUpdateManager.this.updInfo.version = webVersion.toString(); // reformat the version number to the format major.minor.revision

                    Version currVersion = SAPUpdateManager.this.getVersion();
                    String currModName = SAPUpdateManager.this.getModName();
                    if (webVersion.versionType != EnumVersionType.RELEASE) {
                        if (ConfigurationManager.subscribeToUnstable
                                || currVersion.versionType != EnumVersionType.RELEASE) {
                            if (webVersion.versionType.ordinal() > currVersion.versionType.ordinal()) {
                                switch (webVersion.versionType) {
                                case BETA:
                                    ModCntManPack.UPD_LOG.printf(Level.INFO, "A beta for %s is available: %s",
                                            currModName, webVersion);
                                    break;
                                case RELEASECANDIDATE:
                                    ModCntManPack.UPD_LOG.printf(Level.INFO,
                                            "A release candidate for %s is available: %s", currModName, webVersion);
                                    break;
                                default:
                                    ModCntManPack.UPD_LOG.printf(Level.INFO, "No new update for %s is available.",
                                            currModName);
                                    SAPUpdateManager.setChecked(SAPUpdateManager.this.getId());
                                    return;
                                }

                                SAPUpdateManager.setHasUpdate(SAPUpdateManager.this.mgrId, webVersion.toString());
                                return;
                            } else if (webVersion.preVersionNr > currVersion.preVersionNr) {
                                switch (webVersion.versionType) {
                                case ALPHA:
                                    ModCntManPack.UPD_LOG.printf(Level.INFO, "A new alpha for %s is out: %s",
                                            currModName, webVersion);
                                    break;
                                case BETA:
                                    ModCntManPack.UPD_LOG.printf(Level.INFO, "A new beta for %s is out: %s",
                                            currModName, webVersion);
                                    break;
                                case RELEASECANDIDATE:
                                    ModCntManPack.UPD_LOG.printf(Level.INFO,
                                            "A new release candidate for %s is out: %s", currModName, webVersion);
                                    break;
                                default:
                                    ModCntManPack.UPD_LOG.printf(Level.INFO, "No new update for %s is available.",
                                            currModName);
                                    SAPUpdateManager.setChecked(SAPUpdateManager.this.getId());
                                    return;
                                }

                                SAPUpdateManager.setHasUpdate(SAPUpdateManager.this.mgrId, webVersion.toString());
                                return;
                            }
                        }
                    } else {
                        if (webVersion.major > currVersion.major) {
                            ModCntManPack.UPD_LOG.printf(Level.INFO, "New major update for %s is out: %s",
                                    currModName, webVersion);
                            SAPUpdateManager.setHasUpdate(SAPUpdateManager.this.mgrId, webVersion.toString());
                            return;
                        } else if (webVersion.major == currVersion.major) {
                            if (webVersion.minor > currVersion.minor) {
                                ModCntManPack.UPD_LOG.printf(Level.INFO, "New minor update for %s is out: %s",
                                        currModName, webVersion);
                                SAPUpdateManager.setHasUpdate(SAPUpdateManager.this.mgrId, webVersion.toString());
                                return;
                            } else if (webVersion.minor == currVersion.minor) {
                                if (webVersion.revision > currVersion.revision) {
                                    ModCntManPack.UPD_LOG.printf(Level.INFO, "New bugfix update for %s is out: %s",
                                            currModName, webVersion);
                                    SAPUpdateManager.setHasUpdate(SAPUpdateManager.this.mgrId,
                                            webVersion.toString());
                                    return;
                                } else if (webVersion.revision == currVersion.revision
                                        && currVersion.versionType != EnumVersionType.RELEASE) {
                                    ModCntManPack.UPD_LOG.printf(Level.INFO,
                                            "A stable release for %s is available: %s", currModName, webVersion);
                                    SAPUpdateManager.setHasUpdate(SAPUpdateManager.this.mgrId,
                                            webVersion.toString());
                                    return;
                                }
                            }
                        }
                    }

                    ModCntManPack.UPD_LOG.printf(Level.INFO, "No new update for %s is available.", currModName);
                } catch (IOException ioex) {
                    ModCntManPack.UPD_LOG.printf(Level.WARN,
                            String.format("Update Check for %s failed!", SAPUpdateManager.this.modName), ioex);
                }

                SAPUpdateManager.setChecked(SAPUpdateManager.this.getId());
            }
        };

        new Thread(threadProcessor, "SAPUpdateThread").start();
    }

    public void checkForUpdate() {
        if (!this.checkedForUpdate) {
            this.check();
            this.checkedForUpdate = true;
        }
    }

    public Version getVersion() {
        return this.version;
    }

    public String getModName() {
        return this.modName;
    }

    public URL getUpdateURL() {
        return this.updURL;
    }

    public String getModInfoURL() {
        return this.modInfoURL;
    }

    public File getModJar() {
        return this.modPackedJar;
    }

    public boolean isModJarValid() {
        return this.modPackedJar != null && this.modPackedJar.isFile()
                && this.modPackedJar.getName().endsWith(".jar");
    }

    public int getId() {
        return this.mgrId;
    }

    public UpdateFile getUpdateInfo() {
        return this.updInfo;
    }

    public EnumUpdateSeverity getVersionDiffSeverity() {
        if (this.updInfo.severityOverride != null && this.updInfo.severityOverride.length() > 0) {
            return EnumUpdateSeverity.valueOf(this.updInfo.severityOverride);
        }

        Version updVersion = new Version(this.updInfo.version);

        if (updVersion.major > this.version.major) {
            return EnumUpdateSeverity.SEVERE;
        } else if (updVersion.major == this.version.major) {
            if (updVersion.minor >= this.version.minor + 4) {
                return EnumUpdateSeverity.SEVERE;
            } else if (updVersion.minor > this.version.minor) {
                return EnumUpdateSeverity.MAJOR;
            } else if (updVersion.minor == this.version.minor) {
                if (updVersion.revision >= this.version.revision + 8) {
                    return EnumUpdateSeverity.SEVERE;
                } else if (updVersion.revision >= this.version.revision + 4) {
                    return EnumUpdateSeverity.MAJOR;
                } else if (updVersion.revision > this.version.revision) {
                    return EnumUpdateSeverity.MINOR;
                }
            }
        }

        return EnumUpdateSeverity.UNKNOWN;
    }

    public void runUpdate() {
        URL dl = this.updInfo.getDownload();
        if (dl != null) {
            this.downloader = new UpdateDownloader(dl, this.modPackedJar);
        }
    }

    public static class UpdateFile {
        @JsonRequired
        public String version;
        public String downloadUrl;
        public String description;
        public String severityOverride;
        public String[] changelog;

        public UpdateFile() {
        }

        public URL getDownload() {
            try {
                URL dl = new URL(this.downloadUrl);
                dl.toURI();
                return dl;
            } catch (MalformedURLException | URISyntaxException e) {
                return null;
            }
        }

        public Version getVersionInst() {
            return new Version(version);
        }
    }

    public enum EnumUpdateSeverity {
        MINOR(EnumChatFormatting.GREEN), MAJOR(EnumChatFormatting.YELLOW), SEVERE(EnumChatFormatting.RED), UNKNOWN(
                EnumChatFormatting.WHITE);

        public final EnumChatFormatting format;

        EnumUpdateSeverity(EnumChatFormatting formatting) {
            this.format = formatting;
        }
    }

    public static final class Version implements Cloneable {
        public final int revision;
        public final int minor;
        public final int major;
        public final EnumVersionType versionType;
        public final int preVersionNr;

        private static final Pattern[] VERSION_PATTERNS = new Pattern[] {
                // {1.7.10-}1.0.0{-beta{.2}}
                Pattern.compile(
                        "(\\d+.\\d+[\\.|_]\\d+-)?(?<major>\\d+)\\.(?<minor>\\d+)[\\.|_](?<revision>\\d+)(-(?<prType>alpha|beta|rc)(\\.(?<prNr>\\d+))?)?") };

        public Version(int majorNr, int minorNr, int revisionNr) {
            this.major = majorNr;
            this.minor = minorNr;
            this.revision = revisionNr;
            this.versionType = EnumVersionType.RELEASE;
            this.preVersionNr = 0;
        }

        public Version(int majorNr, int minorNr, int revisionNr, EnumVersionType type, int preVersionNr) {
            this.major = majorNr;
            this.minor = minorNr;
            this.revision = revisionNr;
            this.versionType = type;
            this.preVersionNr = preVersionNr;
        }

        public Version(String version) {
            if (version != null) {
                Matcher matcher;
                for (Pattern verPattern : VERSION_PATTERNS) {
                    matcher = verPattern.matcher(version);
                    if (matcher.find()) {
                        this.major = Integer.valueOf(matcher.group("major"));
                        this.minor = Integer.valueOf(matcher.group("minor"));
                        this.revision = Integer.valueOf(matcher.group("revision"));
                        if (matcher.group("prType") != null) {
                            String prType = matcher.group("prType");
                            switch (prType) {
                            case "alpha":
                                this.versionType = EnumVersionType.ALPHA;
                                break;
                            case "beta":
                                this.versionType = EnumVersionType.BETA;
                                break;
                            case "rc":
                                this.versionType = EnumVersionType.RELEASECANDIDATE;
                                break;
                            default:
                                this.versionType = EnumVersionType.UNKNOWN;
                            }

                            this.preVersionNr = matcher.group("prNr") != null
                                    ? Integer.valueOf(matcher.group("prNr"))
                                    : 1;
                        } else {
                            this.versionType = EnumVersionType.RELEASE;
                            this.preVersionNr = 0;
                        }
                        return;
                    }
                }
            }

            this.major = -1;
            this.minor = -1;
            this.revision = -1;
            this.versionType = EnumVersionType.UNKNOWN;
            this.preVersionNr = -1;
            //           FMLLog.log(ModCntManPack.UPD_LOG, Level.WARN,
            //                      "Version Number for the mod %s could not be compiled! The version number %s does not have the required formatting!",
            //                      this.modName, version);
        }

        @Override
        public String toString() {
            if (this.versionType == EnumVersionType.RELEASE) {
                return String.format("%d.%d.%d", this.major, this.minor, this.revision);
            } else {
                return String.format("%d.%d.%d-%s.%d", this.major, this.minor, this.revision, this.versionType,
                        this.preVersionNr);
            }
        }

        @Override
        public Version clone() {
            return new Version(this.major, this.minor, this.revision, this.versionType, this.preVersionNr);
        }
    }

    public enum EnumVersionType {
        ALPHA("alpha"), BETA("beta"), RELEASECANDIDATE("rc"), RELEASE, UNKNOWN;

        private final String versionStr;

        EnumVersionType() {
            this.versionStr = this.name();
        }

        EnumVersionType(String verStr) {
            this.versionStr = verStr;
        }

        @Override
        public String toString() {
            return this.versionStr;
        }
    }

    /**
     * code from http://stackoverflow.com/a/14245807
     */

    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.FIELD)
    @interface JsonRequired {
    }

    static class AnnotatedDeserializer<T> implements JsonDeserializer<T> {
        public T deserialize(JsonElement je, Type type, JsonDeserializationContext jdc) throws JsonParseException {
            T pojo = new Gson().fromJson(je, type);

            Field[] fields = pojo.getClass().getDeclaredFields();
            for (Field f : fields) {
                if (f.getAnnotation(JsonRequired.class) != null) {
                    try {
                        f.setAccessible(true);
                        if (f.get(pojo) == null) {
                            throw new JsonParseException("Missing field in JSON: " + f.getName());
                        }
                    } catch (IllegalArgumentException | IllegalAccessException ex) {
                        ModCntManPack.UPD_LOG.log(Level.WARN, ex, null);
                    }
                }
            }

            return pojo;
        }
    }
}