diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/setting/Settings.java b/HMCL/src/main/java/org/jackhuang/hmcl/setting/Settings.java index afb484687..679ec8f85 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/setting/Settings.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/setting/Settings.java @@ -45,6 +45,8 @@ import java.util.*; import java.util.logging.Level; import java.util.stream.Collectors; +import static org.jackhuang.hmcl.util.Lang.tryCast; + public class Settings { public static final Gson GSON = new GsonBuilder() .registerTypeAdapter(VersionSetting.class, VersionSetting.Serializer.INSTANCE) @@ -71,7 +73,7 @@ public class Settings { for (Iterator> iterator = SETTINGS.getAccounts().iterator(); iterator.hasNext(); ) { Map settings = iterator.next(); - AccountFactory factory = Accounts.ACCOUNT_FACTORY.get(Lang.get(settings, "type", String.class).orElse("")); + AccountFactory factory = Accounts.ACCOUNT_FACTORY.get(tryCast(settings.get("type"), String.class).orElse("")); if (factory == null) { // unrecognized account type, so remove it. iterator.remove(); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/FXUtils.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/FXUtils.java index ed8998a08..edd802ba3 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/FXUtils.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/FXUtils.java @@ -19,6 +19,7 @@ package org.jackhuang.hmcl.ui; import com.jfoenix.adapters.ReflectionHelper; import com.jfoenix.controls.*; + import javafx.animation.Animation; import javafx.animation.Interpolator; import javafx.animation.KeyFrame; @@ -42,11 +43,14 @@ import javafx.scene.input.ScrollEvent; import javafx.scene.layout.Region; import javafx.scene.shape.Rectangle; import javafx.util.Duration; + import org.jackhuang.hmcl.Launcher; import org.jackhuang.hmcl.util.*; import java.io.File; import java.io.IOException; +import java.lang.reflect.Constructor; +import java.lang.reflect.Method; import java.net.URI; import java.util.List; import java.util.concurrent.CountDownLatch; @@ -55,8 +59,7 @@ import java.util.function.Consumer; import java.util.function.Supplier; import java.util.logging.Level; -import static org.jackhuang.hmcl.util.ReflectionHelper.call; -import static org.jackhuang.hmcl.util.ReflectionHelper.construct; +import static org.jackhuang.hmcl.util.Lang.tryCast; public final class FXUtils { private FXUtils() { @@ -125,7 +128,7 @@ public final class FXUtils { } public static void removeListener(Node node, String key) { - Lang.cast(node.getProperties().get(key), ListenerPair.class) + tryCast(node.getProperties().get(key), ListenerPair.class) .ifPresent(info -> { info.unbind(); node.getProperties().remove(key); @@ -214,17 +217,22 @@ public final class FXUtils { public static void installTooltip(Node node, double openDelay, double visibleDelay, double closeDelay, Tooltip tooltip) { try { // Java 8 - call(construct(Class.forName("javafx.scene.control.Tooltip$TooltipBehavior"), new Duration(openDelay), new Duration(visibleDelay), new Duration(closeDelay), false), - "install", node, tooltip); - } catch (Throwable e) { + Class behaviorClass = Class.forName("javafx.scene.control.Tooltip$TooltipBehavior"); + Constructor behaviorConstructor = behaviorClass.getDeclaredConstructor(Duration.class, Duration.class, Duration.class, boolean.class); + behaviorConstructor.setAccessible(true); + Object behavior = behaviorConstructor.newInstance(new Duration(openDelay), new Duration(visibleDelay), new Duration(closeDelay), false); + Method installMethod = behaviorClass.getDeclaredMethod("install", Node.class, Tooltip.class); + installMethod.setAccessible(true); + installMethod.invoke(behavior, node, tooltip); + } catch (ReflectiveOperationException e) { try { // Java 9 - call(tooltip, "setShowDelay", new Duration(openDelay)); - call(tooltip, "setShowDuration", new Duration(visibleDelay)); - call(tooltip, "setHideDelay", new Duration(closeDelay)); - } catch (Throwable e2) { + Tooltip.class.getMethod("setShowDelay", Duration.class).invoke(tooltip, new Duration(openDelay)); + Tooltip.class.getMethod("setShowDuration", Duration.class).invoke(tooltip, new Duration(visibleDelay)); + Tooltip.class.getMethod("setHideDelay", Duration.class).invoke(tooltip, new Duration(closeDelay)); + } catch (ReflectiveOperationException e2) { e.addSuppressed(e2); - Logging.LOG.log(Level.SEVERE, "Cannot install tooltip by reflection", e); + Logging.LOG.log(Level.SEVERE, "Cannot install tooltip", e); } Tooltip.install(node, tooltip); } @@ -333,7 +341,7 @@ public final class FXUtils { */ @SuppressWarnings("unchecked") public static void unbindEnum(JFXComboBox comboBox) { - ChangeListener listener = Lang.get(comboBox.getProperties(), "FXUtils.bindEnum.listener", ChangeListener.class).orElse(null); + ChangeListener listener = tryCast(comboBox.getProperties().get("FXUtils.bindEnum.listener"), ChangeListener.class).orElse(null); if (listener == null) return; comboBox.getSelectionModel().selectedIndexProperty().removeListener(listener); } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/DownloadWizardProvider.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/DownloadWizardProvider.java index b4f0ea869..530f032a7 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/DownloadWizardProvider.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/DownloadWizardProvider.java @@ -35,6 +35,7 @@ import java.io.File; import java.util.Map; import static org.jackhuang.hmcl.Launcher.i18n; +import static org.jackhuang.hmcl.util.Lang.tryCast; public final class DownloadWizardProvider implements WizardProvider { private Profile profile; @@ -67,9 +68,9 @@ public final class DownloadWizardProvider implements WizardProvider { if (!settings.containsKey(ModpackPage.MODPACK_FILE)) return null; - File selected = Lang.get(settings, ModpackPage.MODPACK_FILE, File.class).orElse(null); - Modpack modpack = Lang.get(settings, ModpackPage.MODPACK_CURSEFORGE_MANIFEST, Modpack.class).orElse(null); - String name = Lang.get(settings, ModpackPage.MODPACK_NAME, String.class).orElse(null); + File selected = tryCast(settings.get(ModpackPage.MODPACK_FILE), File.class).orElse(null); + Modpack modpack = tryCast(settings.get(ModpackPage.MODPACK_CURSEFORGE_MANIFEST), Modpack.class).orElse(null); + String name = tryCast(settings.get(ModpackPage.MODPACK_NAME), String.class).orElse(null); if (selected == null || modpack == null || name == null) return null; return ModpackHelper.getInstallTask(profile, selected, name, modpack); diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/auth/authlibinjector/AuthlibInjectorAccountFactory.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/auth/authlibinjector/AuthlibInjectorAccountFactory.java index e3886dc05..92abd7647 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/auth/authlibinjector/AuthlibInjectorAccountFactory.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/auth/authlibinjector/AuthlibInjectorAccountFactory.java @@ -6,7 +6,6 @@ import org.jackhuang.hmcl.auth.CharacterSelector; import org.jackhuang.hmcl.auth.yggdrasil.YggdrasilService; import org.jackhuang.hmcl.auth.yggdrasil.YggdrasilSession; import org.jackhuang.hmcl.util.ExceptionalSupplier; -import org.jackhuang.hmcl.util.Lang; import org.jackhuang.hmcl.util.NetworkUtils; import org.jackhuang.hmcl.util.UUIDTypeAdapter; @@ -15,6 +14,8 @@ import java.util.Map; import java.util.Objects; import java.util.UUID; +import static org.jackhuang.hmcl.util.Lang.tryCast; + public class AuthlibInjectorAccountFactory extends AccountFactory { private final ExceptionalSupplier injectorJarPathSupplier; @@ -43,13 +44,13 @@ public class AuthlibInjectorAccountFactory extends AccountFactory new IllegalArgumentException("storage does not have username")); - String clientToken = Lang.get(storage, "clientToken", String.class) + String clientToken = tryCast(storage.get("clientToken"), String.class) .orElseThrow(() -> new IllegalArgumentException("storage does not have client token.")); - String character = Lang.get(storage, "clientToken", String.class) + String character = tryCast(storage.get("clientToken"), String.class) .orElseThrow(() -> new IllegalArgumentException("storage does not have selected character name.")); - String apiRoot = Lang.get(storage, "serverBaseURL", String.class) + String apiRoot = tryCast(storage.get("serverBaseURL"), String.class) .orElseThrow(() -> new IllegalArgumentException("storage does not have API root.")); return new AuthlibInjectorAccount(new YggdrasilService(new AuthlibInjectorProvider(apiRoot), proxy), diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/auth/offline/OfflineAccountFactory.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/auth/offline/OfflineAccountFactory.java index 6b666664d..347ac8a23 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/auth/offline/OfflineAccountFactory.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/auth/offline/OfflineAccountFactory.java @@ -20,14 +20,12 @@ package org.jackhuang.hmcl.auth.offline; import org.jackhuang.hmcl.auth.AccountFactory; import org.jackhuang.hmcl.auth.CharacterSelector; import org.jackhuang.hmcl.util.DigestUtils; -import org.jackhuang.hmcl.util.Lang; -import org.jackhuang.hmcl.util.Logging; import org.jackhuang.hmcl.util.UUIDTypeAdapter; import java.net.Proxy; import java.util.Map; -import java.util.UUID; -import java.util.logging.Level; + +import static org.jackhuang.hmcl.util.Lang.tryCast; /** * @@ -46,9 +44,9 @@ public class OfflineAccountFactory extends AccountFactory { @Override public OfflineAccount fromStorage(Map storage, Proxy proxy) { - String username = Lang.get(storage, "username", String.class) + String username = tryCast(storage.get("username"), String.class) .orElseThrow(() -> new IllegalStateException("Offline account configuration malformed.")); - String uuid = Lang.get(storage, "uuid", String.class) + String uuid = tryCast(storage.get("uuid"), String.class) .orElse(getUUIDFromUserName(username)); // Check if the uuid is vaild diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/auth/yggdrasil/YggdrasilAccountFactory.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/auth/yggdrasil/YggdrasilAccountFactory.java index 9e324dad6..e4e15dde8 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/auth/yggdrasil/YggdrasilAccountFactory.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/auth/yggdrasil/YggdrasilAccountFactory.java @@ -20,7 +20,6 @@ package org.jackhuang.hmcl.auth.yggdrasil; import org.jackhuang.hmcl.auth.AccountFactory; import org.jackhuang.hmcl.auth.AuthenticationException; import org.jackhuang.hmcl.auth.CharacterSelector; -import org.jackhuang.hmcl.util.Lang; import org.jackhuang.hmcl.util.UUIDTypeAdapter; import java.net.Proxy; @@ -28,6 +27,8 @@ import java.util.Map; import java.util.Objects; import java.util.UUID; +import static org.jackhuang.hmcl.util.Lang.tryCast; + /** * * @author huangyuhui @@ -57,11 +58,11 @@ public class YggdrasilAccountFactory extends AccountFactory { Objects.requireNonNull(storage); Objects.requireNonNull(proxy); - String username = Lang.get(storage, "username", String.class) + String username = tryCast(storage.get("username"), String.class) .orElseThrow(() -> new IllegalArgumentException("storage does not have username")); - String clientToken = Lang.get(storage, "clientToken", String.class) + String clientToken = tryCast(storage.get("clientToken"), String.class) .orElseThrow(() -> new IllegalArgumentException("storage does not have client token.")); - String character = Lang.get(storage, "clientToken", String.class) + String character = tryCast(storage.get("clientToken"), String.class) .orElseThrow(() -> new IllegalArgumentException("storage does not have selected character name.")); return new YggdrasilAccount(new YggdrasilService(provider, proxy), username, clientToken, character, YggdrasilSession.fromStorage(storage)); diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/auth/yggdrasil/YggdrasilSession.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/auth/yggdrasil/YggdrasilSession.java index 1fcd80163..e901b105b 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/auth/yggdrasil/YggdrasilSession.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/auth/yggdrasil/YggdrasilSession.java @@ -1,12 +1,13 @@ package org.jackhuang.hmcl.auth.yggdrasil; -import org.jackhuang.hmcl.util.Lang; import org.jackhuang.hmcl.util.UUIDTypeAdapter; import java.util.HashMap; import java.util.Map; import java.util.Optional; +import static org.jackhuang.hmcl.util.Lang.tryCast; + public class YggdrasilSession { private final String accessToken; @@ -54,20 +55,20 @@ public class YggdrasilSession { } public static YggdrasilSession fromStorage(Map storage) { - Optional profileId = Lang.get(storage, "uuid", String.class); - Optional profileName = Lang.get(storage, "displayName", String.class); + Optional profileId = tryCast(storage.get("uuid"), String.class); + Optional profileName = tryCast(storage.get("displayName"), String.class); GameProfile profile = null; if (profileId.isPresent() && profileName.isPresent()) { profile = new GameProfile(UUIDTypeAdapter.fromString(profileId.get()), profileName.get(), - Lang.get(storage, "profileProperties", Map.class).map(PropertyMap::fromMap).orElseGet(PropertyMap::new)); + tryCast(storage.get("profileProperties"), Map.class).map(PropertyMap::fromMap).orElseGet(PropertyMap::new)); } return new YggdrasilSession( - Lang.get(storage, "accessToken", String.class).orElse(null), + tryCast(storage.get("accessToken"), String.class).orElse(null), profile, null, - Lang.get(storage, "userid", String.class) - .map(userId -> new User(userId, Lang.get(storage, "userProperties", Map.class).map(PropertyMap::fromMap).orElse(null))) + tryCast(storage.get("userid"), String.class) + .map(userId -> new User(userId, tryCast(storage.get("userProperties"), Map.class).map(PropertyMap::fromMap).orElse(null))) .orElse(null) ); } diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/util/Lang.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/util/Lang.java index 845eea10a..91ae8ae4b 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/util/Lang.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/util/Lang.java @@ -218,12 +218,12 @@ public final class Lang { * @param the type that {@code obj} is being cast to. * @return {@code obj} in the type of {@code V}. */ - @SuppressWarnings("unchecked") - public static Optional cast(Object obj, Class clazz) { - if (obj == null || !ReflectionHelper.isInstance(clazz, obj)) + public static Optional tryCast(Object obj, Class clazz) { + if (clazz.isInstance(obj)) { + return Optional.of(clazz.cast(obj)); + } else { return Optional.empty(); - else - return Optional.of((V) obj); + } } /** @@ -240,20 +240,6 @@ public final class Lang { return Optional.ofNullable(list.get(index)); } - /** - * Get the value to which the specific key is mapped, - * or {@code null} if {@code map} has no mapping for the key - * or the value is not in the type of {@code clazz}. - * - * @param map the map - * @param key the key for finding the associate value. - * @param the type of values in {@code map} - * @return the value to which the specific key is mapped, or {@code null} if {@code map} has no mapping for the key or the type is not correct. - */ - public static Optional get(Map map, Object key, Class clazz) { - return cast(map.get(key), clazz); - } - /** * Join two collections into one list. * diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/util/OperatingSystem.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/util/OperatingSystem.java index d84670492..30cbbf58f 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/util/OperatingSystem.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/util/OperatingSystem.java @@ -24,6 +24,11 @@ import java.io.File; import java.lang.management.ManagementFactory; import java.nio.charset.Charset; import java.util.Locale; +import java.util.Optional; + +import javax.management.JMException; +import javax.management.MBeanServer; +import javax.management.ObjectName; /** * Represents the operating system. @@ -103,11 +108,9 @@ public enum OperatingSystem { else CURRENT_OS = UNKNOWN; - Object bytes = ReflectionHelper.call(ManagementFactory.getOperatingSystemMXBean(), "getTotalPhysicalMemorySize"); - if (bytes instanceof Long) - TOTAL_MEMORY = (int) (((Long) bytes) / 1024 / 1024); - else - TOTAL_MEMORY = 1024; + TOTAL_MEMORY = getTotalPhysicalMemorySize() + .map(bytes -> (int) (bytes / 1024 / 1024)) + .orElse(1024); SUGGESTED_MEMORY = (int) (Math.round(1.0 * TOTAL_MEMORY / 4.0 / 128.0) * 128); @@ -117,6 +120,19 @@ public enum OperatingSystem { SYSTEM_ARCHITECTURE = arch; } + private static Optional getTotalPhysicalMemorySize() { + MBeanServer mBeanServer = ManagementFactory.getPlatformMBeanServer(); + try { + Object attribute = mBeanServer.getAttribute(new ObjectName("java.lang", "type", "OperatingSystem"), "TotalPhysicalMemorySize"); + if (attribute instanceof Long) { + return Optional.of((Long) attribute); + } + } catch (JMException e) { + return Optional.empty(); + } + return Optional.empty(); + } + public static void setClipboard(String string) { ClipboardContent c = new ClipboardContent(); c.putString(string); diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/util/ReflectionHelper.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/util/ReflectionHelper.java index ad90e0ac3..0b3597f33 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/util/ReflectionHelper.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/util/ReflectionHelper.java @@ -17,136 +17,12 @@ */ package org.jackhuang.hmcl.util; -import sun.misc.Unsafe; - -import java.lang.reflect.*; -import java.security.AccessController; -import java.security.PrivilegedActionException; -import java.security.PrivilegedExceptionAction; -import java.util.Arrays; -import java.util.Map; -import java.util.Optional; - /** * * @author huangyuhui */ public final class ReflectionHelper { - private static final Unsafe UNSAFE; - private static final long OBJECT_FIELD_OFFSET; - private static final Map> PRIMITIVES; - - static { - try { - UNSAFE = (Unsafe) AccessController.doPrivileged((PrivilegedExceptionAction) () -> { - Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe"); - theUnsafe.setAccessible(true); - return theUnsafe.get(null); - }); - - OBJECT_FIELD_OFFSET = UNSAFE.objectFieldOffset(getField(AccessibleObject.class, "override")); - - } catch (PrivilegedActionException ex) { - throw new AssertionError(ex); - } - - PRIMITIVES = Lang.mapOf( - new Pair<>("byte", Byte.class), - new Pair<>("short", Short.class), - new Pair<>("int", Integer.class), - new Pair<>("long", Long.class), - new Pair<>("char", Character.class), - new Pair<>("float", Float.class), - new Pair<>("double", Double.class), - new Pair<>("boolean", Boolean.class) - ); - } - - private static void setAccessibleForcibly(AccessibleObject object) { - UNSAFE.putBoolean(object, OBJECT_FIELD_OFFSET, true); - } - - public static Method getMethod(Object object, String name) { - return getMethod(object.getClass(), name); - } - - public static Method getMethod(Class clazz, String name) { - try { - Method m = clazz.getDeclaredMethod(name); - setAccessibleForcibly(m); - return m; - } catch (Exception e) { - return null; - } - } - - public static Field getField(Object object, String name) { - return getField(object.getClass(), name); - } - - public static Field getField(Class clazz, String name) { - try { - Field f = clazz.getDeclaredField(name); - setAccessibleForcibly(f); - return f; - } catch (Exception e) { - return null; - } - } - - public static Object call(Class cls, String name, Object object, Object... args) { - try { - if (args.length == 0) - try { - return cls.getDeclaredField(name).get(object); - } catch (NoSuchFieldException ignored) { - } - if ("new".equals(name)) { - for (Constructor c : cls.getDeclaredConstructors()) - if (checkParameter(c, args)) - return c.newInstance(args); - } else - return forMethod(cls, name, args).get().invoke(object, args); - throw new RuntimeException(); - } catch (Exception e) { - throw new IllegalArgumentException("Cannot find '" + name + "' in class '" + cls.getName() + "'", e); - } - } - - public static Object construct(Class clazz, Object... args) { - return call(clazz, "new", null, args); - } - - public static Object call(Object obj, String name, Object... args) { - return call(obj.getClass(), name, obj, args); - } - - public static boolean checkParameter(Executable exec, Object... args) { - Class[] cArgs = exec.getParameterTypes(); - if (args.length == cArgs.length) { - for (int i = 0; i < args.length; ++i) { - Object arg = args[i]; - if (arg != null ? !isInstance(cArgs[i], arg) : cArgs[i].isPrimitive()) - return false; - } - setAccessibleForcibly(exec); - return true; - } else - return false; - } - - public static boolean isInstance(Class superClass, Object obj) { - return superClass.isInstance(obj) || PRIMITIVES.get(superClass.getName()) == obj.getClass(); - } - - public static Optional forMethod(Class cls, String name, Object... args) { - return Arrays.stream(cls.getDeclaredMethods()) - .filter(s -> name.equals(s.getName())) - .filter(s -> checkParameter(s, args)) - .findFirst(); - } - public static StackTraceElement getCaller() { StackTraceElement[] elements = Thread.currentThread().getStackTrace(); StackTraceElement caller = elements[2];