br.com.blackhubos.eventozero.updater.github.searcher.GitHubSearcher.java Source code

Java tutorial

Introduction

Here is the source code for br.com.blackhubos.eventozero.updater.github.searcher.GitHubSearcher.java

Source

/**
 *
 * EventoZero - Advanced event factory and executor for Bukkit and Spigot.
 * Copyright  2016 BlackHub OS and contributors.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 *
 */
package br.com.blackhubos.eventozero.updater.github.searcher;

import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;
import org.json.simple.parser.ParseException;

import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.regex.Pattern;

import br.com.blackhubos.eventozero.updater.assets.Asset;
import br.com.blackhubos.eventozero.updater.assets.versions.Version;
import br.com.blackhubos.eventozero.updater.formater.BooleanFormatter;
import br.com.blackhubos.eventozero.updater.formater.MultiTypeFormatter;
import br.com.blackhubos.eventozero.updater.github.formatter.GitHubDateFormatter;
import br.com.blackhubos.eventozero.updater.searcher.Searcher;
import br.com.blackhubos.eventozero.util.ThreadUtils;

public class GitHubSearcher implements Searcher {

    private static final String GITHUB_RELEASES_URL = "https://github.com/BlackHubOS/EventoZero/releases";
    private static final String GITHUB_API_URL = "https://api.github.com/repos/BlackHubOS/EventoZero/";
    private static final String RELEASES_PATH = "releases";
    private static final String LATEST_PATH = "/latest";
    private static final String TAG_PATH = "/tags";

    @Override
    public Optional<Version> getLatestVersion() {
        try {
            // Conecta ao URL de ultimas verses
            Optional<Collection<Version>> versions = connect(Optional.of(LATEST_PATH));
            // Verifica se h alguma
            if (versions.isPresent()) {
                // Retorna a primeira
                Iterator<Version> versionIterator = versions.get().iterator();
                return Optional.of(versionIterator.next());
            }
        } catch (IOException | ParseException | java.text.ParseException e) {
            e.printStackTrace();
        }
        return Optional.empty();
    }

    @Override
    public Optional<Version> getLatestVersionFor(String mcVersion) {
        Objects.requireNonNull(mcVersion);
        // Faz um loop em todas verses
        for (Version version : getAllVersion()) {
            // Faz um loop em todas verses suportadas
            for (String supported : version.getSupportedVersions()) {
                // Verifica se a verso do minecraft inicia com a verso suportada
                if (mcVersion.startsWith(supported)) {
                    // Retorna a verso
                    return Optional.of(version);
                }
            }
        }

        return Optional.empty();
    }

    @Override
    public Optional<Version> getLatestStableVersion() {

        try {
            // Conecta ao URL de todas verses
            Optional<Collection<Version>> versions = connect(Optional.empty());

            // Verifica se encontrou alguma (NullPointerException jamais)
            if (versions.isPresent()) {
                // Obtem a lista e ordena ela
                Collection<Version> versionCollection = versions.get();
                List<Version> versionList = Version.sortVersions(versionCollection, true);

                // Faz um loop para verificar qual a ultima verso sem bug critico ou em pre-release.
                // Se a ultima verso disponivel estiver sem bug ela ser retornada
                for (Version version : versionList) {
                    if (!version.isCriticalBug() && !version.isPreRelease()) {
                        return Optional.of(version);
                    }
                }
            }
        } catch (IOException | ParseException | java.text.ParseException e) {
            e.printStackTrace();
        }
        return Optional.empty();
    }

    @Override
    public Optional<Version> getLatestStableVersionFor(String mcVersion) {
        Objects.requireNonNull(mcVersion);
        // Faz um loop em todas verses
        for (Version version : getAllVersion()) {
            // Pula a verso se ela no for estvel
            if (version.isCriticalBug() || version.isPreRelease())
                continue;
            // Faz um loop em todas verses suportadas
            for (String supported : version.getSupportedVersions()) {
                // Verifica se a verso do minecraft inicia com a verso suportada
                if (mcVersion.startsWith(supported)) {
                    // Retorna a verso
                    return Optional.of(version);
                }
            }
        }

        return Optional.empty();
    }

    @Override
    public Collection<Version> getAllVersion() {
        try {
            // Conecta ao URL de todas verses
            Optional<Collection<Version>> versions = connect(Optional.empty());

            // Verifica se encontrou alguma (NullPointerException no sentirei saudades)
            if (versions.isPresent()) {
                // Retorna a lista de verses ordenadas
                return Version.sortVersions(versions.get(), true);
            }
        } catch (IOException | ParseException | java.text.ParseException e) {
            e.printStackTrace();
        }
        // Retorna uma lista vazia caso tenhamos algum erro ao obter verso
        return Collections.emptyList();
    }

    @Override
    public Optional<Version> findVersion(String tag) {
        try {
            // Conecta ao URL de todas verses
            Optional<Collection<Version>> versions = connect(Optional.of(TAG_PATH + "/" + tag));

            // Verifica se encontrou alguma (NullPointerException cade voc?)
            if (versions.isPresent()) {
                // Retorna a primeira (que ser a unica neste caso)
                Iterator<Version> versionIterator = versions.get().iterator();
                return Optional.of(versionIterator.next());
            }
        } catch (IOException | ParseException | java.text.ParseException ignored) {
        }
        // Retorna uma lista vazia caso no encontre a verso informada
        return Optional.empty();
    }

    @Override
    public String getReleasesUrl() {
        return GITHUB_RELEASES_URL;
    }

    private Optional<Collection<Version>> connect(Optional<String> additionalUrl)
            throws IOException, ParseException, java.text.ParseException {
        // Formatadores de valor a partir de textos e objetos
        MultiTypeFormatter multiFormatter = new MultiTypeFormatter();
        BooleanFormatter booleanFormatter = new BooleanFormatter();
        GitHubDateFormatter dateFormatter = new GitHubDateFormatter();
        multiFormatter.registerFormatter(booleanFormatter);
        multiFormatter.registerFormatter(dateFormatter);

        // Coleo de todas as verses
        Collection<Version> versionList = new LinkedList<>();

        // Conexo e abertura de stream para obter o JSON
        URL gitHubUrl = new URL(
                GITHUB_API_URL + RELEASES_PATH + (additionalUrl.isPresent() ? additionalUrl.get() : ""));
        URLConnection urlConnection = gitHubUrl.openConnection();
        InputStream inputStream = urlConnection.getInputStream();

        // Parser do JSON
        JSONParser parser = new JSONParser();
        Object array = parser.parse(new InputStreamReader(inputStream));

        // Verifica se  uma array (ou seja, se tem varias versoes)
        if (array instanceof JSONArray) {
            JSONArray jsonArray = (JSONArray) array;

            // Faz loop em todos objetos da array
            for (Object jsonObj : jsonArray) {
                // Verifica um objeto (para evitar problemas de ClassCannotCastException)
                if (jsonObj instanceof JSONObject) {
                    // Processa a verso
                    processJsonObject((JSONObject) jsonObj, multiFormatter, versionList);
                }
            }
        } else {
            // Caso no seja vrias verses processa somente 1
            processJsonObject((JSONObject) array, multiFormatter, versionList);
        }

        return (!versionList.isEmpty() ? Optional.of(versionList) : Optional.empty());
    }

    @SuppressWarnings("unchecked")
    private void processJsonObject(JSONObject jobject, MultiTypeFormatter formatter,
            Collection<Version> versionList) {
        /**
         * Variaveis do {@link Version}
         */
        String name = null;
        String version = null;
        Collection<Asset> downloadUrl = new ArrayList<>();
        String commitish = null;
        String changelog = null;
        Date creationDate = null;
        Date publishDate = null;
        long id = Long.MIN_VALUE;
        boolean criticalBug = false;
        boolean preRelease = false;
        List<String> supportedVersions = new ArrayList<>();
        /**
         * /Variaveis do {@link Version}
         */

        for (Map.Entry object : (Set<Map.Entry>) jobject.entrySet()) {

            Object key = object.getKey();
            Object value = object.getValue();
            String stringValue = String.valueOf(value);
            switch (GitHubAPIInput.parseObject(key)) {
            // Tag geralmente  a verso
            case TAG_NAME: {
                version = stringValue;
                break;
            }

            // Data de criao
            case CREATED_AT: {
                creationDate = formatter.format(stringValue, Date.class).get();
                break;
            }

            // Data de publicao
            case PUBLISHED_AT: {
                publishDate = formatter.format(stringValue, Date.class).get();
                break;
            }

            // Assets/Artefatos ou Arquivos (processado externamente)
            case ASSETS: {
                // Array com multiplos artefatos
                JSONArray jsonArray = (JSONArray) value;

                for (Object assetsJsonObject : jsonArray) {
                    // Obtem o objeto a partir da array de artefatos
                    JSONObject jsonAsset = (JSONObject) assetsJsonObject;
                    // Obtm o artefato a partir do objeto
                    Optional<Asset> assetOptional = Asset.parseJsonObject(jsonAsset, formatter);
                    //  bom evitar um null n :P
                    if (assetOptional.isPresent()) {
                        // Adiciona o artefato caso ele seja encontrado
                        downloadUrl.add(assetOptional.get());
                    }
                }
                break;
            }

            // Obtem o nome (titulo) da verso
            case NAME: {
                name = stringValue;
                break;
            }

            // Numero de identificao do GitHub (nem sei se vamos usar)
            case ID: {
                id = Long.parseLong(stringValue);
                break;
            }

            // Obtm a mensagem, geralmente nosso changelog, e define se  uma verso de bug critico
            case BODY: {
                changelog = stringValue;
                // Define se  verso de bug critico
                criticalBug = changelog.endsWith("!!!CRITICAL BUG FOUND!!!")
                        || changelog.endsWith("CRITICAL BUG FOUND") || changelog.endsWith("CRITICAL BUG");

                // Regex para obter a linha que diz as verses suportadas
                Pattern supportedPattern = Pattern.compile("^(Verses|Supported)", Pattern.CASE_INSENSITIVE);

                // Faz loop nas linhas
                for (String line : changelog.split("\n")) {
                    // Procura o regex na linha
                    if (supportedPattern.matcher(line).find()) {
                        // Remove as letras
                        line = line.replaceAll("[^\\d. ]+", "").trim();
                        // Adiciona a lista
                        supportedVersions.addAll(Arrays.asList(line.split(" ")));
                    }
                }

                break;
            }

            // Formata a boolean e verifica se ela  uma pre-release (alpha, beta, etc)
            case PRERELEASE: {
                Optional<Boolean> booleanOptional = formatter.format(value, Boolean.class);

                // Evitar um nullinho :D
                if (!booleanOptional.isPresent()) {
                    preRelease = false;
                    break;
                }

                preRelease = booleanOptional.get();
                break;
            }

            // Commitish geralmente  a branch ou a Commit relacionada a verso
            case TARGET_COMMITISH: {
                commitish = stringValue;
                break;
            }

            default: {
                break;
            }
            }
        }

        // Verifica se o ID  Diferente do valor minimo, isto vai fazer com que ns saibamos se alguma verso foi encontrada ou no :D
        if (id != Long.MIN_VALUE) {
            // Cria uma nova verso e adiciona a lista
            Version versionInstance = new Version(name, version, supportedVersions, downloadUrl, commitish,
                    changelog, creationDate, publishDate, id, criticalBug, preRelease);
            versionList.add(versionInstance);
        }
    }

    /**
     * Neste ENUM esto os valores que iremos obter, no momento so somente estes (nem sei porque
     * tanto)
     */
    enum GitHubAPIInput {
        UNKNOWN, TAG_NAME, CREATED_AT, BODY, ASSETS, PRERELEASE, TARGET_COMMITISH, NAME, ID, PUBLISHED_AT;

        // Obter um dos valores acima a partir de um objeto
        public static GitHubAPIInput parseObject(Object objectToParse) {
            if (objectToParse instanceof String) {
                String stringToParse = (String) objectToParse;
                try {
                    return GitHubAPIInput.valueOf(stringToParse.toUpperCase());
                } catch (IllegalArgumentException ignored) {
                }
            }

            return GitHubAPIInput.UNKNOWN;
        }

    }

}