itdelatrisu.opsu.Utils.java Source code

Java tutorial

Introduction

Here is the source code for itdelatrisu.opsu.Utils.java

Source

/*
 * opsu! - an open-source osu! client
 * Copyright (C) 2014, 2015 Jeffrey Han
 *
 * opsu! 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.
 *
 * opsu! 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 opsu!.  If not, see <http://www.gnu.org/licenses/>.
 */

package itdelatrisu.opsu;

import com.sun.istack.internal.Nullable;
import itdelatrisu.opsu.downloads.Download;

import java.io.*;
import java.net.HttpURLConnection;
import java.net.SocketTimeoutException;
import java.net.URL;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.cert.X509Certificate;
import java.util.Arrays;
import java.util.Scanner;
import java.util.jar.JarFile;

import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.lwjgl.input.Keyboard;
import org.newdawn.slick.Animation;
import org.newdawn.slick.Color;
import org.newdawn.slick.util.Log;

import com.sun.jna.platform.FileUtils;
import yugecin.opsudance.core.DisplayContainer;
import yugecin.opsudance.core.NotNull;
import yugecin.opsudance.core.errorhandling.ErrorHandler;

import static yugecin.opsudance.core.InstanceContainer.*;

/**
 * Contains miscellaneous utilities.
 */
public class Utils {
    /**
     * List of illegal filename characters.
     * @see #cleanFileName(String, char)
     */
    private final static int[] illegalChars = { 34, 60, 62, 124, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
            15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 58, 42, 63, 92, 47 };
    static {
        Arrays.sort(illegalChars);
    }

    // This class should not be instantiated.
    private Utils() {
    }

    /**
     * Initializes game settings and class data.
     */
    public static void init(DisplayContainer displayContainer) {
        // TODO clean this up

        // game settings
    }

    /**
     * Draws an animation based on its center.
     * @param anim the animation to draw
     * @param x the center x coordinate
     * @param y the center y coordinate
     */
    public static void drawCentered(Animation anim, float x, float y) {
        anim.draw(x - (anim.getWidth() / 2f), y - (anim.getHeight() / 2f));
    }

    /**
     * Returns the luminance of a color.
     * @param c the color
     */
    public static float getLuminance(Color c) {
        return 0.299f * c.r + 0.587f * c.g + 0.114f * c.b;
    }

    /**
     * Clamps a value between a lower and upper bound.
     * @param val the value to clamp
     * @param low the lower bound
     * @param high the upper bound
     * @return the clamped value
     * @author fluddokt
     */
    public static int clamp(int val, int low, int high) {
        if (val < low)
            return low;
        if (val > high)
            return high;
        return val;
    }

    /**
     * Clamps a value between a lower and upper bound.
     * @param val the value to clamp
     * @param low the lower bound
     * @param high the upper bound
     * @return the clamped value
     * @author fluddokt
     */
    public static float clamp(float val, float low, float high) {
        if (val < low)
            return low;
        if (val > high)
            return high;
        return val;
    }

    /**
     * Clamps a value between a lower and upper bound.
     * @param val the value to clamp
     * @param low the lower bound
     * @param high the upper bound
     * @return the clamped value
     * @author fluddokt
     */
    public static double clamp(double val, double low, double high) {
        if (val < low)
            return low;
        if (val > high)
            return high;
        return val;
    }

    /**
     * Returns the distance between two points.
     * @param x1 the x-component of the first point
     * @param y1 the y-component of the first point
     * @param x2 the x-component of the second point
     * @param y2 the y-component of the second point
     * @return the Euclidean distance between points (x1,y1) and (x2,y2)
     */
    public static float distance(float x1, float y1, float x2, float y2) {
        float v1 = x1 - x2;
        float v2 = y1 - y2;
        return (float) Math.sqrt(v1 * v1 + v2 * v2);
    }

    public static double distance(double x1, double y1, double x2, double y2) {
        double dx = x1 - x2;
        double dy = y1 - y2;
        return Math.sqrt(dx * dx + dy * dy);
    }

    /**
     * Linear interpolation of a and b at t.
     */
    public static float lerp(float a, float b, float t) {
        return a * (1 - t) + b * t;
    }

    /**
     * Returns true if a game input key is pressed (mouse/keyboard left/right).
     * @return true if pressed
     */
    public static boolean isGameKeyPressed() {
        /*
        boolean mouseDown = !Options.isMouseDisabled() && (
        input.isMouseButtonDown(Input.MOUSE_LEFT_BUTTON) ||
        input.isMouseButtonDown(Input.MOUSE_RIGHT_BUTTON));
        return (mouseDown ||
        input.isKeyDown(Options.getGameKeyLeft()) ||
        input.isKeyDown(Options.getGameKeyRight()));
        */
        return true;
    }

    /**
     * Returns a human-readable representation of a given number of bytes.
     * @param bytes the number of bytes
     * @return the string representation
     * @author aioobe (http://stackoverflow.com/a/3758880)
     */
    public static String bytesToString(long bytes) {
        if (bytes < 1024)
            return bytes + " B";
        int exp = (int) (Math.log(bytes) / Math.log(1024));
        char pre = "KMGTPE".charAt(exp - 1);
        return String.format("%.1f %cB", bytes / Math.pow(1024, exp), pre);
    }

    /**
     * Cleans a file name.
     * @param badFileName the original name string
     * @param replace the character to replace illegal characters with (or 0 if none)
     * @return the cleaned file name
     * @author Sarel Botha (http://stackoverflow.com/a/5626340)
     */
    public static String cleanFileName(String badFileName, char replace) {
        if (badFileName == null)
            return null;

        boolean doReplace = (replace > 0 && Arrays.binarySearch(illegalChars, replace) < 0);
        StringBuilder cleanName = new StringBuilder();
        for (int i = 0, n = badFileName.length(); i < n; i++) {
            int c = badFileName.charAt(i);
            if (Arrays.binarySearch(illegalChars, c) < 0)
                cleanName.append((char) c);
            else if (doReplace)
                cleanName.append(replace);
        }
        return cleanName.toString();
    }

    /**
     * Deletes a file or directory.  If a system trash directory is available,
     * the file or directory will be moved there instead.
     * @param file the file or directory to delete
     * @return true if moved to trash, and false if deleted
     * @throws IOException if given file does not exist
     */
    public static boolean deleteToTrash(File file) throws IOException {
        if (file == null)
            throw new IOException("File cannot be null.");
        if (!file.exists())
            throw new IOException(String.format("File '%s' does not exist.", file.getAbsolutePath()));

        // move to system trash, if possible
        FileUtils fileUtils = FileUtils.getInstance();
        if (fileUtils.hasTrash()) {
            try {
                fileUtils.moveToTrash(new File[] { file });
                return true;
            } catch (IOException e) {
                Log.warn(String.format("Failed to move file '%s' to trash.", file.getAbsolutePath()), e);
            }
        }

        // delete otherwise
        if (file.isDirectory())
            deleteDirectory(file);
        else
            file.delete();
        return false;
    }

    /**
     * Recursively deletes all files and folders in a directory, then
     * deletes the directory itself.
     * @param dir the directory to delete
     */
    public static void deleteDirectory(File dir) {
        if (dir == null || !dir.isDirectory())
            return;

        // recursively delete contents of directory
        File[] files = dir.listFiles();
        if (files != null && files.length > 0) {
            for (File file : files) {
                if (file.isDirectory())
                    deleteDirectory(file);
                else
                    file.delete();
            }
        }

        // delete the directory
        dir.delete();
    }

    /**
     * Returns a the contents of a URL as a string.
     * @param url the remote URL
     * @return the contents as a string, or null if any error occurred
     * @author Roland Illig (http://stackoverflow.com/a/4308662)
     * @throws IOException if an I/O exception occurs
     */
    public static String readDataFromUrl(URL url) throws IOException {
        // open connection
        HttpURLConnection conn = (HttpURLConnection) url.openConnection();
        conn.setConnectTimeout(Download.CONNECTION_TIMEOUT);
        conn.setReadTimeout(Download.READ_TIMEOUT);
        conn.setUseCaches(false);
        try {
            conn.connect();
        } catch (SocketTimeoutException e) {
            Log.warn("Connection to server timed out.", e);
            throw e;
        }

        if (Thread.interrupted())
            return null;

        // read contents
        try (InputStream in = conn.getInputStream()) {
            BufferedReader rd = new BufferedReader(new InputStreamReader(in));
            StringBuilder sb = new StringBuilder();
            int c;
            while ((c = rd.read()) != -1)
                sb.append((char) c);
            return sb.toString();
        } catch (SocketTimeoutException e) {
            Log.warn("Connection to server timed out.", e);
            throw e;
        }
    }

    /**
     * Returns a JSON object from a URL.
     * @param url the remote URL
     * @return the JSON object, or null if an error occurred
     * @throws IOException if an I/O exception occurs
     */
    public static JSONObject readJsonObjectFromUrl(URL url) throws IOException {
        String s = Utils.readDataFromUrl(url);
        JSONObject json = null;
        if (s != null) {
            try {
                json = new JSONObject(s);
            } catch (JSONException e) {
                ErrorHandler.error("Failed to create JSON object.", e).show();
            }
        }
        return json;
    }

    /**
     * Returns a JSON array from a URL.
     * @param url the remote URL
     * @return the JSON array, or null if an error occurred
     * @throws IOException if an I/O exception occurs
     */
    public static JSONArray readJsonArrayFromUrl(URL url) throws IOException {
        String s = Utils.readDataFromUrl(url);
        JSONArray json = null;
        if (s != null) {
            try {
                json = new JSONArray(s);
            } catch (JSONException e) {
                ErrorHandler.error("Failed to create JSON array.", e).show();
            }
        }
        return json;
    }

    /**
     * Converts an input stream to a string.
     * @param is the input stream
     * @author Pavel Repin, earcam (http://stackoverflow.com/a/5445161)
     */
    public static String convertStreamToString(InputStream is) {
        try (Scanner s = new Scanner(is)) {
            return s.useDelimiter("\\A").hasNext() ? s.next() : "";
        }
    }

    /**
     * Returns the md5 hash of a file in hex form.
     * @param file the file to hash
     * @return the md5 hash
     */
    public static String getMD5(File file) {
        try {
            InputStream in = new BufferedInputStream(new FileInputStream(file));
            MessageDigest md = MessageDigest.getInstance("MD5");
            byte[] buf = new byte[4096];

            while (true) {
                int len = in.read(buf);
                if (len < 0)
                    break;
                md.update(buf, 0, len);
            }
            in.close();

            byte[] md5byte = md.digest();
            StringBuilder result = new StringBuilder();
            for (byte b : md5byte)
                result.append(String.format("%02x", b));
            return result.toString();
        } catch (NoSuchAlgorithmException | IOException e) {
            ErrorHandler.error("Failed to calculate MD5 hash.", e).show();
        }
        return null;
    }

    /**
     * Returns a formatted time string for a given number of seconds.
     * @param seconds the number of seconds
     * @return the time as a readable string
     */
    public static String getTimeString(int seconds) {
        if (seconds < 60)
            return (seconds == 1) ? "1 second" : String.format("%d seconds", seconds);
        else if (seconds < 3600)
            return String.format("%02d:%02d", seconds / 60, seconds % 60);
        else
            return String.format("%02d:%02d:%02d", seconds / 3600, (seconds / 60) % 60, seconds % 60);
    }

    /**
     * Parses the integer string argument as a boolean:
     * {@code 1} is {@code true}, and all other values are {@code false}.
     * @param s the {@code String} containing the boolean representation to be parsed
     * @return the boolean represented by the string argument
     */
    public static boolean parseBoolean(String s) {
        return (Integer.parseInt(s) == 1);
    }

    /**
     * Returns the git hash of the remote-tracking branch 'origin/master' from the
     * most recent update to the working directory (e.g. fetch or successful push).
     * @return the 40-character SHA-1 hash, or null if it could not be determined
     */
    @Nullable
    public static String getGitHash() {
        if (env.isJarRunning)
            return null;
        File f = new File(".git/refs/remotes/origin/master");
        if (!f.isFile())
            f = new File("../.git/refs/remotes/origin/master");
        if (!f.isFile())
            return null;
        try (BufferedReader in = new BufferedReader(new FileReader(f))) {
            char[] sha = new char[40];
            if (in.read(sha, 0, sha.length) < sha.length)
                return null;
            for (int i = 0; i < sha.length; i++) {
                if (Character.digit(sha[i], 16) == -1)
                    return null;
            }
            return String.valueOf(sha);
        } catch (IOException e) {
            return null;
        }
    }

    /**
     * Switches validation of SSL certificates on or off by installing a default
     * all-trusting {@link TrustManager}.
     * @param enabled whether to validate SSL certificates
     * @author neu242 (http://stackoverflow.com/a/876785)
     */
    public static void setSSLCertValidation(boolean enabled) {
        // create a trust manager that does not validate certificate chains
        TrustManager[] trustAllCerts = new TrustManager[] { new X509TrustManager() {
            @Override
            public X509Certificate[] getAcceptedIssuers() {
                return new X509Certificate[0];
            }

            @Override
            public void checkClientTrusted(X509Certificate[] certs, String authType) {
            }

            @Override
            public void checkServerTrusted(X509Certificate[] certs, String authType) {
            }
        } };

        // install the all-trusting trust manager
        try {
            SSLContext sc = SSLContext.getInstance("SSL");
            sc.init(null, enabled ? null : trustAllCerts, null);
            HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
        } catch (Exception e) {
        }
    }

    public static int getQuadrant(double x, double y) {
        if (x < displayContainer.width / 2d) {
            return y < displayContainer.height / 2d ? 2 : 3;
        }
        return y < displayContainer.height / 2d ? 1 : 4;
    }

    /*
    public static Color shiftHue(Color color, double H) {
       double U = Math.cos(H * Math.PI / 180d);
       double W = Math.sin(H * Math.PI / 180d);
        
       Color n = new Color(0, 0, 0);
       n.r = (float) ((0.299d + 0.701d * U + 0.168d * W) * color.r + (0.587d - 0.587d * U + 0.330d * W) * color.g + (0.114d - 0.114d * U - 0.497 * W) * color.b);
       n.g = (float) ((0.299 + 0.299 * U - 0.328 * W) * color.r + (0.587d - 0.413 * U + 0.035 * W) * color.g + (0.114d - 0.114d * U - 0.292 * W) * color.b);
       n.b = (float) ((0.299d + 0.300d * U + 1.250d * W) * color.r + (0.587d - 0.585d * U + 1.050d * W) * color.g + (0.114 - 0.886 * U - 0.203 * W) * color.b);
       return n;
    }
    */

    public static float[] mirrorPoint(float x, float y) {
        double dx = x - displayContainer.width / 2d;
        double dy = y - displayContainer.height / 2d;
        double ang = Math.atan2(dy, dx);
        double d = -Math.sqrt(dx * dx + dy * dy);
        return new float[] { (float) (displayContainer.width / 2d + Math.cos(ang) * d),
                (float) (displayContainer.height / 2d + Math.sin(ang) * d) };
    }

    public static float[] mirrorPoint(float x, float y, float degrees) {
        double dx = x - displayContainer.width / 2d;
        double dy = y - displayContainer.height / 2d;
        double ang = Math.atan2(dy, dx) + (degrees * Math.PI / 180d);
        double d = Math.sqrt(dx * dx + dy * dy);
        return new float[] { (float) (displayContainer.width / 2d + Math.cos(ang) * d),
                (float) (displayContainer.height / 2d + Math.sin(ang) * d) };
    }

    /**
     * Returns the file extension of a file.
     * @param file the file name
     */
    public static String getFileExtension(String file) {
        int i = file.lastIndexOf('.');
        return (i != -1) ? file.substring(i + 1).toLowerCase() : "";
    }

    public static boolean isValidGameKey(int key) {
        return (key != Keyboard.KEY_ESCAPE && key != Keyboard.KEY_SPACE && key != Keyboard.KEY_UP
                && key != Keyboard.KEY_DOWN && key != Keyboard.KEY_F7 && key != Keyboard.KEY_F10
                && key != Keyboard.KEY_F12);
    }

    public static void unpackFromJar(@NotNull JarFile jarfile, @NotNull File unpackedFile, @NotNull String filename)
            throws IOException {
        InputStream in = jarfile.getInputStream(jarfile.getEntry(filename));
        OutputStream out = new FileOutputStream(unpackedFile);

        byte[] buffer = new byte[65536];
        int bufferSize;
        while ((bufferSize = in.read(buffer, 0, buffer.length)) != -1) {
            out.write(buffer, 0, bufferSize);
        }

        in.close();
        out.close();
    }

}