| /* |
| * 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; |
| |
| import java.io.Reader; |
| import java.io.StringReader; |
| import java.io.Writer; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| |
| /** |
| * A simple "JeXL Template" engine. |
| * |
| * <p>At the base is an evaluator similar to the Unified EL evaluator used in JSP/JSF based on JEXL. |
| * At the top is a template engine inspired by Velocity that uses JEXL (instead of OGNL/VTL) as the scripting |
| * language.</p> |
| * |
| * <p>The evaluator is intended to be used in configuration modules, XML based frameworks or JSP taglibs |
| * and facilitate the implementation of expression evaluation.</p> |
| * |
| * <p>The template engine is intended to output any form of text; html, XML, CSV...</p> |
| * |
| * @since 3.0 |
| */ |
| public abstract class JxltEngine { |
| |
| /** |
| * The sole type of (runtime) exception the JxltEngine can throw. |
| */ |
| public static class Exception extends JexlException { |
| |
| /** Serial version UID. */ |
| private static final long serialVersionUID = 201112030113L; |
| |
| /** |
| * Creates an Exception. |
| * |
| * @param info the contextual information |
| * @param msg the exception message |
| * @param cause the exception cause |
| */ |
| public Exception(JexlInfo info, String msg, Throwable cause) { |
| super(info, msg, cause); |
| } |
| } |
| |
| /** |
| * A unified expression that can mix immediate, deferred and nested sub-expressions as well as string constants; |
| * <ul> |
| * <li>The "immediate" syntax is of the form <code>"...${jexl-expr}..."</code></li> |
| * <li>The "deferred" syntax is of the form <code>"...#{jexl-expr}..."</code></li> |
| * <li>The "nested" syntax is of the form <code>"...#{...${jexl-expr0}...}..."</code></li> |
| * <li>The "composite" syntax is of the form <code>"...${jexl-expr0}... #{jexl-expr1}..."</code></li> |
| * </ul> |
| * |
| * <p>Deferred and immediate expression carry different intentions:</p> |
| * |
| * <ul> |
| * <li>An immediate expression indicate that evaluation is intended to be performed close to |
| * the definition/parsing point.</li> |
| * <li>A deferred expression indicate that evaluation is intended to occur at a later stage.</li> |
| * </ul> |
| * |
| * <p>For instance: <code>"Hello ${name}, now is #{time}"</code> is a composite "deferred" expression since one |
| * of its subexpressions is deferred. Furthermore, this (composite) expression intent is |
| * to perform two evaluations; one close to its definition and another one in a later |
| * phase.</p> |
| * |
| * <p>The API reflects this feature in 2 methods, prepare and evaluate. The prepare method |
| * will evaluate the immediate subexpression and return an expression that contains only |
| * the deferred subexpressions (and constants), a prepared expression. Such a prepared expression |
| * is suitable for a later phase evaluation that may occur with a different JexlContext. |
| * Note that it is valid to call evaluate without prepare in which case the same JexlContext |
| * is used for the 2 evaluation phases.</p> |
| * |
| * <p>In the most common use-case where deferred expressions are to be kept around as properties of objects, |
| * one should createExpression and prepare an expression before storing it and evaluate it each time |
| * the property storing it is accessed.</p> |
| * |
| * <p>Note that nested expression use the JEXL syntax as in:</p> |
| * |
| * <blockquote><code>"#{${bar}+'.charAt(2)'}"</code></blockquote> |
| * |
| * <p>The most common mistake leading to an invalid expression being the following:</p> |
| * |
| * <blockquote><code>"#{${bar}charAt(2)}"</code></blockquote> |
| * |
| * <p>Also note that methods that createExpression evaluate expressions may throw <em>unchecked</em> exceptions; |
| * The {@link JxltEngine.Exception} are thrown when the engine instance is in "non-silent" mode |
| * but since these are RuntimeException, user-code <em>should</em> catch them where appropriate.</p> |
| * |
| * @since 2.0 |
| */ |
| public interface Expression { |
| |
| /** |
| * Generates this expression's string representation. |
| * |
| * @return the string representation |
| */ |
| String asString(); |
| |
| /** |
| * Adds this expression's string representation to a StringBuilder. |
| * |
| * @param strb the builder to fill |
| * @return the builder argument |
| */ |
| StringBuilder asString(StringBuilder strb); |
| |
| /** |
| * Evaluates this expression. |
| * |
| * <p>If the underlying JEXL engine is silent, errors will be logged through its logger as warning.</p> |
| * |
| * @param context the variable context |
| * @return the result of this expression evaluation or null if an error occurs and the {@link JexlEngine} is |
| * running in silent mode |
| * @throws Exception if an error occurs and the {@link JexlEngine} |
| * is not silent |
| */ |
| Object evaluate(JexlContext context); |
| |
| /** |
| * Retrieves this expression's source expression. |
| * <p> |
| * If this expression was prepared, this allows to retrieve the |
| * original expression that lead to it.</p> |
| * <p>Other expressions return themselves.</p> |
| * |
| * @return the source expression |
| */ |
| Expression getSource(); |
| |
| /** |
| * Gets the list of variables accessed by this expression. |
| * <p>This method will visit all nodes of the sub-expressions and extract all variables whether they |
| * are written in 'dot' or 'bracketed' notation. (a.b is equivalent to a['b']).</p> |
| * |
| * @return the set of variables, each as a list of strings (ant-ish variables use more than 1 string) |
| * or the empty set if no variables are used |
| */ |
| Set<List<String>> getVariables(); |
| |
| /** |
| * Checks whether this expression is deferred. |
| * |
| * @return true if deferred, false otherwise |
| */ |
| boolean isDeferred(); |
| |
| /** |
| * Checks whether this expression is immediate. |
| * |
| * @return true if immediate, false otherwise |
| */ |
| boolean isImmediate(); |
| |
| /** |
| * Evaluates the immediate sub-expressions. |
| * |
| * <p>When the expression is dependant upon immediate and deferred sub-expressions, |
| * evaluates the immediate sub-expressions with the context passed as parameter |
| * and returns this expression deferred form.</p> |
| * |
| * <p>In effect, this binds the result of the immediate sub-expressions evaluation in the |
| * context, allowing to differ evaluation of the remaining (deferred) expression within another context. |
| * This only has an effect to nested and composite expressions that contain differed and |
| * immediate sub-expressions.</p> |
| * |
| * <p>If the underlying JEXL engine is silent, errors will be logged through its logger as warning.* </p> |
| * |
| * @param context the context to use for immediate expression evaluations |
| * @return an {@link Expression} or null if an error occurs and the {@link JexlEngine} is running |
| * in silent mode |
| * @throws Exception if an error occurs and the {@link JexlEngine} is not in silent mode |
| */ |
| Expression prepare(JexlContext context); |
| |
| /** |
| * Formats this expression, adding its source string representation in |
| * comments if available: 'expression /*= source *\/'' . |
| * |
| * @return the formatted expression string |
| */ |
| @Override |
| String toString(); |
| } |
| |
| /** |
| * Creates a a {@link Expression} from an expression string. |
| * Uses and fills up the expression cache if any. |
| * |
| * <p>If the underlying JEXL engine is silent, errors will be logged through its logger as warnings.</p> |
| * |
| * @param expression the {@link Template} string expression |
| * @return the {@link Expression}, null if silent and an error occurred |
| * @throws Exception if an error occurs and the {@link JexlEngine} is not silent |
| */ |
| public Expression createExpression(String expression) { |
| return createExpression(null, expression); |
| } |
| |
| /** |
| * Creates a a {@link Expression} from an expression string. |
| * Uses and fills up the expression cache if any. |
| * |
| * <p>If the underlying JEXL engine is silent, errors will be logged through its logger as warnings.</p> |
| * |
| * @param info the {@link JexlInfo} source information |
| * @param expression the {@link Template} string expression |
| * @return the {@link Expression}, null if silent and an error occured |
| * @throws Exception if an error occurs and the {@link JexlEngine} is not silent |
| */ |
| public abstract Expression createExpression(JexlInfo info, String expression); |
| |
| /** |
| * A template is a JEXL script that evaluates by writing its content through a Writer. |
| * <p> |
| * The source text is parsed considering each line beginning with '$$' (as default pattern) as JEXL script code |
| * and all others as Unified JEXL expressions; those expressions will be invoked from the script during |
| * evaluation and their output gathered through a writer. |
| * It is thus possible to use looping or conditional construct "around" expressions generating output. |
| * </p> |
| * For instance: |
| * <blockquote><pre> |
| * $$ for(var x : [1, 3, 5, 42, 169]) { |
| * $$ if (x == 42) { |
| * Life, the universe, and everything |
| * $$ } else if (x > 42) { |
| * The value $(x} is over fourty-two |
| * $$ } else { |
| * The value ${x} is under fourty-two |
| * $$ } |
| * $$ } |
| * </pre></blockquote> |
| * |
| * <p>Will evaluate as:</p> |
| * |
| * <blockquote><pre> |
| * The value 1 is under fourty-two |
| * The value 3 is under fourty-two |
| * The value 5 is under fourty-two |
| * Life, the universe, and everything |
| * The value 169 is over fourty-two |
| * </pre></blockquote> |
| * |
| * <p>During evaluation, the template context exposes its writer as '$jexl' which is safe to use in this case. |
| * This allows writing directly through the writer without adding new-lines as in:</p> |
| * |
| * <blockquote><pre> |
| * $$ for(var cell : cells) { $jexl.print(cell); $jexl.print(';') } |
| * </pre></blockquote> |
| * |
| * <p>A template is expanded as one JEXL script and a list of template expressions; each template expression is |
| * being replaced in the script by a call to jexl:print(expr) (the expr is in fact the expr number in the template). |
| * This integration uses a specialized JexlContext (TemplateContext) that serves as a namespace (for jexl:) |
| * and stores the template expression array and the writer (java.io.Writer) that the 'jexl:print(...)' |
| * delegates the output generation to.</p> |
| * |
| * @since 3.0 |
| */ |
| public interface Template { |
| |
| /** |
| * Recreate the template source from its inner components. |
| * |
| * @return the template source rewritten |
| */ |
| String asString(); |
| |
| /** |
| * Evaluates this template. |
| * |
| * @param context the context to use during evaluation |
| * @param writer the writer to use for output |
| */ |
| void evaluate(JexlContext context, Writer writer); |
| |
| /** |
| * Evaluates this template. |
| * |
| * @param context the context to use during evaluation |
| * @param writer the writer to use for output |
| * @param args the arguments |
| */ |
| void evaluate(JexlContext context, Writer writer, Object... args); |
| |
| /** |
| * Prepares this template by expanding any contained deferred TemplateExpression. |
| * |
| * @param context the context to prepare against |
| * @return the prepared version of the template |
| */ |
| Template prepare(JexlContext context); |
| |
| /** |
| * Gets the list of variables accessed by this template. |
| * <p>This method will visit all nodes of the sub-expressions and extract all variables whether they |
| * are written in 'dot' or 'bracketed' notation. (a.b is equivalent to a['b']).</p> |
| * |
| * @return the set of variables, each as a list of strings (ant-ish variables use more than 1 string) |
| * or the empty set if no variables are used |
| */ |
| Set<List<String>> getVariables(); |
| |
| /** |
| * Gets the list of parameters expected by this template. |
| * |
| * @return the parameter names array |
| */ |
| String[] getParameters(); |
| |
| /** |
| * Gets this script pragmas. |
| * |
| * @return the (non null, may be empty) pragmas map |
| */ |
| Map<String, Object> getPragmas(); |
| } |
| |
| /** |
| * Creates a new template. |
| * |
| * @param info the jexl info (file, line, column) |
| * @param prefix the directive prefix |
| * @param source the source |
| * @param parms the parameter names |
| * @return the template |
| */ |
| public abstract Template createTemplate(JexlInfo info, String prefix, Reader source, String... parms); |
| |
| /** |
| * Creates a new template. |
| * |
| * @param info the source info |
| * @param parms the parameter names |
| * @param source the source |
| * @return the template |
| */ |
| public Template createTemplate(JexlInfo info, String source, String... parms) { |
| return createTemplate(info, "$$", new StringReader(source), parms); |
| } |
| |
| /** |
| * Creates a new template. |
| * |
| * @param info the source info |
| * @param source the source |
| * @return the template |
| */ |
| public Template createTemplate(JexlInfo info, String source) { |
| return createTemplate(info, "$$", new StringReader(source), (String[]) null); |
| } |
| |
| /** |
| * Creates a new template. |
| * |
| * @param prefix the directive prefix |
| * @param source the source |
| * @param parms the parameter names |
| * @return the template |
| */ |
| public Template createTemplate(String prefix, Reader source, String... parms) { |
| return createTemplate(null, prefix, source, parms); |
| } |
| |
| /** |
| * Creates a new template. |
| * |
| * @param source the source |
| * @param parms the parameter names |
| * @return the template |
| */ |
| public Template createTemplate(String source, String... parms) { |
| return createTemplate(null, source, parms); |
| } |
| |
| /** |
| * Creates a new template. |
| * |
| * @param source the source |
| * @return the template |
| */ |
| public Template createTemplate(String source) { |
| return createTemplate(null, source); |
| } |
| |
| /** |
| * Gets the {@link JexlEngine} underlying this template engine. |
| * |
| * @return the JexlEngine |
| */ |
| public abstract JexlEngine getEngine(); |
| |
| /** |
| * Clears the cache. |
| */ |
| public abstract void clearCache(); |
| } |