1515 */
1616package com .diffplug .freshmark ;
1717
18+ import java .util .function .Function ;
19+ import java .util .regex .Matcher ;
20+ import java .util .regex .Pattern ;
21+
1822import javax .script .ScriptEngine ;
1923import javax .script .ScriptException ;
2024
2933 * A CommentScript has the following form:
3034 * <pre>
3135 * {@code
32- * [INTRON] sectionName
36+ * [COMMENT_START sectionName
3337 * script
3438 * script
35- * [EXON ]
36- * lastProgramExecutionResult
37- * lastProgramExecutionResult
38- * lastProgramExecutionResult
39- * [INTRON] /sectionName [EXON ]
39+ * COMMENT_END ]
40+ * body
41+ * body
42+ * body
43+ * [COMMENT_START /sectionName COMMENT_END ]
4044 * }
4145 * </pre>
4246 * This class is a minimal implementation of a CommentScript. To create a CommentScript,
4347 * you must provide:
4448 * <ul>
45- * <li>The intron and exon strings in the constructor .</li>
49+ * <li>A {@link Parser} to split the comment text from the body text .</li>
4650 * <li>{@link #keyToValue} - defines how template keys in the script string are transformed into values</li>
4751 * <li>{@link #setupScriptEngine} - initializes any functions or variables which should be available to the script</li>
4852 * </ul>
49- * See {@link FreshMark} for a sample implementation.
53+ * @see FreshMark
5054 */
51- public abstract class CommentScript {
55+ public abstract class CommentScript implements Parser . SectionCompiler {
5256 /**
5357 * Creates a CommentScript using the given parser to
5458 * delineate and combine comment blocks.
@@ -61,30 +65,37 @@ protected CommentScript(Parser parser) {
6165 final Parser parser ;
6266
6367 /** Compiles a single section/script/input combo into the appropriate output. */
64- final ParserIntronExon .SectionCompiler compiler = new ParserIntronExon .SectionCompiler () {
65- @ Override
66- public String compileSection (String section , String script , String input ) {
67- return Errors .rethrow ().get (() -> {
68- ScriptEngine engine = setupScriptEngine (section );
68+ @ Override
69+ public String compileSection (String section , String script , String input ) {
70+ return Errors .rethrow ().get (() -> {
71+ ScriptEngine engine = setupScriptEngine (section );
6972
70- // apply the templating engine to the script
71- String templatedProgram = template (section , script );
72- // populate the input data
73- engine .put ("input" , input );
74- // evaluate the script and get the result
75- engine .eval (templatedProgram );
76- return Check .cast (engine .get ("output" ), String .class );
77- });
78- }
79- };
73+ // apply the templating engine to the script
74+ String templatedProgram = template (section , script );
75+ // populate the input data
76+ engine .put ("input" , input );
77+ // evaluate the script and get the result
78+ engine .eval (templatedProgram );
79+ return Check .cast (engine .get ("output" ), String .class );
80+ });
81+ }
8082
8183 /** Compiles the given input string. Input must contain only unix newlines, output is guaranteed to be the same. */
8284 public String compile (String input ) {
83- return parser .compile (input , compiler );
85+ return parser .compile (input , this );
8486 }
8587
86- /** For the given section, perform templating on the given script. */
87- protected abstract String template (String section , String script );
88+ /**
89+ * Performs templating on the script before passing it to the {@link ScriptEngine} created by {@link #setupScriptEngine}.
90+ * <p>
91+ * Defaults to mustache-based templating which uses {@link #keyToValue(String, String)} to decode keys.
92+ */
93+ protected String template (String section , String script ) {
94+ return mustacheTemplate (script , key -> keyToValue (section , key ));
95+ }
96+
97+ /** For the given section, return the templated value for the given key. */
98+ protected abstract String keyToValue (String section , String script );
8899
89100 /**
90101 * For the given section, setup any built-in functions and variables.
@@ -93,4 +104,22 @@ public String compile(String input) {
93104 * be extracted for you, but you must do everything else.
94105 */
95106 protected abstract ScriptEngine setupScriptEngine (String section ) throws ScriptException ;
107+
108+ /** Mustache templating. */
109+ static String mustacheTemplate (String input , Function <String , String > keyToValue ) {
110+ Matcher matcher = MUSTACHE_PATTERN .matcher (input );
111+ StringBuilder result = new StringBuilder (input .length () * 3 / 2 );
112+
113+ int lastElement = 0 ;
114+ while (matcher .find ()) {
115+ result .append (matcher .group (1 ));
116+ result .append (keyToValue .apply (matcher .group (2 )));
117+ lastElement = matcher .end ();
118+ }
119+ result .append (input .substring (lastElement ));
120+ return result .toString ();
121+ }
122+
123+ /** Regex which matches for {@code {{key}}}. */
124+ private static final Pattern MUSTACHE_PATTERN = Pattern .compile ("(.*?)\\ {\\ {(.*?)\\ }\\ }" , Pattern .DOTALL );
96125}
0 commit comments