diff --git a/pom.xml b/pom.xml index 1340001..49ee6f7 100755 --- a/pom.xml +++ b/pom.xml @@ -70,11 +70,6 @@ 4.12 test - - org.parabot - internal-api - 1.53.1 - diff --git a/src/main/java/org/parabot/api/Configuration.java b/src/main/java/org/parabot/api/Configuration.java new file mode 100644 index 0000000..c8d19c6 --- /dev/null +++ b/src/main/java/org/parabot/api/Configuration.java @@ -0,0 +1,14 @@ +package org.parabot.api; + +/** + * @author JKetelaar + */ +public class Configuration { + + public static final String BOT_TITLE = "Parabot"; + public static final String BOT_SLOGAN = "The best RuneScape private server bot"; + + public static final String V3_API_ENDPOINT = "http://v3.bdn.parabot.org/api/"; + + public static final String LIBRARIES_DOWNLOAD = V3_API_ENDPOINT + "bot/libraries/download"; +} diff --git a/src/main/java/org/parabot/api/calculations/Random.java b/src/main/java/org/parabot/api/calculations/Random.java new file mode 100644 index 0000000..b873087 --- /dev/null +++ b/src/main/java/org/parabot/api/calculations/Random.java @@ -0,0 +1,26 @@ +package org.parabot.api.calculations; + +/** + * A random class is used for generating random numbers + * + * @author Everel + */ +public class Random { + private final static java.util.Random RANDOM = new java.util.Random(); + + /** + * Randomizes a number between minimum and maximum + * + * @param min + * @param max + * @return randomized number + */ + public static int between(final int min, final int max) { + try { + return min + (max == min ? 0 : RANDOM.nextInt(max - min)); + } catch (Exception e) { + return min + (max - min); + } + } + +} \ No newline at end of file diff --git a/src/main/java/org/parabot/api/io/Directories.java b/src/main/java/org/parabot/api/io/Directories.java new file mode 100644 index 0000000..1dd3bcf --- /dev/null +++ b/src/main/java/org/parabot/api/io/Directories.java @@ -0,0 +1,277 @@ +package org.parabot.api.io; + +import org.parabot.api.misc.OperatingSystem; +import org.parabot.api.output.Verboser; + +import javax.swing.*; +import java.io.File; +import java.io.FilenameFilter; +import java.io.IOException; +import java.util.*; + +/** + * Holds and manages Parabot's used directories + * + * @author Everel + * @author JKetelaar + * @author Matt + */ +public class Directories { + + private static Map cached; + private static File temp = null; + + static { + cached = new HashMap<>(); + switch (OperatingSystem.getOS()) { + case WINDOWS: + cached.put("Root", new JFileChooser().getFileSystemView().getDefaultDirectory()); + break; + default: + cached.put("Root", new File(System.getProperty("user.home"))); + break; + } + + Verboser.verbose("Caching directories..."); + cached.put("Root", getDefaultDirectory()); + cached.put("Workspace", new File(cached.get("Root"), "/2006Scape/")); + cached.put("Sources", new File(cached.get("Root"), "/2006Scape/scripts/sources/")); + cached.put("Compiled", new File(cached.get("Root"), "/2006Scape/scripts/compiled/")); + cached.put("Resources", new File(cached.get("Root"), "/2006Scape/scripts/resources/")); + cached.put("Settings", new File(cached.get("Root"), "/2006Scape/settings/")); + cached.put("Servers", new File(cached.get("Root"), "/2006Scape/servers/")); + cached.put("Cache", new File(cached.get("Root"), "/2006Scape/cache/")); + cached.put("Screenshots", new File(cached.get("Root"), "/2006Scape/screenshots/")); + Verboser.verbose("Directories cached."); + + clearCache(259200); + } + + /** + * Set script bin folder + * + * @param f + */ + public static void setScriptCompiledDirectory(File f) { + if (!f.isDirectory()) { + throw new IllegalArgumentException(f + "is not a directory."); + } + cached.put("Compiled", f); + } + + /** + * Set server bin folder + * + * @param f + */ + public static void setServerCompiledDirectory(File f) { + if (!f.isDirectory()) { + throw new IllegalArgumentException(f + "is not a directory."); + } + cached.put("Servers", f); + } + + /** + * Returns the root directory outside of the main Parabot folder. + * + * @return + */ + public static File getDefaultDirectory() { + return cached.get("Root"); + } + + /** + * Returns the Parabot folder. + * + * @return + */ + public static File getWorkspace() { + return cached.get("Workspace"); + } + + /** + * Returns the script sources folder. + * + * @return + */ + public static File getScriptSourcesPath() { + return cached.get("Sources"); + } + + /** + * Returns the compiled scripts folder. + * + * @return + */ + public static File getScriptCompiledPath() { + return cached.get("Compiled"); + } + + /** + * Returns the scripts resources folder. + * + * @return + */ + public static File getResourcesPath() { + return cached.get("Resources"); + } + + /** + * Returns the Parabot settings folder. + * + * @return + */ + public static File getSettingsPath() { + return cached.get("Settings"); + } + + /** + * Returns the Parabot servers folder. + * + * @return + */ + public static File getServerPath() { + return cached.get("Servers"); + } + + /** + * Returns the Parabot cache folder. + * + * @return + */ + public static File getCachePath() { + return cached.get("Cache"); + } + + /** + * Returns the redirected Home Directory + * + * @return + */ + public static File getHomeDir() { + return cached.get("Home"); + } + + /** + * Returns the screenshot folder. + * + * @return + */ + public static File getScreenshotDir() { + return cached.get("Screenshots"); + } + + /** + * Validates all directories and makes them if necessary + */ + public static void validate() { + final File defaultPath = getDefaultDirectory(); + if (defaultPath == null || !defaultPath.exists()) { + throw new RuntimeException("Default path not found"); + } + final Queue files = new LinkedList(); + files.addAll(cached.values()); + while (files.size() > 0) { + final File file = files.poll(); + if (!file.exists()) { + Verboser.verbose("Generating directory: " + file.getAbsolutePath()); + file.mkdirs(); + if (!file.exists()) { + System.err.println("Failed to make directory: " + file.getAbsolutePath()); + } + } + } + } + + public static File getTempDirectory() { + if (temp != null) { + return temp; + } + int randomNum = new Random().nextInt(999999999); + temp = new File(getResourcesPath(), randomNum + "/"); + temp.mkdirs(); + temp.deleteOnExit(); + return temp; + } + + /** + * Clears the cache based on the latest modification + * + * @param remove A long that represents the amount of seconds that a file may have since the latest modification + * @param force Defines if the cache folder, within user.home, should also be removed + */ + public static void clearCache(int remove, boolean force) { + File[] cache = getCachePath().listFiles(); + if (cache != null) { + for (File f : cache) { + if (f != null && System.currentTimeMillis() / 1000 - f.lastModified() / 1000 > remove) { + Verboser.verbose("Clearing " + (f.isDirectory() ? "directory " : "file ") + f.getName() + " from cache (" + (force ? "Force-cleared" : "Expired") + ")..."); + + try { + FileUtil.delete(f); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + } + } + + private static void clearCache(int remove) { + clearCache(remove, false); + } + + public static void clearCache() { + clearCache(0, true); + } + + /** + * @param file Directory to be removed + */ + private static void removeDirectory(File file, boolean root) { + if (file.isDirectory()) { + if (file.list().length == 0) { + file.delete(); + Verboser.verbose("Directory is deleted : " + + file.getAbsolutePath()); + } else { + String files[] = file.list(); + for (String temp : files) { + File fileDelete = new File(file, temp); + removeDirectory(fileDelete, false); + } + + if (file.list().length == 0) { + file.delete(); + Verboser.verbose("Directory is deleted: " + + file.getAbsolutePath()); + } + } + if (root) { + file.delete(); + } + } else { + file.delete(); + Verboser.verbose("File is deleted : " + file.getAbsolutePath()); + } + } + + /** + * Returns an array of files with from a given directory and a given extension + * + * @param directory The directory where should be searched + * @param extension The extension to be searched for, including the dot (like .json) + * @return An array of of files that match the request + */ + public static File[] listFilesWithExtension(File directory, final String extension) { + return directory.listFiles(new FilenameFilter() { + public boolean accept(File dir, String filename) { + return filename.endsWith(extension); + } + }); + } + + public static File[] listJSONFiles(File directory) { + return listFilesWithExtension(directory, ".json"); + } +} diff --git a/src/main/java/org/parabot/api/io/FileUtil.java b/src/main/java/org/parabot/api/io/FileUtil.java new file mode 100644 index 0000000..423698e --- /dev/null +++ b/src/main/java/org/parabot/api/io/FileUtil.java @@ -0,0 +1,123 @@ +package org.parabot.api.io; + +import org.parabot.api.output.Verboser; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.nio.channels.FileChannel; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; + +/** + * @author JKetelaar + */ +public class FileUtil { + + public static String getChecksum(File file) { + if (file.isFile()) { + try { + MessageDigest md = MessageDigest.getInstance("MD5"); + if (file.exists()) { + FileInputStream fis = new FileInputStream(file); + byte[] dataBytes = new byte[1024]; + + int nread; + + while ((nread = fis.read(dataBytes)) != -1) { + md.update(dataBytes, 0, nread); + } + + byte[] mdbytes = md.digest(); + + StringBuilder sb = new StringBuilder(""); + for (int i = 0; i < mdbytes.length; i++) { + sb.append(Integer.toString((mdbytes[i] & 0xff) + 0x100, 16).substring(1)); + } + + return sb.toString(); + } + } catch (NoSuchAlgorithmException | IOException e) { + e.printStackTrace(); + } + } + + return null; + } + + public static byte[] getChecksumBytes(File file) { + if (file.isFile()) { + try { + MessageDigest md = MessageDigest.getInstance("MD5"); + if (file.exists()) { + FileInputStream fis = new FileInputStream(file); + byte[] dataBytes = new byte[1024]; + + int nread; + + while ((nread = fis.read(dataBytes)) != -1) { + md.update(dataBytes, 0, nread); + } + + return md.digest(); + } + } catch (NoSuchAlgorithmException | IOException e) { + e.printStackTrace(); + } + } + + return null; + } + + public static void copyFile(File sourceFile, File destFile) + throws IOException { + if (!sourceFile.exists()) { + return; + } + if (!destFile.exists()) { + destFile.createNewFile(); + } + FileChannel source = null; + FileChannel destination = null; + source = new FileInputStream(sourceFile).getChannel(); + destination = new FileOutputStream(destFile).getChannel(); + if (source != null) { + destination.transferFrom(source, 0, source.size()); + } + if (source != null) { + source.close(); + } + destination.close(); + } + + public static void delete(File file) throws IOException { + if (file.isDirectory()) { + if (file.list().length == 0) { + + boolean success = file.delete(); + Verboser.verbose(success ? "Deleted " + file.getAbsolutePath() + " OK" : + "FAILED to delete " + file.getAbsolutePath()); + } else { + + String files[] = file.list(); + + for (String temp : files) { + File fileDelete = new File(file, temp); + delete(fileDelete); + } + + if (file.list().length == 0) { + boolean success = file.delete(); + Verboser.verbose(success ? "Deleted " + file.getAbsolutePath() + " OK" : + "FAILED to delete " + file.getAbsolutePath()); + } + } + + } else { + boolean success = file.delete(); + Verboser.verbose(success ? "Deleted " + file.getAbsolutePath() + " OK" : + "FAILED to delete " + file.getAbsolutePath()); + } + } +} \ No newline at end of file diff --git a/src/main/java/org/parabot/api/io/ImageUtil.java b/src/main/java/org/parabot/api/io/ImageUtil.java new file mode 100644 index 0000000..3b41156 --- /dev/null +++ b/src/main/java/org/parabot/api/io/ImageUtil.java @@ -0,0 +1,43 @@ +package org.parabot.api.io; + +import javax.imageio.ImageIO; +import java.awt.*; +import java.io.File; +import java.io.IOException; +import java.net.URL; + +/** + * @author Fryslan + */ +public class ImageUtil { + + /** + * Gets an image from local device + * + * @param location Location of the image + * @return image from location. + */ + public Image getLocalImage(String location) { + try { + File sourceimage = new File(location); + return ImageIO.read(sourceimage); + } catch (IOException e) { + return null; + } + } + + /** + * Gets an image from the given URL. + * + * @param url Url of th image. + * @return image from location. + */ + public Image getURLImage(String url) { + try { + URL sourceimage = new URL(url); + return ImageIO.read(sourceimage); + } catch (IOException e) { + return null; + } + } +} diff --git a/src/main/java/org/parabot/api/io/ProgressListener.java b/src/main/java/org/parabot/api/io/ProgressListener.java new file mode 100644 index 0000000..b23c1d5 --- /dev/null +++ b/src/main/java/org/parabot/api/io/ProgressListener.java @@ -0,0 +1,44 @@ +package org.parabot.api.io; + +/** + * Keeps track of a progress + * + * @author Everel + */ +public interface ProgressListener { + + /** + * Called when progress increased + * + * @param value + */ + void onProgressUpdate(double value); + + /** + * Updates upload speed + * + * @param mbPerSecond + */ + void updateDownloadSpeed(double mbPerSecond); + + /** + * Sets the message displayed + * @param message The Text to show + */ + void updateMessage(String message); + + /** + * Combination of the two + * @param message + * @param progress + */ + void updateMessageAndProgress(String message, double progress); + + /** + * Usage would be + *
double before = getCurrentProgress() 
setCurrent(0)
for 0 .. 100: setCurrent(x)
done!
setCurrent(before)
+ * @return + */ + double getCurrentProgress(); + +} diff --git a/src/main/java/org/parabot/api/io/SizeInputStream.java b/src/main/java/org/parabot/api/io/SizeInputStream.java new file mode 100644 index 0000000..7d0bace --- /dev/null +++ b/src/main/java/org/parabot/api/io/SizeInputStream.java @@ -0,0 +1,62 @@ +package org.parabot.api.io; + +import java.io.IOException; +import java.io.InputStream; + +/** + * @author Everel + */ +public class SizeInputStream extends InputStream { + public int bytesRead; + private ProgressListener l; + private InputStream in; + private long startTime; + private double size; + + public SizeInputStream(InputStream in, int size, ProgressListener l) { + this.in = in; + this.size = size; + this.l = l; + this.startTime = System.currentTimeMillis(); + } + + public int available() { + return ((int) size - bytesRead); + } + + public int read() throws IOException { + int b = in.read(); + if (b != -1) { + bytesRead++; + } + updateListener(); + return b; + } + + public int read(byte[] b) throws IOException { + int read = in.read(b); + bytesRead += read; + updateListener(); + return read; + } + + public int read(byte[] b, int off, int len) throws IOException { + int read = in.read(b, off, len); + bytesRead += read; + updateListener(); + return read; + } + + private void updateListener() { + if (l != null) { + double percent = (bytesRead / size) * 100.0D; + l.onProgressUpdate(percent); + + long curTime = System.currentTimeMillis(); + double timeSeconds = (curTime - startTime) / 1000.0D; + double speed = bytesRead / (1024.0D * 1024.0D) / timeSeconds; + l.updateDownloadSpeed(speed); + } + } +} + diff --git a/src/main/java/org/parabot/api/io/WebUtil.java b/src/main/java/org/parabot/api/io/WebUtil.java new file mode 100644 index 0000000..6eaa746 --- /dev/null +++ b/src/main/java/org/parabot/api/io/WebUtil.java @@ -0,0 +1,330 @@ +package org.parabot.api.io; + +import org.json.simple.parser.JSONParser; +import org.parabot.api.output.Verboser; + +import java.io.*; +import java.net.*; + +/** + * A WebUtil class fetches data from an URL + * + * @author Everel + */ +public class WebUtil { + + private static JSONParser jsonParser; + + private static String agent = "Mozilla/5.0 (Wind0ws NT 6.1; WOW64) AppleWebKit/535.1 (KHTML, like Gecko) Chrome/13.0.782.112 Safari/535.1"; + + /** + * Gets useragent + * + * @return useragent + */ + public static String getUserAgent() { + return agent; + } + + /** + * Agent to set at a URL connection + * + * @param userAgent + */ + public static void setUserAgent(final String userAgent) { + agent = userAgent; + } + + /** + * Fetches content of a page + * + * @param location + * @return contents of page + * @throws MalformedURLException + */ + public static String getContents(final String location) + throws MalformedURLException { + return getContents(new URL(location)); + } + + public static String getContents(final String location, String parameters) throws MalformedURLException { + return getContents(new URL(location), parameters); + } + + /** + * Get contents from URL + * + * @param url + * @return page contents + */ + public static String getContents(final URL url) { + return getContents(getConnection(url)); + } + + public static String getContents(final URL url, final String parameters) { + return getContents(getConnection(url), parameters); + } + + /** + * Gets contents from URLConnection + * + * @param urlConnection + * @return page contents + */ + public static String getContents(URLConnection urlConnection) { + try { + final BufferedReader in = getReader(urlConnection); + final StringBuilder builder = new StringBuilder(); + String line; + if (in != null) { + while ((line = in.readLine()) != null) { + builder.append(line); + } + in.close(); + } + return builder.toString(); + } catch (Throwable t) { + t.printStackTrace(); + } + return null; + } + + public static String getContents(URLConnection urlConnection, String parameters) { + try { + urlConnection.setDoOutput(true); + OutputStreamWriter wr = new OutputStreamWriter(urlConnection.getOutputStream()); + wr.write(parameters); + wr.flush(); + wr.close(); + + final BufferedReader in = new BufferedReader(new InputStreamReader(urlConnection.getInputStream())); + final StringBuilder builder = new StringBuilder(); + String line; + while ((line = in.readLine()) != null) { + builder.append(line); + } + return builder.toString(); + } catch (Throwable t) { + t.printStackTrace(); + } + return null; + } + + /** + * Gets buffered reader from string url + * + * @param url + * @return bufferedreader + */ + public static BufferedReader getReader(final String url) { + try { + return getReader(new URL(url)); + } catch (MalformedURLException e) { + e.printStackTrace(); + } + return null; + } + + /** + * Gets BufferedReader from URL + * + * @param url + * @return BufferedReader from URL + */ + public static BufferedReader getReader(final URL url) { + return getReader(getConnection(url)); + } + + public static BufferedReader getReader(final URLConnection urlConnection) { + try { + return new BufferedReader(new InputStreamReader( + urlConnection.getInputStream())); + } catch (Throwable t) { + t.printStackTrace(); + } + return null; + } + + /** + * Gets inputstream from url + * + * @param url + * @return inputstream from url + */ + public static InputStream getInputStream(final URL url) { + final URLConnection con = getConnection(url); + try { + return con.getInputStream(); + } catch (IOException e) { + e.printStackTrace(); + } + return null; + } + + /** + * Opens a connection + * + * @param url + * @return URLConnection to URL + */ + public static URLConnection getConnection(final URL url) { + try { + final URLConnection con = url.openConnection(); + con.setRequestProperty("User-Agent", agent); + return con; + } catch (Throwable t) { + t.printStackTrace(); + } + return null; + } + + public static URLConnection getConnection(final URL url, String parameters) { + try { + final URLConnection con = getConnection(url); + con.setDoOutput(true); + OutputStreamWriter wr = new OutputStreamWriter(con.getOutputStream()); + wr.write(parameters); + wr.flush(); + wr.close(); + + return con; + } catch (Throwable t) { + t.printStackTrace(); + } + return null; + } + + public static BufferedReader getReader(final URL url, String username, String password) { + try { + String data = URLEncoder.encode("username", "UTF-8") + "=" + username; + data += "&" + URLEncoder.encode("password", "UTF-8") + "=" + password; + + URLConnection connection = url.openConnection(); + + connection.setDoOutput(true); + OutputStreamWriter wr = new OutputStreamWriter(connection.getOutputStream()); + wr.write(data); + wr.flush(); + wr.close(); + + return new BufferedReader(new InputStreamReader(connection.getInputStream())); + } catch (Throwable t) { + t.printStackTrace(); + } + return null; + } + + public static URLConnection getConnection(final URL url, String username, String password) { + try { + String data = URLEncoder.encode("username", "UTF-8") + "=" + username; + data += "&" + URLEncoder.encode("password", "UTF-8") + "=" + password; + + URLConnection connection = url.openConnection(); + + connection.setDoOutput(true); + OutputStreamWriter wr = new OutputStreamWriter(connection.getOutputStream()); + wr.write(data); + wr.flush(); + wr.close(); + + return connection; + } catch (Throwable t) { + t.printStackTrace(); + } + return null; + } + + /** + * Downloads a file on the internet + * + * @param url + * @param destination + */ + public static void downloadFile(final URL url, final File destination) { + downloadFile(url, destination, null, null, null); + } + + /** + * Downloads a file on the internet + * + * @param url + * @param destination + * @param listener + */ + public static void downloadFile(final URL url, final File destination, + final ProgressListener listener) { + downloadFile(url, destination, listener, null, null); + } + + /** + * Downloads a file on the internet + * + * @param url + * @param destination + * @param listener + */ + public static void downloadFile(final URL url, final File destination, + final ProgressListener listener, String username, String password) { + try { + final URLConnection connection; + if (username == null || password == null) { + connection = getConnection(url); + } else { + connection = getConnection(url, username, password); + } + final int size = connection.getContentLength(); + SizeInputStream sizeInputStream = new SizeInputStream( + connection.getInputStream(), size, listener); + BufferedInputStream in = new BufferedInputStream(sizeInputStream); + FileOutputStream fileOut = new FileOutputStream(destination); + final double before = listener == null ? 0d : listener.getCurrentProgress(); + final long startTime = System.currentTimeMillis(); + int totalRead = 0; + try { + byte data[] = new byte[1024]; + int count; + while ((count = in.read(data, 0, 1024)) != -1) { + fileOut.write(data, 0, count); + totalRead += count; + if (listener != null) { + double progress = (((double)totalRead / (double)size) * 100d); + String rate = (totalRead / Math.max(1, ((System.currentTimeMillis()-startTime)/1000))) + "/s"; + listener.updateMessageAndProgress(String.format("[%s] Downloading... %02f%% (%s of %s bytes) %s from %s to %s", WebUtil.class.getName(), progress, totalRead, size, rate, url.getPath(), destination.getAbsolutePath()), + progress); + } + } + } finally { + in.close(); + fileOut.close(); + } + if (listener != null) { + listener.onProgressUpdate(before); + } + Verboser.verbose("[WebUtil] Downloaded " + totalRead + " bytes from " + url + " -> " + destination.getAbsolutePath()); + } catch (Throwable t) { + t.printStackTrace(); + } + } + + /** + * Converts file format to url format + * + * @param file + * @return url to file + */ + public static URL toURL(File file) { + try { + return file.toURI().toURL(); + } catch (MalformedURLException e) { + e.printStackTrace(); + } + return null; + } + + public static JSONParser getJsonParser() { + if (jsonParser == null) { + jsonParser = new JSONParser(); + } + return jsonParser; + } +} \ No newline at end of file diff --git a/src/main/java/org/parabot/api/io/build/BuildPath.java b/src/main/java/org/parabot/api/io/build/BuildPath.java new file mode 100644 index 0000000..bafe195 --- /dev/null +++ b/src/main/java/org/parabot/api/io/build/BuildPath.java @@ -0,0 +1,19 @@ +package org.parabot.api.io.build; + +import java.lang.reflect.Method; +import java.net.URL; +import java.net.URLClassLoader; + +public class BuildPath { + + public static void add(final URL url) { + try { + Method method = URLClassLoader.class.getDeclaredMethod("addURL", URL.class); + method.setAccessible(true); + method.invoke((URLClassLoader) ClassLoader.getSystemClassLoader(), url); + } catch (Exception e) { + e.printStackTrace(); + } + } + +} \ No newline at end of file diff --git a/src/main/java/org/parabot/api/io/images/Images.java b/src/main/java/org/parabot/api/io/images/Images.java new file mode 100644 index 0000000..0028ac2 --- /dev/null +++ b/src/main/java/org/parabot/api/io/images/Images.java @@ -0,0 +1,30 @@ +package org.parabot.api.io.images; + +import javax.imageio.ImageIO; +import java.awt.image.BufferedImage; +import java.util.HashMap; + +/** + * + * Caches and loads images from resource + * + * @author Everel, JKetelaar + * + */ +public class Images { + private static final HashMap IMAGE_CACHE = new HashMap<>(); + + public static BufferedImage getResource(final String resource) { + if(IMAGE_CACHE.containsKey(resource)) { + return IMAGE_CACHE.get(resource); + } + try { + final BufferedImage img = ImageIO.read(Images.class.getResourceAsStream(resource)); + IMAGE_CACHE.put(resource, img); + return img; + } catch (Throwable t) { + throw new RuntimeException("Failed to load image from resource. " + t.getMessage()); + } + } + +} \ No newline at end of file diff --git a/src/main/java/org/parabot/api/io/libraries/Environment.java b/src/main/java/org/parabot/api/io/libraries/Environment.java new file mode 100644 index 0000000..092dc0c --- /dev/null +++ b/src/main/java/org/parabot/api/io/libraries/Environment.java @@ -0,0 +1,23 @@ +package org.parabot.api.io.libraries; + +import org.parabot.api.io.WebUtil; + +/** + * @author JKetelaar + */ +public class Environment { + + /** + * Loads library into environment + * + * @param library + */ + public static void loadLibrary(Library library) { + if (library.requiresJar()) { + if (!library.hasJar()) { + WebUtil.downloadFile(library.getDownloadLink(), library.getJarFile()); + } + library.init(); + } + } +} diff --git a/src/main/java/org/parabot/api/io/libraries/Library.java b/src/main/java/org/parabot/api/io/libraries/Library.java new file mode 100644 index 0000000..a72c8bf --- /dev/null +++ b/src/main/java/org/parabot/api/io/libraries/Library.java @@ -0,0 +1,67 @@ +package org.parabot.api.io.libraries; + +import java.io.File; +import java.net.MalformedURLException; +import java.net.URL; + +/** + * + * @author Everel + * + */ +public abstract class Library { + + /** + * Determines if this library jar has already been downloaded. + * @return false if library jar has not been downloaded, otherwise true + */ + public boolean hasJar() { + return getJarFile().exists(); + } + + /** + * Adds the library to the buildpath and validates if it has been added + */ + public abstract void init(); + + /** + * Determines if library has been added to the buildpath + * @return true if library has been added to the buildpath, otherwise false. + */ + public abstract boolean isAdded(); + + /** + * Gets the local file target/location of the jar file + * @return local file (target) to library + */ + public abstract File getJarFile(); + + /** + * Gets download url to the library + * @return url + */ + public abstract URL getDownloadLink(); + + /** + * Defines if the system requires a jar + * @return boolean + */ + public abstract boolean requiresJar(); + + + /** + * Fetches URL from {@link Library#getJarFile()} + * @return URL to local library jar file + */ + public URL getJarFileURL() { + try { + return getJarFile().toURI().toURL(); + } catch (MalformedURLException e) { + e.printStackTrace(); + } + return null; + } + + public abstract String getLibraryName(); + +} diff --git a/src/main/java/org/parabot/api/io/libraries/jpushbullet/JPushBullet.java b/src/main/java/org/parabot/api/io/libraries/jpushbullet/JPushBullet.java new file mode 100644 index 0000000..e4a3fa8 --- /dev/null +++ b/src/main/java/org/parabot/api/io/libraries/jpushbullet/JPushBullet.java @@ -0,0 +1,67 @@ +package org.parabot.api.io.libraries.jpushbullet; + +import org.parabot.api.Configuration; +import org.parabot.api.io.Directories; +import org.parabot.api.io.build.BuildPath; +import org.parabot.api.io.libraries.Library; + +import java.io.File; +import java.net.URL; + +/** + * @author EmmaStone + */ +public class JPushBullet extends Library { + private static boolean valid; + + @Override + public void init() { + if (!hasJar()) { + System.err.println("Failed to load jpushbullet... [jar missing]"); + return; + } + BuildPath.add(getJarFileURL()); + + try { + Class.forName("com.github.sheigutn.pushbullet.Pushbullet"); + valid = true; + } catch (ClassNotFoundException e) { + System.err + .println("Failed to add jpushbullet to build path, or incorrupt download"); + } + } + + @Override + public boolean isAdded() { + return valid; + } + + @Override + public File getJarFile() { + return new File(Directories.getCachePath(), "jpushbullet.jar"); + } + + @Override + public URL getDownloadLink() { + try { + return new URL(Configuration.LIBRARIES_DOWNLOAD + "/JPushBullet"); + } catch (Throwable t) { + t.printStackTrace(); + } + return null; + } + + @Override + public boolean requiresJar() { + return true; + } + + @Override + public String getLibraryName() { + return "JPushBullet"; + } + + public static boolean isValid() { + return valid; + } +} diff --git a/src/main/java/org/parabot/api/misc/JavaUtil.java b/src/main/java/org/parabot/api/misc/JavaUtil.java new file mode 100644 index 0000000..964ee11 --- /dev/null +++ b/src/main/java/org/parabot/api/misc/JavaUtil.java @@ -0,0 +1,15 @@ +package org.parabot.api.misc; + +/** + * @author JKetelaar + */ +public class JavaUtil { + public static double JAVA_VERSION = getVersion(); + + static double getVersion() { + String version = System.getProperty("java.version"); + int pos = version.indexOf('.'); + pos = version.indexOf('.', pos + 1); + return Double.parseDouble(version.substring(0, pos)); + } +} \ No newline at end of file diff --git a/src/main/java/org/parabot/api/misc/OperatingSystem.java b/src/main/java/org/parabot/api/misc/OperatingSystem.java new file mode 100644 index 0000000..2c57000 --- /dev/null +++ b/src/main/java/org/parabot/api/misc/OperatingSystem.java @@ -0,0 +1,26 @@ +package org.parabot.api.misc; + +/** + * This class is used for detecting the user's operating system + * + * @author Everel + */ +public enum OperatingSystem { + + WINDOWS, LINUX, MAC, OTHER; + + public static final OperatingSystem getOS() { + String str = System.getProperty("os.name").toLowerCase(); + if (str.contains("win")) { + return OperatingSystem.WINDOWS; + } + if (str.contains("mac")) { + return OperatingSystem.MAC; + } + if (str.contains("nix") || str.contains("nux")) { + return OperatingSystem.LINUX; + } + return OperatingSystem.OTHER; + } + +} diff --git a/src/main/java/org/parabot/api/misc/ProjectProperties.java b/src/main/java/org/parabot/api/misc/ProjectProperties.java new file mode 100644 index 0000000..5edfc9a --- /dev/null +++ b/src/main/java/org/parabot/api/misc/ProjectProperties.java @@ -0,0 +1,44 @@ +package org.parabot.api.misc; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Properties; + +/** + * @author JKetelaar + */ +public class ProjectProperties { + + private static ProjectProperties instance; + private Properties cached = new Properties(); + + private ProjectProperties() { + setProperties(); + } + + public static Version getProjectVersion() { + return new Version(getInstance().getCached().getProperty("application.version")); + } + + public static ProjectProperties getInstance() { + return instance == null ? instance = new ProjectProperties() : instance; + } + + private void setProperties() { + InputStream input; + try { + String propertiesFileName = "storage/internal.properties"; + + input = getClass().getClassLoader() + .getResourceAsStream(propertiesFileName); + + cached.load(input); + } catch (IOException e) { + e.printStackTrace(); + } + } + + private Properties getCached() { + return cached; + } +} diff --git a/src/main/java/org/parabot/api/misc/StringUtil.java b/src/main/java/org/parabot/api/misc/StringUtil.java new file mode 100644 index 0000000..54bdc33 --- /dev/null +++ b/src/main/java/org/parabot/api/misc/StringUtil.java @@ -0,0 +1,117 @@ +package org.parabot.api.misc; + +/** + * @author mkyong, JKetelaar + */ +public class StringUtil { + + private static java.util.Random random = new java.util.Random(); + private static char[] chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890".toCharArray(); + + public static String implode(String separator, String... data) { + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < data.length - 1; i++) { + //data.length - 1 => to not add separator at the end + if (!data[i].matches(" *")) {//empty string are ""; " "; " "; and so on + sb.append(data[i]); + sb.append(separator); + } + } + sb.append(data[data.length - 1].trim()); + return sb.toString(); + } + + public static String convertHexToString(String hex) { + + StringBuilder sb = new StringBuilder(); + StringBuilder temp = new StringBuilder(); + + for (int i = 0; i < hex.length() - 1; i += 2) { + + // grab the hex in pairs + String output = hex.substring(i, (i + 2)); + // convert hex to decimal + int decimal = Integer.parseInt(output, 16); + // convert the decimal to character + sb.append((char) decimal); + + temp.append(decimal); + } + + return sb.toString(); + } + + public static String randomString(final int length) { + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < 20; i++) { + char c = chars[random.nextInt(chars.length)]; + sb.append(c); + } + return sb.toString(); + } + + public static long longForName(String name) { + long l = 0L; + for (int i = 0; i < name.length() && i < 12; i++) { + char c = name.charAt(i); + l *= 37L; + if (c >= 'A' && c <= 'Z') { + l += (1 + c) - 65; + } else if (c >= 'a' && c <= 'z') { + l += (1 + c) - 97; + } else if (c >= '0' && c <= '9') { + l += (27 + c) - 48; + } + } + + while (l % 37L == 0L && l != 0L) { + l /= 37L; + } + + return l; + } + + public static String nameForLong(long name) { + try { + if (name <= 0L || name >= 0x5b5b57f8a98a5dd1L) { + return "invalid_name"; + } + if (name % 37L == 0L) { + return "invalid_name"; + } + + int i = 0; + char ac[] = new char[12]; + while (name != 0L) { + long l1 = name; + name /= 37L; + ac[11 - i++] = chars[(int) (l1 - name * 37L)]; + } + return new String(ac, 12 - i, i); + } catch (Exception e) { + e.printStackTrace(); + } + + return null; + } + + public static String fixName(String name) { + if (name.length() > 0) { + char ac[] = name.toCharArray(); + for (int j = 0; j < ac.length; j++) + if (ac[j] == '_') { + ac[j] = ' '; + if (j + 1 < ac.length && ac[j + 1] >= 'a' && ac[j + 1] <= 'z') { + ac[j + 1] = (char) ((ac[j + 1] + 65) - 97); + } + } + + if (ac[0] >= 'a' && ac[0] <= 'z') { + ac[0] = (char) ((ac[0] + 65) - 97); + } + return new String(ac); + } else { + return name; + } + } +} \ No newline at end of file diff --git a/src/main/java/org/parabot/api/misc/Version.java b/src/main/java/org/parabot/api/misc/Version.java new file mode 100644 index 0000000..b55bcc4 --- /dev/null +++ b/src/main/java/org/parabot/api/misc/Version.java @@ -0,0 +1,55 @@ +package org.parabot.api.misc; + +public class Version implements Comparable { + + private String version; + + public Version(String version) { + if (version == null) { + throw new IllegalArgumentException("Version can not be null"); + } + + if (!version.matches("[0-9]+(\\.[0-9]+)*") && !version.matches("[0-9]+(\\.[0-9]+)*-RC-([0-9]+)")) { + throw new IllegalArgumentException("Invalid version format"); + } + this.version = version; + } + + public final String get() { + return this.version; + } + + public boolean isNightly() { + return this.version.contains("RC"); + } + + @Override + public int compareTo(Version that) { + if (that == null) { + return 1; + } + + String[] thisParts = this.get().split("\\."); + String[] thatParts = that.get().split("\\."); + int length = Math.max(thisParts.length, thatParts.length); + + for (int i = 0; i < length; i++) { + int thisPart = i < thisParts.length ? + Integer.parseInt(thisParts[i]) : 0; + int thatPart = i < thatParts.length ? + Integer.parseInt(thatParts[i]) : 0; + if (thisPart < thatPart) { + return -1; + } + if (thisPart > thatPart) { + return 1; + } + } + return 0; + } + + @Override + public boolean equals(Object that) { + return this == that || that != null && this.getClass() == that.getClass() && this.compareTo((Version) that) == 0; + } +} \ No newline at end of file diff --git a/src/main/java/org/parabot/api/notifications/NotificationManager.java b/src/main/java/org/parabot/api/notifications/NotificationManager.java new file mode 100644 index 0000000..08d0b21 --- /dev/null +++ b/src/main/java/org/parabot/api/notifications/NotificationManager.java @@ -0,0 +1,113 @@ +package org.parabot.api.notifications; + +import org.parabot.api.notifications.types.MacNotificationType; +import org.parabot.api.notifications.types.NotificationType; +import org.parabot.api.notifications.types.PushBulletNotificationType; + +import java.util.ArrayList; +import java.util.Iterator; + +/** + * @author JKetelaar + */ +public class NotificationManager { + + private static NotificationManager context; + + private ArrayList notificationTypes; + private ArrayList enabledTypes; + + public NotificationManager() { + this.notificationTypes = new ArrayList<>(); + this.enabledTypes = new ArrayList<>(); + + this.fillNotificationTypes(); + } + + public ArrayList getNotificationTypes() { + return notificationTypes; + } + + public void addNotificationType(NotificationType type) { + this.notificationTypes.add(type); + } + + private void fillNotificationTypes() { + this.notificationTypes.add(new MacNotificationType()); + this.notificationTypes.add(new PushBulletNotificationType()); + } + + public void enableNotificationType(NotificationType type) { + this.enabledTypes.add(type); + type.enable(); + } + + public void disableNotificationType(NotificationType type) { + Iterator iterator = this.enabledTypes.iterator(); + while(iterator.hasNext()){ + NotificationType t = (NotificationType) iterator.next(); + if (t.getName().equalsIgnoreCase(type.getName())){ + iterator.remove(); + break; + } + } + } + + public void sendNotification(String message) { + for (NotificationType notificationType : this.enabledTypes) { + notificationType.notify(message); + } + } + + public void sendNotification(String header, String message) { + for (NotificationType notificationType : this.enabledTypes) { + notificationType.notify(header, message); + } + } + + public void sendNotification(String title, String header, String message) { + for (NotificationType notificationType : this.enabledTypes) { + notificationType.notify(title, header, message); + } + } + + public ArrayList getEnabledTypes() { + return enabledTypes; + } + + public ArrayList getAvailableNotificationTypes() { + ArrayList types = new ArrayList<>(); + for (NotificationType notificationType : this.notificationTypes) { + boolean inAdded = false; + for (NotificationType enabledType : this.enabledTypes) { + if (enabledType.getName().equalsIgnoreCase(notificationType.getName())) { + inAdded = true; + } + } + + if (!inAdded) { + types.add(notificationType); + } + } + + return types; + } + + public NotificationType getNotificationType(String name) { + for (NotificationType notificationType : this.notificationTypes) { + if (notificationType.getName().equalsIgnoreCase(name)) { + return notificationType; + } + } + + return null; + } + + public static NotificationManager getContext() { + if (context == null) { + context = new NotificationManager(); + } + + return context; + } +} diff --git a/src/main/java/org/parabot/api/notifications/types/MacNotificationType.java b/src/main/java/org/parabot/api/notifications/types/MacNotificationType.java new file mode 100644 index 0000000..8c23696 --- /dev/null +++ b/src/main/java/org/parabot/api/notifications/types/MacNotificationType.java @@ -0,0 +1,43 @@ +package org.parabot.api.notifications.types; + +import org.parabot.api.misc.OperatingSystem; + +import java.io.IOException; + +/** + * @author JKetelaar + */ +public class MacNotificationType extends NotificationType { + + public MacNotificationType() { + super("Mac"); + } + + @Override + public boolean isAvailable() { + return OperatingSystem.getOS().equals(OperatingSystem.MAC); + } + + @Override + public void notify(String title, String header, String message) { + final StringBuilder src = new StringBuilder(); + + src.append("display notification") + .append(" \"").append(message).append("\""); + if (title != null && title.length() > 0) { + src.append(" with title ").append("\"").append(title).append("\""); + } + if (header != null && header.length() > 0) { + src.append(" subtitle ").append("\"").append(header).append("\""); + } + src.append(" sound name \"Ping\" "); + + final Runtime rt = Runtime.getRuntime(); + final String[] cmd = new String[]{"/usr/bin/osascript", "-e", src.toString()}; + try { + rt.exec(cmd); + } catch (IOException e) { + e.printStackTrace(); + } + } +} diff --git a/src/main/java/org/parabot/api/notifications/types/NotificationType.java b/src/main/java/org/parabot/api/notifications/types/NotificationType.java new file mode 100644 index 0000000..dbe1464 --- /dev/null +++ b/src/main/java/org/parabot/api/notifications/types/NotificationType.java @@ -0,0 +1,33 @@ +package org.parabot.api.notifications.types; + +import org.parabot.api.Configuration; + +/** + * @author JKetelaar + */ +public abstract class NotificationType { + + private String name; + + public NotificationType(String name) { + this.name = name; + } + + public abstract boolean isAvailable(); + + public abstract void notify(String title, String header, String message); + + public void notify(String header, String message){ + notify(Configuration.BOT_TITLE, header, message); + } + + public void notify(String message){ + notify("Notification", message); + } + + public String getName() { + return name; + } + + public void enable(){} +} diff --git a/src/main/java/org/parabot/api/notifications/types/PushBulletNotificationType.java b/src/main/java/org/parabot/api/notifications/types/PushBulletNotificationType.java new file mode 100644 index 0000000..24e20db --- /dev/null +++ b/src/main/java/org/parabot/api/notifications/types/PushBulletNotificationType.java @@ -0,0 +1,55 @@ +package org.parabot.api.notifications.types; + +import org.parabot.api.io.libraries.Environment; +import org.parabot.api.io.libraries.jpushbullet.JPushBullet; +import org.parabot.api.notifications.types.pushbullet.PushBulletController; + +import javax.swing.*; + +/** + * @author JKetelaar + */ +public class PushBulletNotificationType extends NotificationType { + + private boolean enabled = false; + + public PushBulletNotificationType() { + super("PushBullet"); + } + + @Override + public boolean isAvailable() { + double version = 1.0; + + try { + version = Double.parseDouble(System.getProperty("java.specification.version")); + } catch (NumberFormatException ignored) { + } + + return version >= 1.8; + } + + @Override + public void enable() { + final String message = "Please insert your PushBullet API key, so we could send notifications."; + + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + String s = JOptionPane.showInputDialog(null, message, "PushBullet API key", JOptionPane.QUESTION_MESSAGE); + + if (s != null) { + Environment.loadLibrary(new JPushBullet()); + enabled = PushBulletController.pushNote("Parabot", "PushBullets notifications have been enabled for Parabot", s); + } + } + }); + } + + @Override + public void notify(String title, String header, String message) { + if (this.enabled) { + PushBulletController.pushNote(title, message); + } + } +} diff --git a/src/main/java/org/parabot/api/notifications/types/pushbullet/PushBulletController.java b/src/main/java/org/parabot/api/notifications/types/pushbullet/PushBulletController.java new file mode 100644 index 0000000..3471a3e --- /dev/null +++ b/src/main/java/org/parabot/api/notifications/types/pushbullet/PushBulletController.java @@ -0,0 +1,52 @@ +package org.parabot.api.notifications.types.pushbullet; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + +/** + * @author JKetelaar + */ +public class PushBulletController { + + private static Object pushBulletInstance; + + public static void setPushBulletInstance(String key){ + ClassLoader classLoader = PushBulletController.class.getClassLoader(); + try { + PushBulletController.pushBulletInstance = classLoader.loadClass("com.github.sheigutn.pushbullet.Pushbullet").getConstructors()[0].newInstance(key); + } catch (ClassNotFoundException | IllegalAccessException | InstantiationException | InvocationTargetException e) { + e.printStackTrace(); + } + } + + public static Object getPushBulletInstance(String key){ + PushBulletController.setPushBulletInstance(key); + + return PushBulletController.pushBulletInstance; + } + + public static boolean pushNote(String title, String message, String key){ + PushBulletController.setPushBulletInstance(key); + + return PushBulletController.pushNote(title, message); + } + + public static boolean pushNote(String title, String message){ + Class[] cArg = new Class[2]; + cArg[0] = String.class; + cArg[1] = String.class; + + if (PushBulletController.pushBulletInstance != null){ + try { + Method method = PushBulletController.pushBulletInstance.getClass().getMethod("pushNote", cArg); + method.setAccessible(true); + method.invoke(PushBulletController.pushBulletInstance, title, message); + + return true; + } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) { + return false; + } + } + return false; + } +} diff --git a/src/main/java/org/parabot/api/output/Logger.java b/src/main/java/org/parabot/api/output/Logger.java new file mode 100644 index 0000000..7a171b9 --- /dev/null +++ b/src/main/java/org/parabot/api/output/Logger.java @@ -0,0 +1,94 @@ +package org.parabot.api.output; + +/** + * @author Fryslan + */ +public class Logger { + + public enum LoggerColor { + RESET("\u001B[0m"), + BLACK("\u001B[30m"), + WHITE("\u001B[37m"), + RED("\u001B[31m"), + GREEN("\u001B[32m"), + YELLOW("\u001B[33m"), + BLUE("\u001B[34m"), + PURPLE("\u001B[35m"), + CYAN("\u001B[36m"); + + private final String ansi; + + LoggerColor(String ansi) { + this.ansi = ansi; + } + + public String getAnsi() { + return ansi; + } + } + + + /** + * Prints Error data in Red. + * + * @param tag Reference to the location , I.E Core, UserInterface. + * @param data Error data. + */ + public static void error(String tag, String data) { + System.out.println(LoggerColor.RED.getAnsi() + "[ERROR] : " + tag + " - " + data + LoggerColor.RESET.getAnsi()); + } + + /** + * Prints Error data in Red. + * + * @param tag Reference to the location , I.E Core, UserInterface. + * @param data Error data. + * @param throwable Error or Exception. + */ + public static void error(String tag, String data, Throwable throwable) { + System.out.println(LoggerColor.RED.getAnsi() + "[ERROR] : " + tag + " - " + data); + throwable.printStackTrace(); + System.out.println(LoggerColor.RESET.getAnsi()); + + } + + /** + * Prints Information data in Blue. + * + * @param tag Reference to the location , I.E Core, UserInterface. + * @param data Information data. + */ + public static void info(String tag, String data) { + System.out.println(LoggerColor.BLUE.getAnsi() + "[INFO] : " + tag + " - " + data + LoggerColor.RESET.getAnsi()); + } + + /** + * Prints Warning data in Yellow. + * + * @param tag Reference to the location , I.E Core, UserInterface. + * @param data Warning data. + */ + public static void warning(String tag, String data) { + System.out.println(LoggerColor.YELLOW.getAnsi() + "[WARNING] : " + tag + " - " + data + LoggerColor.RESET.getAnsi()); + } + + /** + * Prints Debug data in Green, if debugging is enabled. + * + * @param tag Reference to the location , I.E Core, UserInterface. + * @param data Debug data. + */ + public static void debug(String tag, String data) { + System.out.println(LoggerColor.GREEN.getAnsi() + "[DEBUG] : " + tag + " - " + data + LoggerColor.RESET.getAnsi()); + } + + /** + * @param tag Reference to the location , I.E Core, UserInterface. + * @param data Data to Print. + * @param color Color to print the the data in. + * @param header Reference to the reason this message is printed I.E Warning, Debug. + */ + public static void custom(String tag, String data, LoggerColor color, String header) { + System.out.println(color.getAnsi() + "[" + header.toUpperCase() + "] : " + tag + " - " + data + LoggerColor.RESET.getAnsi()); + } +} diff --git a/src/main/java/org/parabot/api/output/Verboser.java b/src/main/java/org/parabot/api/output/Verboser.java new file mode 100644 index 0000000..538575f --- /dev/null +++ b/src/main/java/org/parabot/api/output/Verboser.java @@ -0,0 +1,10 @@ +package org.parabot.api.output; + +/** + * @author JKetelaar + */ +public class Verboser { + public static void verbose(String s) { + System.out.println(s); + } +} diff --git a/src/main/java/org/parabot/api/ui/JavaFxUtil.java b/src/main/java/org/parabot/api/ui/JavaFxUtil.java new file mode 100644 index 0000000..9097369 --- /dev/null +++ b/src/main/java/org/parabot/api/ui/JavaFxUtil.java @@ -0,0 +1,142 @@ +package org.parabot.api.ui; + +import javafx.application.Platform; +import javafx.embed.swing.JFXPanel; +import javafx.fxml.FXMLLoader; +import javafx.scene.Scene; +import javafx.scene.layout.Pane; +import org.parabot.api.io.ProgressListener; +import org.parabot.api.io.WebUtil; +import org.parabot.api.output.Verboser; + +import javax.swing.*; +import java.awt.event.WindowAdapter; +import java.io.File; +import java.io.IOException; +import java.net.URL; + +/** + * If a JavaFX program runs as an "Application" it can only be launched once due to + * how the UI threadding works. To solve this, JavaFX should be used by embedded the FX content inside an FXPanel, + * which itself should just fill the content of a empty JFrame. + * + * @author Shadowrs + */ +public abstract class JavaFxUtil { + + private JFrame frame; + private JFXPanel jfxp; + private JavaFxUtil instance; + private final URL end; + private final File target; + private final Class controller; + private final ProgressListener listener; + + /** + * Constructor to use when stylesheet.fxml is in the Parabot Jar + * + * @param fxmlSheet Location to .fxml such as "/storage/ui/notifications.fxml" + */ + public JavaFxUtil(final URL fxmlSheet, final Class controller) { + this.end = fxmlSheet; + this.target = null; + this.listener = null; + this.controller = controller; + launchJFX(); + } + + /** + * Constructor to use when stylesheet.fxml requires downloading from a remote target. + * + * @param endpoint + * @param target + * @param listener + * @param controller + */ + public JavaFxUtil(URL endpoint, final File target, final ProgressListener listener, final Class controller) { + this.end = endpoint; + this.target = target; + this.listener = listener; + this.controller = controller; + + if (!target.exists() || !target.canRead()) { + WebUtil.downloadFile(end, target, listener); + } + Verboser.verbose("ui from " + end); + + launchJFX(); + } + + /** + * Kick off the GUI by creating a JFrame on the Swing Thread, then load the JavaFX on the Platform Thread + */ + private void launchJFX() { + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + createJFrame(); + Platform.setImplicitExit(false); + Platform.runLater(new Runnable() { + @Override + public void run() { + addJavaFX(); + } + }); + } + }); + } + + /** + * Create the JFrame and attach the window listener + */ + private void createJFrame() { + Verboser.verbose("Creating JFrame for JavaFXPanel"); + if (getFrame() != null) { + System.err.println("frame exists"); + return; + } + frame = new JFrame(); + jfxp = new JFXPanel(); + getFrame().add(jfxp); + getFrame().setSize(230, 300); + getFrame().setLocationRelativeTo(null); + getFrame().setVisible(true); + + getFrame().setDefaultCloseOperation(JFrame.HIDE_ON_CLOSE); + getFrame().addWindowListener(getWindowAdapter()); + Verboser.verbose("frame created: " + getFrame()); + } + + protected abstract WindowAdapter getWindowAdapter(); + + /** + * Add the JavaFX based on the controller and stylesheet to the Scene of the JavaFxPanel + */ + private void addJavaFX() { + Verboser.verbose("panel init"); + instance = this; + try { + FXMLLoader loader = new FXMLLoader(end); // ui.fxml + if (loader.getController() == null) { + loader.setController(controller.newInstance()); + } + Pane page = (Pane) loader.load(); + + Scene scene = new Scene(page); + jfxp.setScene(scene); + getFrame().pack(); + + onLaunched(); + Verboser.verbose("UI showing"); + } catch (IOException | InstantiationException | IllegalAccessException e) { + System.err.println("Error loading ui.fxml!"); + e.printStackTrace(); + } + } + + protected abstract void onLaunched(); + + public JFrame getFrame() { + return frame; + } +} diff --git a/src/main/java/org/parabot/api/ui/SwingUtil.java b/src/main/java/org/parabot/api/ui/SwingUtil.java new file mode 100644 index 0000000..f9e5659 --- /dev/null +++ b/src/main/java/org/parabot/api/ui/SwingUtil.java @@ -0,0 +1,66 @@ +package org.parabot.api.ui; + +import org.parabot.api.io.images.Images; +import org.parabot.api.misc.JavaUtil; +import org.parabot.api.misc.OperatingSystem; + +import javax.swing.*; +import java.awt.*; +import java.lang.reflect.Method; + +/** + * Holds various swing util based methods + * + * @author Dane, JKetelaar + */ +public class SwingUtil { + + /** + * Packs, centers, and shows the frame. + * + * @param f + */ + public static void finalize(JFrame f) { + f.pack(); + f.setLocationRelativeTo(null); + f.setVisible(true); + } + + /** + * Adds the dock icon to mac users + * + * @param f + */ + public static void setParabotIcons(JFrame f) { + setParabotIcon(f); + } + + /** + * Adds the dock icon to mac users + * + * @param f + */ + public static boolean setParabotIcon(JFrame f) { + f.setIconImage(Images.getResource("/storage/images/icon.png")); + + if (OperatingSystem.getOS() == OperatingSystem.MAC) { + Image image = Images.getResource("/storage/images/icon.png"); + try { + if (JavaUtil.JAVA_VERSION >= 9) { + Class taskbar = Class.forName("java.awt.Taskbar"); + Object application = taskbar.getMethod("getTaskbar").invoke(null); + taskbar.getMethod("setIconImage", Image.class).invoke(application, image); + } else { + Class util = Class.forName("com.apple.eawt.Application"); + Object application = util.getMethod("getApplication").invoke(null); + Method setDockIconImage = util.getMethod("setDockIconImage", Image.class); + setDockIconImage.invoke(application, Images.getResource("/storage/images/icon.png")); + } + } catch (Throwable t) { + return false; + } + } + + return true; + } +} \ No newline at end of file diff --git a/src/main/resources/storage/images/icon.png b/src/main/resources/storage/images/icon.png new file mode 100644 index 0000000..33ef49f Binary files /dev/null and b/src/main/resources/storage/images/icon.png differ diff --git a/src/main/resources/storage/internal.properties b/src/main/resources/storage/internal.properties new file mode 100644 index 0000000..263d0ff --- /dev/null +++ b/src/main/resources/storage/internal.properties @@ -0,0 +1 @@ +application.version=${project.version}${build.version} \ No newline at end of file diff --git a/src/test/java/org/parabot/MacOSTest.java b/src/test/java/org/parabot/MacOSTest.java new file mode 100644 index 0000000..5be5eb8 --- /dev/null +++ b/src/test/java/org/parabot/MacOSTest.java @@ -0,0 +1,20 @@ +package org.parabot; + +import org.junit.Assert; +import org.junit.Test; +import org.parabot.api.misc.OperatingSystem; +import org.parabot.api.ui.SwingUtil; + +import javax.swing.*; + +public class MacOSTest { + + @Test + public void testMacOS() { + if (OperatingSystem.getOS().equals(OperatingSystem.MAC)) { + JFrame frame = new JFrame(); + frame.setSize(500, 500); + Assert.assertTrue(SwingUtil.setParabotIcon(frame)); + } + } +} diff --git a/src/test/java/org/parabot/NotificationTest.java b/src/test/java/org/parabot/NotificationTest.java new file mode 100644 index 0000000..456fb60 --- /dev/null +++ b/src/test/java/org/parabot/NotificationTest.java @@ -0,0 +1,16 @@ +package org.parabot; + +import org.junit.Assert; +import org.junit.Test; +import org.parabot.api.notifications.NotificationManager; + +/** + * @author JKetelaar + */ +public class NotificationTest { + @Test + public void testAmount(){ + NotificationManager manager = new NotificationManager(); + Assert.assertTrue(manager.getNotificationTypes().size() > 0); + } +}