diff --git a/JavaCompat/pom.xml b/JavaCompat/pom.xml new file mode 100644 index 00000000..4c9f664b --- /dev/null +++ b/JavaCompat/pom.xml @@ -0,0 +1,15 @@ + + + 4.0.0 + + + com.gmail.filoghost.holographicdisplays + holographicdisplays-parent + 2.3.0-SNAPSHOT + + + holographicdisplays-javacompat + HolographicDisplays Java Compat + + diff --git a/JavaCompat/src/main/java/java/lang/StackWalker.java b/JavaCompat/src/main/java/java/lang/StackWalker.java new file mode 100644 index 00000000..858de4d1 --- /dev/null +++ b/JavaCompat/src/main/java/java/lang/StackWalker.java @@ -0,0 +1,20 @@ +package java.lang; + +import java.util.function.Function; +import java.util.stream.Stream; + +public class StackWalker { + + public static interface StackFrame { + StackTraceElement toStackTraceElement(); + } + + public static StackWalker getInstance() { + return null; + } + + public T walk(Function, ? extends T> function) { + return null; + } + +} diff --git a/JavaCompat/src/main/java/java/util/Optional.java b/JavaCompat/src/main/java/java/util/Optional.java new file mode 100644 index 00000000..8ea90908 --- /dev/null +++ b/JavaCompat/src/main/java/java/util/Optional.java @@ -0,0 +1,9 @@ +package java.util; + +public class Optional { + + public T orElse(T other) { + return null; + } + +} diff --git a/JavaCompat/src/main/java/java/util/function/Function.java b/JavaCompat/src/main/java/java/util/function/Function.java new file mode 100644 index 00000000..5bc7a22e --- /dev/null +++ b/JavaCompat/src/main/java/java/util/function/Function.java @@ -0,0 +1,7 @@ +package java.util.function; + +public interface Function { + + R apply(T t); + +} diff --git a/JavaCompat/src/main/java/java/util/stream/Stream.java b/JavaCompat/src/main/java/java/util/stream/Stream.java new file mode 100644 index 00000000..5989a99b --- /dev/null +++ b/JavaCompat/src/main/java/java/util/stream/Stream.java @@ -0,0 +1,13 @@ +package java.util.stream; + +import java.util.Optional; + +public interface Stream { + + public Stream skip(long n); + + public Stream limit(long maxSize); + + public Optional findFirst(); + +} \ No newline at end of file diff --git a/Plugin/src/main/java/com/gmail/filoghost/holographicdisplays/HolographicDisplays.java b/Plugin/src/main/java/com/gmail/filoghost/holographicdisplays/HolographicDisplays.java index b115ed8a..e60a588e 100644 --- a/Plugin/src/main/java/com/gmail/filoghost/holographicdisplays/HolographicDisplays.java +++ b/Plugin/src/main/java/com/gmail/filoghost/holographicdisplays/HolographicDisplays.java @@ -35,6 +35,7 @@ import com.gmail.filoghost.holographicdisplays.task.StartupLoadHologramsTask; import com.gmail.filoghost.holographicdisplays.task.WorldPlayerCounterTask; import com.gmail.filoghost.holographicdisplays.util.ConsoleLogger; import com.gmail.filoghost.holographicdisplays.util.NMSVersion; +import com.gmail.filoghost.holographicdisplays.util.Utils; import com.gmail.filoghost.holographicdisplays.util.VersionUtils; public class HolographicDisplays extends JavaPlugin { @@ -161,7 +162,7 @@ public class HolographicDisplays extends JavaPlugin { if (requiredVersionError == null) { ProtocolLibHook protocolLibHook; - if (VersionUtils.classExists("com.comphenix.protocol.wrappers.WrappedDataWatcher$WrappedDataWatcherObject")) { + if (Utils.classExists("com.comphenix.protocol.wrappers.WrappedDataWatcher$WrappedDataWatcherObject")) { // Only the new version contains this class ConsoleLogger.log(Level.INFO, "Found ProtocolLib, using new version."); protocolLibHook = new com.gmail.filoghost.holographicdisplays.bridge.protocollib.current.ProtocolLibHookImpl(); diff --git a/Utils/pom.xml b/Utils/pom.xml index 206a752d..eb4d3db7 100644 --- a/Utils/pom.xml +++ b/Utils/pom.xml @@ -36,6 +36,13 @@ 1.8-R0.1-SNAPSHOT true + + + ${project.groupId} + holographicdisplays-javacompat + 2.3.0-SNAPSHOT + provided + diff --git a/Utils/src/main/java/com/gmail/filoghost/holographicdisplays/util/Utils.java b/Utils/src/main/java/com/gmail/filoghost/holographicdisplays/util/Utils.java index 0d0f89c8..0b4a8ad3 100644 --- a/Utils/src/main/java/com/gmail/filoghost/holographicdisplays/util/Utils.java +++ b/Utils/src/main/java/com/gmail/filoghost/holographicdisplays/util/Utils.java @@ -53,15 +53,10 @@ public class Utils extends Object { return new ArrayList(); } - public static Set newSet() { return new HashSet(); } - @SuppressWarnings("unchecked") - public static T[] listToArray(List list) { - return (T[]) list.toArray(); - } public static int floor(double num) { int floor = (int) num; @@ -107,10 +102,12 @@ public class Utils extends Object { return join(elements, separator, 0, elements.size()); } + public static String sanitize(String s) { return s != null ? s : "null"; } + public static boolean isThereNonNull(Object... objects) { if (objects == null) { return false; @@ -124,4 +121,14 @@ public class Utils extends Object { return false; } + + + public static boolean classExists(String className) { + try { + Class.forName(className); + return true; + } catch (Throwable t) { + return false; + } + } } diff --git a/Utils/src/main/java/com/gmail/filoghost/holographicdisplays/util/VersionUtils.java b/Utils/src/main/java/com/gmail/filoghost/holographicdisplays/util/VersionUtils.java index 02a20a7c..876533aa 100644 --- a/Utils/src/main/java/com/gmail/filoghost/holographicdisplays/util/VersionUtils.java +++ b/Utils/src/main/java/com/gmail/filoghost/holographicdisplays/util/VersionUtils.java @@ -79,14 +79,4 @@ public class VersionUtils { return isVersionGreaterEqual(reference, lowest) && isVersionLessEqual(reference, highest); } - - public static boolean classExists(String className) { - try { - Class.forName(className); - return true; - } catch (Throwable t) { - return false; - } - } - } diff --git a/Utils/src/main/java/com/gmail/filoghost/holographicdisplays/util/reflection/ReflectionUtils.java b/Utils/src/main/java/com/gmail/filoghost/holographicdisplays/util/reflection/ReflectionUtils.java index 617a0b11..4c0a0842 100644 --- a/Utils/src/main/java/com/gmail/filoghost/holographicdisplays/util/reflection/ReflectionUtils.java +++ b/Utils/src/main/java/com/gmail/filoghost/holographicdisplays/util/reflection/ReflectionUtils.java @@ -1,47 +1,69 @@ package com.gmail.filoghost.holographicdisplays.util.reflection; -import java.lang.reflect.Method; +import java.lang.StackWalker.StackFrame; +import java.util.function.Function; import java.util.logging.Level; +import java.util.stream.Stream; import com.gmail.filoghost.holographicdisplays.util.ConsoleLogger; +import com.gmail.filoghost.holographicdisplays.util.Utils; public class ReflectionUtils { - private static Method getStackTraceElementMethod; - private static Method getStackTraceDepthMethod; + private static final boolean HAS_JAVA9_STACKWALKER = Utils.classExists("java.lang.StackWalker"); - private static boolean stackTraceErrorPrinted; + private static final ReflectMethod GET_STACKTRACE_DEPTH_METHOD = new ReflectMethod(Throwable.class, "getStackTraceDepth"); + private static final ReflectMethod GET_STACKTRACE_ELEMENT_METHOD = new ReflectMethod(Throwable.class, "getStackTraceElement", int.class); + + private static boolean stackTraceError; /** * If you only need one stack trace element this is faster than Throwable.getStackTrace()[element], - * it doesn't generate the full stack trace. + * it doesn't generate the full stack trace (except as fallback). */ - public static StackTraceElement getStackTraceElement(int index) { - try { - if (getStackTraceElementMethod == null) { - getStackTraceElementMethod = Throwable.class.getDeclaredMethod("getStackTraceElement", int.class); - getStackTraceElementMethod.setAccessible(true); - } - if (getStackTraceDepthMethod == null) { - getStackTraceDepthMethod = Throwable.class.getDeclaredMethod("getStackTraceDepth"); - getStackTraceDepthMethod.setAccessible(true); - } - - Throwable dummyThrowable = new Throwable(); - int depth = (Integer) getStackTraceDepthMethod.invoke(dummyThrowable); - - if (index < depth) { - return (StackTraceElement) getStackTraceElementMethod.invoke(new Throwable(), index); - } else { - return null; - } - } catch (Throwable t) { - if (!stackTraceErrorPrinted) { - ConsoleLogger.log(Level.WARNING, "Unable to get a stacktrace element, please inform the developer. You will only see this error once to avoid spam.", t); - stackTraceErrorPrinted = true; + public static StackTraceElement getStackTraceElement(final int index) { + // Use the faster methods only if there hasn't been any error while executing them previously + if (!stackTraceError) { + try { + if (HAS_JAVA9_STACKWALKER) { + // Use the Java 9 StackWalker API + StackFrame result = StackWalker.getInstance().walk(new Function, StackFrame>() { + + @Override + public StackFrame apply(Stream stream) { + return stream.skip(index).limit(1).findFirst().orElse(null); + } + + }); + return result != null ? result.toStackTraceElement() : null; + + } else { + // Use reflection to avoid generating the full stacktrace + Throwable dummyThrowable = new Throwable(); + int depth = GET_STACKTRACE_DEPTH_METHOD.invoke(dummyThrowable); + + if (index < depth) { + return GET_STACKTRACE_ELEMENT_METHOD.invoke(dummyThrowable, index); + } else { + return null; + } + } + } catch (Throwable t) { + if (!stackTraceError) { + ConsoleLogger.log(Level.WARNING, "Unable to get a stacktrace element, please inform the developer. You will only see this error once and a fallback method will be used.", t); + stackTraceError = true; + } } + } + + // Fallback slower method, generates the full stack trace (it should never be called if everything works as expected) + StackTraceElement[] fullStackTrace = new Throwable().getStackTrace(); + if (index < fullStackTrace.length) { + return fullStackTrace[index]; + } else { return null; } + } } diff --git a/pom.xml b/pom.xml index cfbcd391..cb2bd357 100644 --- a/pom.xml +++ b/pom.xml @@ -18,6 +18,7 @@ API + JavaCompat Utils NMS/Interfaces NMS/v1_8_R1