/* * Copyright (C) 2012 Brendan Robert (BLuRry) brendan.robert@gmail.com. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * MA 02110-1301 USA */ package jace.core; import jace.Emulator; import jace.config.Configuration; import jace.config.InvokableAction; import java.io.File; import java.io.InputStream; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.Set; import java.util.logging.Level; import java.util.logging.Logger; import java.util.stream.Collectors; import javafx.application.Platform; import javafx.geometry.Pos; import javafx.scene.control.Alert; import javafx.scene.control.ButtonType; import javafx.scene.control.ContentDisplay; import javafx.scene.control.Label; import javafx.scene.effect.DropShadow; import javafx.scene.image.Image; import javafx.scene.image.ImageView; import javafx.scene.paint.Color; import org.reflections.Reflections; /** * This is a set of helper functions which do not belong anywhere else. * Functions vary from introspection, discovery, and string/pattern matching. * * @author Brendan Robert (BLuRry) brendan.robert@gmail.com */ public class Utility { static Reflections reflections = new Reflections("jace"); public static Set findAllSubclasses(Class clazz) { return reflections.getSubTypesOf(clazz); } //------------------------------ String comparators /** * Rank two strings similarity in terms of distance The lower the number, * the more similar these strings are to each other See: * http://en.wikipedia.org/wiki/Levenshtein_distance#Computing_Levenshtein_distance * * @param s * @param t * @return Distance (lower means a closer match, zero is identical) */ public static int levenshteinDistance(String s, String t) { if (s == null || t == null || s.length() == 0 || t.length() == 0) { return Integer.MAX_VALUE; } s = s.toLowerCase().replaceAll("[^a-zA-Z0-9\\s]", ""); t = t.toLowerCase().replaceAll("[^a-zA-Z0-9\\s]", ""); int m = s.length(); int n = t.length(); int[][] dist = new int[m + 1][n + 1]; for (int i = 1; i <= m; i++) { dist[i][0] = i; } for (int i = 1; i <= n; i++) { dist[0][i] = i; } for (int j = 1; j <= n; j++) { for (int i = 1; i <= m; i++) { if (s.charAt(i - 1) == t.charAt(j - 1)) { dist[i][j] = dist[i - 1][j - 1]; } else { int del = dist[i - 1][j] + 1; int insert = dist[i][j - 1] + 1; int sub = dist[i - 1][j - 1] + 1; dist[i][j] = Math.min(Math.min(del, insert), sub); } } } return dist[m][n]; } /** * Normalize distance based on longest string * @param s * @param t * @return Similarity ranking, higher is better */ public static int adjustedLevenshteinDistance(String s, String t) { return Math.max(s.length(), t.length()) - levenshteinDistance(s, t); } /** * Compare strings based on a tally of similar patterns found, using a fixed * search window The resulting score is heavily penalized if the strings * differ greatly in length This is not as efficient as levenshtein, so it's * only used as a tie-breaker. * * @param c1 * @param c2 * @param width Search window size * @return Overall similarity score (higher is better) */ public static double rankMatch(String c1, String c2, int width) { double score = 0; String s1 = c1.toLowerCase(); String s2 = c2.toLowerCase(); for (int i = 0; i < s1.length() + 1 - width; i++) { String m = s1.substring(i, i + width); int j = 0; while ((j = s2.indexOf(m, j)) > -1) { score += width; j++; } } double l1 = s1.length(); double l2 = s2.length(); // If the two strings are equivilent in length, the score is higher // If the two strings are different in length, the score is adjusted lower depending on how large the difference is // This is offset just a hair for tuning purposes double adjustment = (Math.min(l1, l2) / Math.max(l1, l2)) + 0.1; return score * adjustment * adjustment; } public static String join(Collection c, String d) { return c.stream().collect(Collectors.joining(d)); } private static boolean isHeadless = false; public static void setHeadlessMode(boolean headless) { isHeadless = headless; if (Emulator.instance == null && headless) { Emulator.instance = new Emulator(Collections.emptyList()); } } public static boolean isHeadlessMode() { return isHeadless; } public static Optional loadIcon(String filename) { if (isHeadless) { return Optional.empty(); } InputStream stream = Utility.class.getClassLoader().getResourceAsStream("jace/data/" + filename); return Optional.of(new Image(stream)); } public static Optional