| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package org.apache.commons.jexl3.internal; |
| |
| import org.apache.commons.jexl3.JexlContext; |
| import org.apache.commons.jexl3.JexlInfo; |
| import org.apache.commons.jexl3.JxltEngine; |
| import org.apache.commons.jexl3.internal.TemplateEngine.Block; |
| import org.apache.commons.jexl3.internal.TemplateEngine.BlockType; |
| import org.apache.commons.jexl3.internal.TemplateEngine.TemplateExpression; |
| import org.apache.commons.jexl3.parser.ASTJexlScript; |
| import java.io.Reader; |
| import java.io.Writer; |
| import java.util.ArrayList; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| |
| /** |
| * A Template instance. |
| */ |
| public final class TemplateScript implements JxltEngine.Template { |
| /** The prefix marker. */ |
| private final String prefix; |
| /** The array of source blocks. */ |
| private final Block[] source; |
| /** The resulting script. */ |
| private final ASTJexlScript script; |
| /** The TemplateEngine expressions called by the script. */ |
| private final TemplateExpression[] exprs; |
| /** The engine. */ |
| private final TemplateEngine jxlt; |
| |
| /** |
| * Creates a new template from an character input. |
| * @param engine the template engine |
| * @param info the source info |
| * @param directive the prefix for lines of code; can not be "$", "${", "#" or "#{" |
| since this would preclude being able to differentiate directives and jxlt expressions |
| * @param reader the input reader |
| * @param parms the parameter names |
| * @throws NullPointerException if either the directive prefix or input is null |
| * @throws IllegalArgumentException if the directive prefix is invalid |
| */ |
| public TemplateScript(TemplateEngine engine, JexlInfo info, String directive, Reader reader, String... parms) { |
| if (directive == null) { |
| throw new NullPointerException("null prefix"); |
| } |
| if (Character.toString(engine.getImmediateChar()).equals(directive) |
| || (Character.toString(engine.getImmediateChar()) + "{").equals(directive) |
| || Character.toString(engine.getDeferredChar()).equals(directive) |
| || (Character.toString(engine.getDeferredChar()) + "{").equals(directive)) { |
| throw new IllegalArgumentException(directive + ": is not a valid directive pattern"); |
| } |
| if (reader == null) { |
| throw new NullPointerException("null input"); |
| } |
| this.jxlt = engine; |
| Scope scope = parms == null ? null : new Scope(null, parms); |
| prefix = directive; |
| List<Block> blocks = jxlt.readTemplate(prefix, reader); |
| List<TemplateExpression> uexprs = new ArrayList<TemplateExpression>(); |
| StringBuilder strb = new StringBuilder(); |
| int nuexpr = 0; |
| int codeStart = -1; |
| for (int b = 0; b < blocks.size(); ++b) { |
| Block block = blocks.get(b); |
| if (block.getType() == BlockType.VERBATIM) { |
| strb.append("jexl:print("); |
| strb.append(nuexpr++); |
| strb.append(");\n"); |
| } else { |
| // keep track of first block of code, the frame creator |
| if (codeStart < 0) { |
| codeStart = b; |
| } |
| strb.append(block.getBody()); |
| } |
| } |
| // create the script |
| if (info == null) { |
| info = jxlt.getEngine().createInfo(); |
| } |
| // allow lambda defining params |
| script = jxlt.getEngine().parse(info.at(0, 0), strb.toString(), scope, false, false).script(); |
| scope = script.getScope(); |
| // create the exprs using the code frame for those appearing after the first block of code |
| for (int b = 0; b < blocks.size(); ++b) { |
| Block block = blocks.get(b); |
| if (block.getType() == BlockType.VERBATIM) { |
| uexprs.add( |
| jxlt.parseExpression( |
| info.at(block.getLine(), 0), |
| block.getBody(), |
| b > codeStart ? scope : null) |
| ); |
| } |
| } |
| source = blocks.toArray(new Block[blocks.size()]); |
| exprs = uexprs.toArray(new TemplateExpression[uexprs.size()]); |
| } |
| |
| /** |
| * Private ctor used to expand deferred expressions during prepare. |
| * @param engine the template engine |
| * @param thePrefix the directive prefix |
| * @param theSource the source |
| * @param theScript the script |
| * @param theExprs the expressions |
| */ |
| TemplateScript(TemplateEngine engine, |
| String thePrefix, |
| Block[] theSource, |
| ASTJexlScript theScript, |
| TemplateExpression[] theExprs) { |
| jxlt = engine; |
| prefix = thePrefix; |
| source = theSource; |
| script = theScript; |
| exprs = theExprs; |
| } |
| |
| /** |
| * @return script |
| */ |
| ASTJexlScript getScript() { |
| return script; |
| } |
| |
| /** |
| * @return exprs |
| */ |
| TemplateExpression[] getExpressions() { |
| return exprs; |
| } |
| |
| @Override |
| public String toString() { |
| StringBuilder strb = new StringBuilder(); |
| for (Block block : source) { |
| block.toString(strb, prefix); |
| } |
| return strb.toString(); |
| } |
| |
| @Override |
| public String asString() { |
| StringBuilder strb = new StringBuilder(); |
| int e = 0; |
| for (int b = 0; b < source.length; ++b) { |
| Block block = source[b]; |
| if (block.getType() == BlockType.DIRECTIVE) { |
| strb.append(prefix); |
| } else { |
| exprs[e++].asString(strb); |
| } |
| } |
| return strb.toString(); |
| } |
| |
| @Override |
| public TemplateScript prepare(JexlContext context) { |
| Scope.Frame frame = script.createFrame((Object[]) null); |
| TemplateExpression[] immediates = new TemplateExpression[exprs.length]; |
| for (int e = 0; e < exprs.length; ++e) { |
| immediates[e] = exprs[e].prepare(frame, context); |
| } |
| return new TemplateScript(jxlt, prefix, source, script, immediates); |
| } |
| |
| @Override |
| public void evaluate(JexlContext context, Writer writer) { |
| evaluate(context, writer, (Object[]) null); |
| } |
| |
| @Override |
| public void evaluate(JexlContext context, Writer writer, Object... args) { |
| Scope.Frame frame = script.createFrame(args); |
| Interpreter interpreter = new TemplateInterpreter(jxlt.getEngine(), context, frame, exprs, writer); |
| interpreter.interpret(script); |
| } |
| |
| @Override |
| public Set<List<String>> getVariables() { |
| Engine.VarCollector collector = new Engine.VarCollector(); |
| for (TemplateExpression expr : exprs) { |
| expr.getVariables(collector); |
| } |
| return collector.collected(); |
| } |
| |
| @Override |
| public String[] getParameters() { |
| return script.getParameters(); |
| } |
| |
| @Override |
| public Map<String, Object> getPragmas() { |
| return script.getPragmas(); |
| } |
| } |