001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017 018package org.apache.commons.jexl3; 019 020import java.math.MathContext; 021import java.util.Collections; 022import java.util.Map; 023import org.apache.commons.jexl3.internal.Engine; 024 025/** 026 * Flags and properties that can alter the evaluation behavior. 027 * The flags, briefly explained, are the following: 028 * <ul> 029 * <li>silent: whether errors throw exception</li> 030 * <li>safe: whether navigation through null is an error</li> 031 * <li>cancellable: whether thread interruption is an error</li> 032 * <li>lexical: whether redefining local variables is an error</li> 033 * <li>lexicalShade: whether local variables shade global ones even outside their scope</li> 034 * <li>strict: whether unknown or unsolvable identifiers are errors</li> 035 * <li>strictArithmetic: whether null as operand is an error</li> 036 * <li>sharedInstance: whether these options can be modified at runtime during execution (expert)</li> 037 * </ul> 038 * The sensible default is cancellable, strict and strictArithmetic. 039 * <p>This interface replaces the now deprecated JexlEngine.Options. 040 * @since 3.2 041 */ 042public final class JexlOptions { 043 /** The shared instance bit. */ 044 private static final int SHARED = 7; 045 /** The local shade bit. */ 046 private static final int SHADE = 6; 047 /** The antish var bit. */ 048 private static final int ANTISH = 5; 049 /** The lexical scope bit. */ 050 private static final int LEXICAL = 4; 051 /** The safe bit. */ 052 private static final int SAFE = 3; 053 /** The silent bit. */ 054 private static final int SILENT = 2; 055 /** The strict bit. */ 056 private static final int STRICT = 1; 057 /** The cancellable bit. */ 058 private static final int CANCELLABLE = 0; 059 /** The flags names ordered. */ 060 private static final String[] NAMES = { 061 "cancellable", "strict", "silent", "safe", "lexical", "antish", "lexicalShade", "sharedInstance" 062 }; 063 /** Default mask .*/ 064 private static int DEFAULT = 1 /*<< CANCELLABLE*/ | 1 << STRICT | 1 << ANTISH | 1 << SAFE; 065 /** The arithmetic math context. */ 066 private MathContext mathContext = null; 067 /** The arithmetic math scale. */ 068 private int mathScale = Integer.MIN_VALUE; 069 /** The arithmetic strict math flag. */ 070 private boolean strictArithmetic = true; 071 /** The default flags, all but safe. */ 072 private int flags = DEFAULT; 073 /** The namespaces .*/ 074 private Map<String, Object> namespaces = Collections.emptyMap(); 075 076 /** 077 * Sets the value of a flag in a mask. 078 * @param ordinal the flag ordinal 079 * @param mask the flags mask 080 * @param value true or false 081 * @return the new flags mask value 082 */ 083 private static int set(final int ordinal, final int mask, final boolean value) { 084 return value? mask | (1 << ordinal) : mask & ~(1 << ordinal); 085 } 086 087 /** 088 * Checks the value of a flag in the mask. 089 * @param ordinal the flag ordinal 090 * @param mask the flags mask 091 * @return the mask value with this flag or-ed in 092 */ 093 private static boolean isSet(final int ordinal, final int mask) { 094 return (mask & 1 << ordinal) != 0; 095 } 096 097 /** 098 * Default ctor. 099 */ 100 public JexlOptions() {} 101 102 /** 103 * Sets the default (static, shared) option flags. 104 * <p> 105 * Whenever possible, we recommend using JexlBuilder methods to unambiguously instantiate a JEXL 106 * engine; this method should only be used for testing / validation. 107 * <p>A '+flag' or 'flag' will set the option named 'flag' as true, '-flag' set as false. 108 * The possible flag names are: 109 * cancellable, strict, silent, safe, lexical, antish, lexicalShade 110 * <p>Calling JexlBuilder.setDefaultOptions("+safe") once before JEXL engine creation 111 * may ease validating JEXL3.2 in your environment. 112 * @param flags the flags to set 113 */ 114 public static void setDefaultFlags(final String...flags) { 115 DEFAULT = parseFlags(DEFAULT, flags); 116 } 117 118 /** 119 * Parses flags by name. 120 * <p>A '+flag' or 'flag' will set flag as true, '-flag' set as false. 121 * The possible flag names are: 122 * cancellable, strict, silent, safe, lexical, antish, lexicalShade 123 * @param mask the initial mask state 124 * @param flags the flags to set 125 * @return the flag mask updated 126 */ 127 public static int parseFlags(int mask, final String...flags) { 128 for(String name : flags) { 129 boolean b = true; 130 if (name.charAt(0) == '+') { 131 name = name.substring(1); 132 } else if (name.charAt(0) == '-') { 133 name = name.substring(1); 134 b = false; 135 } 136 for(int flag = 0; flag < NAMES.length; ++flag) { 137 if (NAMES[flag].equals(name)) { 138 if (b) { 139 mask |= (1 << flag); 140 } else { 141 mask &= ~(1 << flag); 142 } 143 break; 144 } 145 } 146 } 147 return mask; 148 } 149 150 /** 151 * Sets this option flags using the +/- syntax. 152 * @param opts the option flags 153 */ 154 public void setFlags(final String[] opts) { 155 flags = parseFlags(flags, opts); 156 } 157 158 /** 159 * The MathContext instance used for +,-,/,*,% operations on big decimals. 160 * @return the math context 161 */ 162 public MathContext getMathContext() { 163 return mathContext; 164 } 165 166 /** 167 * The BigDecimal scale used for comparison and coercion operations. 168 * @return the scale 169 */ 170 public int getMathScale() { 171 return mathScale; 172 } 173 174 /** 175 * Checks whether evaluation will attempt resolving antish variable names. 176 * @return true if antish variables are solved, false otherwise 177 */ 178 public boolean isAntish() { 179 return isSet(ANTISH, flags); 180 } 181 182 /** 183 * Checks whether evaluation will throw JexlException.Cancel (true) or 184 * return null (false) if interrupted. 185 * @return true when cancellable, false otherwise 186 */ 187 public boolean isCancellable() { 188 return isSet(CANCELLABLE, flags); 189 } 190 191 /** 192 * Checks whether runtime variable scope is lexical. 193 * <p>If true, lexical scope applies to local variables and parameters. 194 * Redefining a variable in the same lexical unit will generate errors. 195 * @return true if scope is lexical, false otherwise 196 */ 197 public boolean isLexical() { 198 return isSet(LEXICAL, flags); 199 } 200 201 /** 202 * Checks whether local variables shade global ones. 203 * <p>After a symbol is defined as local, dereferencing it outside its 204 * scope will trigger an error instead of seeking a global variable of the 205 * same name. To further reduce potential naming ambiguity errors, 206 * global variables (ie non local) must be declared to be assigned (@link JexlContext#has(String) ) 207 * when this flag is on; attempting to set an undeclared global variables will 208 * raise an error. 209 * @return true if lexical shading is applied, false otherwise 210 */ 211 public boolean isLexicalShade() { 212 return isSet(SHADE, flags); 213 } 214 215 /** 216 * Checks whether the engine considers null in navigation expression as 217 * errors during evaluation.. 218 * @return true if safe, false otherwise 219 */ 220 public boolean isSafe() { 221 return isSet(SAFE, flags); 222 } 223 224 /** 225 * Checks whether the engine will throw a {@link JexlException} when an 226 * error is encountered during evaluation. 227 * @return true if silent, false otherwise 228 */ 229 public boolean isSilent() { 230 return isSet(SILENT, flags); 231 } 232 233 /** 234 * Checks whether the engine considers unknown variables, methods and 235 * constructors as errors during evaluation. 236 * @return true if strict, false otherwise 237 */ 238 public boolean isStrict() { 239 return isSet(STRICT, flags); 240 } 241 242 /** 243 * Checks whether the arithmetic triggers errors during evaluation when null 244 * is used as an operand. 245 * @return true if strict, false otherwise 246 */ 247 public boolean isStrictArithmetic() { 248 return strictArithmetic; 249 } 250 251 /** 252 * Sets whether the engine will attempt solving antish variable names from 253 * context. 254 * @param flag true if antish variables are solved, false otherwise 255 */ 256 public void setAntish(final boolean flag) { 257 flags = set(ANTISH, flags, flag); 258 } 259 260 /** 261 * Sets whether the engine will throw JexlException.Cancel (true) or return 262 * null (false) when interrupted during evaluation. 263 * @param flag true when cancellable, false otherwise 264 */ 265 public void setCancellable(final boolean flag) { 266 flags = set(CANCELLABLE, flags, flag); 267 } 268 269 /** 270 * Sets whether the engine uses a strict block lexical scope during 271 * evaluation. 272 * @param flag true if lexical scope is used, false otherwise 273 */ 274 public void setLexical(final boolean flag) { 275 flags = set(LEXICAL, flags, flag); 276 } 277 278 /** 279 * Sets whether the engine strictly shades global variables. 280 * Local symbols shade globals after definition and creating global 281 * variables is prohibited during evaluation. 282 * If setting to lexical shade, lexical scope is also set. 283 * @param flag true if creation is allowed, false otherwise 284 */ 285 public void setLexicalShade(final boolean flag) { 286 flags = set(SHADE, flags, flag); 287 if (flag) { 288 flags = set(LEXICAL, flags, true); 289 } 290 } 291 292 /** 293 * Sets the arithmetic math context. 294 * @param mcontext the context 295 */ 296 public void setMathContext(final MathContext mcontext) { 297 this.mathContext = mcontext; 298 } 299 300 /** 301 * Sets the arithmetic math scale. 302 * @param mscale the scale 303 */ 304 public void setMathScale(final int mscale) { 305 this.mathScale = mscale; 306 } 307 308 /** 309 * Sets whether the engine considers null in navigation expression as errors 310 * during evaluation. 311 * @param flag true if safe, false otherwise 312 */ 313 public void setSafe(final boolean flag) { 314 flags = set(SAFE, flags, flag); 315 } 316 317 /** 318 * Sets whether the engine will throw a {@link JexlException} when an error 319 * is encountered during evaluation. 320 * @param flag true if silent, false otherwise 321 */ 322 public void setSilent(final boolean flag) { 323 flags = set(SILENT, flags, flag); 324 } 325 326 /** 327 * Sets whether the engine considers unknown variables, methods and 328 * constructors as errors during evaluation. 329 * @param flag true if strict, false otherwise 330 */ 331 public void setStrict(final boolean flag) { 332 flags = set(STRICT, flags, flag); 333 } 334 335 /** 336 * Sets the strict arithmetic flag. 337 * @param stricta true or false 338 */ 339 public void setStrictArithmetic(final boolean stricta) { 340 this.strictArithmetic = stricta; 341 } 342 343 /** 344 * Whether these options are immutable at runtime. 345 * <p>Expert mode; allows instance handled through context to be shared 346 * instead of copied. 347 * @param flag true if shared, false if not 348 */ 349 public void setSharedInstance(final boolean flag) { 350 flags = set(SHARED, flags, flag); 351 } 352 353 /** 354 * @return false if a copy of these options is used during execution, 355 * true if those can potentially be modified 356 */ 357 public boolean isSharedInstance() { 358 return isSet(SHARED, flags); 359 } 360 361 /** 362 * Set options from engine. 363 * @param jexl the engine 364 * @return this instance 365 */ 366 public JexlOptions set(final JexlEngine jexl) { 367 if (jexl instanceof Engine) { 368 ((Engine) jexl).optionsSet(this); 369 } 370 return this; 371 } 372 373 /** 374 * Set options from options. 375 * @param src the options 376 * @return this instance 377 */ 378 public JexlOptions set(final JexlOptions src) { 379 mathContext = src.mathContext; 380 mathScale = src.mathScale; 381 strictArithmetic = src.strictArithmetic; 382 flags = src.flags; 383 namespaces = src.namespaces; 384 return this; 385 } 386 387 /** 388 * Gets the optional map of namespaces. 389 * @return the map of namespaces, may be empty, not null 390 */ 391 public Map<String, Object> getNamespaces() { 392 return namespaces; 393 } 394 395 /** 396 * Sets the optional map of namespaces. 397 * @param ns a namespaces map 398 */ 399 public void setNamespaces(final Map<String, Object> ns) { 400 this.namespaces = ns == null? Collections.emptyMap() : ns; 401 } 402 403 /** 404 * Creates a copy of this instance. 405 * @return a copy 406 */ 407 public JexlOptions copy() { 408 return new JexlOptions().set(this); 409 } 410 411}