Skip to content

Commit 8b9eac2

Browse files
committed
Add testing for finding the best method
1 parent 1bac491 commit 8b9eac2

3 files changed

Lines changed: 180 additions & 20 deletions

File tree

src/main/java/com/hubspot/jinjava/el/ext/JinjavaBeanELResolver.java

Lines changed: 31 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import com.hubspot.jinjava.interpret.DeferredValueException;
66
import com.hubspot.jinjava.interpret.JinjavaInterpreter;
77
import com.hubspot.jinjava.util.EagerReconstructionUtils;
8+
import java.lang.invoke.MethodType;
89
import java.lang.reflect.Array;
910
import java.lang.reflect.Constructor;
1011
import java.lang.reflect.Field;
@@ -195,6 +196,7 @@ protected Object invokeBestMatch(
195196

196197
protected Method findMethod(Object base, String name, Object[] params, int paramCount) {
197198
Method varArgsMethod = null;
199+
198200
Method[] methods = base.getClass().getMethods();
199201
List<Method> potentialMethods = new LinkedList<>();
200202

@@ -211,20 +213,8 @@ protected Method findMethod(Object base, String name, Object[] params, int param
211213
final Method finalVarArgsMethod = varArgsMethod;
212214
return potentialMethods
213215
.stream()
214-
.filter(
215-
method -> {
216-
for (int i = 0; i < method.getParameterTypes().length; i++) {
217-
if (
218-
params[i] != null &&
219-
!method.getParameterTypes()[i].isAssignableFrom(params[i].getClass())
220-
) {
221-
return false;
222-
}
223-
}
224-
return true;
225-
}
226-
)
227-
.findAny()
216+
.filter(method -> checkAssignableParameterTypes(params, method))
217+
.min(JinjavaBeanELResolver::pickMoreSpecificMethod)
228218
.orElseGet(
229219
() ->
230220
potentialMethods
@@ -239,6 +229,33 @@ protected Method findMethod(Object base, String name, Object[] params, int param
239229
);
240230
}
241231

232+
private static boolean checkAssignableParameterTypes(Object[] params, Method method) {
233+
for (int i = 0; i < method.getParameterTypes().length; i++) {
234+
Class<?> paramType = method.getParameterTypes()[i];
235+
if (paramType.isPrimitive()) {
236+
paramType = MethodType.methodType(paramType).wrap().returnType();
237+
}
238+
if (params[i] != null && !paramType.isAssignableFrom(params[i].getClass())) {
239+
return false;
240+
}
241+
}
242+
return true;
243+
}
244+
245+
private static int pickMoreSpecificMethod(Method methodA, Method methodB) {
246+
Class<?>[] typesA = methodA.getParameterTypes();
247+
Class<?>[] typesB = methodB.getParameterTypes();
248+
for (int i = 0; i < typesA.length; i++) {
249+
if (!typesA[i].isAssignableFrom(typesB[i])) {
250+
if (typesB[i].isPrimitive()) {
251+
return 1;
252+
}
253+
return -1;
254+
}
255+
}
256+
return 1;
257+
}
258+
242259
private static Method findAccessibleMethod(Method method) {
243260
Method result = findPublicAccessibleMethod(method);
244261
if (result == null && method != null && Modifier.isPublic(method.getModifiers())) {

src/test/java/com/hubspot/jinjava/EagerTest.java

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1241,12 +1241,10 @@ public void itReconstructsWordsFromInsideNestedExpressions() {
12411241

12421242
@Test
12431243
public void itReconstructsWordsFromInsideNestedExpressionsSecondPass() {
1244-
// interpreter.getContext().put("deferred", new PyList(new ArrayList<>()));
1245-
// expectedTemplateInterpreter.assertExpectedNonEagerOutput(
1246-
// "reconstructs-words-from-inside-nested-expressions.expected"
1247-
// );
1248-
String foo = interpreter.render("{{ 'foo'.replace('foo', '') }}");
1249-
String bar = "";
1244+
interpreter.getContext().put("deferred", new PyList(new ArrayList<>()));
1245+
expectedTemplateInterpreter.assertExpectedNonEagerOutput(
1246+
"reconstructs-words-from-inside-nested-expressions.expected"
1247+
);
12501248
}
12511249

12521250
@Test
Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
package com.hubspot.jinjava.el.ext;
2+
3+
import static org.assertj.core.api.Assertions.assertThat;
4+
5+
import com.hubspot.jinjava.el.JinjavaELContext;
6+
import javax.el.ELContext;
7+
import org.junit.Before;
8+
import org.junit.Test;
9+
10+
public class JinjavaBeanELResolverTest {
11+
private JinjavaBeanELResolver jinjavaBeanELResolver;
12+
private ELContext elContext;
13+
14+
@Before
15+
public void setUp() throws Exception {
16+
jinjavaBeanELResolver = new JinjavaBeanELResolver();
17+
elContext = new JinjavaELContext();
18+
}
19+
20+
@Test
21+
public void itInvokesProperStringReplace() {
22+
assertThat(
23+
jinjavaBeanELResolver.invoke(
24+
elContext,
25+
"abcd",
26+
"replace",
27+
null,
28+
new Object[] { "abcd", "efgh" }
29+
)
30+
)
31+
.isEqualTo("efgh");
32+
assertThat(
33+
jinjavaBeanELResolver.invoke(
34+
elContext,
35+
"abcd",
36+
"replace",
37+
null,
38+
new Object[] { 'a', 'e' }
39+
)
40+
)
41+
.isEqualTo("ebcd");
42+
}
43+
44+
@Test
45+
public void itInvokesBestMethodWithSingleParam() {
46+
class Temp {
47+
48+
public String getResult(int a) {
49+
return "int";
50+
}
51+
52+
public String getResult(String a) {
53+
return "String";
54+
}
55+
56+
public String getResult(Object a) {
57+
return "Object";
58+
}
59+
60+
public String getResult(CharSequence a) {
61+
return "CharSequence";
62+
}
63+
}
64+
Temp var = new Temp();
65+
assertThat(
66+
jinjavaBeanELResolver.invoke(
67+
elContext,
68+
var,
69+
"getResult",
70+
null,
71+
new Object[] { 1 }
72+
)
73+
)
74+
.isEqualTo("int");
75+
assertThat(
76+
jinjavaBeanELResolver.invoke(
77+
elContext,
78+
var,
79+
"getResult",
80+
null,
81+
new Object[] { "1" }
82+
)
83+
)
84+
.isEqualTo("String");
85+
assertThat(
86+
jinjavaBeanELResolver.invoke(
87+
elContext,
88+
var,
89+
"getResult",
90+
null,
91+
new Object[] { new Object() }
92+
)
93+
)
94+
.isEqualTo("Object");
95+
}
96+
97+
@Test
98+
public void itPrefersPrimitives() {
99+
class Temp {
100+
101+
public String getResult(int a, Integer b) {
102+
return "int Integer";
103+
}
104+
105+
public String getResult(int a, Object b) {
106+
return "int Object";
107+
}
108+
109+
public String getResult(Number a, int b) {
110+
return "Number int";
111+
}
112+
}
113+
Temp var = new Temp();
114+
assertThat(
115+
jinjavaBeanELResolver.invoke(
116+
elContext,
117+
var,
118+
"getResult",
119+
null,
120+
new Object[] { 1, 2 }
121+
)
122+
)
123+
.isEqualTo("int Integer");
124+
assertThat(
125+
jinjavaBeanELResolver.invoke(
126+
elContext,
127+
var,
128+
"getResult",
129+
null,
130+
new Object[] { 1, Integer.valueOf(2) }
131+
)
132+
)
133+
.isEqualTo("int Integer"); // should be "int object", but we can't figure that out
134+
assertThat(
135+
jinjavaBeanELResolver.invoke(
136+
elContext,
137+
var,
138+
"getResult",
139+
null,
140+
new Object[] { Integer.valueOf(1), 2 }
141+
)
142+
)
143+
.isEqualTo("int Integer"); // should be "Number int", but we can't figure that out
144+
}
145+
}

0 commit comments

Comments
 (0)