1 package org.testng.mustache; 2 3 import org.testng.collections.Lists; 4 5 import java.io.IOException; 6 import java.util.List; 7 import java.util.Map; 8 9 public class Mustache { 10 run(String template, Map<String, Object> m)11 public String run(String template, Map<String, Object> m) throws IOException { 12 return run(template, new Model(m)); 13 } 14 run(String template, Model model)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