This is an automated email from the ASF dual-hosted git repository. ggregory pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/commons-jexl.git
commit 4f4be9fff8f78d6a1960b263e7d0299ff832e485 Author: Gary D. Gregory <[email protected]> AuthorDate: Sat Nov 1 08:46:37 2025 -0400 Add Javadoc overview --- .../org/apache/commons/jexl3/doc-files/logo.png | Bin 0 -> 9495 bytes src/main/javadoc/overview.html | 344 +++++++++++++++++++++ 2 files changed, 344 insertions(+) diff --git a/src/main/java/org/apache/commons/jexl3/doc-files/logo.png b/src/main/java/org/apache/commons/jexl3/doc-files/logo.png new file mode 100644 index 00000000..3f3ee8dc Binary files /dev/null and b/src/main/java/org/apache/commons/jexl3/doc-files/logo.png differ diff --git a/src/main/javadoc/overview.html b/src/main/javadoc/overview.html new file mode 100644 index 00000000..db5af069 --- /dev/null +++ b/src/main/javadoc/overview.html @@ -0,0 +1,344 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> +<!-- +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 + + https://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. +--> +<html> +<head> +<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> +</head> +<body> + <img alt="Apache Commons IO" src="org/apache/commons/jexl3/doc-files/logo.png"> + <h1>Apache Commons JEXL</h1> + <h2>Introduction</h2> + <p>JEXL is a library intended to facilitate the implementation of dynamic and scripting features in applications and frameworks written in Java.</p> + <p>JEXL implements an Expression Language based on some extensions to the JSTL Expression Language supporting most of the constructs seen in shell-script + or ECMAScript.</p> + <p>Its goal is to expose scripting features usable by technical operatives or consultants working with enterprise platforms. In many use cases, JEXL + allows end-users of an application to code their own scripts or expressions and ensure their execution within controlled functional constraints.</p> + <p> + The library exposes a small footprint API - the <a href="apidocs/org/apache/commons/jexl3/package-summary.html#usage">core features</a> fit in 3 classes and + 10 methods - that can be used in various conditions: + </p> + <ul> + <li>Scripting features: + <ul> + <li>Your application lets (advanced) users evaluate or define some simple expressions like computation formulas.</li> + </ul> + </li> + <li>Module or component configuration: + <ul> + <li>Your application has configuration files (eventually generated by a design module) consumed by the end-user module that would benefit from + variables and expressions.</li> + <li>When it would be convenient to use IOC but overall complexity doesn't require (or can't depend upon) a full-blown library (Spring, Guice...).</li> + </ul> + </li> + <li>Loose-coupling of interfaces and implementations or duck-typing: + <ul> + <li>You have optional classes that your code cant consider as compilation dependencies.</li> + <li>You have to integrate and call "legacy" code or use components that you don't want to strongly depend upon.</li> + </ul> + </li> + <li>Simple template capabilities: + <ul> + <li>Your application has basic template requirements and JSPs or Velocity would be overkill or too inconvenient to deploy.</li> + </ul> + </li> + </ul> + <p> + JEXL name stands for Java EXpression Language, a simple expression language originally inspired by Apache Velocity and the Expression Language defined in + the JavaServer Pages Standard Tag Library version 1.1 (JSTL) and JavaServer Pages version 2.0 (JSP). JEXL 2.0 added features inspired by <a + href="https://en.wikipedia.org/wiki/Unified_Expression_Language">Unified EL</a>. The syntax is now close to a mix of ECMAScript and "shell-script" making + it easy to master by technical operatives or consultants. The objects exposed and their behavior obviously need to be documented though... + </p> + <p>The API and the expression language exploit Java-beans naming patterns through introspection to expose property getters and setters. It also considers + public class fields as properties and allows to invoke any accessible method.</p> + <h2>A Detailed Example</h2> + + <div class="container"> + <div class="header"> + <h1>StreamTest.java</h1> + <p class="meta">Package: org.apache.commons.jexl3.examples | Apache Commons JEXL3</p> + </div> + <div class="code-container"> + <!-- TODO Using a style in the head doesn't override the Javadoc style. --> + <pre> + <code> + <span class="keyword">package</span> org.apache.commons.jexl3.examples; + +<span class="keyword">import static</span> java.lang.Boolean.TRUE; +<span class="keyword">import static</span> org.junit.jupiter.api.Assertions.assertEquals; +<span class="keyword">import static</span> org.junit.jupiter.api.Assertions.assertInstanceOf; +<span class="keyword">import static</span> org.junit.jupiter.api.Assertions.assertTrue; + +<span class="keyword">import</span> java.net.URI; +<span class="keyword">import</span> java.util.Arrays; +<span class="keyword">import</span> java.util.Collection; +<span class="keyword">import</span> java.util.Collections; +<span class="keyword">import</span> java.util.List; +<span class="keyword">import</span> java.util.Objects; +<span class="keyword">import</span> java.util.stream.Collectors; +<span class="keyword">import</span> java.util.stream.Stream; + +<span class="keyword">import</span> org.apache.commons.jexl3.JexlBuilder; +<span class="keyword">import</span> org.apache.commons.jexl3.JexlContext; +<span class="keyword">import</span> org.apache.commons.jexl3.JexlEngine; +<span class="keyword">import</span> org.apache.commons.jexl3.JexlFeatures; +<span class="keyword">import</span> org.apache.commons.jexl3.JexlScript; +<span class="keyword">import</span> org.apache.commons.jexl3.MapContext; +<span class="keyword">import</span> org.apache.commons.jexl3.introspection.JexlPermissions; +<span class="keyword">import</span> org.apache.commons.jexl3.introspection.JexlPermissions.ClassPermissions; +<span class="keyword">import</span> org.junit.jupiter.api.Test; + +<span class="comment">/**</span> +<span class="comment"> * A test around scripting streams.</span> +<span class="comment"> */</span> +<span class="keyword">class</span> <span class="type">StreamTest</span> { + + <span class="comment">/**</span> +<span class="comment"> * A MapContext that can operate on streams and collections.</span> +<span class="comment"> */</span> + <span class="keyword">public static class</span> <span class="type">CollectionContext</span> <span class="keyword">extends</span> <span class="type">MapContext</span> { + + <span class="comment">/**</span> +<span class="comment"> * This allows using a JEXL lambda as a filter.</span> +<span class="comment"> *</span> +<span class="comment"> * @param collection the collection</span> +<span class="comment"> * @param filter the lambda to use as filter</span> +<span class="comment"> * @return the filtered result as a list</span> +<span class="comment"> */</span> + <span class="keyword">public</span> <span class="type">List</span><?> <span class="method">filter</span>(<span class="keyword">final</span> <span + class="type">Collection</span><?> collection, <span class="keyword">final</span> <span class="type">JexlScript</span> filter) { + <span class="keyword">return</span> collection.<span class="method">stream</span>() + .<span class="method">filter</span>(x -> x != <span class="keyword">null</span> && TRUE.<span class="method">equals</span>(filter.<span + class="method">execute</span>(<span class="keyword">this</span>, x))) + .<span class="method">collect</span>(Collectors.<span class="method">toList</span>()); + } + + <span class="comment">/**</span> +<span class="comment"> * This allows using a JEXL lambda as a mapper.</span> +<span class="comment"> *</span> +<span class="comment"> * @param collection the collection</span> +<span class="comment"> * @param mapper the lambda to use as mapper</span> +<span class="comment"> * @return the mapped result as a list</span> +<span class="comment"> */</span> + <span class="keyword">public</span> <span class="type">List</span><?> <span class="method">map</span>(<span class="keyword">final</span> <span + class="type">Collection</span><?> collection, <span class="keyword">final</span> <span class="type">JexlScript</span> mapper) { + <span class="keyword">return</span> collection.<span class="method">stream</span>() + .<span class="method">map</span>(x -> mapper.<span class="method">execute</span>(<span class="keyword">this</span>, x)) + .<span class="method">filter</span>(Objects::nonNull) + .<span class="method">collect</span>(Collectors.<span class="method">toList</span>()); + } + } + + <span class="comment">/**</span> +<span class="comment"> * A MapContext that can operate on streams and collections.</span> +<span class="comment"> */</span> + <span class="keyword">public static class</span> <span class="type">StreamContext</span> <span class="keyword">extends</span> <span class="type">MapContext</span> { + + <span class="comment">/**</span> +<span class="comment"> * This allows using a JEXL lambda as a filter.</span> +<span class="comment"> *</span> +<span class="comment"> * @param stream the stream</span> +<span class="comment"> * @param filter the lambda to use as filter</span> +<span class="comment"> * @return the filtered stream</span> +<span class="comment"> */</span> + <span class="keyword">public</span> <span class="type">Stream</span><?> <span class="method">filter</span>(<span class="keyword">final</span> <span + class="type">Stream</span><?> stream, <span class="keyword">final</span> <span class="type">JexlScript</span> filter) { + <span class="keyword">return</span> stream.<span class="method">filter</span>(x -> x != <span class="keyword">null</span> && TRUE.<span + class="method">equals</span>(filter.<span class="method">execute</span>(<span class="keyword">this</span>, x))); + } + + <span class="comment">/**</span> +<span class="comment"> * This allows using a JEXL lambda as a mapper.</span> +<span class="comment"> *</span> +<span class="comment"> * @param stream the stream</span> +<span class="comment"> * @param mapper the lambda to use as mapper</span> +<span class="comment"> * @return the mapped stream</span> +<span class="comment"> */</span> + <span class="keyword">public</span> <span class="type">Stream</span><?> <span class="method">map</span>(<span class="keyword">final</span> <span + class="type">Stream</span><?> stream, <span class="keyword">final</span> <span class="type">JexlScript</span> mapper) { + <span class="keyword">return</span> stream.<span class="method">map</span>(x -> mapper.<span class="method">execute</span>(<span class="keyword">this</span>, x)); + } + } + + <span class="comment">/** Our engine instance. */</span> + <span class="keyword">private final</span> <span class="type">JexlEngine</span> jexl; + + <span class="keyword">public</span> <span class="method">StreamTest</span>() { + <span class="comment">// Restricting features; no loops, no side effects</span> + <span class="keyword">final</span> <span class="type">JexlFeatures</span> features = <span class="keyword">new</span> <span class="type">JexlFeatures</span>() + .<span class="method">loops</span>(<span class="keyword">false</span>) + .<span class="method">sideEffectGlobal</span>(<span class="keyword">false</span>) + .<span class="method">sideEffect</span>(<span class="keyword">false</span>); + <span class="comment">// Restricted permissions to a safe set but with URI allowed</span> + <span class="keyword">final</span> <span class="type">JexlPermissions</span> permissions = <span class="keyword">new</span> <span class="type">ClassPermissions</span>(java.net.URI.<span + class="keyword">class</span>); + <span class="comment">// Create the engine</span> + jexl = <span class="keyword">new</span> <span class="type">JexlBuilder</span>() + .<span class="method">features</span>(features) + .<span class="method">permissions</span>(permissions) + .<span class="method">namespaces</span>(Collections.<span class="method">singletonMap</span>(<span class="string">"URI"</span>, java.net.URI.<span + class="keyword">class</span>)) + .<span class="method">create</span>(); + } + + <span class="annotation">@Test</span> + <span class="keyword">void</span> <span class="method">testURICollection</span>() { + <span class="comment">// A collection map/filter aware context</span> + <span class="keyword">final</span> <span class="type">JexlContext</span> sctxt = <span class="keyword">new</span> <span class="type">CollectionContext</span>(); + <span class="comment">// Some uris</span> + <span class="keyword">final</span> <span class="type">List</span><<span class="type">URI</span>> uris = Arrays.<span class="method">asList</span>( + URI.<span class="method">create</span>(<span class="string">"http://[email protected]:8000?qry=true"</span>), + URI.<span class="method">create</span>(<span class="string">"https://commons.apache.org/releases/prepare.html"</span>), + URI.<span class="method">create</span>(<span class="string">"mailto:[email protected]"</span>) + ); + + <span class="comment">// filter, all results schemes start with 'http'</span> + <span class="keyword">final</span> <span class="type">JexlScript</span> filter = jexl.<span class="method">createScript</span>( + <span class="string">"list.filter(uri -> uri.scheme =^ 'http')"</span>, + <span class="string">"list"</span>); + <span class="keyword">final</span> <span class="type">Object</span> filtered = filter.<span class="method">execute</span>(sctxt, uris); + <span class="method">assertInstanceOf</span>(List.<span class="keyword">class</span>, filtered); + <span class="type">List</span><<span class="type">URI</span>> result = (<span class="type">List</span><<span class="type">URI</span>>) filtered; + <span class="method">assertEquals</span>(<span class="number">2</span>, result.<span class="method">size</span>()); + <span class="keyword">for</span>(<span class="keyword">final</span> <span class="type">URI</span> uri : result) { + <span class="method">assertTrue</span>(uri.<span class="method">getScheme</span>().<span class="method">startsWith</span>(<span class="string">"http"</span>)); + } + + <span class="comment">// map, all results scheme now 'https'</span> + <span class="keyword">final</span> <span class="type">JexlScript</span> mapper = jexl.<span class="method">createScript</span>( + <span class="string">"list.map(uri -> uri.scheme =^ 'http'? URI:create(`https://${uri.host}`) : null)"</span>, + <span class="string">"list"</span>); + <span class="keyword">final</span> <span class="type">Object</span> transformed = mapper.<span class="method">execute</span>(sctxt, uris); + <span class="method">assertInstanceOf</span>(List.<span class="keyword">class</span>, transformed); + result = (<span class="type">List</span><<span class="type">URI</span>>) transformed; + <span class="method">assertEquals</span>(<span class="number">2</span>, result.<span class="method">size</span>()); + <span class="keyword">for</span> (<span class="keyword">final</span> <span class="type">URI</span> uri : result) { + <span class="method">assertEquals</span>(<span class="string">"https"</span>, uri.<span class="method">getScheme</span>()); + } + } + + <span class="annotation">@Test</span> + <span class="keyword">void</span> <span class="method">testURIStream</span>() { + <span class="comment">// Assume a collection of uris need to be processed and transformed to be simplified ;</span> + <span class="comment">// we want only http/https ones, only the host part and using a https scheme</span> + <span class="keyword">final</span> <span class="type">List</span><<span class="type">URI</span>> uris = Arrays.<span class="method">asList</span>( + URI.<span class="method">create</span>(<span class="string">"http://[email protected]:8000?qry=true"</span>), + URI.<span class="method">create</span>(<span class="string">"https://commons.apache.org/releases/prepare.html"</span>), + URI.<span class="method">create</span>(<span class="string">"mailto:[email protected]"</span>) + ); + <span class="comment">// Create the test control, the expected result of our script evaluation</span> + <span class="keyword">final</span> <span class="type">List</span><?> control = uris.<span class="method">stream</span>() + .<span class="method">map</span>(uri -> uri.<span class="method">getScheme</span>().<span class="method">startsWith</span>(<span + class="string">"http"</span>)? <span class="string">"https://"</span> + uri.<span class="method">getHost</span>() : <span class="keyword">null</span>) + .<span class="method">filter</span>(Objects::nonNull) + .<span class="method">collect</span>(Collectors.<span class="method">toList</span>()); + <span class="method">assertEquals</span>(<span class="number">2</span>, control.<span class="method">size</span>()); + + <span class="comment">// Create scripts:</span> + <span class="comment">// uri is the name of the variable used as parameter; the beans are exposed as properties</span> + <span class="comment">// note that it is also used in the backquoted string</span> + <span class="keyword">final</span> <span class="type">JexlScript</span> mapper = jexl.<span class="method">createScript</span>(<span class="string">"uri.scheme =^ 'http'? `https://${uri.host}` : null"</span>, <span + class="string">"uri"</span>); + <span class="comment">// using the bang-bang / !! - JScript like - is the way to coerce to boolean in the filter</span> + <span class="keyword">final</span> <span class="type">JexlScript</span> transform = jexl.<span class="method">createScript</span>( + <span class="string">"list.stream().map(mapper).filter(x -> !!x).collect(Collectors.toList())"</span>, <span class="string">"list"</span>); + + <span class="comment">// Execute scripts:</span> + <span class="keyword">final</span> <span class="type">JexlContext</span> sctxt = <span class="keyword">new</span> <span class="type">StreamContext</span>(); + <span class="comment">// expose the static methods of Collectors; java.util.* is allowed by permissions</span> + sctxt.<span class="method">set</span>(<span class="string">"Collectors"</span>, Collectors.<span class="keyword">class</span>); + <span class="comment">// expose the mapper script as a global variable in the context</span> + sctxt.<span class="method">set</span>(<span class="string">"mapper"</span>, mapper); + + <span class="keyword">final</span> <span class="type">Object</span> transformed = transform.<span class="method">execute</span>(sctxt, uris); + <span class="method">assertInstanceOf</span>(List.<span class="keyword">class</span>, transformed); + <span class="method">assertEquals</span>(control, transformed); + } +} +</code> + </pre> + </div> + </div> + + <h2>Extensions to JSTL Expression Language</h2> + <p> + JEXL attempts to bring some of the lessons learned by the Velocity community about expression languages in templating to a wider audience. <a + href="https://commons.apache.org/jelly">Commons Jelly</a> needed Velocity-ish method access, it just had to have it. + </p> + <p> + It must be noted that JEXL is <strong>not</strong> a compatible implementation of EL as defined in JSTL 1.1 (JSR-052) or JSP 2.0 (JSR-152). For a compatible + implementation of these specifications, see the <a href="https://commons.apache.org/el">Commons EL</a> project. + </p> + <p>While JEXL 3.3 is now closer to JScript (without prototypes), its roots are the expression language defined in JSTL and its has improved upon its + syntax in a few areas:</p> + <ul> + <li>Support for invocation of any accessible method (see example above).</li> + <li>Support for setting/getting any accessible public field.</li> + <li>A general <span class="literal">new()</span> method allowing to instantiate objects. + </li> + <li>A general <span class="literal">size()</span> method, which works on: + <ul> + <li><span class="literal">String</span> - returns length</li> + <li><span class="literal">Map</span> - returns number of keys</li> + <li><span class="literal">List</span> - returns number of elements.</li> + </ul> + </li> + <li>A general <span class="literal">empty()</span> method, which works on Collections and Strings. + </li> + <li>Support for the ternary operator 'a ? b : c' - and its GNU-C / "Elvis" variant 'a ?: c'.</li> + <li>Support for the Perl-like regex matching operators '=~' and '!~'</li> + <li>Support for the CSS3-inspired 'startsWith' and 'endsWith' operators '=^' and '=$'</li> + <li>Support for user-defined functions.</li> + <li>Misc : '+' has been overloaded to be use as a String concatenation operator</li> + </ul> + + <h2>Related Resources</h2> + <p>JEXL is not a product of the Java Community Process (JCP), but it provides a similar expression syntax. For more information about JSP 2.0 EL and JSTL + 1.1 EL:</p> + <ul> + <li><a href="http://www.oracle.com/technetwork/java/index-jsp-138231.html">JSP 2.0</a> is covered by Java Specification Requests (JSR) <a + href="http://www.jcp.org/en/jsr/detail?id=152">JSR-152: JavaServer Pages 2.0 Specification</a>.</li> + <li>Apache has an implementation of the expression language for JSP 2.0, called <a href="https://commons.apache.org/el/index.html">EL</a> + </li> + <li><a href="http://www.oracle.com/technetwork/java/index-jsp-135995.html">JSTL 1.1</a> is covered by <a href="http://jcp.org/en/jsr/detail?id=52">JSR + 52: A Standard Tag Library for JavaServer Pages</a>. See the <a href="http://docs.oracle.com/javaee/5/jstl/1.1/docs/tlddocs/">JSTL API</a>.</li> + <li>Apache has a <a href="http://tomcat.apache.org/taglibs/standard/">JSTL Implementation</a>. + </li> + </ul> + <h3>Velocity</h3> + <p> + <a href="http://velocity.apache.org/">Apache Velocity</a> implements a similar expression language. + </p> + <p> + In particular the <a href="http://velocity.apache.org/engine/devel/user-guide.html#References">References</a> section of the User Guide has some good + information on properties and method which correlate directly to JEXL. + </p> + + <h2>Projects Using Commons JEXL</h2> + <ul> + <li><a href="https://commons.apache.org/configuration">Commons Configuration</a></li> + <li><a href="https://commons.apache.org/scxml">Commons SCXML</a></li> + <li><a href="https://commons.apache.org/jelly">Jelly</a></li> + </ul> + + <h2>Requirements</h2> + <ul> + <li>Java 8 or above.</li> + <li>If using OSGi, R7 or above.</li> + </ul> +</body> +</html>
