forked from spullara/mustache.java
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathSimpleObjectHandler.java
More file actions
134 lines (120 loc) · 4.21 KB
/
SimpleObjectHandler.java
File metadata and controls
134 lines (120 loc) · 4.21 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
package com.github.mustachejava.reflect;
import com.github.mustachejava.*;
import com.github.mustachejava.util.Wrapper;
import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
public class SimpleObjectHandler extends BaseObjectHandler {
@Override
public Binding createBinding(final String name, TemplateContext tc, Code code) {
return new Binding() {
// We find the wrapper just once since only the name is needed
private Wrapper wrapper = find(name, null);
@Override
public Object get(List<Object> scopes) {
return wrapper.call(scopes);
}
};
}
@Override
public Wrapper find(final String name, final List<Object> scopes) {
return scopes1 -> {
for (int i = scopes1.size() - 1; i >= 0; i--) {
Object scope = scopes1.get(i);
if (scope != null) {
int index = name.indexOf(".");
if (index == -1) {
// Special case Maps
if (scope instanceof Map) {
Map map = (Map) scope;
if (map.containsKey(name)) {
return map.get(name);
} else if (!areMethodsAccessible(map)) {
continue; //don't check methods, move to next scope
}
}
// Check to see if there is a method or field that matches
try {
AccessibleObject ao = lookup(scope.getClass(), name);
if (ao instanceof Method) {
return ((Method) ao).invoke(scope);
} else if (ao instanceof Field) {
return ((Field) ao).get(scope);
}
} catch (InvocationTargetException ie) {
throw new MustacheException("Failed to get " + name + " from " + scope.getClass(), ie);
} catch (IllegalAccessException iae) {
throw new MustacheException("Set accessible failed to get " + name + " from " + scope.getClass(), iae);
}
} else {
// Dig into the dot-notation through recursion
List<Object> subscope = ObjectHandler.makeList(scope);
Wrapper wrapper = find(name.substring(0, index), subscope);
if (wrapper != null) {
scope = wrapper.call(subscope);
if (scope == null) {
continue;
}
subscope = ObjectHandler.makeList(scope);
return find(name.substring(index + 1), ObjectHandler.makeList(subscope)).call(subscope);
}
}
}
}
return null;
};
}
// Used for the member cache
private static class WrapperKey {
private final Class aClass;
private final String name;
private final int hashcode;
WrapperKey(Class aClass, String name) {
this.aClass = aClass;
this.name = name;
hashcode = aClass.hashCode() + 43 * name.hashCode();
}
@Override
public int hashCode() {
return hashcode;
}
@Override
public boolean equals(Object obj) {
if (obj instanceof WrapperKey) {
WrapperKey oKey = (WrapperKey) obj;
return aClass.equals(oKey.aClass) && name.equals(oKey.name);
} else {
return false;
}
}
}
// Cache of classes + name => field mappings
// By keeping this non-static you can release the cache by releasing the handler
private Map<WrapperKey, AccessibleObject> cache = new ConcurrentHashMap<>();
// Used to cache misses
private static AccessibleObject NONE;
static {
try {
NONE = SimpleObjectHandler.class.getDeclaredField("NONE");
} catch (NoSuchFieldException e) {
throw new AssertionError("Failed to init: " + e);
}
}
// Use the cache to find lookup members faster
private AccessibleObject lookup(Class sClass, String name) {
WrapperKey key = new WrapperKey(sClass, name);
AccessibleObject ao = cache.get(key);
if (ao == null) {
ao = findMember(sClass, name);
cache.put(key, ao == null ? NONE : ao);
}
return ao == NONE ? null : ao;
}
protected boolean areMethodsAccessible(Map<?, ?> map) {
return false;
}
}