1313import java .io .InputStreamReader ;
1414import java .io .Reader ;
1515import java .io .StringReader ;
16+ import java .io .StringWriter ;
1617import java .util .ArrayList ;
18+ import java .util .HashMap ;
1719import java .util .LinkedList ;
1820import java .util .List ;
21+ import java .util .Map ;
1922import java .util .concurrent .Callable ;
2023import java .util .concurrent .atomic .AtomicInteger ;
2124
25+ import static com .sampullara .mustache .Mustache .truncate ;
26+
2227/**
2328 * A pseudo interpreter / compiler. Instead of compiling to Java code, it compiles to a
2429 * list of instructions to execute.
@@ -81,7 +86,7 @@ public Mustache parseFile(String path) throws MustacheException {
8186 if (root == null ) {
8287 String fullPath = classpathRoot == null ? path : classpathRoot + "/" + path ;
8388 InputStream resourceAsStream =
84- MustacheBuilder .class .getClassLoader ().getResourceAsStream (fullPath );
89+ MustacheBuilder .class .getClassLoader ().getResourceAsStream (fullPath );
8590 if (resourceAsStream == null ) {
8691 throw new MustacheException (path + " not found in classpath" );
8792 }
@@ -184,7 +189,7 @@ protected List<Code> compile(final Mustache m, final Reader br, String tag, fina
184189 }
185190 if (!variable .equals (tag )) {
186191 throw new MustacheException (
187- "Mismatched start/end tags: " + tag + " != " + variable + " in " + file + ":" + currentLine );
192+ "Mismatched start/end tags: " + tag + " != " + variable + " in " + file + ":" + currentLine );
188193 }
189194
190195 return list ;
@@ -203,7 +208,7 @@ protected List<Code> compile(final Mustache m, final Reader br, String tag, fina
203208 } else {
204209 if (br .read () != '}' ) {
205210 throw new MustacheException (
206- "Improperly closed variable in " + file + ":" + currentLine );
211+ "Improperly closed variable in " + file + ":" + currentLine );
207212 }
208213 }
209214 final String finalName = name ;
@@ -268,7 +273,7 @@ private StringBuilder write(List<Code> list, StringBuilder out, int line) {
268273 private abstract static class SubCode implements Code {
269274 protected final Mustache m ;
270275 protected final String variable ;
271- private final Code [] codes ;
276+ protected final Code [] codes ;
272277 private final int line ;
273278 private final String file ;
274279
@@ -325,6 +330,30 @@ public IterableCode(Mustache m, String variable, List<Code> codes, String file,
325330 public void execute (FutureWriter fw , Scope scope ) throws MustacheException {
326331 execute (fw , m .iterable (scope , variable ));
327332 }
333+
334+ @ Override
335+ public Scope unparse (Scope current , String text , AtomicInteger position , Code [] next ) throws MustacheException {
336+ // I think we have to make iteration greedy and match until we can't find a match
337+ List <Scope > results = new ArrayList <Scope >();
338+ Scope result ;
339+ do {
340+ result = new Scope ();
341+ for (int i = 0 ; i < codes .length && result != null ; i ++) {
342+ if (Mustache .debug ) {
343+ Mustache .line .set (codes [i ].getLine ());
344+ }
345+ Code [] truncate = truncate (codes , i + 1 );
346+ result = codes [i ].unparse (result , text , position , truncate );
347+ }
348+ if (result != null && result .size () > 0 ) {
349+ results .add (result );
350+ } else break ;
351+ } while (true );
352+ if (results .size () != 0 ) {
353+ current .put (variable , results );
354+ }
355+ return current ;
356+ }
328357 }
329358
330359 private static class FunctionCode extends SubCode {
@@ -343,6 +372,46 @@ public void execute(FutureWriter fw, Scope scope) throws MustacheException {
343372 throw new MustacheException ("Not a function: " + function );
344373 }
345374 }
375+
376+ static class MapFunction implements Function <String , String > {
377+ private Map <String , String > map = new HashMap <String , String >();
378+
379+ void put (String input , String value ) {
380+ map .put (input , value );
381+ }
382+
383+ @ Override
384+ public String apply (String input ) {
385+ return map .get (input );
386+ }
387+
388+ public String toString () {
389+ return map .toString ();
390+ }
391+ }
392+
393+ @ Override
394+ public Scope unparse (Scope current , final String text , final AtomicInteger position , Code [] next ) throws MustacheException {
395+ final String value = unparseValueCode (current , text , position , next , false );
396+ if (value == null ) return null ;
397+ MapFunction function = (FunctionCode .MapFunction ) current .get (variable );
398+ if (function == null ) {
399+ function = new MapFunction ();
400+ put (current , variable , function );
401+ }
402+ StringWriter sw = new StringWriter ();
403+ FutureWriter fw = new FutureWriter (sw );
404+ try {
405+ for (Code code : codes ) {
406+ code .execute (fw , current );
407+ }
408+ fw .flush ();
409+ } catch (IOException e ) {
410+ throw new MustacheException ("Failed to evaluate function body" , e );
411+ }
412+ function .put (sw .toString (), value );
413+ return current ;
414+ }
346415 }
347416
348417 private static class IfIterableCode extends SubCode {
@@ -358,6 +427,23 @@ public void execute(FutureWriter fw, Scope scope) throws MustacheException {
358427 }
359428 execute (fw , m .ifiterable (scope , variable ));
360429 }
430+
431+ @ Override
432+ public Scope unparse (Scope current , String text , AtomicInteger position , Code [] next ) throws MustacheException {
433+ // Like the iterable version with only one
434+ Scope result = new Scope ();
435+ for (int i = 0 ; i < codes .length && result != null ; i ++) {
436+ if (Mustache .debug ) {
437+ Mustache .line .set (codes [i ].getLine ());
438+ }
439+ Code [] truncate = truncate (codes , i + 1 );
440+ result = codes [i ].unparse (result , text , position , truncate );
441+ }
442+ if (result != null && result .size () > 0 ) {
443+ put (current , variable , result );
444+ }
445+ return current ;
446+ }
361447 }
362448
363449 private static class InvertedIterableCode extends SubCode {
@@ -373,6 +459,24 @@ public void execute(FutureWriter fw, Scope scope) throws MustacheException {
373459 }
374460 execute (fw , m .inverted (scope , variable ));
375461 }
462+
463+ @ Override
464+ public Scope unparse (Scope current , String text , AtomicInteger position , Code [] next ) throws MustacheException {
465+ // Like the iterable version with only one
466+ Scope result = new Scope ();
467+ for (int i = 0 ; i < codes .length && result != null ; i ++) {
468+ if (Mustache .debug ) {
469+ Mustache .line .set (codes [i ].getLine ());
470+ }
471+ Code [] truncate = truncate (codes , i + 1 );
472+ result = codes [i ].unparse (result , text , position , truncate );
473+ }
474+ if (result != null ) {
475+ current .putAll (result );
476+ put (current , variable , false );
477+ }
478+ return current ;
479+ }
376480 }
377481
378482 private static class PartialCode implements Code {
@@ -409,6 +513,14 @@ public Object call() throws Exception {
409513 public int getLine () {
410514 return line ;
411515 }
516+
517+ @ Override
518+ public Scope unparse (Scope current , String text , AtomicInteger position , Code [] next ) throws MustacheException {
519+ Scope unparse = m .partial (variable ).unparse (text , position );
520+ if (unparse == null ) return null ;
521+ put (current , variable , unparse );
522+ return current ;
523+ }
412524 }
413525
414526 private static class WriteValueCode implements Code {
@@ -433,6 +545,59 @@ public void execute(FutureWriter fw, Scope scope) throws MustacheException {
433545 public int getLine () {
434546 return line ;
435547 }
548+
549+ @ Override
550+ public Scope unparse (Scope current , String text , AtomicInteger position , Code [] next ) throws MustacheException {
551+ String value = unparseValueCode (current , text , position , next , encoded );
552+ if (value != null ) {
553+ put (current , name , value );
554+ return current ;
555+ }
556+ return null ;
557+ }
558+
559+ }
560+
561+ private static String unparseValueCode (Scope current , String text , AtomicInteger position , Code [] next , boolean encoded ) throws MustacheException {
562+ AtomicInteger probePosition = new AtomicInteger (position .get ());
563+ Code [] truncate = truncate (next , 1 );
564+ Scope result = null ;
565+ int lastposition = position .get ();
566+ while (next .length != 0 && probePosition .get () < text .length ()) {
567+ lastposition = probePosition .get ();
568+ result = next [0 ].unparse (current , text , probePosition , truncate );
569+ if (result == null ) {
570+ probePosition .incrementAndGet ();
571+ } else {
572+ break ;
573+ }
574+ }
575+ if (result != null ) {
576+ String value = text .substring (position .get (), lastposition );
577+ if (encoded ) {
578+ // Decode
579+ }
580+ position .set (lastposition );
581+ return value ;
582+ }
583+ return null ;
584+ }
585+
586+ private static void put (Scope result , String name , Object value ) {
587+ String [] splits = name .split ("[.]" );
588+ Scope depth = result ;
589+ for (int i = 0 ; i < splits .length ; i ++) {
590+ if (i < splits .length - 1 ) {
591+ Scope tmp = (Scope ) result .get (splits [i ]);
592+ if (tmp == null ) {
593+ tmp = new Scope ();
594+ }
595+ depth .put (splits [i ], tmp );
596+ depth = tmp ;
597+ } else {
598+ depth .put (splits [i ], value );
599+ }
600+ }
436601 }
437602
438603 private static class WriteCode implements Code {
@@ -457,9 +622,20 @@ public int getLine() {
457622 return line ;
458623 }
459624
625+ @ Override
626+ public Scope unparse (Scope current , String text , AtomicInteger position , Code [] next ) throws MustacheException {
627+ if (position .get () + rest .length () <= text .length ()) {
628+ String substring = text .substring (position .get (), position .get () + rest .length ());
629+ if (rest .toString ().equals (substring )) {
630+ position .addAndGet (rest .length ());
631+ return current ;
632+ }
633+ }
634+ return null ;
635+ }
636+
460637 public void append (String append ) {
461638 rest .append (append );
462639 }
463-
464640 }
465641}
0 commit comments