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