Skip to content

Commit 4153666

Browse files
author
Igor Polevoy
committed
implemented nested tags #207
1 parent aceed68 commit 4153666

File tree

11 files changed

+304
-134
lines changed

11 files changed

+304
-134
lines changed

activeweb-testing/src/main/java/app/controllers/SimpleController.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,5 +23,8 @@
2323
* @author Igor Polevoy
2424
*/
2525
public class SimpleController extends AppController {
26-
public void index(){}
26+
public void index(){
27+
28+
redirect();
29+
}
2730
}

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

Lines changed: 53 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,14 @@
55
import org.javalite.templator.tags.ListTag;
66

77
import java.io.Writer;
8+
import java.lang.reflect.Field;
9+
import java.lang.reflect.InvocationTargetException;
10+
import java.lang.reflect.Method;
811
import java.util.List;
912
import java.util.Map;
1013

1114
import static org.javalite.common.Collections.list;
15+
import static org.javalite.common.Inflector.capitalize;
1216

1317
/**
1418
* This class represents a custom tag written in Java and linked into template manager, such as:
@@ -38,10 +42,6 @@ public int getArgumentsEndIndex() {
3842
return argumentsEndIndex;
3943
}
4044

41-
public void setArgumentsEndIndex(int argumentsEndIndex) {
42-
this.argumentsEndIndex = argumentsEndIndex;
43-
}
44-
4545
public int getTagStartIndex() {
4646
return tagStartIndex;
4747
}
@@ -54,10 +54,6 @@ public int getTagEndIndex() {
5454
return tagEndIndex;
5555
}
5656

57-
public void setTagEndIndex(int tagEndIndex) {
58-
this.tagEndIndex = tagEndIndex;
59-
}
60-
6157
public String getBody() {
6258
return body;
6359
}
@@ -186,4 +182,53 @@ public boolean marchArgumentEnd(String template, int index){
186182
}
187183
return false;
188184
}
185+
186+
/**
187+
* Tries to get a property value from object.
188+
*
189+
* @param obj object to get property value from
190+
* @param propertyName name of property
191+
* @return value of property, or null if not found
192+
*/
193+
protected final Object getValue(Object obj, String propertyName) throws InvocationTargetException, IllegalAccessException {
194+
195+
Object val = null;
196+
197+
//try map
198+
if (obj instanceof Map) {
199+
Map objectMap = (Map) obj;
200+
val = objectMap.get(propertyName);
201+
}
202+
203+
//try generic get method
204+
if (val == null) {
205+
try {
206+
Method m = obj.getClass().getMethod("get", String.class);
207+
val = m.invoke(obj, propertyName);
208+
} catch (NoSuchMethodException ignore) {
209+
}
210+
}
211+
212+
if (val == null) {
213+
//try properties
214+
try {
215+
Method m = obj.getClass().getMethod("get" + capitalize(propertyName));
216+
val = m.invoke(obj);
217+
} catch (NoSuchMethodException ignore) {
218+
}
219+
}
220+
221+
if (val == null) {
222+
// try public fields
223+
try {
224+
Field f = obj.getClass().getDeclaredField(propertyName);
225+
val = f.get(obj);
226+
227+
} catch (NoSuchFieldException ignore) {
228+
} catch (IllegalAccessException ignore) {
229+
}
230+
}
231+
232+
return val;
233+
}
189234
}

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

Lines changed: 15 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -30,24 +30,29 @@ class MergeTag extends AbstractTag {
3030
public void setArguments(String argumentLine) {
3131
super.setArguments(argumentLine);
3232
this.expression = argumentLine.contains(".");
33-
boolean hasBuiltIn = argumentLine.contains(" ");
33+
boolean hasBuiltIn = argumentLine.trim().contains(" ");
3434

35-
if (argumentLine.length() - argumentLine.replace(" ", "").length() > 1)
36-
throw new ParseException("Merge token: " + argumentLine + " has more that one space");
35+
//TODO: write tests for exceptional conditions:
36+
if ((argumentLine.length() - argumentLine.replace(" ", "").length()) > 1)
37+
throw new ParseException("Merge field: '" + argumentLine + "' has more than one space");
3738

3839

39-
if (argumentLine.length() - argumentLine.replace(".", "").length() > 1)
40-
throw new ParseException("Merge token: " + argumentLine + " has more that one dots");
40+
if ((argumentLine.length() - argumentLine.replace(".", "").length()) > 1)
41+
throw new ParseException("Merge field: '" + argumentLine + "' has more than one dots");
4142

4243
if (hasBuiltIn) {
43-
String[] parts = Util.split(argumentLine, ' ');
44+
String[] parts = Util.split(argumentLine.trim(), ' ');
4445
String builtInName = parts[1];
4546
builtIn = TemplatorConfig.instance().getBuiltIn(builtInName);
4647

4748
if (expression) {
48-
parts = Util.split(parts[0], '.');
49-
this.objectName = parts[0];
50-
this.propertyName = parts[1];
49+
String[] expr = Util.split(parts[0], '.');
50+
51+
if(expr.length != 2)
52+
throw new ParseException("Expression: '" + parts[0] + "' must include object name and property name separated by a single dot. Nothing else.");
53+
54+
this.objectName = expr[0];
55+
this.propertyName = expr[1];
5156
}
5257
} else if (expression) {
5358
String[] parts = Util.split(argumentLine, '.');
@@ -68,42 +73,7 @@ public void process(Map values, Writer writer) {
6873
if (expression) {
6974
if (values.containsKey(objectName)) {
7075
Object obj = values.get(objectName);
71-
Object val = null;
72-
73-
//try map
74-
if (obj instanceof Map) {
75-
Map objectMap = (Map) obj;
76-
val = objectMap.get(propertyName);
77-
}
78-
79-
//try generic get method
80-
if (val == null) {
81-
try {
82-
Method m = obj.getClass().getMethod("get", String.class);
83-
val = m.invoke(obj, propertyName);
84-
} catch (NoSuchMethodException ignore) {
85-
}
86-
}
87-
88-
if (val == null) {
89-
//try properties
90-
try {
91-
Method m = obj.getClass().getMethod("get" + capitalize(propertyName));
92-
val = m.invoke(obj);
93-
} catch (NoSuchMethodException ignore) {
94-
}
95-
}
96-
97-
if (val == null) {
98-
// try public fields
99-
try {
100-
Field f = obj.getClass().getDeclaredField(propertyName);
101-
val = f.get(obj);
102-
103-
} catch (NoSuchFieldException ignore) {
104-
} catch (IllegalAccessException ignore) {
105-
}
106-
}
76+
Object val = getValue(obj, propertyName);
10777

10878
if (val != null) {
10979
if (builtIn != null) {

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

Lines changed: 36 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,10 @@
11
package org.javalite.templator;
22

3-
import org.javalite.templator.tags.IfTag;
4-
import org.javalite.templator.tags.ListTag;
5-
63
import java.io.Writer;
7-
import java.util.*;
8-
9-
import static org.javalite.common.Util.split;
4+
import java.util.ArrayList;
5+
import java.util.List;
6+
import java.util.Map;
7+
import java.util.Stack;
108

119
/**
1210
* @author Igor Polevoy on 1/10/15.
@@ -16,11 +14,11 @@ public class Template {
1614
private final List<TemplateToken> templateTokens = new ArrayList<TemplateToken>();
1715

1816
public Template(String template) {
19-
try{
17+
try {
2018
parse(template);
21-
}catch(ParseException e){
19+
} catch (ParseException e) {
2220
throw e;
23-
}catch(Exception e){
21+
} catch (Exception e) {
2422
throw new TemplateException(e);
2523
}
2624

@@ -31,9 +29,11 @@ List<TemplateToken> templateTokens() {
3129
return templateTokens;
3230
}
3331

32+
Stack<AbstractTag> stack = new Stack<AbstractTag>();
33+
3434
private void parse(String template) throws IllegalAccessException, InstantiationException {
3535
Map<String, Class> tags = TemplatorConfig.instance().getTags();
36-
Stack<AbstractTag> stack = new Stack<AbstractTag>();
36+
3737
List<AbstractTag> tagsList = new ArrayList<AbstractTag>();
3838

3939
//TODO: separate iterations through all tags, and focus on stack
@@ -45,7 +45,7 @@ private void parse(String template) throws IllegalAccessException, Instantiation
4545
tag = (AbstractTag) tagClass.newInstance();
4646

4747
//match tag start, such as "<#list"
48-
if (stack.isEmpty() && tag.matchStartAtIndex(template, templateIndex)) {
48+
if (tag.matchStartAtIndex(template, templateIndex)) {
4949
tag.setTagStartIndex(templateIndex - tag.getTagStart().length());
5050
stack.push(tag);
5151
continue;
@@ -66,28 +66,33 @@ private void parse(String template) throws IllegalAccessException, Instantiation
6666

6767
//match tag end, such as: <#list people as person > body </#list>
6868
// ---^
69-
if (!stack.isEmpty() && stack.peek().matchEndTag(template, templateIndex)) {
70-
AbstractTag currentTag = stack.pop();
71-
tagsList.add(currentTag);
69+
if (!stack.isEmpty()
70+
&& stack.peek().matchEndTag(template, templateIndex)) {
7271

73-
String arguments;
74-
if (currentTag.getArgumentsEndIndex() != -1) { // we have body. case like this: <#list people as person > body </#list>
75-
arguments = template.substring(currentTag.getTagStartIndex() + currentTag.getTagStart().length(), currentTag.getArgumentsEndIndex());
76-
String body = template.substring(currentTag.getArgumentsEndIndex() + 1, currentTag.getTagEndIndex());
77-
currentTag.setBody(body);
72+
AbstractTag currentTag = stack.pop();
73+
if (stack.size() == 0) {
74+
tagsList.add(currentTag);
75+
String arguments;
76+
if (currentTag.getArgumentsEndIndex() != -1) { // we have body. case like this: <#list people as person > body </#list>
77+
arguments = template.substring(currentTag.getTagStartIndex() + currentTag.getTagStart().length(), currentTag.getArgumentsEndIndex());
78+
String body = template.substring(currentTag.getArgumentsEndIndex() + 1, currentTag.getTagEndIndex());
79+
currentTag.setBody(body);
80+
} else {
81+
// this is what is between the start and end tag in case there is no body:
82+
///<@blah arguments for blah />
83+
arguments = template.substring(currentTag.getTagStartIndex() + currentTag.getTagStart().length(), currentTag.getTagEndIndex());
84+
}
85+
currentTag.setArguments(arguments);
7886
} else {
79-
// this is what is between the start and end tag in case there is no body:
80-
///<@blah arguments for blah />
81-
arguments = template.substring(currentTag.getTagStartIndex() + currentTag.getTagStart().length(), currentTag.getTagEndIndex());
87+
break;
8288
}
83-
currentTag.setArguments(arguments);
8489
}
8590
}
8691
}
8792

88-
if(tagsList.size() == 0){ // assuming that this is just text, no funny business
93+
if (tagsList.size() == 0) { // assuming that this is just text, no funny business
8994
templateTokens.add(new StringToken(template));
90-
}else{
95+
} else {
9196
//now we need to collect string chunks in between
9297
for (int i = 0; i < tagsList.size(); i++) {
9398
AbstractTag currentTag = tagsList.get(i);
@@ -98,17 +103,17 @@ private void parse(String template) throws IllegalAccessException, Instantiation
98103
}
99104

100105
// if there is a gap between current and previous
101-
if(tagsList.size() > 1 && i != 0){
102-
int startIndex = tagsList.get(i - 1).getTagEndIndex() + 1;
106+
if (tagsList.size() > 1 && i != 0) {
107+
int startIndex = tagsList.get(i - 1).getTagEndIndex() + tagsList.get(i - 1).getMatchingEnd().length();
103108
int endIndex = currentTag.getTagStartIndex();
104-
StringToken st = new StringToken(template.substring(startIndex, endIndex ));
109+
StringToken st = new StringToken(template.substring(startIndex, endIndex));
105110
templateTokens.add(st);
106111
}
107112

108113
templateTokens.add(currentTag);
109114

110115
// if this is the last tag and there is some text after
111-
if(i == (tagsList.size() - 1) && template.length() > currentTag.getTagEndIndex()) {
116+
if (i == (tagsList.size() - 1) && template.length() > currentTag.getTagEndIndex()) {
112117
StringToken st = new StringToken(template.substring(currentTag.getTagEndIndex() + currentTag.getMatchingEnd().length()));
113118
templateTokens.add(st);
114119
}
@@ -120,23 +125,13 @@ private void parse(String template) throws IllegalAccessException, Instantiation
120125
}
121126
}
122127

123-
private static class Token {
124-
int index;
125-
String token;
126-
127-
private Token(int index, String token) {
128-
this.index = index;
129-
this.token = token;
130-
}
131-
}
132-
133-
134128
public void process(Map values, Writer writer) {
135-
136129
try {
137130
for (TemplateToken token : templateTokens) {
138131
token.process(values, writer);
139132
}
133+
} catch (TemplateException e) {
134+
throw e;
140135
} catch (Exception e) {
141136
throw new TemplateException(e);
142137
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ public void registerBuiltIn(String name, BuiltIn builtIn) {
8080

8181
public BuiltIn getBuiltIn(String name) {
8282
if (!builtIns.containsKey(name))
83-
throw new TemplateException("BuiltIn named '" + name + "' was not registered");
83+
throw new TemplateException("Built-in named '" + name + "' was not registered");
8484

8585
try{
8686
return builtIns.get(name);

0 commit comments

Comments
 (0)