Skip to content

Commit 53537ba

Browse files
dwickernjknack
authored andcommitted
Fix formatters not being evaluated a second time (#548)
The Formatter is only called the first time. Using a formatter a second time, even in the same template, will call toString instead. This happens because FormatterChain used an iterator, which has a cursor pointing to a different element on the second evaluation.
1 parent 849fd5c commit 53537ba

2 files changed

Lines changed: 72 additions & 7 deletions

File tree

handlebars/src/main/java/com/github/jknack/handlebars/internal/FormatterChain.java

Lines changed: 28 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@
1919

2020
import static org.apache.commons.lang3.Validate.notNull;
2121

22-
import java.util.Iterator;
2322
import java.util.List;
2423

2524
import com.github.jknack.handlebars.Formatter;
@@ -32,24 +31,46 @@
3231
*/
3332
public class FormatterChain implements Formatter.Chain {
3433

35-
/** Pointer to next formatter. */
36-
private Iterator<Formatter> chain;
34+
/** List of available formatters. */
35+
private List<Formatter> chain;
36+
37+
/** Index of the current formatter. */
38+
private int index;
3739

3840
/**
3941
* Creates a new {@link FormatterChain}.
4042
*
4143
* @param formatter List of available formatters.
4244
*/
4345
public FormatterChain(final List<Formatter> formatter) {
44-
this.chain = formatter.iterator();
46+
this.chain = formatter;
47+
}
48+
49+
/**
50+
* Creates a new {@link FormatterChain}.
51+
*
52+
* @param formatter List of available formatters.
53+
* @param index Index of the current formatter.
54+
*/
55+
private FormatterChain(final List<Formatter> formatter, final int index) {
56+
this.chain = formatter;
57+
this.index = index;
58+
}
59+
60+
/**
61+
* Gets the next formatter in the chain.
62+
* @return The formatter at the next index.
63+
*/
64+
private FormatterChain next() {
65+
return new FormatterChain(chain, index + 1);
4566
}
4667

4768
@Override
4869
public Object format(final Object value) {
4970
Object output;
50-
if (chain.hasNext()) {
51-
Formatter formatter = chain.next();
52-
output = formatter.format(value, this);
71+
Formatter formatter = chain.get(index);
72+
if (formatter != null) {
73+
output = formatter.format(value, next());
5374
notNull(output, "Formatter " + formatter.getClass() + " returned a null result for " + value);
5475
} else {
5576
output = value.toString();
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
package com.github.jknack.handlebars;
2+
3+
import org.junit.Test;
4+
5+
import java.io.IOException;
6+
import java.util.Date;
7+
8+
import static org.junit.Assert.assertEquals;
9+
10+
public class FormatterTest extends AbstractTest {
11+
12+
static final long now = System.currentTimeMillis();
13+
14+
@Override
15+
protected void configure(final Handlebars handlebars) {
16+
handlebars.with(new Formatter() {
17+
18+
@Override
19+
public Object format(final Object value, final Chain chain) {
20+
if (value instanceof Date) {
21+
return ((Date) value).getTime();
22+
}
23+
return chain.format(value);
24+
}
25+
26+
});
27+
}
28+
29+
@Test
30+
public void useFormatterTwice() throws IOException {
31+
Template t = compile("time is {{this}}/{{this}}");
32+
33+
assertEquals("time is " + now + "/" + now, t.apply(new Date(now)));
34+
}
35+
36+
@Test
37+
public void useTemplateTwice() throws IOException {
38+
Template t = compile("time is {{this}}");
39+
40+
assertEquals("time is " + now, t.apply(new Date(now)));
41+
assertEquals("time is " + now, t.apply(new Date(now)));
42+
}
43+
44+
}

0 commit comments

Comments
 (0)