Skip to content

Commit af87461

Browse files
author
Igor Polevoy
committed
#217 Improve TemplateParser performance
1 parent e1ccbce commit af87461

File tree

6 files changed

+163
-164
lines changed

6 files changed

+163
-164
lines changed

javalite-templator/src/main/java/org/javalite/templator/AbstractTag.java

Lines changed: 2 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,13 @@
11
package org.javalite.templator;
22

33
import org.javalite.common.Inflector;
4-
import org.javalite.templator.tags.IfTag;
5-
import org.javalite.templator.tags.ListTag;
64

75
import java.io.Writer;
8-
import java.lang.reflect.Field;
9-
import java.lang.reflect.InvocationTargetException;
10-
import java.lang.reflect.Method;
11-
import java.util.HashMap;
126
import java.util.List;
137
import java.util.Map;
148

159
import static org.javalite.common.Collections.list;
16-
import static org.javalite.common.Inflector.capitalize;
10+
1711

1812
/**
1913
* This class represents a custom tag written in Java and linked into template manager, such as:
@@ -26,7 +20,7 @@
2620
*/
2721
public abstract class AbstractTag extends TemplateToken {
2822

29-
private ThreadLocal<Map<String, Method>> methodCache = new ThreadLocal<Map<String, Method>>();
23+
3024
private String argumentLine, body, tagName, matchingEnd = null;
3125
private int tagStartIndex = -1, tagEndIndex = -1, argumentsEndIndex = -1;
3226

@@ -179,89 +173,6 @@ public boolean marchArgumentEnd(String template, int index) {
179173
}
180174
return false;
181175
}
182-
183-
/**
184-
* Tries to get a property value from object.
185-
*
186-
* @param obj object to get property value from
187-
* @param propertyName name of property
188-
* @return value of property, or null if not found
189-
*/
190-
protected final Object getValue(Object obj, String propertyName) throws InvocationTargetException, IllegalAccessException, NoSuchMethodException {
191-
192-
193-
Object val = null;
194-
195-
//try map
196-
if (obj instanceof Map) {
197-
Map objectMap = (Map) obj;
198-
return objectMap.get(propertyName);
199-
}
200-
201-
if (val == null) {
202-
//try properties
203-
val = executeMethod(obj, "get" + capitalize(propertyName), null);
204-
if(val != null)
205-
return val;
206-
}
207-
208-
209-
//try generic get method
210-
if (val == null) {
211-
val = executeMethod(obj, "get", propertyName);
212-
if(val != null)
213-
return val;
214-
}
215-
216-
217-
if (val == null) {
218-
// try public fields
219-
try {
220-
//TODO: optimize the same as methods.
221-
Field f = obj.getClass().getDeclaredField(propertyName);
222-
val = f.get(obj);
223-
if(val != null)
224-
return val;
225-
226-
} catch (NoSuchFieldException ignore) {
227-
} catch (IllegalAccessException ignore) {
228-
}
229-
}
230-
231-
return val;
232-
}
233-
234-
private Object executeMethod(Object obj, String methodName, String propertyName) throws InvocationTargetException, IllegalAccessException {
235-
236-
//quick hack:
237-
if (methodCache.get() == null) {
238-
methodCache.set(new HashMap<String, Method>());
239-
}
240-
241-
242-
String key = obj.getClass().getName() + "#" + methodName;
243-
Method m = null;
244-
245-
if (!methodCache.get().containsKey(key)) {
246-
try {
247-
m = propertyName == null ? obj.getClass().getMethod(methodName) : obj.getClass().getMethod(methodName, String.class);
248-
} catch (NoSuchMethodException e) {}
249-
250-
// if we find a method, we will cache it, if not we will cache null
251-
methodCache.get().put(key, m);
252-
} else if (methodCache.get().get(key) == null) { // we did not find this method last time!
253-
return null;
254-
} else {
255-
m = methodCache.get().get(key); // method found!
256-
}
257-
258-
if(m != null){
259-
return propertyName == null ? m.invoke(obj) : m.invoke(obj, propertyName);
260-
}else{
261-
return null;
262-
}
263-
}
264-
265176
public boolean matchMiddle(String template, int templateIndex) {
266177
return false;
267178
}
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
package org.javalite.templator;
2+
3+
import java.lang.reflect.Field;
4+
import java.lang.reflect.InvocationTargetException;
5+
import java.lang.reflect.Method;
6+
import java.util.HashMap;
7+
import java.util.Map;
8+
9+
/**
10+
* @author Igor Polevoy on 4/18/15.
11+
*/
12+
public class MethodExecutor {
13+
14+
private ThreadLocal<Map<String, Method>> methodCache = new ThreadLocal<Map<String, Method>>();
15+
16+
/**
17+
* Tries to get a property value from object.
18+
*
19+
* @param obj object to get property value from
20+
* @param propertyName name of property
21+
* @return value of property, or null if not found
22+
*/
23+
protected final Object getValue(Object obj, String propertyName) throws InvocationTargetException, IllegalAccessException, NoSuchMethodException {
24+
Object val = null;
25+
26+
//try map
27+
if (obj instanceof Map) {
28+
Map objectMap = (Map) obj;
29+
return objectMap.get(propertyName);
30+
}
31+
32+
if (val == null) {
33+
//try properties
34+
val = executeMethod(obj, "get" + fastCapitalize(propertyName), null);
35+
if(val != null)
36+
return val;
37+
}
38+
39+
//try generic get method
40+
if (val == null) {
41+
val = executeMethod(obj, "get", propertyName);
42+
if(val != null)
43+
return val;
44+
}
45+
46+
47+
if (val == null) {
48+
// try public fields
49+
try {
50+
//TODO: optimize the same as methods.
51+
Field f = obj.getClass().getDeclaredField(propertyName);
52+
val = f.get(obj);
53+
if(val != null)
54+
return val;
55+
56+
} catch (NoSuchFieldException ignore) {
57+
} catch (IllegalAccessException ignore) {
58+
}
59+
}
60+
61+
return val;
62+
}
63+
64+
protected Object executeMethod(Object obj, String methodName, String propertyName) throws InvocationTargetException, IllegalAccessException {
65+
66+
//quick hack:
67+
Map<String, Method> cache = methodCache.get();
68+
if (cache == null) {
69+
methodCache.set(cache = new HashMap<String, Method>());
70+
}
71+
72+
String key = obj.getClass().getName() + "#" + methodName;
73+
Method m = null;
74+
75+
if (!cache.containsKey(key)) {
76+
try {
77+
m = propertyName == null ? obj.getClass().getMethod(methodName) : obj.getClass().getMethod(methodName, String.class);
78+
} catch (NoSuchMethodException e) {}
79+
80+
// if we find a method, we will cache it, if not we will cache null
81+
cache.put(key, m);
82+
} else if (cache.get(key) == null) { // we did not find this method last time!
83+
return null;
84+
} else {
85+
m = cache.get(key); // method found!
86+
}
87+
88+
if(m != null){
89+
return propertyName == null ? m.invoke(obj) : m.invoke(obj, propertyName);
90+
}else{
91+
return null;
92+
}
93+
}
94+
95+
protected String fastCapitalize(String text){
96+
char first = text.charAt(0);
97+
first = (char)(first - 32);
98+
return first + text.substring(1);
99+
}
100+
}

javalite-templator/src/main/java/org/javalite/templator/TemplateToken.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
/**
88
* @author Igor Polevoy on 1/10/15.
99
*/
10-
abstract class TemplateToken {
10+
abstract class TemplateToken extends MethodExecutor {
1111
abstract void process(Map values, Writer writer) throws Exception;
1212
abstract String originalValue();
1313
}

javalite-templator/src/main/java/org/javalite/templator/tags/ListTag.java

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,10 @@
77
import org.javalite.templator.TemplateException;
88

99
import java.io.Writer;
10-
import java.util.*;
10+
import java.util.Arrays;
11+
import java.util.Collection;
12+
import java.util.List;
13+
import java.util.Map;
1114

1215
import static org.javalite.common.Collections.list;
1316

@@ -51,6 +54,7 @@ public void setArguments(String argumentLine) {
5154
}
5255
}
5356

57+
private ThreadLocal<Map<String, Object>> mapCache = new ThreadLocal<Map<String, Object>>();
5458
@Override
5559
@SuppressWarnings("unchecked")
5660
public void process(Map values, Writer writer) {
@@ -78,14 +82,12 @@ public void process(Map values, Writer writer) {
7882
Object[] objects = targetCollection.toArray();
7983
for (int i = 0; i < objects.length; i++) {
8084
Object val = objects[i];
81-
Map newVals = new HashMap(values);
82-
newVals.put(varName, val);
83-
newVals.put(varName + "_index", i);
84-
newVals.put(varName + "_has_next", i < (objects.length - 1));
85-
bodyTemplate.process(newVals, writer);
85+
values.put(varName, val);
86+
values.put(varName + "_index", i);
87+
values.put(varName + "_has_next", i < (objects.length - 1));
88+
bodyTemplate.process(values, writer);
8689
}
8790
}
88-
8991
} catch (Exception e) {
9092
throw new TemplateException(e);
9193
}
Lines changed: 11 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,17 @@
11
package org.javalite.templator.template_parser;
22

3+
import org.javalite.templator.MethodExecutor;
34
import org.javalite.templator.TemplateException;
45

56
import java.lang.reflect.Field;
67
import java.lang.reflect.InvocationTargetException;
7-
import java.lang.reflect.Method;
8-
import java.util.HashMap;
98
import java.util.List;
109
import java.util.Map;
11-
import static org.javalite.common.Inflector.*;
1210

1311
/**
1412
* @author Eric Nielsen
1513
*/
16-
class ChainedIds {
14+
class ChainedIds extends MethodExecutor {
1715
private final String firstId;
1816
private final List<String> ids;
1917

@@ -24,10 +22,10 @@ class ChainedIds {
2422

2523
private Object valueOf(Object obj, String propertyName) throws InvocationTargetException, IllegalAccessException {
2624

27-
if (propertyName.endsWith("()")) {
28-
return executeMethod(obj, propertyName.substring(0, propertyName.length() - 2), null);
25+
if (propertyName.endsWith("()")) {
26+
return executeMethod(obj, propertyName.substring(0, propertyName.length() - 2), null);
2927

30-
}
28+
}
3129

3230
Object val = null;
3331

@@ -39,16 +37,16 @@ private Object valueOf(Object obj, String propertyName) throws InvocationTargetE
3937

4038
if (val == null) {
4139
//try properties
42-
val = executeMethod(obj, "get" + capitalize(propertyName), null);
43-
if(val != null)
40+
val = executeMethod(obj, "get" + fastCapitalize(propertyName), null);
41+
if (val != null)
4442
return val;
4543
}
4644

4745

4846
//try generic get method
4947
if (val == null) {
5048
val = executeMethod(obj, "get", propertyName);
51-
if(val != null)
49+
if (val != null)
5250
return val;
5351
}
5452

@@ -59,7 +57,7 @@ private Object valueOf(Object obj, String propertyName) throws InvocationTargetE
5957
//TODO: optimize the same as methods.
6058
Field f = obj.getClass().getDeclaredField(propertyName);
6159
val = f.get(obj);
62-
if(val != null)
60+
if (val != null)
6361
return val;
6462

6563
} catch (NoSuchFieldException ignore) {
@@ -70,50 +68,15 @@ private Object valueOf(Object obj, String propertyName) throws InvocationTargetE
7068
return val;
7169
}
7270

73-
private ThreadLocal<Map<String, Method>> methodCache = new ThreadLocal<Map<String, Method>>();
74-
75-
private Object executeMethod(Object obj, String methodName, String propertyName) throws InvocationTargetException, IllegalAccessException {
76-
77-
//quick hack:
78-
if (methodCache.get() == null) {
79-
methodCache.set(new HashMap<String, Method>());
80-
}
81-
82-
83-
String key = obj.getClass().getName() + "#" + methodName;
84-
Method m = null;
85-
86-
if (!methodCache.get().containsKey(key)) {
87-
try {
88-
m = propertyName == null ? obj.getClass().getMethod(methodName) : obj.getClass().getMethod(methodName, String.class);
89-
} catch (NoSuchMethodException e) {}
90-
91-
// if we find a method, we will cache it, if not we will cache null
92-
methodCache.get().put(key, m);
93-
} else if (methodCache.get().get(key) == null) { // we did not find this method last time!
94-
return null;
95-
} else {
96-
m = methodCache.get().get(key); // method found!
97-
}
98-
99-
if(m != null){
100-
return propertyName == null ? m.invoke(obj) : m.invoke(obj, propertyName);
101-
}else{
102-
return null;
103-
}
104-
}
105-
106-
10771
Object valueFrom(Map values) {
108-
try{
72+
try {
10973
Object obj = values.get(firstId);
11074
for (String id : ids) {
11175
obj = valueOf(obj, id);
11276
}
11377
return obj;
114-
}catch(Exception e){
78+
} catch (Exception e) {
11579
throw new TemplateException(e);
11680
}
117-
11881
}
11982
}

0 commit comments

Comments
 (0)