Skip to content

Commit cae6033

Browse files
committed
#210 Implemented IfNode.
Pending: fix TemplateParser.parse() methods and implement ListNode.
1 parent 4aedede commit cae6033

File tree

3 files changed

+106
-45
lines changed

3 files changed

+106
-45
lines changed

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

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
*
99
* @author Eric Nielsen
1010
*/
11-
class IfNode extends ParentNode {
11+
class IfNode extends RootNode {
1212
private final Exp exp;
1313

1414
IfNode(Exp exp) {
@@ -17,6 +17,8 @@ class IfNode extends ParentNode {
1717

1818
@Override
1919
void process(Map values, Appendable appendable) throws IOException {
20-
appendable.append(String.valueOf(exp.resultWith(values)));
20+
if (exp.resultWith(values)) {
21+
super.process(values, appendable);
22+
}
2123
}
2224
}

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

Lines changed: 90 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,11 @@
99
* BNF rules:
1010
*
1111
* <pre>
12-
* WHITESPACE = Character.isWhitespace()+
1312
* IDENTIFIER = Character.isJavaIdentifierStart() Character.isJavaIdentifierPart()*
1413
* IDENTIFIER_OR_FUNCTION = IDENTIFIER "()"?
1514
* CHAINED_IDENTIFIERS = IDENTIFIER ('.' IDENTIFIER_OR_FUNCTION)*
1615
* LOWER_ALPHA = [a-z]+
17-
* VAR = "%{" CHAINED_IDENTIFIER (WHITESPACE LOWER_ALPHA)? '}'
16+
* VAR = "%{" CHAINED_IDENTIFIER (LOWER_ALPHA)? '}'
1817
*
1918
* CONST = .*
2019
*
@@ -25,16 +24,15 @@
2524
* FACTOR = COMPARISON | ('!' FACTOR) | ('(' EXP ')')
2625
*
2726
*
28-
* TAG_START = "&lt;#"
29-
* TAG_END = "&lt;/#"
27+
* TAG_START = '&lt' '#'
28+
* TAG_END = '&lt' '/' '#' LOWER_ALPHA '>'
3029
* IF_TAG = TAG_START "if" '(' EXPRESSION ')' '&gt;' LIST_TAG_BODY TAG_END "if" '&gt;'
3130
* </pre>
3231
*
3332
* @author Eric Nielsen
3433
*/
3534
public final class TemplateParser {
3635

37-
private ParentNode parent = new RootNode();
3836
private final String source;
3937
private int index = -1;
4038
private int currentCodePoint;
@@ -54,6 +52,10 @@ private boolean next() {
5452
}
5553
}
5654

55+
private boolean accept(int codePoint) {
56+
return currentCodePoint == codePoint && next();
57+
}
58+
5759
private boolean __whitespace() { return Character.isWhitespace(currentCodePoint); }
5860

5961
@SuppressWarnings("empty-statement")
@@ -73,7 +75,7 @@ private boolean _identifier() {
7375
private boolean _identifierOrFunction() {
7476
if (!_identifier()) { return false; }
7577
if (currentCodePoint == '(') {
76-
if (!(next() && currentCodePoint == ')' && next())) { return false; }
78+
if (!(next() && accept(')'))) { return false; }
7779
}
7880
return true;
7981
}
@@ -104,37 +106,40 @@ private String _lowerAlpha() {
104106
}
105107

106108
private VarNode _var() {
107-
if (!(currentCodePoint == '%' && next() && currentCodePoint == '{' && next())) { return null; }
109+
if (!(accept('%') && accept('{'))) { return null; }
110+
_whitespace(); // optional
108111
Identifiers ident = _chainedIdentifiers();
109112
if (ident == null) { return null; }
110113
BuiltIn builtIn = null;
111114
if (_whitespace()) {
112115
String builtInName = _lowerAlpha();
113-
if (builtInName == null) { return null; }
114-
builtIn = TemplatorConfig.instance().getBuiltIn(builtInName);
116+
if (builtInName != null) {
117+
builtIn = TemplatorConfig.instance().getBuiltIn(builtInName);
118+
_whitespace(); // optional
119+
}
115120
}
116121
if (currentCodePoint != '}') { return null; }
117122
next();
118123
return new VarNode(ident, builtIn);
119124
}
120125

121126
private Op _comparisonOp() {
122-
if (currentCodePoint == '=' && next()) {
123-
return currentCodePoint == '=' && next() ? Op.eq : null;
124-
} else if (currentCodePoint == '>' && next()) {
127+
if (accept('=')) {
128+
return accept('=') ? Op.eq : null;
129+
} else if (accept('>')) {
125130
if (currentCodePoint == '=') {
126131
return next() ? Op.gte : null;
127132
} else {
128133
return Op.gt;
129134
}
130-
} else if (currentCodePoint == '<' && next()) {
135+
} else if (accept('<')) {
131136
if (currentCodePoint == '=') {
132137
return next() ? Op.lte : null;
133138
} else {
134139
return Op.lt;
135140
}
136141
} else {
137-
return currentCodePoint == '!' && next() && currentCodePoint == '=' && next() ? Op.neq : null;
142+
return accept('!') && accept('=') ? Op.neq : null;
138143
}
139144
}
140145

@@ -155,7 +160,7 @@ private Exp _exp() {
155160
if (leftExp == null) { return null; }
156161
_whitespace(); // optional
157162
if (currentCodePoint == '|') {
158-
if (!(next() && currentCodePoint == '|' && next())) { return null; }
163+
if (!(next() && accept('|'))) { return null; }
159164
_whitespace(); // optional
160165
Exp rightExp = _term();
161166
if (rightExp == null) { return null; }
@@ -169,7 +174,7 @@ private Exp _term() {
169174
if (leftExp == null) { return null; }
170175
_whitespace(); // optional
171176
if (currentCodePoint == '&') {
172-
if (!(next() && currentCodePoint == '&' && next())) { return null; }
177+
if (!(next() && accept('&'))) { return null; }
173178
_whitespace(); // optional
174179
Exp rightExp = _factor();
175180
if (rightExp == null) { return null; }
@@ -186,7 +191,7 @@ private Exp _factor() {
186191
Exp exp = _exp();
187192
if (exp == null) { return null; }
188193
_whitespace(); // optional
189-
if (!(currentCodePoint == ')' && next())) { return null; }
194+
if (!accept(')')) { return null; }
190195
return exp;
191196
case '!':
192197
if (!next()) { return null; }
@@ -198,57 +203,110 @@ private Exp _factor() {
198203
}
199204

200205
private boolean _tagStart() {
201-
return currentCodePoint == '<' && next() && currentCodePoint == '#' && next();
206+
if (!accept('<')) { return false; }
207+
_whitespace(); // optional
208+
if (!accept('#')) { return false; }
209+
return true;
202210
}
203211

204-
private boolean _tagEnd() {
205-
return currentCodePoint == '<' && next() && currentCodePoint == '/' && next() && currentCodePoint == '#' && next();
212+
private String _tagEnd() {
213+
if (!accept('<')) { return null; }
214+
_whitespace(); // optional
215+
if (!accept('/')) { return null; }
216+
_whitespace(); // optional
217+
if (!accept('#')) { return null; }
218+
String tagName = _lowerAlpha();
219+
if (tagName == null) { return null; }
220+
_whitespace(); // optional
221+
if (currentCodePoint != '>') { return null; }
222+
next();
223+
return tagName;
206224
}
207225

208-
private Node _ifTag() {
226+
private Node _ifTagStart() {
209227
if (!_tagStart()) { return null; }
210228
String tagName = _lowerAlpha();
211229
if (!"if".equals(tagName)) { return null; }
212230
_whitespace(); // optional
213-
if (!(currentCodePoint == '(' && next())) { return null; }
231+
if (!accept('(')) { return null; }
214232
_whitespace(); // optional
215233
Exp exp = _exp();
216234
if (exp == null) { return null; }
217235
_whitespace(); // optional
218-
if (!(currentCodePoint == ')' && next())) { return null; }
236+
if (!accept(')')) { return null; }
219237
_whitespace(); // optional
220-
if (currentCodePoint != '>') { return null; }
221-
next();
238+
if (!accept('>')) { return null; }
222239
return new IfNode(exp);
223240
}
224241

225242
Node parse() {
243+
ParentNode root = new RootNode();
244+
//TODO: refactor this
245+
if (next()) {
246+
for (;;) {
247+
int startIndex = index;
248+
Node node = _ifTagStart();
249+
if (node != null) {
250+
addConstEndingAt(startIndex, root);
251+
constStartIndex = index;
252+
if (!"if".equals(parse((ParentNode) node))) {
253+
node = null;
254+
}
255+
} else {
256+
node = _var();
257+
}
258+
if (node == null) {
259+
if (startIndex == index) {
260+
if (!next()) { break; }
261+
}
262+
} else {
263+
addConstEndingAt(startIndex, root);
264+
constStartIndex = index;
265+
root.children.add(node);
266+
}
267+
}
268+
}
269+
addConstEndingAt(source.length(), root);
270+
return root;
271+
}
272+
273+
private String parse(ParentNode root) {
226274
//TODO: refactor this
275+
constStartIndex = index;
227276
if (next()) {
228277
for (;;) {
229278
int startIndex = index;
230-
Node node = _ifTag();
279+
String tagEndName = _tagEnd();
280+
if (tagEndName != null) {
281+
addConstEndingAt(startIndex, root);
282+
constStartIndex = index;
283+
return tagEndName;
284+
}
285+
Node node = _ifTagStart();
231286
if (node == null) {
232287
node = _var();
288+
} else {
289+
if (!"if".equals(parse((ParentNode) node))) {
290+
node = null;
291+
}
233292
}
234293
if (node == null) {
235294
if (startIndex == index) {
236295
if (!next()) { break; }
237296
}
238297
} else {
239-
addConstEndingAt(startIndex);
298+
addConstEndingAt(startIndex, root);
240299
constStartIndex = index;
241-
parent.children.add(node);
300+
root.children.add(node);
242301
}
243302
}
244303
}
245-
addConstEndingAt(source.length());
246-
return parent;
304+
return null;
247305
}
248306

249-
private void addConstEndingAt(int endIndex) {
307+
private void addConstEndingAt(int endIndex, ParentNode root) {
250308
if (endIndex - 1 > constStartIndex) {
251-
parent.children.add(new ConstNode(source.substring(constStartIndex, endIndex)));
309+
root.children.add(new ConstNode(source.substring(constStartIndex, endIndex)));
252310
}
253311
}
254312
}

javalite-templator/src/test/java/org/javalite/templator/TemplateParserSpec.java

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -48,26 +48,27 @@ public void shouldParseBuiltIn() throws Exception {
4848

4949
@Test
5050
public void shouldParseExpressionWithEq() throws Exception {
51-
ParentNode node = (ParentNode) new TemplateParser("<#if(left==right)>").parse();
52-
the(node.children.size()).shouldBeEqual(1);
53-
the(process(node, map("left", "help!", "right", "help!"))).shouldBeEqual("true");
54-
the(process(node, map("left", "help!", "right", "help?"))).shouldBeEqual("false");
51+
ParentNode node = (ParentNode) new TemplateParser("<b><<#if(left==right)>tada</#if>></b>").parse();
52+
the(node.children.size()).shouldBeEqual(3);
53+
the(process(node, map("left", "help!", "right", "help!"))).shouldBeEqual("<b><tada></b>");
54+
the(process(node, map("left", "help!", "right", "help?"))).shouldBeEqual("<b><></b>");
5555
}
5656

5757
@Test
5858
public void shouldParseExpressionWithNotNeq() throws Exception {
59-
ParentNode node = (ParentNode) new TemplateParser("<#if(!left!=right)>").parse();
59+
ParentNode node = (ParentNode) new TemplateParser("<#if(!left!=right)>tada</#if>").parse();
6060
the(node.children.size()).shouldBeEqual(1);
61-
the(process(node, map("left", 0, "right", 0))).shouldBeEqual("true");
62-
the(process(node, map("left", 0, "right", 1))).shouldBeEqual("false");
61+
the(process(node, map("left", 0, "right", 0))).shouldBeEqual("tada");
62+
the(process(node, map("left", 0, "right", 1))).shouldBeEqual("");
6363
}
6464

6565
@Test
6666
public void shouldParseExpressionWithAndParensOr() throws Exception {
67-
ParentNode node = (ParentNode) new TemplateParser("<#if (first > second && (first > third || third <= second))>").parse();
67+
ParentNode node = (ParentNode) new TemplateParser(
68+
"<#if (first > second && (first > third || third <= second))><b>%{first}</b></#if>").parse();
6869
the(node.children.size()).shouldBeEqual(1);
69-
the(process(node, map("first", "foo", "second", "bar", "third", "baa"))).shouldBeEqual("true");
70-
the(process(node, map("first", 1, "second", 0, "third", 0))).shouldBeEqual("true");
71-
the(process(node, map("first", 1, "second", 0, "third", 1))).shouldBeEqual("false");
70+
the(process(node, map("first", "foo", "second", "bar", "third", "baa"))).shouldBeEqual("<b>foo</b>");
71+
the(process(node, map("first", 1, "second", 0, "third", 0))).shouldBeEqual("<b>1</b>");
72+
the(process(node, map("first", 1, "second", 0, "third", 1))).shouldBeEqual("");
7273
}
7374
}

0 commit comments

Comments
 (0)