1package org.testng.mustache;
2
3import org.testng.collections.Lists;
4
5import java.io.IOException;
6import java.util.List;
7import java.util.Map;
8
9public class Mustache {
10
11  public String run(String template, Map<String, Object> m) throws IOException {
12    return run(template, new Model(m));
13  }
14
15  String run(String template, Model model) throws IOException {
16    int lineNumber = 0;
17
18    List<BaseChunk> chunks = Lists.newArrayList();
19    int ti = 0;
20    while (ti < template.length()) {
21      int start;
22      int end;
23      if (template.charAt(ti) == '\n') lineNumber++;
24
25      if (template.charAt(ti) == '{' && ti + 1 < template.length()
26          && template.charAt(ti + 1) == '{') {
27        int index = ti + 2;
28        start = index;
29        boolean foundEnd = false;
30        while (index < template.length() && ! foundEnd) {
31          index++;
32          foundEnd = template.charAt(index) == '}' && index + 1 < template.length()
33              && template.charAt(index + 1) == '}';
34        }
35
36        if (foundEnd) {
37          end = index;
38          String variable = template.substring(start, end);
39          p("Found variable:" + variable);
40          if (variable.startsWith("#")) {
41            // Complex variable {{#foo}}.
42            String conditionalVariable = variable.substring(1);
43            Value value = model.resolveValue(conditionalVariable);
44            int endIndex = findClosingIndex(template, ti, conditionalVariable);
45            Object v = value.get();
46            if (v == null) {
47              // Null condition, do nothing
48            } else if (v instanceof Iterable) {
49              // Iterable, call a sub Mustache to process this chunk in a loop
50              // after pushing a new submodel
51              Iterable it = (Iterable) v;
52              String subTemplate = template.substring(ti + variable.length() + 4, endIndex);
53              for (Object o : it) {
54                model.push(conditionalVariable, o);
55                String r = new Mustache().run(subTemplate, model);
56                model.popSubModel();
57                chunks.add(new StringChunk(model, r));
58              }
59            } else {
60              // Scope, call a sub Mustache to process this chunk
61              // after pushing a new submodel
62              String subTemplate = template.substring(ti + variable.length() + 4, endIndex);
63              model.push(conditionalVariable, v);
64              String r = new Mustache().run(subTemplate, model);
65              model.popSubModel();
66              chunks.add(new StringChunk(model, r));
67            }
68            ti = endIndex + variable.length() + 4;
69          } else {
70            // Regular variable {{foo}}
71            chunks.add(new VariableChunk(model, variable));
72            ti += variable.length() + 4;
73          }
74        } else {
75          throw new RuntimeException("Unclosed variable at line " + lineNumber);
76        }
77      } else {
78        chunks.add(new StringChunk(model, "" + template.charAt(ti)));
79        ti++;
80      }
81    } // while
82
83    p("******************** Final composition, chunks:");
84    StringBuilder result = new StringBuilder();
85    p("*** Template:" + template);
86    for (BaseChunk bc : chunks) {
87      p("***  " + bc);
88    }
89
90    for (BaseChunk bc : chunks) {
91      String c = bc.compose();
92      result.append(c);
93    }
94    p("*** Final result:" + result);
95    return result.toString();
96  }
97
98  private int findClosingIndex(String template, int ti,
99      String conditionalVariable) {
100    int result = template.lastIndexOf("{{/" + conditionalVariable);
101    return result;
102  }
103
104  private void p(String string) {
105    if (false) {
106      System.out.println(string);
107    }
108  }
109
110  public static void main(String[] args) throws IOException {
111  }
112}
113