diff --git a/.travis.yml b/.travis.yml
index e313ff1..d3cea56 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -3,9 +3,8 @@ sudo: false
language: java
jdk:
- - oraclejdk7
- - openjdk7
- - openjdk6
+ - oraclejdk8
+ - openjdk8
cache:
directories:
diff --git a/pom.xml b/pom.xml
index 271c906..9aa85d9 100644
--- a/pom.xml
+++ b/pom.xml
@@ -91,6 +91,8 @@
1.6
1.6
+ 1.8
+ 1.8
true
true
true
@@ -101,6 +103,7 @@
+
org.apache.felix
maven-bundle-plugin
@@ -115,6 +118,7 @@
+
+
org.apache.maven.plugins
maven-resources-plugin
@@ -140,6 +145,7 @@
UTF-8
+
org.apache.maven.plugins
maven-surefire-plugin
@@ -148,6 +154,7 @@
**/*Test.java
+
org.apache.maven.plugins
maven-javadoc-plugin
@@ -158,6 +165,7 @@
-Xdoclint:all -Xdoclint:-missing
+
org.apache.maven.plugins
maven-jar-plugin
@@ -184,6 +192,7 @@
256m
+
org.eluder.coveralls
coveralls-maven-plugin
diff --git a/src/main/java/com/samskivert/mustache/DefaultCollector.java b/src/main/java/com/samskivert/mustache/DefaultCollector.java
index 13d1eec..9e3a8f6 100644
--- a/src/main/java/com/samskivert/mustache/DefaultCollector.java
+++ b/src/main/java/com/samskivert/mustache/DefaultCollector.java
@@ -10,7 +10,9 @@ import java.lang.reflect.Method;
import java.util.AbstractList;
import java.util.Iterator;
+import java.util.LinkedHashSet;
import java.util.Map;
+import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
/**
@@ -41,6 +43,7 @@ public class DefaultCollector extends BasicCollector
Mustache.VariableFetcher fetcher = super.createFetcher(ctx, name);
if (fetcher != null) return fetcher;
+ // first check for a getter which provides the value
Class> cclass = ctx.getClass();
final Method m = getMethod(cclass, name);
if (m != null) {
@@ -51,6 +54,7 @@ public class DefaultCollector extends BasicCollector
};
}
+ // next check for a getter which provides the value
final Field f = getField(cclass, name);
if (f != null) {
return new Mustache.VariableFetcher() {
@@ -60,6 +64,17 @@ public class DefaultCollector extends BasicCollector
};
}
+ // finally check for a default interface method which provides the value (this is left to
+ // last because it's much more expensive and hopefully something already matched above)
+ final Method im = getIfaceMethod(cclass, name);
+ if (im != null) {
+ return new Mustache.VariableFetcher() {
+ public Object get (Object ctx, String name) throws Exception {
+ return im.invoke(ctx);
+ }
+ };
+ }
+
return null;
}
@@ -69,15 +84,38 @@ public class DefaultCollector extends BasicCollector
}
protected Method getMethod (Class> clazz, String name) {
+ // first check up the superclass chain
+ for (Class> cc = clazz; cc != null && cc != Object.class; cc = cc.getSuperclass()) {
+ Method m = getMethodOn(cc, name);
+ if (m != null) return m;
+ }
+ return null;
+ }
+
+ protected Method getIfaceMethod (Class> clazz, String name) {
+ // enumerate the transitive closure of all interfaces implemented by clazz
+ Set> ifaces = new LinkedHashSet>();
+ for (Class> cc = clazz; cc != null && cc != Object.class; cc = cc.getSuperclass()) {
+ addIfaces(ifaces, cc, false);
+ }
+ // now search those in the order that we found them
+ for (Class> iface : ifaces) {
+ Method m = getMethodOn(iface, name);
+ if (m != null) return m;
+ }
+ return null;
+ }
+
+ private void addIfaces (Set> ifaces, Class> clazz, boolean isIface) {
+ if (isIface) ifaces.add(clazz);
+ for (Class> iface : clazz.getInterfaces()) addIfaces(ifaces, iface, true);
+ }
+
+ protected Method getMethodOn (Class> clazz, String name) {
Method m;
try {
m = clazz.getDeclaredMethod(name);
- if (!m.getReturnType().equals(void.class)) {
- if (!m.isAccessible()) {
- m.setAccessible(true);
- }
- return m;
- }
+ if (!m.getReturnType().equals(void.class)) return makeAccessible(m);
} catch (Exception e) {
// fall through
}
@@ -85,12 +123,7 @@ public class DefaultCollector extends BasicCollector
String upperName = Character.toUpperCase(name.charAt(0)) + name.substring(1);
try {
m = clazz.getDeclaredMethod("get" + upperName);
- if (!m.getReturnType().equals(void.class)) {
- if (!m.isAccessible()) {
- m.setAccessible(true);
- }
- return m;
- }
+ if (!m.getReturnType().equals(void.class)) return makeAccessible(m);
} catch (Exception e) {
// fall through
}
@@ -98,23 +131,19 @@ public class DefaultCollector extends BasicCollector
try {
m = clazz.getDeclaredMethod("is" + upperName);
if (m.getReturnType().equals(boolean.class) ||
- m.getReturnType().equals(Boolean.class)) {
- if (!m.isAccessible()) {
- m.setAccessible(true);
- }
- return m;
- }
+ m.getReturnType().equals(Boolean.class)) return makeAccessible(m);
} catch (Exception e) {
// fall through
}
- Class> sclass = clazz.getSuperclass();
- if (sclass != Object.class && sclass != null) {
- return getMethod(clazz.getSuperclass(), name);
- }
return null;
}
+ private Method makeAccessible (Method m) {
+ if (!m.isAccessible()) m.setAccessible(true);
+ return m;
+ }
+
protected Field getField (Class> clazz, String name) {
Field f;
try {
diff --git a/src/test/java/com/samskivert/mustache/MustacheTest.java b/src/test/java/com/samskivert/mustache/MustacheTest.java
index 67c5881..c67968d 100644
--- a/src/test/java/com/samskivert/mustache/MustacheTest.java
+++ b/src/test/java/com/samskivert/mustache/MustacheTest.java
@@ -41,6 +41,22 @@ public class MustacheTest
});
}
+ public interface HasDefault {
+ default String getFoo () { return "bar"; }
+ }
+ public interface Interloper extends HasDefault {
+ default String getFoo () { return "bang"; }
+ }
+ @Test public void testDefaultMethodVariable () {
+ test("bar", "{{foo}}", new HasDefault() {
+ });
+ test("bang", "{{foo}}", new Interloper() {
+ });
+ test("bong", "{{foo}}", new Interloper() {
+ public String getFoo () { return "bong"; }
+ });
+ }
+
@Test public void testPropertyVariable () {
test("bar", "{{foo}}", new Object() {
String getFoo () { return "bar"; }