2121
2222package com .predic8 .membrane .core .lang .groovy .adapted ;
2323
24+ import com .predic8 .membrane .core .lang .ScriptingUtils ;
2425import edu .umd .cs .findbugs .annotations .SuppressFBWarnings ;
2526import groovy .lang .*;
2627import groovy .text .Template ;
4647import java .util .Map ;
4748import java .util .concurrent .atomic .AtomicInteger ;
4849
50+ import static com .predic8 .membrane .core .lang .ScriptingUtils .BINDING ;
51+
4952/**
5053 * Adapted from <a href="https://github.com/apache/groovy/blob/master/subprojects/groovy-templates/src/main/groovy/groovy/text/StreamingTemplateEngine.java">Apache Groovy</a> .
5154 *
5558 * methods.
5659 *
5760 * We therefore decided to copy the code of the inner class here (see first commit) and then made
58- * some adjustments (the following commits).
61+ * some adjustments (the following commits, see "adjustment" comments ).
5962 */
6063public class StreamingTemplateEngine extends groovy .text .StreamingTemplateEngine {
6164 @ Override
@@ -86,7 +89,11 @@ private static class StreamingTemplate implements Template {
8689 + "return { _p, _s, _b, out -> "
8790 + "int _i = 0;"
8891 + "try {"
89- + "delegate = new Binding(_b);" ;
92+ // adjustment: using the GroovyBuiltInFunctions binding implementation
93+ // provided from the outside (via the 'fn' map entry) allows the script
94+ // to directly call Membrane functions. This enables templating syntax like
95+ // ${xpath('/root/element')} .
96+ + "delegate = _b.get('" + BINDING + "');" ;
9097
9198 /**
9299 * The 'footer' we use for the resulting groovy script source
@@ -514,7 +521,7 @@ private int parseDollarIdentifier(int c,
514521 final StringBuilder target ,
515522 final Position sourcePosition ,
516523 final Position targetPosition ) throws IOException , FinishedReadingException {
517- append (target , targetPosition , "out<<" );
524+ append (target , targetPosition , "out<<escape( " ); // adjustment: escape the output
518525 append (target , targetPosition , (char ) c );
519526
520527 while (true ) {
@@ -525,7 +532,7 @@ private int parseDollarIdentifier(int c,
525532 append (target , targetPosition , (char ) c );
526533 }
527534
528- append (target , targetPosition , ";" );
535+ append (target , targetPosition , ") ;" );
529536
530537 return c ;
531538 }
@@ -562,10 +569,13 @@ private void parseDollarCurlyIdentifier(final Reader reader,
562569 final StringBuilder target ,
563570 final Position sourcePosition ,
564571 final Position targetPosition ) throws IOException , FinishedReadingException {
565- append (target , targetPosition , "out<<\" \" \" ${" );
572+ append (target , targetPosition , "out<<\" \" \" ${escape( " ); // adjustment: escape the output
566573
567574 while (true ) {
568575 int c = read (reader , sourcePosition );
576+ // adjustment: escape the output
577+ if (c == '}' )
578+ append (target , targetPosition , ')' );
569579 append (target , targetPosition , (char ) c );
570580 if (c == '}' ) break ;
571581 }
@@ -612,11 +622,11 @@ private void parseExpression(final Reader reader,
612622 final StringBuilder target ,
613623 final Position sourcePosition ,
614624 final Position targetPosition ) throws IOException , FinishedReadingException {
615- append (target , targetPosition , "out<<\" \" \" ${" );
625+ append (target , targetPosition , "out<<\" \" \" ${escape( " ); // adjustment: escape the output
616626
617627 readAndAppend (reader , target , sourcePosition , targetPosition );
618628
619- append (target , targetPosition , "}\" \" \" ;" );
629+ append (target , targetPosition , ") }\" \" \" ;" );
620630 }
621631
622632 @ Override
0 commit comments