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 super Stream, ? 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