View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  
18  package org.apache.commons.jexl3;
19  
20  import java.nio.charset.Charset;
21  import java.util.Arrays;
22  import java.util.Collection;
23  import java.util.Map;
24  import java.util.function.IntFunction;
25  import java.util.function.Supplier;
26  
27  import org.apache.commons.jexl3.internal.Engine;
28  import org.apache.commons.jexl3.internal.SoftCache;
29  import org.apache.commons.jexl3.introspection.JexlPermissions;
30  import org.apache.commons.jexl3.introspection.JexlSandbox;
31  import org.apache.commons.jexl3.introspection.JexlUberspect;
32  import org.apache.commons.jexl3.parser.JexlScriptParser;
33  import org.apache.commons.logging.Log;
34  
35  /**
36   * Configures and builds a JexlEngine.
37   *
38   * <p>
39   *     The builder allow fine-tuning an engine instance behavior according to various control needs.
40   *     Check <em>{@link #JexlBuilder()}</em> for permission impacts starting with <em>JEXL 3.3</em>.
41   * </p><p>
42   *     Broad configurations elements are controlled through the features ({@link JexlFeatures}) that can restrict JEXL
43   *  syntax - for instance, only expressions with no-side effects - and permissions ({@link JexlPermissions}) that control
44   *  the visible set of objects - for instance, avoiding access to any object in java.rmi.* -.
45   *  </p><p>
46   *     Fine error control and runtime-overridable behaviors are implemented through options ({@link JexlOptions}). Most
47   * common flags accessible from the builder are reflected in its options ({@link #options()}).
48   * </p><p>
49   *     The {@code silent} flag tells the engine what to do with the error; when true, errors are logged as
50   * warning, when false, they throw {@link JexlException} exceptions.
51   * </p><p>
52   *     The {@code strict} flag tells the engine when and if null as operand is considered an error. The {@code safe}
53   * flog determines if safe-navigation is used. Safe-navigation allows an  evaluation shortcut and return null in expressions
54   * that attempts dereferencing null, typically a method call or accessing a property.
55   * </p><p>
56   *     The {@code lexical} and {@code lexicalShade} flags can be used to enforce a lexical scope for
57   * variables and parameters. The {@code lexicalShade} can be used to further ensure no global variable can be
58   * used with the same name as a local one even after it goes out of scope. The corresponding feature flags should be
59   * preferred since they will detect violations at parsing time. (see {@link JexlFeatures})
60   * </p><p>
61   *     The following rules apply on silent and strict flags:
62   * </p>
63   * <ul>
64   * <li>When "silent" &amp; "not-strict":
65   * <p> 0 &amp; null should be indicators of "default" values so that even in an case of error,
66   * something meaningful can still be inferred; may be convenient for configurations.
67   * </p>
68   * </li>
69   * <li>When "silent" &amp; "strict":
70   * <p>One should probably consider using null as an error case - ie, every object
71   * manipulated by JEXL should be valued; the ternary operator, especially the '?:' form
72   * can be used to workaround exceptional cases.
73   * Use case could be configuration with no implicit values or defaults.
74   * </p>
75   * </li>
76   * <li>When "not-silent" &amp; "not-strict":
77   * <p>The error control grain is roughly on par with JEXL 1.0</p>
78   * </li>
79   * <li>When "not-silent" &amp; "strict":
80   * <p>The finest error control grain is obtained; it is the closest to Java code -
81   * still augmented by "script" capabilities regarding automated conversions and type matching.
82   * </p>
83   * </li>
84   * </ul>
85   */
86  public class JexlBuilder {
87      /**
88       * The set of default permissions used when creating a new builder.
89       * <p>Static but modifiable so these default permissions can be changed to a purposeful set.</p>
90       * <p>In JEXL 3.3, these are {@link JexlPermissions#RESTRICTED}.</p>
91       * <p>In JEXL 3.2, these were equivalent to {@link JexlPermissions#UNRESTRICTED}.</p>
92       */
93      private static JexlPermissions PERMISSIONS = JexlPermissions.RESTRICTED;
94  
95      /** The default maximum expression length to hit the expression cache. */
96      protected static final int CACHE_THRESHOLD = 64;
97  
98      /**
99       * Sets the default permissions.
100      * @param permissions the permissions
101      */
102     public static void setDefaultPermissions(final JexlPermissions permissions) {
103         PERMISSIONS = permissions == null ? JexlPermissions.RESTRICTED : permissions;
104     }
105 
106     /** The JexlUberspect instance. */
107     private JexlUberspect uberspect;
108 
109     /** The {@link JexlUberspect} resolver strategy. */
110     private JexlUberspect.ResolverStrategy strategy;
111 
112     /** The set of permissions. */
113     private JexlPermissions permissions;
114 
115     /** The sandbox. */
116     private JexlSandbox sandbox;
117 
118     /** The Log to which all JexlEngine messages will be logged. */
119     private Log logger;
120 
121     /** Whether error messages will carry debugging information. */
122     private Boolean debug;
123 
124     /** Whether interrupt throws JexlException.Cancel. */
125     private Boolean cancellable;
126 
127     /** The options. */
128     private final JexlOptions options = new JexlOptions();
129 
130     /** Whether getVariables considers all potential equivalent syntactic forms. */
131     private int collectMode = 1;
132 
133     /** The {@link JexlArithmetic} instance. */
134     private JexlArithmetic arithmetic;
135 
136     /** The cache size. */
137     private int cache = -1;
138 
139     /** The cache class factory. */
140     private IntFunction<JexlCache<?,?>> cacheFactory = SoftCache::new;
141 
142    /** The parser class factory. */
143    private Supplier<JexlScriptParser> parserFactory;
144 
145     /** The stack overflow limit. */
146     private int stackOverflow = Integer.MAX_VALUE;
147 
148     /** The maximum expression length to hit the expression cache. */
149     private int cacheThreshold = CACHE_THRESHOLD;
150 
151     /** The charset. */
152     private Charset charset = Charset.defaultCharset();
153 
154     /** The class loader. */
155     private ClassLoader loader;
156 
157     /** The features. */
158     private JexlFeatures features;
159 
160     /**
161      * Default constructor.
162      * <p>
163      * As of JEXL 3.3, to reduce the security risks inherent to JEXL&quot;s purpose, the builder will use a set of
164      * restricted permissions as a default to create the {@link JexlEngine} instance. This will greatly reduce which classes
165      * and methods are visible to JEXL and usable in scripts using default implicit behaviors.
166      * </p><p>
167      * However, without mitigation, this change will likely break some scripts at runtime, especially those exposing
168      * your own class instances through arguments, contexts or namespaces.
169      * The new default set of allowed packages and denied classes is described by {@link JexlPermissions#RESTRICTED}.
170      * </p><p>
171      * The recommended mitigation if your usage of JEXL is impacted is to first thoroughly review what should be
172      * allowed and exposed to script authors and implement those through a set of {@link JexlPermissions};
173      * those are easily created using {@link JexlPermissions#parse(String...)}.
174      * </p><p>
175      * In the urgent case of a strict 3.2 compatibility, the simplest and fastest mitigation is to use the 'unrestricted'
176      * set of permissions. The builder must be explicit about it either by setting the default permissions with a
177      * statement like {@code JexlBuilder.setDefaultPermissions(JexlPermissions.UNRESTRICTED);} or with a more precise
178      * one like <code>new JexlBuilder().permissions({@link JexlPermissions#UNRESTRICTED})</code>.
179      * </p><p>
180      * Note that an explicit call to {@link #uberspect(JexlUberspect)} will supersede any permissions related behavior
181      * by using the {@link JexlUberspect} provided as argument used as-is in the created {@link JexlEngine}.
182      * </p>
183      * @since 3.3
184      */
185     public JexlBuilder() {
186         this.permissions = PERMISSIONS;
187     }
188 
189     /**
190      * Is antish resolution enabled?
191      * @return whether antish resolution is enabled
192      */
193     public boolean antish() {
194         return options.isAntish();
195     }
196 
197     /**
198      * Sets whether the engine will resolve antish variable names.
199      *
200      * @param flag true means antish resolution is enabled, false disables it
201      * @return this builder
202      */
203     public JexlBuilder antish(final boolean flag) {
204         options.setAntish(flag);
205         return this;
206     }
207 
208     /**
209      * Gets the JexlArithmetic instance the engine will use.
210      * @return the arithmetic
211      */
212     public JexlArithmetic arithmetic() {
213         return this.arithmetic;
214     }
215 
216     /**
217      * Sets the JexlArithmetic instance the engine will use.
218      *
219      * @param a the arithmetic
220      * @return this builder
221      */
222     public JexlBuilder arithmetic(final JexlArithmetic a) {
223         this.arithmetic = a;
224         options.setStrictArithmetic(a.isStrict());
225         options.setMathContext(a.getMathContext());
226         options.setMathScale(a.getMathScale());
227         return this;
228     }
229 
230     /**
231      * Sets whether logical expressions (&quot;&quot; , ||) coerce their result to boolean.
232      * @param flag true or false
233      * @return this builder
234      */
235     public JexlBuilder booleanLogical(final boolean flag) {
236       options.setBooleanLogical(flag);
237       return this;
238     }
239 
240     /**
241      * Gets the expression cache size the engine will use.
242      * @return the cache size
243      */
244     public int cache() {
245       return cache;
246     }
247 
248     /**
249      * Sets the expression cache size the engine will use.
250      * <p>The cache will contain at most {@code size} expressions of at most {@code cacheThreshold} length.
251      * Note that all JEXL caches are held through SoftReferences and may be garbage-collected.</p>
252      *
253      * @param size if not strictly positive, no cache is used.
254      * @return this builder
255      */
256     public JexlBuilder cache(final int size) {
257         this.cache = size;
258         return this;
259     }
260 
261     /**
262      * Gets the expression-cache factory the engine will use.
263      * @return the cache factory
264      */
265     public IntFunction<JexlCache<?, ?>> cacheFactory() {
266       return this.cacheFactory;
267     }
268 
269     /**
270      * Sets the expression-cache factory the engine will use.
271      *
272      * @param factory the function to produce a cache.
273      * @return this builder
274      */
275     public JexlBuilder cacheFactory(final IntFunction<JexlCache<?, ?>> factory) {
276       this.cacheFactory = factory;
277       return this;
278     }
279 
280   /**
281    * Gets the Jexl script parser factory the engine will use.
282    * @return the cache factory
283    * @since 3.5.0
284    */
285   public Supplier<JexlScriptParser> parserFactory() {
286     return this.parserFactory;
287   }
288 
289   /**
290    * Sets the Jexl script parser factory the engine will use.
291    *
292    * @param factory the function to produce a cache.
293    * @return this builder
294    * @since 3.5.0
295    */
296     public JexlBuilder parserFactory(final Supplier<JexlScriptParser> factory) {
297       this.parserFactory = factory;
298       return this;
299     }
300 
301     /**
302      * Gets the maximum length for an expression to be cached.
303      * @return the cache threshold
304      */
305     public int cacheThreshold() {
306         return cacheThreshold;
307     }
308 
309     /**
310      * Sets the maximum length for an expression to be cached.
311      * <p>Expression whose length is greater than this expression cache length threshold will
312      * bypass the cache.</p>
313      * <p>It is expected that a "long" script will be parsed once and its reference kept
314      * around in user-space structures; the jexl expression cache has no added-value in this case.</p>
315      *
316      * @param length if not strictly positive, the value is silently replaced by the default value (64).
317      * @return this builder
318      */
319     public JexlBuilder cacheThreshold(final int length) {
320         this.cacheThreshold = length > 0? length : CACHE_THRESHOLD;
321         return this;
322     }
323 
324     /**
325      * Gets the cancellable information flag
326      * @return the cancellable information flag
327      * @since 3.1
328      */
329     public Boolean cancellable() {
330         return this.cancellable;
331     }
332 
333     /**
334      * Sets the engine behavior upon interruption: throw an JexlException.Cancel or terminates the current evaluation
335      * and return null.
336      *
337      * @param flag true implies the engine throws the exception, false makes the engine return null.
338      * @return this builder
339      * @since 3.1
340      */
341     public JexlBuilder cancellable(final boolean flag) {
342         this.cancellable = flag;
343         options.setCancellable(flag);
344         return this;
345     }
346 
347     /**
348      * Gets the charset
349      * @return the charset
350      */
351     public Charset charset() {
352         return charset;
353     }
354 
355     /**
356      * Sets the charset to use.
357      *
358      * @param arg the charset
359      * @return this builder
360      * @since 3.1
361      */
362     public JexlBuilder charset(final Charset arg) {
363         this.charset = arg;
364         return this;
365     }
366 
367     /**
368      * Does the variable collection follow strict syntactic rule?
369      * @return true if variable collection follows strict syntactic rule
370      * @since 3.2
371      */
372     public boolean collectAll() {
373         return this.collectMode != 0;
374     }
375 
376     /**
377      * Sets whether the engine variable collectors considers all potential forms of variable syntaxes.
378      *
379      * @param flag true means var collections considers constant array accesses equivalent to dotted references
380      * @return this builder
381      * @since 3.2
382      */
383     public JexlBuilder collectAll(final boolean flag) {
384         return collectMode(flag? 1 : 0);
385     }
386 
387     /**
388      * Gets the collection mode.
389      * @return 0 if variable collection follows strict syntactic rule
390      * @since 3.2
391      */
392     public int collectMode() {
393         return this.collectMode;
394     }
395 
396     /**
397      * Experimental collector mode setter.
398      *
399      * @param mode 0 or 1 as equivalents to false and true, other values are experimental
400      * @return this builder
401      * @since 3.2
402      */
403     public JexlBuilder collectMode(final int mode) {
404         this.collectMode = mode;
405         return this;
406     }
407 
408     /**
409      * Create a new engine
410      * @return a {@link JexlEngine} instance
411      */
412     public JexlEngine create() {
413         return new Engine(this);
414     }
415 
416     /**
417      * Gets the debug flag
418      * @return the debugging information flag
419      */
420     public Boolean debug() {
421         return this.debug;
422     }
423 
424    /**
425      * Sets whether the engine will report debugging information when error occurs.
426      *
427      * @param flag true implies debug is on, false implies debug is off.
428      * @return this builder
429      */
430     public JexlBuilder debug(final boolean flag) {
431         this.debug = flag;
432         return this;
433     }
434 
435     /**
436      * Gets the features the engine will use as a base by default.
437      * @return the features
438      */
439     public JexlFeatures features() {
440         return this.features;
441     }
442 
443     /**
444      * Sets the features the engine will use as a base by default.
445      * <p>Note that the script flag will be ignored; the engine will be able to parse expressions and scripts.
446      * <p>Note also that these will apply to template expressions and scripts.
447      * <p>As a last remark, if lexical or lexicalShade are set as features, this
448      * method will also set the corresponding options.
449      * @param f the features
450      * @return this builder
451      */
452     public JexlBuilder features(final JexlFeatures f) {
453         this.features = f;
454         if (features != null) {
455             if (features.isLexical()) {
456                 options.setLexical(true);
457             }
458             if (features.isLexicalShade()) {
459                 options.setLexicalShade(true);
460             }
461         }
462         return this;
463     }
464 
465     /**
466      * Gets the optional set of imported packages.
467      * @return the set of imports, may be empty, not null
468      */
469     public Collection<String> imports() {
470         return options.getImports();
471     }
472 
473     /**
474      * Sets the optional set of imports.
475      * @param imports the imported packages
476      * @return this builder
477      */
478     public JexlBuilder imports(final Collection<String> imports) {
479         options.setImports(imports);
480         return this;
481     }
482 
483     /**
484      * Sets the optional set of imports.
485      * @param imports the imported packages
486      * @return this builder
487      */
488     public JexlBuilder imports(final String... imports) {
489         return imports(Arrays.asList(imports));
490     }
491 
492     /**
493      * Is lexical scope enabled?
494      * @see JexlOptions#isLexical()
495      * @return whether lexical scope is enabled
496      * @deprecated 3.5.0
497      */
498     @Deprecated
499     public boolean lexical() {
500         return options.isLexical();
501     }
502 
503     /**
504      * Sets whether the engine is in lexical mode.
505      *
506      * @param flag true means lexical function scope is in effect, false implies non-lexical scoping
507      * @return this builder
508      * @since 3.2
509      */
510     public JexlBuilder lexical(final boolean flag) {
511         options.setLexical(flag);
512         return this;
513     }
514 
515     /**
516      * Checks whether lexical shading is enabled.
517      * @see JexlOptions#isLexicalShade()
518      * @return whether lexical shading is enabled
519      * @deprecated 3.5.0
520      */
521     @Deprecated
522     public boolean lexicalShade() {
523         return options.isLexicalShade();
524     }
525 
526     /**
527      * Sets whether the engine is in lexical shading mode.
528      *
529      * @param flag true means lexical shading is in effect, false implies no lexical shading
530      * @return this builder
531      * @since 3.2
532      */
533     public JexlBuilder lexicalShade(final boolean flag) {
534         options.setLexicalShade(flag);
535         return this;
536     }
537 
538     /**
539      * Gets the classloader
540      * @return the class loader
541      */
542     public ClassLoader loader() {
543         return loader;
544     }
545 
546     /**
547      * Sets the charset to use.
548      *
549      * @param arg the charset
550      * @return this builder
551      * @deprecated since 3.1 use {@link #charset(Charset)} instead
552      */
553     @Deprecated
554     public JexlBuilder loader(final Charset arg) {
555         return charset(arg);
556     }
557 
558     /**
559      * Sets the class loader to use.
560      *
561      * @param l the class loader
562      * @return this builder
563      */
564     public JexlBuilder loader(final ClassLoader l) {
565         this.loader = l;
566         return this;
567     }
568 
569     /**
570      * Gets the logger
571      * @return the logger
572      */
573     public Log logger() {
574         return this.logger;
575     }
576 
577     /**
578      * Sets the o.a.c.Log instance to use.
579      *
580      * @param log the logger
581      * @return this builder
582      */
583     public JexlBuilder logger(final Log log) {
584         this.logger = log;
585         return this;
586     }
587 
588     /**
589      * Gets the map of namespaces.
590      * @return the map of namespaces.
591      */
592     public Map<String, Object> namespaces() {
593         return options.getNamespaces();
594     }
595 
596     /**
597      * Sets the default namespaces map the engine will use.
598      * <p>
599      * Each entry key is used as a prefix, each entry value used as a bean implementing
600      * methods; an expression like 'nsx:method(123)' will thus be solved by looking at
601      * a registered bean named 'nsx' that implements method 'method' in that map.
602      * If all methods are static, you may use the bean class instead of an instance as value.
603      * </p>
604      * <p>
605      * If the entry value is a class that has one constructor taking a JexlContext as argument, an instance
606      * of the namespace will be created at evaluation time. It might be a good idea to derive a JexlContext
607      * to carry the information used by the namespace to avoid variable space pollution and strongly type
608      * the constructor with this specialized JexlContext.
609      * </p>
610      * <p>
611      * The key or prefix allows to retrieve the bean that plays the role of the namespace.
612      * If the prefix is null, the namespace is the top-level namespace allowing to define
613      * top-level user-defined namespaces ( ie: myfunc(...) )
614      * </p>
615      * <p>Note that the JexlContext is also used to try to solve top-level namespaces. This allows ObjectContext
616      * derived instances to call methods on the wrapped object.</p>
617      *
618      * @param ns the map of namespaces
619      * @return this builder
620      */
621     public JexlBuilder namespaces(final Map<String, Object> ns) {
622         options.setNamespaces(ns);
623         return this;
624     }
625 
626     /**
627      * Gets the current set of options
628      * @return the current set of options
629      */
630     public JexlOptions options() {
631       return options;
632     }
633 
634     /**
635      * Gets the permissions
636      * @return the permissions
637      */
638     public JexlPermissions permissions() {
639         return this.permissions;
640     }
641 
642     /**
643      * Sets the JexlPermissions instance the engine will use.
644      *
645      * @param p the permissions
646      * @return this builder
647      */
648     public JexlBuilder permissions(final JexlPermissions p) {
649         this.permissions = p;
650         return this;
651     }
652 
653     /**
654      * Is it safe to dereference null?
655      * @return true if safe, false otherwise
656      */
657     public Boolean safe() {
658         return options.isSafe();
659     }
660 
661     /**
662      * Sets whether the engine considers dereferencing null in navigation expressions
663      * as null or triggers an error.
664      * <p>{@code x.y()} if x is null throws an exception when not safe,
665      * return null and warns if it is.</p>
666      * <p>It is recommended to use <em>safe(false)</em> as an explicit default.</p>
667      *
668      * @param flag true means safe navigation, false throws exception when dereferencing null
669      * @return this builder
670      */
671     public JexlBuilder safe(final boolean flag) {
672         options.setSafe(flag);
673         return this;
674     }
675 
676     /**
677      * Gets the sandbox
678      * @return the sandbox
679      */
680     public JexlSandbox sandbox() {
681         return this.sandbox;
682     }
683 
684     /**
685      * Sets the sandbox the engine will use.
686      *
687      * @param box the sandbox
688      * @return this builder
689      */
690     public JexlBuilder sandbox(final JexlSandbox box) {
691         this.sandbox = box;
692         return this;
693     }
694 
695     /**
696      * Is error handling silent?
697      * @return the silent error handling flag
698      */
699     public Boolean silent() {
700         return options.isSilent();
701     }
702 
703     /**
704      * Sets whether the engine will throw JexlException during evaluation when an error is triggered.
705      * <p>When <em>not</em> silent, the engine throws an exception when the evaluation triggers an exception or an
706      * error.</p>
707      * <p>It is recommended to use <em>silent(true)</em> as an explicit default.</p>
708      * @param flag true means no JexlException will occur, false allows them
709      * @return this builder
710      */
711     public JexlBuilder silent(final boolean flag) {
712         options.setSilent(flag);
713         return this;
714     }
715 
716     /**
717      * Gets the cache size
718      * @return the cache size
719      */
720     public int stackOverflow() {
721         return stackOverflow;
722     }
723 
724     /**
725      * Sets the number of script/expression evaluations that can be stacked.
726      * @param size if not strictly positive, limit is reached when Java StackOverflow is thrown.
727      * @return this builder
728      */
729     public JexlBuilder stackOverflow(final int size) {
730         this.stackOverflow = size;
731         return this;
732     }
733 
734     /**
735      * Gets the JexlUberspect strategy
736      * @return the JexlUberspect strategy */
737     public JexlUberspect.ResolverStrategy strategy() {
738         return this.strategy;
739     }
740 
741     /**
742      * Sets the JexlUberspect strategy the engine will use.
743      * <p>This is ignored if the uberspect has been set.
744      *
745      * @param rs the strategy
746      * @return this builder
747      */
748     public JexlBuilder strategy(final JexlUberspect.ResolverStrategy rs) {
749         this.strategy = rs;
750         return this;
751     }
752 
753     /**
754      * Is it strict mode?
755      * @return true if strict, false otherwise */
756     public Boolean strict() {
757         return options.isStrict();
758     }
759 
760     /**
761      * Sets whether the engine considers unknown variables, methods, functions and constructors as errors or
762      * evaluates them as null.
763      * <p>When <em>not</em> strict, operators or functions using null operands return null on evaluation. When
764      * strict, those raise exceptions.</p>
765      * <p>It is recommended to use <em>strict(true)</em> as an explicit default.</p>
766      *
767      * @param flag true means strict error reporting, false allows them to be evaluated as null
768      * @return this builder
769      */
770     public JexlBuilder strict(final boolean flag) {
771         options.setStrict(flag);
772         return this;
773     }
774 
775     /**
776      * Is interpolation strict?
777      * @see JexlOptions#setStrictInterpolation(boolean)
778      * @param flag strict interpolation flag
779      * @return this builder
780      */
781     public JexlBuilder strictInterpolation(final boolean flag) {
782         options.setStrictInterpolation(flag);
783         return this;
784     }
785 
786     /**
787      * Gets the uberspect
788      * @return the uberspect */
789     public JexlUberspect uberspect() {
790         return this.uberspect;
791     }
792 
793     /**
794      * Sets the JexlUberspect instance the engine will use.
795      *
796      * @param u the uberspect
797      * @return this builder
798      */
799     public JexlBuilder uberspect(final JexlUberspect u) {
800         this.uberspect = u;
801         return this;
802     }
803 }