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" & "not-strict":
65 * <p> 0 & 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" & "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" & "not-strict":
77 * <p>The error control grain is roughly on par with JEXL 1.0</p>
78 * </li>
79 * <li>When "not-silent" & "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"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 ("" , ||) 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 }