Skip to content

Commit ae8077e

Browse files
committed
WIP: Handlebars works on Java 15
1 parent 05078c5 commit ae8077e

File tree

4 files changed

+68
-53
lines changed

4 files changed

+68
-53
lines changed

handlebars/src/main/java/com/github/jknack/handlebars/Handlebars.java

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
import java.util.List;
4040
import java.util.Map.Entry;
4141
import java.util.Set;
42+
import java.util.concurrent.atomic.AtomicInteger;
4243

4344
import org.slf4j.Logger;
4445

@@ -176,6 +177,8 @@ public boolean equals(final Object obj) {
176177
*/
177178
public static class Utils {
178179

180+
public static final int javaVersion = javaVersion();
181+
179182
/**
180183
* Evaluate the given object and return true is the object is considered
181184
* empty. Nulls, empty list or array and false values are considered empty.
@@ -233,6 +236,23 @@ public static boolean isEmpty(final Object value) {
233236
public static CharSequence escapeExpression(final CharSequence input) {
234237
return EscapingStrategy.DEF.escape(input);
235238
}
239+
240+
private static int javaVersion() {
241+
String version = System.getProperty("java.version");
242+
if (version.startsWith("1.")) {
243+
version = version.substring(2, 3);
244+
} else {
245+
int dot = version.indexOf(".");
246+
if (dot != -1) {
247+
version = version.substring(0, dot);
248+
}
249+
}
250+
try {
251+
return Integer.parseInt(version);
252+
} catch (NumberFormatException e) {
253+
return 8;
254+
}
255+
}
236256
}
237257

238258
/**

handlebars/src/main/java/com/github/jknack/handlebars/internal/BaseTemplate.java

Lines changed: 21 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import java.io.IOException;
2626
import java.io.Writer;
2727
import java.lang.invoke.MethodHandles;
28+
import java.lang.invoke.MethodType;
2829
import java.lang.reflect.Constructor;
2930
import java.lang.reflect.InvocationHandler;
3031
import java.lang.reflect.Method;
@@ -264,17 +265,26 @@ private boolean isDefault(final Method method) {
264265

265266
private Object invokeDefaultMethod(final Method method, final Class<?> lookupClass,
266267
final Object proxy, final Object... args) throws Throwable {
267-
// Jumping through these hoops is needed because calling unreflectSpecial requires that
268-
// the lookup instance have private access to the special caller. None of the static
269-
// factory methods for Lookup will give us an instance with the access modes we need,
270-
// so we work around it by calling the private constructor via reflection.
271-
Constructor<MethodHandles.Lookup> constructor = MethodHandles.Lookup.class
272-
.getDeclaredConstructor(Class.class, int.class);
273-
constructor.setAccessible(true);
274-
return constructor.newInstance(lookupClass, -1 /* trusted */)
275-
.unreflectSpecial(method, lookupClass)
276-
.bindTo(proxy)
277-
.invokeWithArguments(args);
268+
if (Handlebars.Utils.javaVersion >= 15) {
269+
MethodType methodType = MethodType.methodType(method.getReturnType(),
270+
method.getParameterTypes());
271+
return MethodHandles.lookup()
272+
.findSpecial(lookupClass, method.getName(), methodType, lookupClass)
273+
.bindTo(proxy)
274+
.invokeWithArguments(args);
275+
} else {
276+
// Jumping through these hoops is needed because calling unreflectSpecial requires that
277+
// the lookup instance have private access to the special caller. None of the static
278+
// factory methods for Lookup will give us an instance with the access modes we need,
279+
// so we work around it by calling the private constructor via reflection.
280+
Constructor<MethodHandles.Lookup> constructor = MethodHandles.Lookup.class
281+
.getDeclaredConstructor(Class.class, int.class);
282+
constructor.setAccessible(true);
283+
return constructor.newInstance(lookupClass, -1 /* trusted */)
284+
.unreflectSpecial(method, lookupClass)
285+
.bindTo(proxy)
286+
.invokeWithArguments(args);
287+
}
278288
}
279289

280290
@Override

handlebars/src/test/java/com/github/jknack/handlebars/AbstractTest.java

Lines changed: 1 addition & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -161,25 +161,8 @@ protected Handlebars newHandlebars() {
161161
}
162162

163163
public void withJava(Predicate<Integer> predicate, Task task) throws IOException {
164-
if (predicate.test(getJavaVersion())) {
164+
if (predicate.test(Handlebars.Utils.javaVersion)) {
165165
task.run();
166166
}
167167
}
168-
169-
static int getJavaVersion() {
170-
String version = System.getProperty("java.version");
171-
if (version.startsWith("1.")) {
172-
version = version.substring(2, 3);
173-
} else {
174-
int dot = version.indexOf(".");
175-
if (dot != -1) {
176-
version = version.substring(0, dot);
177-
}
178-
}
179-
try {
180-
return Integer.parseInt(version);
181-
} catch (NumberFormatException e) {
182-
return 8;
183-
}
184-
}
185168
}

handlebars/src/test/java/com/github/jknack/handlebars/context/SetAccessibleValueResolverTest.java

Lines changed: 26 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -7,52 +7,54 @@
77
import java.lang.reflect.Method;
88
import java.util.Collections;
99

10-
import org.junit.Ignore;
1110
import org.junit.Test;
1211

12+
import com.github.jknack.handlebars.Handlebars;
13+
import com.github.jknack.handlebars.ValueResolver;
14+
1315
public class SetAccessibleValueResolverTest {
1416

1517
/*
1618
* The following tests require JDK 9 or greater.
1719
* To keep the tests from failing we use junit assume.
1820
*/
19-
20-
@Ignore
21-
public void testSetAccessibleOnJDK9OrGreater() throws Exception {
22-
assumeTrue(getJavaVersion() >= 9);
21+
@Test
22+
public void shouldPrintWarningAndNotThrowExceptionSetAccesibleOnOnJava9Or17() throws Exception {
23+
assumeTrue(Handlebars.Utils.javaVersion > 8 && Handlebars.Utils.javaVersion <= 14);
2324
MethodValueResolver mv = new MethodValueResolver() {
2425

2526
@Override
2627
protected boolean isUseSetAccessible(Method m) {
2728
return true;
2829
}
2930
};
30-
try {
31-
mv.resolve(Collections.emptyMap(), "doesNotMatter");
32-
fail("Expect InaccessibleObjectException");
33-
} catch (/* InaccessibleObjectException */ Exception e) {
34-
}
31+
Object result = mv.resolve(Collections.emptyMap(), "doesNotMatter");
32+
assertEquals(ValueResolver.UNRESOLVED, result);
33+
}
3534

36-
mv = new MethodValueResolver();
37-
mv.resolve(Collections.emptyMap(), "doesNotMatter");
35+
@Test
36+
public void shouldNotPrintWarningAndNotThrowExceptionSetAccesibleOnOnJava9Or17()
37+
throws Exception {
38+
assumeTrue(Handlebars.Utils.javaVersion > 8 && Handlebars.Utils.javaVersion <= 17);
39+
MethodValueResolver mv = new MethodValueResolver();
3840
Object result = mv.resolve(Collections.emptyMap(), "isEmpty");
3941
assertEquals(Boolean.TRUE, result);
4042
}
4143

42-
static int getJavaVersion() {
43-
String version = System.getProperty("java.version");
44-
if (version.startsWith("1.")) {
45-
version = version.substring(2, 3);
46-
} else {
47-
int dot = version.indexOf(".");
48-
if (dot != -1) {
49-
version = version.substring(0, dot);
44+
@Test
45+
public void shouldThrowExceptionOnJava17orHigher() throws Exception {
46+
assumeTrue(Handlebars.Utils.javaVersion >= 17);
47+
MethodValueResolver mv = new MethodValueResolver() {
48+
49+
@Override
50+
protected boolean isUseSetAccessible(Method m) {
51+
return true;
5052
}
51-
}
53+
};
5254
try {
53-
return Integer.parseInt(version);
54-
} catch (NumberFormatException e) {
55-
return 8;
55+
mv.resolve(Collections.emptyMap(), "doesNotMatter");
56+
fail("Expect InaccessibleObjectException");
57+
} catch (/* InaccessibleObjectException */ Exception e) {
5658
}
5759
}
5860

0 commit comments

Comments
 (0)