diff --git a/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/JexlArithmetic.java b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/JexlArithmetic.java new file mode 100644 index 0000000..243997f --- /dev/null +++ b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/JexlArithmetic.java @@ -0,0 +1,1810 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package aiyh.utils.tool.org.apache.commons.jexl3; + +import aiyh.utils.tool.org.apache.commons.jexl3.internal.IntegerRange; +import aiyh.utils.tool.org.apache.commons.jexl3.internal.LongRange; +import aiyh.utils.tool.org.apache.commons.jexl3.introspection.JexlMethod; + +import java.lang.reflect.Array; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.math.BigDecimal; +import java.math.BigInteger; +import java.math.MathContext; +import java.util.Collection; +import java.util.Map; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * Perform arithmetic, implements JexlOperator methods. + * + *

This is the class to derive to implement new operator behaviors.

+ * + *

The 5 base arithmetic operators (+, - , *, /, %) follow the same evaluation rules regarding their arguments.

+ *
    + *
  1. If both are null, result is 0
  2. + *
  3. If either is a BigDecimal, coerce both to BigDecimal and perform operation
  4. + *
  5. If either is a floating point number, coerce both to Double and perform operation
  6. + *
  7. Else treat as BigInteger, perform operation and attempt to narrow result: + *
      + *
    1. if both arguments can be narrowed to Integer, narrow result to Integer
    2. + *
    3. if both arguments can be narrowed to Long, narrow result to Long
    4. + *
    5. Else return result as BigInteger
    6. + *
    + *
  8. + *
+ *

+ * Note that the only exception thrown by JexlArithmetic is and must be ArithmeticException. + * + * @see JexlOperator + * @since 2.0 + */ +public class JexlArithmetic { + + /** Marker class for null operand exceptions. */ + public static class NullOperand extends ArithmeticException { + } + + /** Double.MAX_VALUE as BigDecimal. */ + protected static final BigDecimal BIGD_DOUBLE_MAX_VALUE = BigDecimal.valueOf(Double.MAX_VALUE); + + /** Double.MIN_VALUE as BigDecimal. */ + protected static final BigDecimal BIGD_DOUBLE_MIN_VALUE = BigDecimal.valueOf(Double.MIN_VALUE); + + /** Long.MAX_VALUE as BigInteger. */ + protected static final BigInteger BIGI_LONG_MAX_VALUE = BigInteger.valueOf(Long.MAX_VALUE); + + /** Long.MIN_VALUE as BigInteger. */ + protected static final BigInteger BIGI_LONG_MIN_VALUE = BigInteger.valueOf(Long.MIN_VALUE); + + /** Default BigDecimal scale. */ + protected static final int BIGD_SCALE = -1; + + /** Whether this JexlArithmetic instance behaves in strict or lenient mode. */ + private final boolean strict; + + /** The big decimal math context. */ + private final MathContext mathContext; + + /** The big decimal scale. */ + private final int mathScale; + + /** The dynamic constructor. */ + private final Constructor ctor; + + /** + * Creates a JexlArithmetic. + *

If you derive your own arithmetic, implement the + * other constructor that may be needed when dealing with options. + * + * @param astrict whether this arithmetic is strict or lenient + */ + public JexlArithmetic(final boolean astrict) { + this(astrict, null, Integer.MIN_VALUE); + } + + /** + * Creates a JexlArithmetic. + *

The constructor to define in derived classes. + * + * @param astrict whether this arithmetic is lenient or strict + * @param bigdContext the math context instance to use for +,-,/,*,% operations on big decimals. + * @param bigdScale the scale used for big decimals. + */ + public JexlArithmetic(final boolean astrict, final MathContext bigdContext, final int bigdScale) { + this.strict = astrict; + this.mathContext = bigdContext == null ? MathContext.DECIMAL128 : bigdContext; + this.mathScale = bigdScale == Integer.MIN_VALUE ? BIGD_SCALE : bigdScale; + Constructor actor = null; + try { + actor = getClass().getConstructor(boolean.class, MathContext.class, int.class); + } catch (final Exception xany) { + // ignore + } + this.ctor = actor; + } + + /** + * Apply options to this arithmetic which eventually may create another instance. + * + * @param options the {@link JexlEngine.Options} to use + * @return an arithmetic with those options set + * @see #createWithOptions(boolean, MathContext, int) + */ + public JexlArithmetic options(final JexlOptions options) { + if (options != null) { + final boolean ostrict = options.isStrictArithmetic(); + MathContext bigdContext = options.getMathContext(); + if (bigdContext == null) { + bigdContext = getMathContext(); + } + int bigdScale = options.getMathScale(); + if (bigdScale == Integer.MIN_VALUE) { + bigdScale = getMathScale(); + } + if (ostrict != isStrict() + || bigdScale != getMathScale() + || bigdContext != getMathContext()) { + return createWithOptions(ostrict, bigdContext, bigdScale); + } + } + return this; + } + + /** + * Apply options to this arithmetic which eventually may create another instance. + * + * @param options the {@link JexlEngine.Options} to use + * @return an arithmetic with those options set + * @see #createWithOptions(boolean, MathContext, int) + * @deprecated 3.2 + */ + @Deprecated + public JexlArithmetic options(final JexlEngine.Options options) { + if (options != null) { + Boolean ostrict = options.isStrictArithmetic(); + if (ostrict == null) { + ostrict = isStrict(); + } + MathContext bigdContext = options.getArithmeticMathContext(); + if (bigdContext == null) { + bigdContext = getMathContext(); + } + int bigdScale = options.getArithmeticMathScale(); + if (bigdScale == Integer.MIN_VALUE) { + bigdScale = getMathScale(); + } + if (ostrict != isStrict() + || bigdScale != getMathScale() + || bigdContext != getMathContext()) { + return createWithOptions(ostrict, bigdContext, bigdScale); + } + } + return this; + } + + /** + * Apply options to this arithmetic which eventually may create another instance. + * + * @param context the context that may extend {@link JexlContext.OptionsHandle} to use + * @return a new arithmetic instance or this + * @see #createWithOptions(boolean, MathContext, int) + * @since 3.1 + */ + public JexlArithmetic options(final JexlContext context) { + if (context instanceof JexlContext.OptionsHandle) { + return options(((JexlContext.OptionsHandle) context).getEngineOptions()); + } + if (context instanceof JexlEngine.Options) { + return options((JexlEngine.Options) context); + } + return this; + } + + /** + * Creates a JexlArithmetic instance. + * Called by options(...) method when another instance of the same class of arithmetic is required. + * + * @param astrict whether this arithmetic is lenient or strict + * @param bigdContext the math context instance to use for +,-,/,*,% operations on big decimals. + * @param bigdScale the scale used for big decimals. + * @return default is a new JexlArithmetic instance + * @see #options(JexlEngine.Options) + * @since 3.1 + */ + protected JexlArithmetic createWithOptions(final boolean astrict, final MathContext bigdContext, final int bigdScale) { + if (ctor != null) { + try { + return ctor.newInstance(astrict, bigdContext, bigdScale); + } catch (IllegalAccessException | IllegalArgumentException + | InstantiationException | InvocationTargetException xany) { + // it was worth the try + } + } + return new JexlArithmetic(astrict, bigdContext, bigdScale); + } + + /** + * The interface that uberspects JexlArithmetic classes. + *

This allows overloaded operator methods discovery.

+ */ + public interface Uberspect { + /** + * Checks whether this uberspect has overloads for a given operator. + * + * @param operator the operator to check + * @return true if an overload exists, false otherwise + */ + boolean overloads(JexlOperator operator); + + /** + * Gets the most specific method for an operator. + * + * @param operator the operator + * @param arg the arguments + * @return the most specific method or null if no specific override could be found + */ + JexlMethod getOperator(JexlOperator operator, Object... arg); + } + + /** + * Helper interface used when creating an array literal. + * + *

The default implementation creates an array and attempts to type it strictly.

+ * + * + */ + public interface ArrayBuilder { + + /** + * Adds a literal to the array. + * + * @param value the item to add + */ + void add(Object value); + + /** + * Creates the actual "array" instance. + * + * @param extended true when the last argument is ', ...' + * @return the array + */ + Object create(boolean extended); + } + + /** + * Called by the interpreter when evaluating a literal array. + * + * @param size the number of elements in the array + * @return the array builder + */ + public ArrayBuilder arrayBuilder(final int size) { + return new aiyh.utils.tool.org.apache.commons.jexl3.internal.ArrayBuilder(size); + } + + /** + * Helper interface used when creating a set literal. + *

The default implementation creates a java.util.HashSet.

+ */ + public interface SetBuilder { + /** + * Adds a literal to the set. + * + * @param value the item to add + */ + void add(Object value); + + /** + * Creates the actual "set" instance. + * + * @return the set + */ + Object create(); + } + + /** + * Called by the interpreter when evaluating a literal set. + * + * @param size the number of elements in the set + * @return the array builder + */ + public SetBuilder setBuilder(final int size) { + return new aiyh.utils.tool.org.apache.commons.jexl3.internal.SetBuilder(size); + } + + /** + * Helper interface used when creating a map literal. + *

The default implementation creates a java.util.HashMap.

+ */ + public interface MapBuilder { + /** + * Adds a new entry to the map. + * + * @param key the map entry key + * @param value the map entry value + */ + void put(Object key, Object value); + + /** + * Creates the actual "map" instance. + * + * @return the map + */ + Object create(); + } + + /** + * Called by the interpreter when evaluating a literal map. + * + * @param size the number of elements in the map + * @return the map builder + */ + public MapBuilder mapBuilder(final int size) { + return new aiyh.utils.tool.org.apache.commons.jexl3.internal.MapBuilder(size); + } + + /** + * Creates a literal range. + *

The default implementation only accepts integers and longs.

+ * + * @param from the included lower bound value (null if none) + * @param to the included upper bound value (null if none) + * @return the range as an iterable + * @throws ArithmeticException as an option if creation fails + */ + public Iterable createRange(final Object from, final Object to) throws ArithmeticException { + final long lfrom = toLong(from); + final long lto = toLong(to); + if ((lfrom >= Integer.MIN_VALUE && lfrom <= Integer.MAX_VALUE) + && (lto >= Integer.MIN_VALUE && lto <= Integer.MAX_VALUE)) { + return IntegerRange.create((int) lfrom, (int) lto); + } + return LongRange.create(lfrom, lto); + } + + /** + * Checks whether this JexlArithmetic instance + * strictly considers null as an error when used as operand unexpectedly. + * + * @return true if strict, false if lenient + */ + public boolean isStrict() { + return this.strict; + } + + /** + * The MathContext instance used for +,-,/,*,% operations on big decimals. + * + * @return the math context + */ + public MathContext getMathContext() { + return mathContext; + } + + /** + * The BigDecimal scale used for comparison and coericion operations. + * + * @return the scale + */ + public int getMathScale() { + return mathScale; + } + + /** + * Ensure a big decimal is rounded by this arithmetic scale and rounding mode. + * + * @param number the big decimal to round + * @return the rounded big decimal + */ + protected BigDecimal roundBigDecimal(final BigDecimal number) { + final int mscale = getMathScale(); + if (mscale >= 0) { + return number.setScale(mscale, getMathContext().getRoundingMode()); + } + return number; + } + + /** + * The result of +,/,-,*,% when both operands are null. + * + * @return Integer(0) if lenient + * @throws ArithmeticException if strict + */ + protected Object controlNullNullOperands() { + if (isStrict()) { + throw new NullOperand(); + } + return 0; + } + + /** + * Throw a NPE if arithmetic is strict. + * + * @throws ArithmeticException if strict + */ + protected void controlNullOperand() { + if (isStrict()) { + throw new NullOperand(); + } + } + + /** + * The float regular expression pattern. + *

+ * The decimal and exponent parts are optional and captured allowing to determine if the number is a real + * by checking whether one of these 2 capturing groups is not empty. + */ + public static final Pattern FLOAT_PATTERN = Pattern.compile("^[+-]?\\d*(\\.\\d*)?([eE][+-]?\\d+)?$"); + + /** + * Test if the passed value is a floating point number, i.e. a float, double + * or string with ( "." | "E" | "e"). + * + * @param val the object to be tested + * @return true if it is, false otherwise. + */ + protected boolean isFloatingPointNumber(final Object val) { + if (val instanceof Float || val instanceof Double) { + return true; + } + if (val instanceof CharSequence) { + final Matcher m = FLOAT_PATTERN.matcher((CharSequence) val); + // first group is decimal, second is exponent; + // one of them must exist hence start({1,2}) >= 0 + return m.matches() && (m.start(1) >= 0 || m.start(2) >= 0); + } + return false; + } + + /** + * Is Object a floating point number. + * + * @param o Object to be analyzed. + * @return true if it is a Float or a Double. + */ + protected boolean isFloatingPoint(final Object o) { + return o instanceof Float || o instanceof Double; + } + + /** + * Is Object a whole number. + * + * @param o Object to be analyzed. + * @return true if Integer, Long, Byte, Short or Character. + */ + protected boolean isNumberable(final Object o) { + return o instanceof Integer + || o instanceof Long + || o instanceof Byte + || o instanceof Short + || o instanceof Character; + } + + /** + * Given a Number, return back the value using the smallest type the result + * will fit into. + *

This works hand in hand with parameter 'widening' in java + * method calls, e.g. a call to substring(int,int) with an int and a long + * will fail, but a call to substring(int,int) with an int and a short will + * succeed.

+ * + * @param original the original number. + * @return a value of the smallest type the original number will fit into. + */ + public Number narrow(final Number original) { + return narrowNumber(original, null); + } + + /** + * Whether we consider the narrow class as a potential candidate for narrowing the source. + * + * @param narrow the target narrow class + * @param source the original source class + * @return true if attempt to narrow source to target is accepted + */ + protected boolean narrowAccept(final Class narrow, final Class source) { + return narrow == null || narrow.equals(source); + } + + /** + * Given a Number, return back the value attempting to narrow it to a target class. + * + * @param original the original number + * @param narrow the attempted target class + * @return the narrowed number or the source if no narrowing was possible + */ + public Number narrowNumber(final Number original, final Class narrow) { + if (original == null) { + return null; + } + Number result = original; + if (original instanceof BigDecimal) { + final BigDecimal bigd = (BigDecimal) original; + // if it's bigger than a double it can't be narrowed + if (bigd.compareTo(BIGD_DOUBLE_MAX_VALUE) > 0 + || bigd.compareTo(BIGD_DOUBLE_MIN_VALUE) < 0) { + return original; + } + try { + final long l = bigd.longValueExact(); + // coerce to int when possible (int being so often used in method parms) + if (narrowAccept(narrow, Integer.class) + && l <= Integer.MAX_VALUE + && l >= Integer.MIN_VALUE) { + return (int) l; + } + if (narrowAccept(narrow, Long.class)) { + return l; + } + } catch (final ArithmeticException xa) { + // ignore, no exact value possible + } + } + if (original instanceof Double || original instanceof Float) { + final double value = original.doubleValue(); + if (narrowAccept(narrow, Float.class) + && value <= Float.MAX_VALUE + && value >= Float.MIN_VALUE) { + result = result.floatValue(); + } + // else it fits in a double only + } else { + if (original instanceof BigInteger) { + final BigInteger bigi = (BigInteger) original; + // if it's bigger than a Long it can't be narrowed + if (bigi.compareTo(BIGI_LONG_MAX_VALUE) > 0 + || bigi.compareTo(BIGI_LONG_MIN_VALUE) < 0) { + return original; + } + } + final long value = original.longValue(); + if (narrowAccept(narrow, Byte.class) + && value <= Byte.MAX_VALUE + && value >= Byte.MIN_VALUE) { + // it will fit in a byte + result = (byte) value; + } else if (narrowAccept(narrow, Short.class) + && value <= Short.MAX_VALUE + && value >= Short.MIN_VALUE) { + result = (short) value; + } else if (narrowAccept(narrow, Integer.class) + && value <= Integer.MAX_VALUE + && value >= Integer.MIN_VALUE) { + result = (int) value; + } + // else it fits in a long + } + return result; + } + + /** + * Given a BigInteger, narrow it to an Integer or Long if it fits and the arguments + * class allow it. + *

+ * The rules are: + * if either arguments is a BigInteger, no narrowing will occur + * if either arguments is a Long, no narrowing to Integer will occur + *

+ * + * @param lhs the left hand side operand that lead to the bigi result + * @param rhs the right hand side operand that lead to the bigi result + * @param bigi the BigInteger to narrow + * @return an Integer or Long if narrowing is possible, the original BigInteger otherwise + */ + protected Number narrowBigInteger(final Object lhs, final Object rhs, final BigInteger bigi) { + // coerce to long if possible + if (!(lhs instanceof BigInteger || rhs instanceof BigInteger) + && bigi.compareTo(BIGI_LONG_MAX_VALUE) <= 0 + && bigi.compareTo(BIGI_LONG_MIN_VALUE) >= 0) { + // coerce to int if possible + final long l = bigi.longValue(); + // coerce to int when possible (int being so often used in method parms) + if (!(lhs instanceof Long || rhs instanceof Long) + && l <= Integer.MAX_VALUE + && l >= Integer.MIN_VALUE) { + return (int) l; + } + return l; + } + return bigi; + } + + /** + * Given a BigDecimal, attempt to narrow it to an Integer or Long if it fits if + * one of the arguments is a numberable. + * + * @param lhs the left hand side operand that lead to the bigd result + * @param rhs the right hand side operand that lead to the bigd result + * @param bigd the BigDecimal to narrow + * @return an Integer or Long if narrowing is possible, the original BigInteger otherwise + */ + protected Number narrowBigDecimal(final Object lhs, final Object rhs, final BigDecimal bigd) { + if (isNumberable(lhs) || isNumberable(rhs)) { + try { + final long l = bigd.longValueExact(); + // coerce to int when possible (int being so often used in method parms) + if (l <= Integer.MAX_VALUE && l >= Integer.MIN_VALUE) { + return (int) l; + } + return l; + } catch (final ArithmeticException xa) { + // ignore, no exact value possible + } + } + return bigd; + } + + /** + * Replace all numbers in an arguments array with the smallest type that will fit. + * + * @param args the argument array + * @return true if some arguments were narrowed and args array is modified, + * false if no narrowing occurred and args array has not been modified + */ + public boolean narrowArguments(final Object[] args) { + boolean narrowed = false; + if (args != null) { + for (int a = 0; a < args.length; ++a) { + final Object arg = args[a]; + if (arg instanceof Number) { + final Number narg = (Number) arg; + final Number narrow = narrow(narg); + if (!narg.equals(narrow)) { + args[a] = narrow; + narrowed = true; + } + } + } + } + return narrowed; + } + + /** + * Given a long, attempt to narrow it to an int. + *

Narrowing will only occur if no operand is a Long. + * + * @param lhs the left hand side operand that lead to the long result + * @param rhs the right hand side operand that lead to the long result + * @param r the long to narrow + * @return an Integer if narrowing is possible, the original Long otherwise + */ + protected Number narrowLong(final Object lhs, final Object rhs, final long r) { + if (!(lhs instanceof Long || rhs instanceof Long) && (int) r == r) { + return (int) r; + } + return r; + } + + /** + * Checks if value class is a number that can be represented exactly in a long. + * + * @param value argument + * @return true if argument can be represented by a long + */ + protected Number asLongNumber(final Object value) { + return value instanceof Long + || value instanceof Integer + || value instanceof Short + || value instanceof Byte + ? (Number) value + : null; + } + + /** + * Add two values together. + *

+ * If any numeric add fails on coercion to the appropriate type, + * treat as Strings and do concatenation. + *

+ * + * @param left left argument + * @param right right argument + * @return left + right. + */ + public Object add(final Object left, final Object right) { + if (left == null && right == null) { + return controlNullNullOperands(); + } + final boolean strconcat = strict + ? left instanceof String || right instanceof String + : left instanceof String && right instanceof String; + if (!strconcat) { + try { + // if both (non null) args fit as long + final Number ln = asLongNumber(left); + final Number rn = asLongNumber(right); + if (ln != null && rn != null) { + final long x = ln.longValue(); + final long y = rn.longValue(); + final long result = x + y; + // detect overflow, see java8 Math.addExact + if (((x ^ result) & (y ^ result)) < 0) { + return BigInteger.valueOf(x).add(BigInteger.valueOf(y)); + } + return narrowLong(left, right, result); + } + // if either are bigdecimal use that type + if (left instanceof BigDecimal || right instanceof BigDecimal) { + final BigDecimal l = toBigDecimal(left); + final BigDecimal r = toBigDecimal(right); + final BigDecimal result = l.add(r, getMathContext()); + return narrowBigDecimal(left, right, result); + } + // if either are floating point (double or float) use double + if (isFloatingPointNumber(left) || isFloatingPointNumber(right)) { + final double l = toDouble(left); + final double r = toDouble(right); + return l + r; + } + // otherwise treat as (big) integers + final BigInteger l = toBigInteger(left); + final BigInteger r = toBigInteger(right); + final BigInteger result = l.add(r); + return narrowBigInteger(left, right, result); + } catch (final NumberFormatException nfe) { + if (left == null || right == null) { + controlNullOperand(); + } + } + } + return toString(left).concat(toString(right)); + } + + /** + * Divide the left value by the right. + * + * @param left left argument + * @param right right argument + * @return left / right + * @throws ArithmeticException if right == 0 + */ + public Object divide(final Object left, final Object right) { + if (left == null && right == null) { + return controlNullNullOperands(); + } + // if both (non null) args fit as long + final Number ln = asLongNumber(left); + final Number rn = asLongNumber(right); + if (ln != null && rn != null) { + final long x = ln.longValue(); + final long y = rn.longValue(); + if (y == 0L) { + throw new ArithmeticException("/"); + } + final long result = x / y; + return narrowLong(left, right, result); + } + // if either are bigdecimal use that type + if (left instanceof BigDecimal || right instanceof BigDecimal) { + final BigDecimal l = toBigDecimal(left); + final BigDecimal r = toBigDecimal(right); + if (BigDecimal.ZERO.equals(r)) { + throw new ArithmeticException("/"); + } + final BigDecimal result = l.divide(r, getMathContext()); + return narrowBigDecimal(left, right, result); + } + // if either are floating point (double or float) use double + if (isFloatingPointNumber(left) || isFloatingPointNumber(right)) { + final double l = toDouble(left); + final double r = toDouble(right); + if (r == 0.0) { + throw new ArithmeticException("/"); + } + return l / r; + } + // otherwise treat as integers + final BigInteger l = toBigInteger(left); + final BigInteger r = toBigInteger(right); + if (BigInteger.ZERO.equals(r)) { + throw new ArithmeticException("/"); + } + final BigInteger result = l.divide(r); + return narrowBigInteger(left, right, result); + } + + /** + * left value modulo right. + * + * @param left left argument + * @param right right argument + * @return left % right + * @throws ArithmeticException if right == 0.0 + */ + public Object mod(final Object left, final Object right) { + if (left == null && right == null) { + return controlNullNullOperands(); + } + // if both (non null) args fit as long + final Number ln = asLongNumber(left); + final Number rn = asLongNumber(right); + if (ln != null && rn != null) { + final long x = ln.longValue(); + final long y = rn.longValue(); + if (y == 0L) { + throw new ArithmeticException("%"); + } + final long result = x % y; + return narrowLong(left, right, result); + } + // if either are bigdecimal use that type + if (left instanceof BigDecimal || right instanceof BigDecimal) { + final BigDecimal l = toBigDecimal(left); + final BigDecimal r = toBigDecimal(right); + if (BigDecimal.ZERO.equals(r)) { + throw new ArithmeticException("%"); + } + final BigDecimal remainder = l.remainder(r, getMathContext()); + return narrowBigDecimal(left, right, remainder); + } + // if either are floating point (double or float) use double + if (isFloatingPointNumber(left) || isFloatingPointNumber(right)) { + final double l = toDouble(left); + final double r = toDouble(right); + if (r == 0.0) { + throw new ArithmeticException("%"); + } + return l % r; + } + // otherwise treat as integers + final BigInteger l = toBigInteger(left); + final BigInteger r = toBigInteger(right); + if (BigInteger.ZERO.equals(r)) { + throw new ArithmeticException("%"); + } + final BigInteger result = l.mod(r); + return narrowBigInteger(left, right, result); + } + + /** + * Checks if the product of the arguments overflows a {@code long}. + *

see java8 Math.multiplyExact + * + * @param x the first value + * @param y the second value + * @param r the product + * @return true if product fits a long, false if it overflows + */ + @SuppressWarnings("MagicNumber") + protected static boolean isMultiplyExact(final long x, final long y, final long r) { + final long ax = Math.abs(x); + final long ay = Math.abs(y); + return !(((ax | ay) >>> (Integer.SIZE - 1) != 0) + && (((y != 0) && (r / y != x)) + || (x == Long.MIN_VALUE && y == -1))); + } + + /** + * Multiply the left value by the right. + * + * @param left left argument + * @param right right argument + * @return left * right. + */ + public Object multiply(final Object left, final Object right) { + if (left == null && right == null) { + return controlNullNullOperands(); + } + // if both (non null) args fit as int + final Number ln = asLongNumber(left); + final Number rn = asLongNumber(right); + if (ln != null && rn != null) { + final long x = ln.longValue(); + final long y = rn.longValue(); + final long result = x * y; + // detect overflow + if (!isMultiplyExact(x, y, result)) { + return BigInteger.valueOf(x).multiply(BigInteger.valueOf(y)); + } + return narrowLong(left, right, result); + } + // if either are bigdecimal use that type + if (left instanceof BigDecimal || right instanceof BigDecimal) { + final BigDecimal l = toBigDecimal(left); + final BigDecimal r = toBigDecimal(right); + final BigDecimal result = l.multiply(r, getMathContext()); + return narrowBigDecimal(left, right, result); + } + // if either are floating point (double or float) use double + if (isFloatingPointNumber(left) || isFloatingPointNumber(right)) { + final double l = toDouble(left); + final double r = toDouble(right); + return l * r; + } + // otherwise treat as integers + final BigInteger l = toBigInteger(left); + final BigInteger r = toBigInteger(right); + final BigInteger result = l.multiply(r); + return narrowBigInteger(left, right, result); + } + + /** + * Subtract the right value from the left. + * + * @param left left argument + * @param right right argument + * @return left - right. + */ + public Object subtract(final Object left, final Object right) { + if (left == null && right == null) { + return controlNullNullOperands(); + } + // if both (non null) args fit as long + final Number ln = asLongNumber(left); + final Number rn = asLongNumber(right); + if (ln != null && rn != null) { + final long x = ln.longValue(); + final long y = rn.longValue(); + final long result = x - y; + // detect overflow, see java8 Math.subtractExact + if (((x ^ y) & (x ^ result)) < 0) { + return BigInteger.valueOf(x).subtract(BigInteger.valueOf(y)); + } + return narrowLong(left, right, result); + } + // if either are bigdecimal use that type + if (left instanceof BigDecimal || right instanceof BigDecimal) { + final BigDecimal l = toBigDecimal(left); + final BigDecimal r = toBigDecimal(right); + final BigDecimal result = l.subtract(r, getMathContext()); + return narrowBigDecimal(left, right, result); + } + // if either are floating point (double or float) use double + if (isFloatingPointNumber(left) || isFloatingPointNumber(right)) { + final double l = toDouble(left); + final double r = toDouble(right); + return l - r; + } + // otherwise treat as integers + final BigInteger l = toBigInteger(left); + final BigInteger r = toBigInteger(right); + final BigInteger result = l.subtract(r); + return narrowBigInteger(left, right, result); + } + + /** + * Negates a value (unary minus for numbers). + * + * @param val the value to negate + * @return the negated value + */ + public Object negate(final Object val) { + if (val == null) { + controlNullOperand(); + return null; + } + if (val instanceof Integer) { + return -((Integer) val); + } + if (val instanceof Double) { + return -((Double) val); + } + if (val instanceof Long) { + return -((Long) val); + } + if (val instanceof BigDecimal) { + return ((BigDecimal) val).negate(); + } + if (val instanceof BigInteger) { + return ((BigInteger) val).negate(); + } + if (val instanceof Float) { + return -((Float) val); + } + if (val instanceof Short) { + return (short) -((Short) val); + } + if (val instanceof Byte) { + return (byte) -((Byte) val); + } + if (val instanceof Boolean) { + return !(Boolean) val; + } + if (val instanceof AtomicBoolean) { + return !((AtomicBoolean) val).get(); + } + throw new ArithmeticException("Object negate:(" + val + ")"); + } + + /** + * Whether negate called with a given argument will always return the same result. + *

This is used to determine whether negate results on number literals can be cached. + * If the result on calling negate with the same constant argument may change between calls, + * which means the function is not deterministic, this method must return false. + * + * @return true if negate is idempotent, false otherwise + * @see #isNegateStable() + */ + public boolean isNegateStable() { + return true; + } + + /** + * Positivize value (unary plus for numbers). + *

C/C++/C#/Java perform integral promotion of the operand, ie + * cast to int if type can represented as int without loss of precision. + * + * @param val the value to positivize + * @return the positive value + * @see #isPositivizeStable() + */ + public Object positivize(final Object val) { + if (val == null) { + controlNullOperand(); + return null; + } + if (val instanceof Short) { + return ((Short) val).intValue(); + } + if (val instanceof Byte) { + return ((Byte) val).intValue(); + } + if (val instanceof Number) { + return val; + } + if (val instanceof Character) { + return (int) (Character) val; + } + if (val instanceof Boolean) { + return val; + } + if (val instanceof AtomicBoolean) { + return ((AtomicBoolean) val).get(); + } + throw new ArithmeticException("Object positivize:(" + val + ")"); + } + + /** + * Whether positivize called with a given argument will always return the same result. + *

This is used to determine whether positivize results on number literals can be cached. + * If the result on calling positivize with the same constant argument may change between calls, + * which means the function is not deterministic, this method must return false. + * + * @return true if positivize is idempotent, false otherwise + */ + public boolean isPositivizeStable() { + return true; + } + + /** + * Test if left contains right (right matches/in left). + *

Beware that this method arguments are the opposite of the operator arguments. + * 'x in y' means 'y contains x'.

+ * + * @param container the container + * @param value the value + * @return test result or null if there is no arithmetic solution + */ + public Boolean contains(final Object container, final Object value) { + if (value == null && container == null) { + // if both are null L == R + return true; + } + if (value == null || container == null) { + // we know both aren't null, therefore L != R + return false; + } + // use arithmetic / pattern matching ? + if (container instanceof Pattern) { + return ((Pattern) container).matcher(value.toString()).matches(); + } + if (container instanceof CharSequence) { + return value.toString().matches(container.toString()); + } + // try contains on map key + if (container instanceof Map) { + if (value instanceof Map) { + return ((Map) container).keySet().containsAll(((Map) value).keySet()); + } + return ((Map) container).containsKey(value); + } + // try contains on collection + if (container instanceof Collection) { + if (value instanceof Collection) { + return ((Collection) container).containsAll((Collection) value); + } + // left in right ? <=> right.contains(left) ? + return ((Collection) container).contains(value); + } + return null; + } + + /** + * Test if left ends with right. + * + * @param left left argument + * @param right right argument + * @return left $= right if there is no arithmetic solution + */ + public Boolean endsWith(final Object left, final Object right) { + if (left == null && right == null) { + // if both are null L == R + return true; + } + if (left == null || right == null) { + // we know both aren't null, therefore L != R + return false; + } + if (left instanceof CharSequence) { + return (toString(left)).endsWith(toString(right)); + } + return null; + } + + /** + * Test if left starts with right. + * + * @param left left argument + * @param right right argument + * @return left ^= right or null if there is no arithmetic solution + */ + public Boolean startsWith(final Object left, final Object right) { + if (left == null && right == null) { + // if both are null L == R + return true; + } + if (left == null || right == null) { + // we know both aren't null, therefore L != R + return false; + } + if (left instanceof CharSequence) { + return (toString(left)).startsWith(toString(right)); + } + return null; + } + + /** + * Check for emptiness of various types: Number, Collection, Array, Map, String. + *

Override or overload this method to add new signatures to the size operators. + * + * @param object the object to check the emptiness of + * @return the boolean or false if object is not null + * @since 3.2 + */ + public Boolean empty(final Object object) { + return object == null || isEmpty(object, false); + } + + /** + * Check for emptiness of various types: Number, Collection, Array, Map, String. + * + * @param object the object to check the emptiness of + * @return the boolean or null if there is no arithmetic solution + */ + public Boolean isEmpty(final Object object) { + return isEmpty(object, object == null); + } + + /** + * Check for emptiness of various types: Number, Collection, Array, Map, String. + * + * @param object the object to check the emptiness of + * @param def the default value if object emptyness can not be determined + * @return the boolean or null if there is no arithmetic solution + */ + public Boolean isEmpty(final Object object, final Boolean def) { + if (object instanceof Number) { + final double d = ((Number) object).doubleValue(); + return Double.isNaN(d) || d == 0.d; + } + if (object instanceof CharSequence) { + return ((CharSequence) object).length() == 0; + } + if (object.getClass().isArray()) { + return Array.getLength(object) == 0; + } + if (object instanceof Collection) { + return ((Collection) object).isEmpty(); + } + // Map isn't a collection + if (object instanceof Map) { + return ((Map) object).isEmpty(); + } + return def; + } + + /** + * Calculate the size of various types: Collection, Array, Map, String. + * + * @param object the object to get the size of + * @return the size of object, 0 if null, 1 if there is no better solution + */ + public Integer size(final Object object) { + return size(object, object == null ? 0 : 1); + } + + /** + * Calculate the size of various types: Collection, Array, Map, String. + * + * @param object the object to get the size of + * @param def the default value if object size can not be determined + * @return the size of object or null if there is no arithmetic solution + */ + public Integer size(final Object object, final Integer def) { + if (object instanceof CharSequence) { + return ((CharSequence) object).length(); + } + if (object.getClass().isArray()) { + return Array.getLength(object); + } + if (object instanceof Collection) { + return ((Collection) object).size(); + } + if (object instanceof Map) { + return ((Map) object).size(); + } + return def; + } + + /** + * Performs a bitwise and. + * + * @param left the left operand + * @param right the right operator + * @return left & right + */ + public Object and(final Object left, final Object right) { + final long l = toLong(left); + final long r = toLong(right); + return l & r; + } + + /** + * Performs a bitwise or. + * + * @param left the left operand + * @param right the right operator + * @return left | right + */ + public Object or(final Object left, final Object right) { + final long l = toLong(left); + final long r = toLong(right); + return l | r; + } + + /** + * Performs a bitwise xor. + * + * @param left the left operand + * @param right the right operator + * @return left ^ right + */ + public Object xor(final Object left, final Object right) { + final long l = toLong(left); + final long r = toLong(right); + return l ^ r; + } + + /** + * Performs a bitwise complement. + * + * @param val the operand + * @return ~val + */ + public Object complement(final Object val) { + final long l = toLong(val); + return ~l; + } + + /** + * Performs a logical not. + * + * @param val the operand + * @return !val + */ + public Object not(final Object val) { + return !toBoolean(val); + } + + /** + * Performs a comparison. + * + * @param left the left operand + * @param right the right operator + * @param operator the operator + * @return -1 if left < right; +1 if left > right; 0 if left == right + * @throws ArithmeticException if either left or right is null + */ + protected int compare(final Object left, final Object right, final String operator) { + if (left != null && right != null) { + if (left instanceof BigDecimal || right instanceof BigDecimal) { + final BigDecimal l = toBigDecimal(left); + final BigDecimal r = toBigDecimal(right); + return l.compareTo(r); + } + if (left instanceof BigInteger || right instanceof BigInteger) { + final BigInteger l = toBigInteger(left); + final BigInteger r = toBigInteger(right); + return l.compareTo(r); + } + if (isFloatingPoint(left) || isFloatingPoint(right)) { + final double lhs = toDouble(left); + final double rhs = toDouble(right); + if (Double.isNaN(lhs)) { + if (Double.isNaN(rhs)) { + return 0; + } + return -1; + } + if (Double.isNaN(rhs)) { + // lhs is not NaN + return +1; + } + if (lhs < rhs) { + return -1; + } + if (lhs > rhs) { + return +1; + } + return 0; + } + if (isNumberable(left) || isNumberable(right)) { + final long lhs = toLong(left); + final long rhs = toLong(right); + if (lhs < rhs) { + return -1; + } + if (lhs > rhs) { + return +1; + } + return 0; + } + if (left instanceof String || right instanceof String) { + return toString(left).compareTo(toString(right)); + } + if ("==".equals(operator)) { + return left.equals(right) ? 0 : -1; + } + if (left instanceof Comparable) { + @SuppressWarnings("unchecked") // OK because of instanceof check above + final Comparable comparable = (Comparable) left; + return comparable.compareTo(right); + } + if (right instanceof Comparable) { + @SuppressWarnings("unchecked") // OK because of instanceof check above + final Comparable comparable = (Comparable) right; + return comparable.compareTo(left); + } + } + throw new ArithmeticException("Object comparison:(" + left + " " + operator + " " + right + ")"); + } + + /** + * Test if left and right are equal. + * + * @param left left argument + * @param right right argument + * @return the test result + */ + public boolean equals(final Object left, final Object right) { + if (left == right) { + return true; + } + if (left == null || right == null) { + return false; + } + if (left instanceof Boolean || right instanceof Boolean) { + return toBoolean(left) == toBoolean(right); + } + return compare(left, right, "==") == 0; + } + + /** + * Test if left < right. + * + * @param left left argument + * @param right right argument + * @return the test result + */ + public boolean lessThan(final Object left, final Object right) { + if ((left == right) || (left == null) || (right == null)) { + return false; + } + return compare(left, right, "<") < 0; + + } + + /** + * Test if left > right. + * + * @param left left argument + * @param right right argument + * @return the test result + */ + public boolean greaterThan(final Object left, final Object right) { + if ((left == right) || left == null || right == null) { + return false; + } + return compare(left, right, ">") > 0; + } + + /** + * Test if left <= right. + * + * @param left left argument + * @param right right argument + * @return the test result + */ + public boolean lessThanOrEqual(final Object left, final Object right) { + if (left == right) { + return true; + } + if (left == null || right == null) { + return false; + } + return compare(left, right, "<=") <= 0; + } + + /** + * Test if left >= right. + * + * @param left left argument + * @param right right argument + * @return the test result + */ + public boolean greaterThanOrEqual(final Object left, final Object right) { + if (left == right) { + return true; + } + if (left == null || right == null) { + return false; + } + return compare(left, right, ">=") >= 0; + } + + /** + * Coerce to a primitive boolean. + *

Double.NaN, null, "false" and empty string coerce to false.

+ * + * @param val value to coerce + * @return the boolean value if coercion is possible, true if value was not null. + */ + public boolean toBoolean(final Object val) { + if (val == null) { + controlNullOperand(); + return false; + } + if (val instanceof Boolean) { + return ((Boolean) val); + } + if (val instanceof Number) { + final double number = toDouble(val); + return !Double.isNaN(number) && number != 0.d; + } + if (val instanceof AtomicBoolean) { + return ((AtomicBoolean) val).get(); + } + if (val instanceof String) { + final String strval = val.toString(); + return !strval.isEmpty() && !"false".equals(strval); + } + // non null value is true + return true; + } + + /** + * Coerce to a primitive int. + *

Double.NaN, null and empty string coerce to zero.

+ *

Boolean false is 0, true is 1.

+ * + * @param val value to coerce + * @return the value coerced to int + * @throws ArithmeticException if val is null and mode is strict or if coercion is not possible + */ + public int toInteger(final Object val) { + if (val == null) { + controlNullOperand(); + return 0; + } + if (val instanceof Double) { + final Double dval = (Double) val; + if (Double.isNaN(dval)) { + return 0; + } + return dval.intValue(); + } + if (val instanceof Number) { + return ((Number) val).intValue(); + } + if (val instanceof String) { + if ("".equals(val)) { + return 0; + } + return Integer.parseInt((String) val); + } + if (val instanceof Boolean) { + return ((Boolean) val) ? 1 : 0; + } + if (val instanceof AtomicBoolean) { + return ((AtomicBoolean) val).get() ? 1 : 0; + } + if (val instanceof Character) { + return ((Character) val); + } + + throw new ArithmeticException("Integer coercion: " + + val.getClass().getName() + ":(" + val + ")"); + } + + /** + * Coerce to a primitive long. + *

Double.NaN, null and empty string coerce to zero.

+ *

Boolean false is 0, true is 1.

+ * + * @param val value to coerce + * @return the value coerced to long + * @throws ArithmeticException if value is null and mode is strict or if coercion is not possible + */ + public long toLong(final Object val) { + if (val == null) { + controlNullOperand(); + return 0L; + } + if (val instanceof Double) { + final Double dval = (Double) val; + if (Double.isNaN(dval)) { + return 0L; + } + return dval.longValue(); + } + if (val instanceof Number) { + return ((Number) val).longValue(); + } + if (val instanceof String) { + if ("".equals(val)) { + return 0L; + } + return Long.parseLong((String) val); + } + if (val instanceof Boolean) { + return ((Boolean) val) ? 1L : 0L; + } + if (val instanceof AtomicBoolean) { + return ((AtomicBoolean) val).get() ? 1L : 0L; + } + if (val instanceof Character) { + return ((Character) val); + } + + throw new ArithmeticException("Long coercion: " + + val.getClass().getName() + ":(" + val + ")"); + } + + /** + * Coerce to a BigInteger. + *

Double.NaN, null and empty string coerce to zero.

+ *

Boolean false is 0, true is 1.

+ * + * @param val the object to be coerced. + * @return a BigDecimal + * @throws ArithmeticException if val is null and mode is strict or if coercion is not possible + */ + public BigInteger toBigInteger(final Object val) { + if (val == null) { + controlNullOperand(); + return BigInteger.ZERO; + } + if (val instanceof BigInteger) { + return (BigInteger) val; + } + if (val instanceof Double) { + final Double dval = (Double) val; + if (Double.isNaN(dval)) { + return BigInteger.ZERO; + } + return BigInteger.valueOf(dval.longValue()); + } + if (val instanceof BigDecimal) { + return ((BigDecimal) val).toBigInteger(); + } + if (val instanceof Number) { + return BigInteger.valueOf(((Number) val).longValue()); + } + if (val instanceof Boolean) { + return BigInteger.valueOf(((Boolean) val) ? 1L : 0L); + } + if (val instanceof AtomicBoolean) { + return BigInteger.valueOf(((AtomicBoolean) val).get() ? 1L : 0L); + } + if (val instanceof String) { + final String string = (String) val; + if ("".equals(string)) { + return BigInteger.ZERO; + } + return new BigInteger(string); + } + if (val instanceof Character) { + final int i = ((Character) val); + return BigInteger.valueOf(i); + } + + throw new ArithmeticException("BigInteger coercion: " + + val.getClass().getName() + ":(" + val + ")"); + } + + /** + * Coerce to a BigDecimal. + *

Double.NaN, null and empty string coerce to zero.

+ *

Boolean false is 0, true is 1.

+ * + * @param val the object to be coerced. + * @return a BigDecimal. + * @throws ArithmeticException if val is null and mode is strict or if coercion is not possible + */ + public BigDecimal toBigDecimal(final Object val) { + if (val instanceof BigDecimal) { + return roundBigDecimal((BigDecimal) val); + } + if (val == null) { + controlNullOperand(); + return BigDecimal.ZERO; + } + if (val instanceof Double) { + if (Double.isNaN(((Double) val))) { + return BigDecimal.ZERO; + } + return roundBigDecimal(new BigDecimal(val.toString(), getMathContext())); + } + if (val instanceof Number) { + return roundBigDecimal(new BigDecimal(val.toString(), getMathContext())); + } + if (val instanceof Boolean) { + return BigDecimal.valueOf(((Boolean) val) ? 1. : 0.); + } + if (val instanceof AtomicBoolean) { + return BigDecimal.valueOf(((AtomicBoolean) val).get() ? 1L : 0L); + } + if (val instanceof String) { + final String string = (String) val; + if ("".equals(string)) { + return BigDecimal.ZERO; + } + return roundBigDecimal(new BigDecimal(string, getMathContext())); + } + if (val instanceof Character) { + final int i = ((Character) val); + return new BigDecimal(i); + } + throw new ArithmeticException("BigDecimal coercion: " + + val.getClass().getName() + ":(" + val + ")"); + } + + /** + * Coerce to a primitive double. + *

Double.NaN, null and empty string coerce to zero.

+ *

Boolean false is 0, true is 1.

+ * + * @param val value to coerce. + * @return The double coerced value. + * @throws ArithmeticException if val is null and mode is strict or if coercion is not possible + */ + public double toDouble(final Object val) { + if (val == null) { + controlNullOperand(); + return 0; + } + if (val instanceof Double) { + return ((Double) val); + } + if (val instanceof Number) { + // The below construct is used rather than ((Number)val).doubleValue() to ensure + // equality between comparing new Double( 6.4 / 3 ) and the jexl expression of 6.4 / 3 + return Double.parseDouble(String.valueOf(val)); + } + if (val instanceof Boolean) { + return ((Boolean) val) ? 1. : 0.; + } + if (val instanceof AtomicBoolean) { + return ((AtomicBoolean) val).get() ? 1. : 0.; + } + if (val instanceof String) { + final String string = (String) val; + if ("".equals(string)) { + return Double.NaN; + } + // the spec seems to be iffy about this. Going to give it a wack anyway + return Double.parseDouble(string); + } + if (val instanceof Character) { + final int i = ((Character) val); + return i; + } + throw new ArithmeticException("Double coercion: " + + val.getClass().getName() + ":(" + val + ")"); + } + + /** + * Coerce to a string. + *

Double.NaN coerce to the empty string.

+ * + * @param val value to coerce. + * @return The String coerced value. + * @throws ArithmeticException if val is null and mode is strict or if coercion is not possible + */ + public String toString(final Object val) { + if (val == null) { + controlNullOperand(); + return ""; + } + if (!(val instanceof Double)) { + return val.toString(); + } + final Double dval = (Double) val; + if (Double.isNaN(dval)) { + return ""; + } + return dval.toString(); + } + + /** + * Use or overload and() instead. + * + * @param lhs left hand side + * @param rhs right hand side + * @return lhs & rhs + * @see JexlArithmetic#and + * @deprecated + */ + @Deprecated + public final Object bitwiseAnd(final Object lhs, final Object rhs) { + return and(lhs, rhs); + } + + /** + * Use or overload or() instead. + * + * @param lhs left hand side + * @param rhs right hand side + * @return lhs | rhs + * @see JexlArithmetic#or + * @deprecated + */ + @Deprecated + public final Object bitwiseOr(final Object lhs, final Object rhs) { + return or(lhs, rhs); + } + + /** + * Use or overload xor() instead. + * + * @param lhs left hand side + * @param rhs right hand side + * @return lhs ^ rhs + * @see JexlArithmetic#xor + * @deprecated + */ + @Deprecated + public final Object bitwiseXor(final Object lhs, final Object rhs) { + return xor(lhs, rhs); + } + + /** + * Use or overload not() instead. + * + * @param arg argument + * @return !arg + * @see JexlArithmetic#not + * @deprecated + */ + @Deprecated + public final Object logicalNot(final Object arg) { + return not(arg); + } + + /** + * Use or overload contains() instead. + * + * @param lhs left hand side + * @param rhs right hand side + * @return contains(rhs, lhs) + * @see JexlArithmetic#contains + * @deprecated + */ + @Deprecated + public final Object matches(final Object lhs, final Object rhs) { + return contains(rhs, lhs); + } +} diff --git a/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/JexlBuilder.java b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/JexlBuilder.java new file mode 100644 index 0000000..3ebc1ea --- /dev/null +++ b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/JexlBuilder.java @@ -0,0 +1,551 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package aiyh.utils.tool.org.apache.commons.jexl3; + +import aiyh.utils.tool.org.apache.commons.jexl3.internal.Engine; +import aiyh.utils.tool.org.apache.commons.jexl3.introspection.JexlSandbox; +import aiyh.utils.tool.org.apache.commons.jexl3.introspection.JexlUberspect; +import org.apache.commons.logging.Log; + +import java.util.Map; +import java.nio.charset.Charset; + +/** + * Configure and builds a JexlEngine. + * + *

The setSilent and setStrict methods allow to fine-tune an engine instance behavior + * according to various error control needs. The strict flag tells the engine when and if null as operand is + * considered an error, the silent flag tells the engine what to do with the error + * (log as warning or throw exception).

+ * + *
    + *
  • When "silent" & "not-strict": + *

    0 & null should be indicators of "default" values so that even in an case of error, + * something meaningful can still be inferred; may be convenient for configurations. + *

    + *
  • + *
  • When "silent" & "strict": + *

    One should probably consider using null as an error case - ie, every object + * manipulated by JEXL should be valued; the ternary operator, especially the '?:' form + * can be used to workaround exceptional cases. + * Use case could be configuration with no implicit values or defaults. + *

    + *
  • + *
  • When "not-silent" & "not-strict": + *

    The error control grain is roughly on par with JEXL 1.0

    + *
  • + *
  • When "not-silent" & "strict": + *

    The finest error control grain is obtained; it is the closest to Java code - + * still augmented by "script" capabilities regarding automated conversions and type matching. + *

    + *
  • + *
+ */ +public class JexlBuilder { + + /** The default maximum expression length to hit the expression cache. */ + protected static final int CACHE_THRESHOLD = 64; + + /** The JexlUberspect instance. */ + private JexlUberspect uberspect = null; + + /** The strategy strategy. */ + private JexlUberspect.ResolverStrategy strategy = null; + + /** The sandbox. */ + private JexlSandbox sandbox = null; + + /** The Log to which all JexlEngine messages will be logged. */ + private Log logger = null; + + /** Whether error messages will carry debugging information. */ + private Boolean debug = null; + + /** Whether interrupt throws JexlException.Cancel. */ + private Boolean cancellable = null; + + /** The options. */ + private final JexlOptions options = new JexlOptions(); + + /** Whether getVariables considers all potential equivalent syntactic forms. */ + private int collectMode = 1; + + /** The {@link JexlArithmetic} instance. */ + private JexlArithmetic arithmetic = null; + + /** The cache size. */ + private int cache = -1; + + /** The stack overflow limit. */ + private int stackOverflow = Integer.MAX_VALUE; + + /** The maximum expression length to hit the expression cache. */ + private int cacheThreshold = CACHE_THRESHOLD; + + /** The charset. */ + private Charset charset = Charset.defaultCharset(); + + /** The class loader. */ + private ClassLoader loader = null; + + /** The features. */ + private JexlFeatures features = null; + + /** + * Sets the JexlUberspect instance the engine will use. + * + * @param u the uberspect + * @return this builder + */ + public JexlBuilder uberspect(final JexlUberspect u) { + this.uberspect = u; + return this; + } + + /** @return the uberspect */ + public JexlUberspect uberspect() { + return this.uberspect; + } + + /** + * Sets the JexlUberspect strategy strategy the engine will use. + *

This is ignored if the uberspect has been set. + * + * @param rs the strategy + * @return this builder + */ + public JexlBuilder strategy(final JexlUberspect.ResolverStrategy rs) { + this.strategy = rs; + return this; + } + + /** @return the strategy strategy */ + public JexlUberspect.ResolverStrategy strategy() { + return this.strategy; + } + + /** @return the current set of options */ + public JexlOptions options() { + return options; + } + + /** + * Sets the JexlArithmetic instance the engine will use. + * + * @param a the arithmetic + * @return this builder + */ + public JexlBuilder arithmetic(final JexlArithmetic a) { + this.arithmetic = a; + options.setStrictArithmetic(a.isStrict()); + options.setMathContext(a.getMathContext()); + options.setMathScale(a.getMathScale()); + return this; + } + + /** @return the arithmetic */ + public JexlArithmetic arithmetic() { + return this.arithmetic; + } + + /** + * Sets the sandbox the engine will use. + * + * @param box the sandbox + * @return this builder + */ + public JexlBuilder sandbox(final JexlSandbox box) { + this.sandbox = box; + return this; + } + + /** @return the sandbox */ + public JexlSandbox sandbox() { + return this.sandbox; + } + + /** + * Sets the features the engine will use as a base by default. + *

Note that the script flag will be ignored; the engine will be able to parse expressions and scripts. + *

Note also that these will apply to template expressions and scripts. + *

As a last remark, if lexical or lexicalShade are set as features, this + * method will also set the corresponding options. + * @param f the features + * @return this builder + */ + public JexlBuilder features(final JexlFeatures f) { + this.features = f; + if (features != null) { + if (features.isLexical()) { + options.setLexical(true); + } + if (features.isLexicalShade()) { + options.setLexicalShade(true); + } + } + return this; + } + + /** @return the features */ + public JexlFeatures features() { + return this.features; + } + + /** + * Sets the o.a.c.Log instance to use. + * + * @param log the logger + * @return this builder + */ + public JexlBuilder logger(final Log log) { + this.logger = log; + return this; + } + + /** @return the logger */ + public Log logger() { + return this.logger; + } + + /** + * Sets the class loader to use. + * + * @param l the class loader + * @return this builder + */ + public JexlBuilder loader(final ClassLoader l) { + this.loader = l; + return this; + } + + /** @return the class loader */ + public ClassLoader loader() { + return loader; + } + + /** + * Sets the charset to use. + * + * @param arg the charset + * @return this builder + * @deprecated since 3.1 use {@link #charset(Charset)} instead + */ + @Deprecated + public JexlBuilder loader(final Charset arg) { + return charset(arg); + } + + /** + * Sets the charset to use. + * + * @param arg the charset + * @return this builder + * @since 3.1 + */ + public JexlBuilder charset(final Charset arg) { + this.charset = arg; + return this; + } + + /** @return the charset */ + public Charset charset() { + return charset; + } + + /** + * Sets whether the engine will resolve antish variable names. + * + * @param flag true means antish resolution is enabled, false disables it + * @return this builder + */ + public JexlBuilder antish(final boolean flag) { + options.setAntish(flag); + return this; + } + + /** @return whether antish resolution is enabled */ + public boolean antish() { + return options.isAntish(); + } + + /** + * Sets whether the engine is in lexical mode. + * + * @param flag true means lexical function scope is in effect, false implies non-lexical scoping + * @return this builder + * @since 3.2 + */ + public JexlBuilder lexical(final boolean flag) { + options.setLexical(flag); + return this; + } + + /** @return whether lexical scope is enabled */ + public boolean lexical() { + return options.isLexical(); + } + + /** + * Sets whether the engine is in lexical shading mode. + * + * @param flag true means lexical shading is in effect, false implies no lexical shading + * @return this builder + * @since 3.2 + */ + public JexlBuilder lexicalShade(final boolean flag) { + options.setLexicalShade(flag); + return this; + } + + /** @return whether lexical shading is enabled */ + public boolean lexicalShade() { + return options.isLexicalShade(); + } + + /** + * Sets whether the engine will throw JexlException during evaluation when an error is triggered. + * + * @param flag true means no JexlException will occur, false allows them + * @return this builder + */ + public JexlBuilder silent(final boolean flag) { + options.setSilent(flag); + return this; + } + + /** @return the silent error handling flag */ + public Boolean silent() { + return options.isSilent(); + } + + /** + * Sets whether the engine considers unknown variables, methods, functions and constructors as errors or + * evaluates them as null. + * + * @param flag true means strict error reporting, false allows them to be evaluated as null + * @return this builder + */ + public JexlBuilder strict(final boolean flag) { + options.setStrict(flag); + return this; + } + + /** @return true if strict, false otherwise */ + public Boolean strict() { + return options.isStrict(); + } + + /** + * Sets whether the engine considers dereferencing null in navigation expressions + * as errors or evaluates them as null. + *

x.y() if x is null throws an exception when not safe, + * return null and warns if it is.

+ * + * @param flag true means safe navigation, false throws exception when dereferencing null + * @return this builder + */ + public JexlBuilder safe(final boolean flag) { + options.setSafe(flag); + return this; + } + + /** @return true if safe, false otherwise */ + public Boolean safe() { + return options.isSafe(); + } + + /** + * Sets whether the engine will report debugging information when error occurs. + * + * @param flag true implies debug is on, false implies debug is off. + * @return this builder + */ + public JexlBuilder debug(final boolean flag) { + this.debug = flag; + return this; + } + + /** @return the debugging information flag */ + public Boolean debug() { + return this.debug; + } + + /** + * Sets the engine behavior upon interruption: throw an JexlException.Cancel or terminates the current evaluation + * and return null. + * + * @param flag true implies the engine throws the exception, false makes the engine return null. + * @return this builder + * @since 3.1 + */ + public JexlBuilder cancellable(final boolean flag) { + this.cancellable = flag; + options.setCancellable(flag); + return this; + } + + /** + * @return the cancellable information flag + * @since 3.1 + */ + public Boolean cancellable() { + return this.cancellable; + } + + /** + * Sets whether the engine variable collectors considers all potential forms of variable syntaxes. + * + * @param flag true means var collections considers constant array accesses equivalent to dotted references + * @return this builder + * @since 3.2 + */ + public JexlBuilder collectAll(final boolean flag) { + return collectMode(flag? 1 : 0); + } + + /** + * Experimental collector mode setter. + * + * @param mode 0 or 1 as equivalents to false and true, other values are experimental + * @return this builder + * @since 3.2 + */ + public JexlBuilder collectMode(final int mode) { + this.collectMode = mode; + return this; + } + + /** + * @return true if variable collection follows strict syntactic rule + * @since 3.2 + */ + public boolean collectAll() { + return this.collectMode != 0; + } + + /** + * @return 0 if variable collection follows strict syntactic rule + * @since 3.2 + */ + public int collectMode() { + return this.collectMode; + } + + /** + * Sets the default namespaces map the engine will use. + *

+ * Each entry key is used as a prefix, each entry value used as a bean implementing + * methods; an expression like 'nsx:method(123)' will thus be solved by looking at + * a registered bean named 'nsx' that implements method 'method' in that map. + * If all methods are static, you may use the bean class instead of an instance as value. + *

+ *

+ * If the entry value is a class that has one constructor taking a JexlContext as argument, an instance + * of the namespace will be created at evaluation time. It might be a good idea to derive a JexlContext + * to carry the information used by the namespace to avoid variable space pollution and strongly type + * the constructor with this specialized JexlContext. + *

+ *

+ * The key or prefix allows to retrieve the bean that plays the role of the namespace. + * If the prefix is null, the namespace is the top-level namespace allowing to define + * top-level user defined namespaces ( ie: myfunc(...) ) + *

+ *

Note that the JexlContext is also used to try to solve top-level namespaces. This allows ObjectContext + * derived instances to call methods on the wrapped object.

+ * + * @param ns the map of namespaces + * @return this builder + */ + public JexlBuilder namespaces(final Map ns) { + options.setNamespaces(ns); + return this; + } + + /** + * @return the map of namespaces. + */ + public Map namespaces() { + return options.getNamespaces(); + } + + /** + * Sets the expression cache size the engine will use. + *

The cache will contain at most size expressions of at most cacheThreshold length. + * Note that all JEXL caches are held through SoftReferences and may be garbage-collected.

+ * + * @param size if not strictly positive, no cache is used. + * @return this builder + */ + public JexlBuilder cache(final int size) { + this.cache = size; + return this; + } + + /** + * @return the cache size + */ + public int cache() { + return cache; + } + + /** + * Sets the maximum length for an expression to be cached. + *

Expression whose length is greater than this expression cache length threshold will + * bypass the cache.

+ *

It is expected that a "long" script will be parsed once and its reference kept + * around in user-space structures; the jexl expression cache has no added-value in this case.

+ * + * @param length if not strictly positive, the value is silently replaced by the default value (64). + * @return this builder + */ + public JexlBuilder cacheThreshold(final int length) { + this.cacheThreshold = length > 0? length : CACHE_THRESHOLD; + return this; + } + + /** + * @return the cache threshold + */ + public int cacheThreshold() { + return cacheThreshold; + } + + /** + * Sets the number of script/expression evaluations that can be stacked. + * @param size if not strictly positive, limit is reached when java StackOverflow is thrown. + * @return this builder + */ + public JexlBuilder stackOverflow(final int size) { + this.stackOverflow = size; + return this; + } + + /** + * @return the cache size + */ + public int stackOverflow() { + return stackOverflow; + } + + /** + * @return a {@link JexlEngine} instance + */ + public JexlEngine create() { + return new Engine(this); + } +} diff --git a/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/JexlContext.java b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/JexlContext.java new file mode 100644 index 0000000..e4fb0a7 --- /dev/null +++ b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/JexlContext.java @@ -0,0 +1,203 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package aiyh.utils.tool.org.apache.commons.jexl3; + +import java.util.concurrent.Callable; +import java.util.concurrent.atomic.AtomicBoolean; + +/** + * Manages variables which can be referenced in a JEXL expression. + * + *

JEXL variable names in their simplest form are 'java-like' identifiers. + * JEXL also considers 'ant' inspired variables expressions as valid. + * For instance, the expression 'x.y.z' is an 'antish' variable and will be resolved as a whole by the context, + * i.e. using the key "x.y.z". This proves to be useful to solve "fully qualified class names".

+ * + *

The interpreter variable resolution algorithm will try the different sequences of identifiers till it finds + * one that exists in the context; if "x" is an object known in the context (JexlContext.has("x") returns true), + * "x.y" will not be looked up in the context but will most likely refer to "x.getY()".

+ * + *

Note that JEXL may use '$jexl' and '$ujexl' variables for internal purpose; setting or getting those + * variables may lead to unexpected results unless specified otherwise.

+ * + * @since 1.0 + */ +public interface JexlContext { + + /** + * Gets the value of a variable. + * + * @param name the variable's name + * @return the value + */ + Object get(String name); + + /** + * Sets the value of a variable. + * + * @param name the variable's name + * @param value the variable's value + */ + void set(String name, Object value); + + /** + * Checks whether a variable is defined in this context. + * + *

A variable may be defined with a null value; this method checks whether the + * value is null or if the variable is undefined.

+ * + * @param name the variable's name + * @return true if it exists, false otherwise + */ + boolean has(String name); + + /** + * A marker interface of the JexlContext that declares how to resolve a namespace from its name; + * it is used by the interpreter during evaluation. + * + *

In JEXL, a namespace is an object that serves the purpose of encapsulating functions; for instance, + * the "math" namespace would be the proper object to expose functions like "log(...)", "sinus(...)", etc.

+ *

+ * In expressions like "ns:function(...)", the resolver is called with resolveNamespace("ns"). + * + *

JEXL itself reserves 'jexl' and 'ujexl' as namespaces for internal purpose; resolving those may lead to + * unexpected results.

+ * + * @since 3.0 + */ + interface NamespaceResolver { + + /** + * Resolves a namespace by its name. + * + * @param name the name + * @return the namespace object + */ + Object resolveNamespace(String name); + } + + /** + * A marker interface of the JexlContext, NamespaceFunctor allows creating an instance + * to delegate namespace methods calls to. + * + *

The functor is created once during the lifetime of a script evaluation.

+ */ + interface NamespaceFunctor { + /** + * Creates the functor object that will be used instead of the namespace. + * + * @param context the context + * @return the namespace functor instance + */ + Object createFunctor(JexlContext context); + } + + /** + * A marker interface of the JexlContext that indicates the interpreter to put this context + * in the JexlEngine thread local context instance during evaluation. + * This allows user functions or methods to access the context during a call. + * Note that the usual caveats wrt using thread local apply (caching/leaking references, etc.); in particular, + * keeping a reference to such a context is to be considered with great care and caution. + * It should also be noted that sharing such a context between threads should implicate synchronizing variable + * accessing the implementation class. + * + * @see JexlEngine#setThreadContext(ThreadLocal) + * @see JexlEngine#getThreadContext() + */ + interface ThreadLocal extends JexlContext { + // no specific method + } + + /** + * A marker interface of the JexlContext that processes annotations. + * It is used by the interpreter during evaluation to execute annotation evaluations. + *

If the JexlContext is not an instance of an AnnotationProcessor, encountering an annotation will generate + * an error or a warning depending on the engine strictness. + * + * @since 3.1 + */ + interface AnnotationProcessor { + /** + * Processes an annotation. + *

All annotations are processed through this method; the statement 'call' is to be performed within + * the processAnnotation method. The implementation must perform the call explicitly. + *

The arguments and the statement must not be referenced or cached for longer than the duration + * of the processAnnotation call. + * + * @param name the annotation name + * @param args the arguments of the annotation, evaluated as arguments of this call + * @param statement the statement that was annotated; the processor should invoke this statement 'call' method + * @return the result of statement.call() + * @throws Exception if annotation processing fails + */ + Object processAnnotation(String name, Object[] args, Callable statement) throws Exception; + } + + /** + * A marker interface of the JexlContext that exposes runtime evaluation options. + * + * @since 3.2 + */ + interface OptionsHandle { + /** + * Retrieves the current set of options though the context. + *

+ * This method will be called once at beginning of evaluation and an interpreter private copy + * of the context handled JexlOptions instance used for the duration of the execution; + * the context handled JexlOptions instance being only used as the source of that copy, + * it can safely alter its boolean flags during execution with no effect, avoiding any behavior ambiguity. + * + * @return the engine options + */ + JexlOptions getEngineOptions(); + } + + /** + * A marker interface of the JexlContext that processes pragmas. + * It is called by the engine before interpreter creation; as a marker of + * JexlContext, it is expected to have access and interact with the context + * instance. + * + * @since 3.2 + */ + interface PragmaProcessor { + /** + * Process one pragma. + * + * @param key the key + * @param value the value + */ + void processPragma(String key, Object value); + } + + /** + * A marker interface of the JexlContext sharing a cancelling flag. + *

A script running in a thread can thus be notified through this reference + * of its cancellation through the context. It uses the same interpreter logic + * that reacts to cancellation and is an alternative to using callable() and/or + * interrupting script interpreter threads. + * + * @since 3.2 + */ + interface CancellationHandle { + /** + * @return a cancelable boolean used by the interpreter + */ + AtomicBoolean getCancellation(); + } +} diff --git a/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/JexlEngine.java b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/JexlEngine.java new file mode 100644 index 0000000..468aba6 --- /dev/null +++ b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/JexlEngine.java @@ -0,0 +1,641 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package aiyh.utils.tool.org.apache.commons.jexl3; + +import aiyh.utils.tool.org.apache.commons.jexl3.introspection.JexlUberspect; + +import java.io.*; +import java.math.MathContext; +import java.net.URL; +import java.nio.charset.Charset; + +/** + * Creates and evaluates JexlExpression and JexlScript objects. + * Determines the behavior of expressions and scripts during their evaluation with respect to: + *

    + *
  • Introspection, see {@link JexlUberspect}
  • + *
  • Arithmetic and comparison, see {@link JexlArithmetic}
  • + *
  • Error reporting
  • + *
  • Logging
  • + *
+ * + *

Note that methods that evaluate expressions may throw unchecked exceptions; + * The {@link JexlException} are thrown in "non-silent" mode but since these are + * RuntimeException, user-code should catch them wherever most appropriate.

+ * + * @since 2.0 + */ +public abstract class JexlEngine { + + /** A marker singleton for invocation failures in tryInvoke. */ + public static final Object TRY_FAILED = new FailObject(); + + /** The failure marker class. */ + private static final class FailObject { + /** + * Default ctor. + */ + private FailObject() { + } + + @Override + public String toString() { + return "tryExecute failed"; + } + } + + /** + * The thread local context. + */ + protected static final ThreadLocal CONTEXT = + new ThreadLocal<>(); + + /** + * Accesses the current thread local context. + * + * @return the context or null + */ + public static JexlContext.ThreadLocal getThreadContext() { + return CONTEXT.get(); + } + + /** + * The thread local engine. + */ + protected static final ThreadLocal ENGINE = + new ThreadLocal<>(); + + /** + * Accesses the current thread local engine. + *

Advanced: you should only use this to retrieve the engine within a method/ctor called through the evaluation + * of a script/expression.

+ * + * @return the engine or null + */ + public static JexlEngine getThreadEngine() { + return ENGINE.get(); + } + + /** + * Sets the current thread local context. + *

This should only be used carefully, for instance when re-evaluating a "stored" script that requires a + * given Namespace resolver. Remember to synchronize access if context is shared between threads. + * + * @param tls the thread local context to set + */ + public static void setThreadContext(final JexlContext.ThreadLocal tls) { + CONTEXT.set(tls); + } + + /** + * Script evaluation options. + *

The JexlContext used for evaluation can implement this interface to alter behavior.

+ * + * @deprecated 3.2 + */ + @Deprecated + public interface Options { + + /** + * The charset used for parsing. + * + * @return the charset + */ + Charset getCharset(); + + /** + * Sets whether the engine will throw a {@link JexlException} when an error is encountered during evaluation. + * + * @return true if silent, false otherwise + */ + Boolean isSilent(); + + /** + * Checks whether the engine considers unknown variables, methods, functions and constructors as errors or + * evaluates them as null. + * + * @return true if strict, false otherwise + */ + Boolean isStrict(); + + /** + * Checks whether the arithmetic triggers errors during evaluation when null is used as an operand. + * + * @return true if strict, false otherwise + */ + Boolean isStrictArithmetic(); + + /** + * Whether evaluation will throw JexlException.Cancel (true) or return null (false) when interrupted. + * + * @return true when cancellable, false otherwise + * @since 3.1 + */ + Boolean isCancellable(); + + /** + * The MathContext instance used for +,-,/,*,% operations on big decimals. + * + * @return the math context + */ + MathContext getArithmeticMathContext(); + + /** + * The BigDecimal scale used for comparison and coercion operations. + * + * @return the scale + */ + int getArithmeticMathScale(); + } + + /** Default features. */ + public static final JexlFeatures DEFAULT_FEATURES = new JexlFeatures(); + + /** + * An empty/static/non-mutable JexlContext singleton used instead of null context. + */ + public static final JexlContext EMPTY_CONTEXT = new EmptyContext(); + + /** + * The empty context class, public for instrospection. + */ + public static final class EmptyContext implements JexlContext { + /** + * Default ctor. + */ + private EmptyContext() { + } + + @Override + public Object get(final String name) { + return null; + } + + @Override + public boolean has(final String name) { + return false; + } + + @Override + public void set(final String name, final Object value) { + throw new UnsupportedOperationException("Not supported in void context."); + } + } + + /** + * An empty/static/non-mutable JexlNamespace singleton used instead of null namespace. + */ + public static final JexlContext.NamespaceResolver EMPTY_NS = new EmptyNamespaceResolver(); + + /** + * The empty/static/non-mutable JexlNamespace class, public for instrospection. + */ + public static final class EmptyNamespaceResolver implements JexlContext.NamespaceResolver { + /** + * Default ctor. + */ + private EmptyNamespaceResolver() { + } + + @Override + public Object resolveNamespace(final String name) { + return null; + } + } + + /** The default Jxlt cache size. */ + private static final int JXLT_CACHE_SIZE = 256; + + /** + * Gets the charset used for parsing. + * + * @return the charset + */ + public abstract Charset getCharset(); + + /** + * Gets this engine underlying {@link JexlUberspect}. + * + * @return the uberspect + */ + public abstract JexlUberspect getUberspect(); + + /** + * Gets this engine underlying {@link JexlArithmetic}. + * + * @return the arithmetic + */ + public abstract JexlArithmetic getArithmetic(); + + /** + * Checks whether this engine is in debug mode. + * + * @return true if debug is on, false otherwise + */ + public abstract boolean isDebug(); + + /** + * Checks whether this engine throws JexlException during evaluation. + * + * @return true if silent, false (default) otherwise + */ + public abstract boolean isSilent(); + + /** + * Checks whether this engine considers unknown variables, methods, functions and constructors as errors. + * + * @return true if strict, false otherwise + */ + public abstract boolean isStrict(); + + /** + * Checks whether this engine will throw JexlException.Cancel (true) or return null (false) when interrupted + * during an execution. + * + * @return true if cancellable, false otherwise + */ + public abstract boolean isCancellable(); + + /** + * Sets the class loader used to discover classes in 'new' expressions. + *

This method is not thread safe; it may be called after JexlEngine + * initialization and allow scripts to use new classes definitions.

+ * + * @param loader the class loader to use + */ + public abstract void setClassLoader(ClassLoader loader); + + /** + * Creates a new {@link JxltEngine} instance using this engine. + * + * @return a JEXL Template engine + */ + public JxltEngine createJxltEngine() { + return createJxltEngine(true); + } + + /** + * Creates a new {@link JxltEngine} instance using this engine. + * + * @param noScript whether the JxltEngine only allows Jexl expressions or scripts + * @return a JEXL Template engine + */ + public JxltEngine createJxltEngine(final boolean noScript) { + return createJxltEngine(noScript, JXLT_CACHE_SIZE, '$', '#'); + } + + /** + * Creates a new instance of {@link JxltEngine} using this engine. + * + * @param noScript whether the JxltEngine only allows JEXL expressions or scripts + * @param cacheSize the number of expressions in this cache, default is 256 + * @param immediate the immediate template expression character, default is '$' + * @param deferred the deferred template expression character, default is '#' + * @return a JEXL Template engine + */ + public abstract JxltEngine createJxltEngine(boolean noScript, int cacheSize, char immediate, char deferred); + + /** + * Clears the expression cache. + */ + public abstract void clearCache(); + + /** + * Creates an JexlExpression from a String containing valid JEXL syntax. + * This method parses the expression which must contain either a reference or an expression. + * + * @param info An info structure to carry debugging information if needed + * @param expression A String containing valid JEXL syntax + * @return An {@link JexlExpression} which can be evaluated using a {@link JexlContext} + * @throws JexlException if there is a problem parsing the script + */ + public abstract JexlExpression createExpression(JexlInfo info, String expression); + + /** + * Creates a JexlExpression from a String containing valid JEXL syntax. + * This method parses the expression which must contain either a reference or an expression. + * + * @param expression A String containing valid JEXL syntax + * @return An {@link JexlExpression} which can be evaluated using a {@link JexlContext} + * @throws JexlException if there is a problem parsing the script + */ + public final JexlExpression createExpression(final String expression) { + return createExpression(null, expression); + } + + /** + * Creates a JexlScript from a String containing valid JEXL syntax. + * This method parses the script and validates the syntax. + * + * @param features A set of features that will be enforced during parsing + * @param info An info structure to carry debugging information if needed + * @param source A string containing valid JEXL syntax + * @param names The script parameter names used during parsing; a corresponding array of arguments containing + * values should be used during evaluation + * @return A {@link JexlScript} which can be executed using a {@link JexlContext} + * @throws JexlException if there is a problem parsing the script + */ + public abstract JexlScript createScript(JexlFeatures features, JexlInfo info, String source, String... names); + + /** + * Creates a JexlScript from a String containing valid JEXL syntax. + * This method parses the script and validates the syntax. + * + * @param info An info structure to carry debugging information if needed + * @param source A string containing valid JEXL syntax + * @param names The script parameter names used during parsing; a corresponding array of arguments containing + * values should be used during evaluation + * @return A {@link JexlScript} which can be executed using a {@link JexlContext} + * @throws JexlException if there is a problem parsing the script + */ + public final JexlScript createScript(final JexlInfo info, final String source, final String... names) { + return createScript(null, info, source, names); + } + + /** + * Creates a Script from a String containing valid JEXL syntax. + * This method parses the script and validates the syntax. + * + * @param scriptText A String containing valid JEXL syntax + * @return A {@link JexlScript} which can be executed using a {@link JexlContext} + * @throws JexlException if there is a problem parsing the script. + */ + public final JexlScript createScript(final String scriptText) { + return createScript(null, null, scriptText, (String[]) null); + } + + /** + * Creates a Script from a String containing valid JEXL syntax. + * This method parses the script and validates the syntax. + * + * @param source A String containing valid JEXL syntax + * @param names The script parameter names used during parsing; a corresponding array of arguments containing + * values should be used during evaluation + * @return A {@link JexlScript} which can be executed using a {@link JexlContext} + * @throws JexlException if there is a problem parsing the script + */ + public final JexlScript createScript(final String source, final String... names) { + return createScript(null, null, source, names); + } + + /** + * Creates a Script from a {@link File} containing valid JEXL syntax. + * This method parses the script and validates the syntax. + * + * @param scriptFile A {@link File} containing valid JEXL syntax. Must not be null. Must be a readable file. + * @return A {@link JexlScript} which can be executed with a {@link JexlContext}. + * @throws JexlException if there is a problem reading or parsing the script. + */ + public final JexlScript createScript(final File scriptFile) { + return createScript(null, null, readSource(scriptFile), (String[]) null); + } + + /** + * Creates a Script from a {@link File} containing valid JEXL syntax. + * This method parses the script and validates the syntax. + * + * @param scriptFile A {@link File} containing valid JEXL syntax. Must not be null. Must be a readable file. + * @param names The script parameter names used during parsing; a corresponding array of arguments containing + * values should be used during evaluation. + * @return A {@link JexlScript} which can be executed with a {@link JexlContext}. + * @throws JexlException if there is a problem reading or parsing the script. + */ + public final JexlScript createScript(final File scriptFile, final String... names) { + return createScript(null, null, readSource(scriptFile), names); + } + + /** + * Creates a Script from a {@link File} containing valid JEXL syntax. + * This method parses the script and validates the syntax. + * + * @param info An info structure to carry debugging information if needed + * @param scriptFile A {@link File} containing valid JEXL syntax. Must not be null. Must be a readable file. + * @param names The script parameter names used during parsing; a corresponding array of arguments containing + * values should be used during evaluation. + * @return A {@link JexlScript} which can be executed with a {@link JexlContext}. + * @throws JexlException if there is a problem reading or parsing the script. + */ + public final JexlScript createScript(final JexlInfo info, final File scriptFile, final String... names) { + return createScript(null, info, readSource(scriptFile), names); + } + + /** + * Creates a Script from a {@link URL} containing valid JEXL syntax. + * This method parses the script and validates the syntax. + * + * @param scriptUrl A {@link URL} containing valid JEXL syntax. Must not be null. + * @return A {@link JexlScript} which can be executed with a {@link JexlContext}. + * @throws JexlException if there is a problem reading or parsing the script. + */ + public final JexlScript createScript(final URL scriptUrl) { + return createScript(null, readSource(scriptUrl), (String[]) null); + } + + /** + * Creates a Script from a {@link URL} containing valid JEXL syntax. + * This method parses the script and validates the syntax. + * + * @param scriptUrl A {@link URL} containing valid JEXL syntax. Must not be null. + * @param names The script parameter names used during parsing; a corresponding array of arguments containing + * values should be used during evaluation. + * @return A {@link JexlScript} which can be executed with a {@link JexlContext}. + * @throws JexlException if there is a problem reading or parsing the script. + */ + public final JexlScript createScript(final URL scriptUrl, final String... names) { + return createScript(null, null, readSource(scriptUrl), names); + } + + /** + * Creates a Script from a {@link URL} containing valid JEXL syntax. + * This method parses the script and validates the syntax. + * + * @param info An info structure to carry debugging information if needed + * @param scriptUrl A {@link URL} containing valid JEXL syntax. Must not be null. + * @param names The script parameter names used during parsing; a corresponding array of arguments containing + * values should be used during evaluation. + * @return A {@link JexlScript} which can be executed with a {@link JexlContext}. + * @throws JexlException if there is a problem reading or parsing the script. + */ + public final JexlScript createScript(final JexlInfo info, final URL scriptUrl, final String... names) { + return createScript(null, info, readSource(scriptUrl), names); + } + + /** + * Accesses properties of a bean using an expression. + *

+ * jexl.get(myobject, "foo.bar"); should equate to + * myobject.getFoo().getBar(); (or myobject.getFoo().get("bar")) + *

+ *

+ * If the JEXL engine is silent, errors will be logged through its logger as warning. + *

+ * + * @param bean the bean to get properties from + * @param expr the property expression + * @return the value of the property + * @throws JexlException if there is an error parsing the expression or during evaluation + */ + public abstract Object getProperty(Object bean, String expr); + + /** + * Accesses properties of a bean using an expression. + *

+ * If the JEXL engine is silent, errors will be logged through its logger as warning. + *

+ * + * @param context the evaluation context + * @param bean the bean to get properties from + * @param expr the property expression + * @return the value of the property + * @throws JexlException if there is an error parsing the expression or during evaluation + */ + public abstract Object getProperty(JexlContext context, Object bean, String expr); + + /** + * Assign properties of a bean using an expression. + *

+ * jexl.set(myobject, "foo.bar", 10); should equate to + * myobject.getFoo().setBar(10); (or myobject.getFoo().put("bar", 10) ) + *

+ *

+ * If the JEXL engine is silent, errors will be logged through its logger as warning. + *

+ * + * @param bean the bean to set properties in + * @param expr the property expression + * @param value the value of the property + * @throws JexlException if there is an error parsing the expression or during evaluation + */ + public abstract void setProperty(Object bean, String expr, Object value); + + /** + * Assign properties of a bean using an expression.

If the JEXL engine is silent, errors will be logged through + * its logger as warning.

+ * + * @param context the evaluation context + * @param bean the bean to set properties in + * @param expr the property expression + * @param value the value of the property + * @throws JexlException if there is an error parsing the expression or during evaluation + */ + public abstract void setProperty(JexlContext context, Object bean, String expr, Object value); + + /** + * Invokes an object's method by name and arguments. + * + * @param obj the method's invoker object + * @param meth the method's name + * @param args the method's arguments + * @return the method returned value or null if it failed and engine is silent + * @throws JexlException if method could not be found or failed and engine is not silent + */ + public abstract Object invokeMethod(Object obj, String meth, Object... args); + + /** + * Creates a new instance of an object using the most appropriate constructor based on the arguments. + * + * @param the type of object + * @param clazz the class to instantiate + * @param args the constructor arguments + * @return the created object instance or null on failure when silent + */ + public abstract T newInstance(Class clazz, Object... args); + + /** + * Creates a new instance of an object using the most appropriate constructor based on the arguments. + * + * @param clazz the name of the class to instantiate resolved through this engine's class loader + * @param args the constructor arguments + * @return the created object instance or null on failure when silent + */ + public abstract Object newInstance(String clazz, Object... args); + + /** + * Creates a JexlInfo instance. + * + * @param fn url/file/template/script user given name + * @param l line number + * @param c column number + * @return a JexlInfo instance + */ + public JexlInfo createInfo(final String fn, final int l, final int c) { + return new JexlInfo(fn, l, c); + } + + /** + * Create an information structure for dynamic set/get/invoke/new. + *

This gathers the class, method and line number of the first calling method + * outside of o.a.c.jexl3.

+ * + * @return a JexlInfo instance + */ + public JexlInfo createInfo() { + return new JexlInfo(); + } + + /** + * Creates a string from a reader. + * + * @param reader to be read. + * @return the contents of the reader as a String. + * @throws IOException on any error reading the reader. + */ + protected static String toString(final BufferedReader reader) throws IOException { + final StringBuilder buffer = new StringBuilder(); + String line; + while ((line = reader.readLine()) != null) { + buffer.append(line).append('\n'); + } + return buffer.toString(); + } + + /** + * Reads a JEXL source from a File. + * + * @param file the script file + * @return the source + */ + protected String readSource(final File file) { + if (file == null) { + throw new NullPointerException("source file is null"); + } + try (BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(file), + getCharset()))) { + return toString(reader); + } catch (final IOException xio) { + throw new JexlException(createInfo(file.toString(), 1, 1), "could not read source File", xio); + } + } + + /** + * Reads a JEXL source from an URL. + * + * @param url the script url + * @return the source + */ + protected String readSource(final URL url) { + if (url == null) { + throw new NullPointerException("source URL is null"); + } + try (BufferedReader reader = new BufferedReader(new InputStreamReader(url.openStream(), getCharset()))) { + return toString(reader); + } catch (final IOException xio) { + throw new JexlException(createInfo(url.toString(), 1, 1), "could not read source URL", xio); + } + } +} diff --git a/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/JexlException.java b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/JexlException.java new file mode 100644 index 0000000..c1d43ca --- /dev/null +++ b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/JexlException.java @@ -0,0 +1,1119 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package aiyh.utils.tool.org.apache.commons.jexl3; + +import aiyh.utils.tool.org.apache.commons.jexl3.parser.JexlNode; +import aiyh.utils.tool.org.apache.commons.jexl3.internal.Debugger; +import aiyh.utils.tool.org.apache.commons.jexl3.parser.JavaccError; +import aiyh.utils.tool.org.apache.commons.jexl3.parser.ParseException; +import aiyh.utils.tool.org.apache.commons.jexl3.parser.TokenMgrException; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.StringReader; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.UndeclaredThrowableException; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +/** + * Wraps any error that might occur during interpretation of a script or expression. + * + * @since 2.0 + */ +public class JexlException extends RuntimeException { + private static final long serialVersionUID = 20210606123900L; + + /** The point of origin for this exception. */ + private final transient JexlNode mark; + + /** The debug info. */ + private final transient JexlInfo info; + + /** Maximum number of characters around exception location. */ + private static final int MAX_EXCHARLOC = 42; + + + /** + * Creates a new JexlException. + * + * @param node the node causing the error + * @param msg the error message + */ + public JexlException(final JexlNode node, final String msg) { + this(node, msg, null); + } + + /** + * Creates a new JexlException. + * + * @param node the node causing the error + * @param msg the error message + * @param cause the exception causing the error + */ + public JexlException(final JexlNode node, final String msg, final Throwable cause) { + this(node, msg != null ? msg : "", unwrap(cause), true); + } + + /** + * Creates a new JexlException. + * + * @param node the node causing the error + * @param msg the error message + * @param cause the exception causing the error + * @param trace whether this exception has a stacktrace and can not be suppressed + */ + protected JexlException(final JexlNode node, final String msg, final Throwable cause, boolean trace) { + super(msg != null ? msg : "", unwrap(cause), !trace, trace); + if (node != null) { + mark = node; + info = node.jexlInfo(); + } else { + mark = null; + info = null; + } + } + + /** + * Creates a new JexlException. + * + * @param jinfo the debugging information associated + * @param msg the error message + * @param cause the exception causing the error + */ + public JexlException(final JexlInfo jinfo, final String msg, final Throwable cause) { + super(msg != null ? msg : "", unwrap(cause)); + mark = null; + info = jinfo; + } + + /** + * Gets the specific information for this exception. + * + * @return the information + */ + public JexlInfo getInfo() { + return detailedInfo(mark, info); + } + + /** + * Creates a string builder pre-filled with common error information (if possible). + * + * @param node the node + * @return a string builder + */ + private static StringBuilder errorAt(final JexlNode node) { + final JexlInfo info = node != null ? detailedInfo(node, node.jexlInfo()) : null; + final StringBuilder msg = new StringBuilder(); + if (info != null) { + msg.append(info); + } else { + msg.append("?:"); + } + msg.append(' '); + return msg; + } + + /** + * Gets the most specific information attached to a node. + * + * @param node the node + * @param info the information + * @return the information or null + * @deprecated 3.2 + */ + @Deprecated + public static JexlInfo getInfo(final JexlNode node, final JexlInfo info) { + return detailedInfo(node, info); + } + + /** + * Gets the most specific information attached to a node. + * + * @param node the node + * @param info the information + * @return the information or null + */ + private static JexlInfo detailedInfo(final JexlNode node, final JexlInfo info) { + if (info != null && node != null) { + final Debugger dbg = new Debugger(); + if (dbg.debug(node)) { + return new JexlInfo(info) { + @Override + public Detail getDetail() { + return dbg; + } + }; + } + } + return info; + } + + /** + * Cleans a JexlException from any org.apache.commons.jexl3.internal stack trace element. + * + * @return this exception + */ + public JexlException clean() { + return clean(this); + } + + /** + * Cleans a Throwable from any org.apache.commons.jexl3.internal stack trace element. + * + * @param the throwable type + * @param xthrow the thowable + * @return the throwable + */ + private static X clean(final X xthrow) { + if (xthrow != null) { + final List stackJexl = new ArrayList<>(); + for (final StackTraceElement se : xthrow.getStackTrace()) { + final String className = se.getClassName(); + if (!className.startsWith("org.apache.commons.jexl3.internal") + && !className.startsWith("org.apache.commons.jexl3.parser")) { + stackJexl.add(se); + } + } + xthrow.setStackTrace(stackJexl.toArray(new StackTraceElement[stackJexl.size()])); + } + return xthrow; + } + + /** + * Unwraps the cause of a throwable due to reflection. + * + * @param xthrow the throwable + * @return the cause + */ + private static Throwable unwrap(final Throwable xthrow) { + if (xthrow instanceof TryFailed + || xthrow instanceof InvocationTargetException + || xthrow instanceof UndeclaredThrowableException) { + return xthrow.getCause(); + } + return xthrow; + } + + /** + * Merge the node info and the cause info to obtain best possible location. + * + * @param info the node + * @param cause the cause + * @return the info to use + */ + private static JexlInfo merge(final JexlInfo info, final JavaccError cause) { + if (cause == null || cause.getLine() < 0) { + return info; + } + if (info == null) { + return new JexlInfo("", cause.getLine(), cause.getColumn()); + } + return new JexlInfo(info.getName(), cause.getLine(), cause.getColumn()); + } + + /** + * Accesses detailed message. + * + * @return the message + */ + protected String detailedMessage() { + Class clazz = getClass(); + String name = clazz == JexlException.class ? "JEXL" : clazz.getSimpleName().toLowerCase(); + return name + " error : " + getDetail(); + } + + /** + * @return this exception specific detail + * @since 3.2 + */ + public final String getDetail() { + return super.getMessage(); + } + + /** + * Formats an error message from the parser. + * + * @param prefix the prefix to the message + * @param expr the expression in error + * @return the formatted message + */ + protected String parserError(final String prefix, final String expr) { + final int length = expr.length(); + if (length < MAX_EXCHARLOC) { + return prefix + " error in '" + expr + "'"; + } + final int me = MAX_EXCHARLOC / 2; + int begin = info.getColumn() - me; + if (begin < 0 || length < me) { + begin = 0; + } else if (begin > length) { + begin = me; + } + int end = begin + MAX_EXCHARLOC; + if (end > length) { + end = length; + } + return prefix + " error near '... " + + expr.substring(begin, end) + " ...'"; + } + + /** + * Pleasing checkstyle. + * + * @return the info + */ + protected JexlInfo info() { + return info; + } + + /** + * Thrown when tokenization fails. + * + * @since 3.0 + */ + public static class Tokenization extends JexlException { + private static final long serialVersionUID = 20210606123901L; + + /** + * Creates a new Tokenization exception instance. + * + * @param info the location info + * @param cause the javacc cause + */ + public Tokenization(final JexlInfo info, final TokenMgrException cause) { + super(merge(info, cause), Objects.requireNonNull(cause).getAfter(), null); + } + + @Override + protected String detailedMessage() { + return parserError("tokenization", getDetail()); + } + } + + /** + * Thrown when parsing fails. + * + * @since 3.0 + */ + public static class Parsing extends JexlException { + private static final long serialVersionUID = 20210606123902L; + + /** + * Creates a new Parsing exception instance. + * + * @param info the location information + * @param cause the javacc cause + */ + public Parsing(final JexlInfo info, final ParseException cause) { + super(merge(info, cause), Objects.requireNonNull(cause).getAfter(), null); + } + + /** + * Creates a new Parsing exception instance. + * + * @param info the location information + * @param msg the message + */ + public Parsing(final JexlInfo info, final String msg) { + super(info, msg, null); + } + + @Override + protected String detailedMessage() { + return parserError("parsing", getDetail()); + } + } + + /** + * Thrown when parsing fails due to an ambiguous statement. + * + * @since 3.0 + */ + public static class Ambiguous extends Parsing { + private static final long serialVersionUID = 20210606123903L; + /** The mark at which ambiguity might stop and recover. */ + private final transient JexlInfo recover; + + /** + * Creates a new Ambiguous statement exception instance. + * + * @param info the location information + * @param expr the source expression line + */ + public Ambiguous(final JexlInfo info, final String expr) { + this(info, null, expr); + } + + /** + * Creates a new Ambiguous statement exception instance. + * + * @param begin the start location information + * @param end the end location information + * @param expr the source expression line + */ + public Ambiguous(final JexlInfo begin, final JexlInfo end, final String expr) { + super(begin, expr); + recover = end; + } + + @Override + protected String detailedMessage() { + return parserError("ambiguous statement", getDetail()); + } + + /** + * Tries to remove this ambiguity in the source. + * + * @param src the source that triggered this exception + * @return the source with the ambiguous statement removed + * or null if no recovery was possible + */ + public String tryCleanSource(final String src) { + final JexlInfo ji = info(); + return ji == null || recover == null + ? src + : sliceSource(src, ji.getLine(), ji.getColumn(), recover.getLine(), recover.getColumn()); + } + } + + /** + * Removes a slice from a source. + * + * @param src the source + * @param froml the begin line + * @param fromc the begin column + * @param tol the to line + * @param toc the to column + * @return the source with the (begin) to (to) zone removed + */ + public static String sliceSource(final String src, final int froml, final int fromc, final int tol, final int toc) { + final BufferedReader reader = new BufferedReader(new StringReader(src)); + final StringBuilder buffer = new StringBuilder(); + String line; + int cl = 1; + try { + while ((line = reader.readLine()) != null) { + if (cl < froml || cl > tol) { + buffer.append(line).append('\n'); + } else { + if (cl == froml) { + buffer.append(line, 0, fromc - 1); + } + if (cl == tol) { + buffer.append(line.substring(toc + 1)); + } + } // else ignore line + cl += 1; + } + } catch (final IOException xignore) { + // damn the checked exceptions :-) + } + return buffer.toString(); + } + + /** + * Thrown when reaching stack-overflow. + * + * @since 3.2 + */ + public static class StackOverflow extends JexlException { + private static final long serialVersionUID = 20210606123904L; + + /** + * Creates a new stack overflow exception instance. + * + * @param info the location information + * @param name the unknown method + * @param cause the exception causing the error + */ + public StackOverflow(final JexlInfo info, final String name, final Throwable cause) { + super(info, name, cause); + } + + @Override + protected String detailedMessage() { + return "stack overflow " + getDetail(); + } + } + + /** + * Thrown when parsing fails due to an invalid assigment. + * + * @since 3.0 + */ + public static class Assignment extends Parsing { + private static final long serialVersionUID = 20210606123905L; + + /** + * Creates a new Assignment statement exception instance. + * + * @param info the location information + * @param expr the source expression line + */ + public Assignment(final JexlInfo info, final String expr) { + super(info, expr); + } + + @Override + protected String detailedMessage() { + return parserError("assignment", getDetail()); + } + } + + /** + * Thrown when parsing fails due to a disallowed feature. + * + * @since 3.2 + */ + public static class Feature extends Parsing { + private static final long serialVersionUID = 20210606123906L; + /** The feature code. */ + private final int code; + + /** + * Creates a new Ambiguous statement exception instance. + * + * @param info the location information + * @param feature the feature code + * @param expr the source expression line + */ + public Feature(final JexlInfo info, final int feature, final String expr) { + super(info, expr); + this.code = feature; + } + + @Override + protected String detailedMessage() { + return parserError(JexlFeatures.stringify(code), getDetail()); + } + } + + /** Used 3 times. */ + private static final String VARQUOTE = "variable '"; + + /** + * The various type of variable issues. + */ + public enum VariableIssue { + /** The variable is undefined. */ + UNDEFINED, + /** The variable is already declared. */ + REDEFINED, + /** The variable has a null value. */ + NULLVALUE; + + /** + * Stringifies the variable issue. + * + * @param var the variable name + * @return the issue message + */ + public String message(final String var) { + switch (this) { + case NULLVALUE: + return VARQUOTE + var + "' is null"; + case REDEFINED: + return VARQUOTE + var + "' is already defined"; + case UNDEFINED: + default: + return VARQUOTE + var + "' is undefined"; + } + } + } + + /** + * Thrown when a variable is unknown. + * + * @since 3.0 + */ + public static class Variable extends JexlException { + private static final long serialVersionUID = 20210606123907L; + /** + * Undefined variable flag. + */ + private final VariableIssue issue; + + /** + * Creates a new Variable exception instance. + * + * @param node the offending ASTnode + * @param var the unknown variable + * @param vi the variable issue + */ + public Variable(final JexlNode node, final String var, final VariableIssue vi) { + super(node, var, null); + issue = vi; + } + + /** + * Creates a new Variable exception instance. + * + * @param node the offending ASTnode + * @param var the unknown variable + * @param undef whether the variable is undefined or evaluated as null + */ + public Variable(final JexlNode node, final String var, final boolean undef) { + this(node, var, undef ? VariableIssue.UNDEFINED : VariableIssue.NULLVALUE); + } + + /** + * Whether the variable causing an error is undefined or evaluated as null. + * + * @return true if undefined, false otherwise + */ + public boolean isUndefined() { + return issue == VariableIssue.UNDEFINED; + } + + /** + * @return the variable name + */ + public String getVariable() { + return getDetail(); + } + + @Override + protected String detailedMessage() { + return issue.message(getVariable()); + } + } + + /** + * Generates a message for a variable error. + * + * @param node the node where the error occurred + * @param variable the variable + * @param undef whether the variable is null or undefined + * @return the error message + * @deprecated 3.2 + */ + @Deprecated + public static String variableError(final JexlNode node, final String variable, final boolean undef) { + return variableError(node, variable, undef ? VariableIssue.UNDEFINED : VariableIssue.NULLVALUE); + } + + /** + * Generates a message for a variable error. + * + * @param node the node where the error occurred + * @param variable the variable + * @param issue the variable kind of issue + * @return the error message + */ + public static String variableError(final JexlNode node, final String variable, final VariableIssue issue) { + final StringBuilder msg = errorAt(node); + msg.append(issue.message(variable)); + return msg.toString(); + } + + /** + * Thrown when a property is unknown. + * + * @since 3.0 + */ + public static class Property extends JexlException { + private static final long serialVersionUID = 20210606123908L; + /** + * Undefined variable flag. + */ + private final boolean undefined; + + /** + * Creates a new Property exception instance. + * + * @param node the offending ASTnode + * @param pty the unknown property + * @deprecated 3.2 + */ + @Deprecated + public Property(final JexlNode node, final String pty) { + this(node, pty, true, null); + } + + /** + * Creates a new Property exception instance. + * + * @param node the offending ASTnode + * @param pty the unknown property + * @param cause the exception causing the error + * @deprecated 3.2 + */ + @Deprecated + public Property(final JexlNode node, final String pty, final Throwable cause) { + this(node, pty, true, cause); + } + + /** + * Creates a new Property exception instance. + * + * @param node the offending ASTnode + * @param pty the unknown property + * @param undef whether the variable is null or undefined + * @param cause the exception causing the error + */ + public Property(final JexlNode node, final String pty, final boolean undef, final Throwable cause) { + super(node, pty, cause); + undefined = undef; + } + + /** + * Whether the variable causing an error is undefined or evaluated as null. + * + * @return true if undefined, false otherwise + */ + public boolean isUndefined() { + return undefined; + } + + /** + * @return the property name + */ + public String getProperty() { + return getDetail(); + } + + @Override + protected String detailedMessage() { + return (undefined ? "undefined" : "null value") + " property '" + getProperty() + "'"; + } + } + + /** + * Generates a message for an unsolvable property error. + * + * @param node the node where the error occurred + * @param pty the property + * @param undef whether the property is null or undefined + * @return the error message + */ + public static String propertyError(final JexlNode node, final String pty, final boolean undef) { + final StringBuilder msg = errorAt(node); + if (undef) { + msg.append("unsolvable"); + } else { + msg.append("null value"); + } + msg.append(" property '"); + msg.append(pty); + msg.append('\''); + return msg.toString(); + } + + /** + * Generates a message for an unsolvable property error. + * + * @param node the node where the error occurred + * @param var the variable + * @return the error message + * @deprecated 3.2 + */ + @Deprecated + public static String propertyError(final JexlNode node, final String var) { + return propertyError(node, var, true); + } + + /** + * Thrown when a method or ctor is unknown, ambiguous or inaccessible. + * + * @since 3.0 + */ + public static class Method extends JexlException { + private static final long serialVersionUID = 20210606123909L; + + /** + * Creates a new Method exception instance. + * + * @param node the offending ASTnode + * @param name the method name + * @deprecated as of 3.2, use call with method arguments + */ + @Deprecated + public Method(final JexlNode node, final String name) { + this(node, name, null); + } + + /** + * Creates a new Method exception instance. + * + * @param info the location information + * @param name the unknown method + * @param cause the exception causing the error + * @deprecated as of 3.2, use call with method arguments + */ + @Deprecated + public Method(final JexlInfo info, final String name, final Throwable cause) { + this(info, name, null, cause); + } + + /** + * Creates a new Method exception instance. + * + * @param node the offending ASTnode + * @param name the method name + * @param args the method arguments + * @since 3.2 + */ + public Method(final JexlNode node, final String name, final Object[] args) { + super(node, methodSignature(name, args)); + } + + /** + * Creates a new Method exception instance. + * + * @param info the location information + * @param name the method name + * @param args the method arguments + * @since 3.2 + */ + public Method(final JexlInfo info, final String name, final Object[] args) { + this(info, name, args, null); + } + + + /** + * Creates a new Method exception instance. + * + * @param info the location information + * @param name the method name + * @param cause the exception causing the error + * @param args the method arguments + * @since 3.2 + */ + public Method(final JexlInfo info, final String name, final Object[] args, final Throwable cause) { + super(info, methodSignature(name, args), cause); + } + + /** + * @return the method name + */ + public String getMethod() { + final String signature = getMethodSignature(); + final int lparen = signature.indexOf('('); + return lparen > 0 ? signature.substring(0, lparen) : signature; + } + + /** + * @return the method signature + * @since 3.2 + */ + public String getMethodSignature() { + return getDetail(); + } + + @Override + protected String detailedMessage() { + return "unsolvable function/method '" + getMethodSignature() + "'"; + } + } + + /** + * Creates a signed-name for a given method name and arguments. + * + * @param name the method name + * @param args the method arguments + * @return a suitable signed name + */ + private static String methodSignature(final String name, final Object[] args) { + if (args != null && args.length > 0) { + final StringBuilder strb = new StringBuilder(name); + strb.append('('); + for (int a = 0; a < args.length; ++a) { + if (a > 0) { + strb.append(", "); + } + final Class clazz = args[a] == null ? Object.class : args[a].getClass(); + strb.append(clazz.getSimpleName()); + } + strb.append(')'); + return strb.toString(); + } + return name; + } + + /** + * Generates a message for a unsolvable method error. + * + * @param node the node where the error occurred + * @param method the method name + * @return the error message + * @deprecated 3.2 + */ + @Deprecated + public static String methodError(final JexlNode node, final String method) { + return methodError(node, method, null); + } + + /** + * Generates a message for a unsolvable method error. + * + * @param node the node where the error occurred + * @param method the method name + * @param args the method arguments + * @return the error message + */ + public static String methodError(final JexlNode node, final String method, final Object[] args) { + final StringBuilder msg = errorAt(node); + msg.append("unsolvable function/method '"); + msg.append(methodSignature(method, args)); + msg.append('\''); + return msg.toString(); + } + + /** + * Thrown when an operator fails. + * + * @since 3.0 + */ + public static class Operator extends JexlException { + private static final long serialVersionUID = 20210606124100L; + + /** + * Creates a new Operator exception instance. + * + * @param node the location information + * @param symbol the operator name + * @param cause the exception causing the error + */ + public Operator(final JexlNode node, final String symbol, final Throwable cause) { + super(node, symbol, cause); + } + + /** + * @return the method name + */ + public String getSymbol() { + return getDetail(); + } + + @Override + protected String detailedMessage() { + return "error calling operator '" + getSymbol() + "'"; + } + } + + /** + * Generates a message for an operator error. + * + * @param node the node where the error occurred + * @param symbol the operator name + * @return the error message + */ + public static String operatorError(final JexlNode node, final String symbol) { + final StringBuilder msg = errorAt(node); + msg.append("error calling operator '"); + msg.append(symbol); + msg.append('\''); + return msg.toString(); + } + + /** + * Thrown when an annotation handler throws an exception. + * + * @since 3.1 + */ + public static class Annotation extends JexlException { + private static final long serialVersionUID = 20210606124101L; + + /** + * Creates a new Annotation exception instance. + * + * @param node the annotated statement node + * @param name the annotation name + * @param cause the exception causing the error + */ + public Annotation(final JexlNode node, final String name, final Throwable cause) { + super(node, name, cause); + } + + /** + * @return the annotation name + */ + public String getAnnotation() { + return getDetail(); + } + + @Override + protected String detailedMessage() { + return "error processing annotation '" + getAnnotation() + "'"; + } + } + + /** + * Generates a message for an annotation error. + * + * @param node the node where the error occurred + * @param annotation the annotation name + * @return the error message + * @since 3.1 + */ + public static String annotationError(final JexlNode node, final String annotation) { + final StringBuilder msg = errorAt(node); + msg.append("error processing annotation '"); + msg.append(annotation); + msg.append('\''); + return msg.toString(); + } + + /** + * Thrown to return a value. + * + * @since 3.0 + */ + public static class Return extends JexlException { + private static final long serialVersionUID = 20210606124102L; + + /** The returned value. */ + private final transient Object result; + + /** + * Creates a new instance of Return. + * + * @param node the return node + * @param msg the message + * @param value the returned value + */ + public Return(final JexlNode node, final String msg, final Object value) { + super(node, msg, null, false); + this.result = value; + } + + /** + * @return the returned value + */ + public Object getValue() { + return result; + } + } + + /** + * Thrown to cancel a script execution. + * + * @since 3.0 + */ + public static class Cancel extends JexlException { + /** + * Creates a new instance of Cancel. + * + * @param node the node where the interruption was detected + */ + public Cancel(final JexlNode node) { + super(node, "execution cancelled", null); + } + } + + /** + * Thrown to break a loop. + * + * @since 3.0 + */ + public static class Break extends JexlException { + private static final long serialVersionUID = 20210606124103L; + + /** + * Creates a new instance of Break. + * + * @param node the break + */ + public Break(final JexlNode node) { + super(node, "break loop", null, false); + } + } + + /** + * Thrown to continue a loop. + * + * @since 3.0 + */ + public static class Continue extends JexlException { + private static final long serialVersionUID = 20210606124104L; + + /** + * Creates a new instance of Continue. + * + * @param node the continue + */ + public Continue(final JexlNode node) { + super(node, "continue loop", null, false); + } + } + + /** + * Thrown when method/ctor invocation fails. + *

These wrap InvocationTargetException as runtime exception + * allowing to go through without signature modifications. + * + * @since 3.2 + */ + public static class TryFailed extends JexlException { + private static final long serialVersionUID = 20210606124105L; + + /** + * Creates a new instance. + * + * @param xany the original invocation target exception + */ + private TryFailed(final InvocationTargetException xany) { + super((JexlInfo) null, "tryFailed", xany.getCause()); + } + } + + /** + * Wrap an invocation exception. + *

Return the cause if it is already a JexlException. + * + * @param xinvoke the invocation exception + * @return a JexlException + */ + public static JexlException tryFailed(final InvocationTargetException xinvoke) { + final Throwable cause = xinvoke.getCause(); + return cause instanceof JexlException + ? (JexlException) cause + : new TryFailed(xinvoke); // fail + } + + + /** + * Detailed info message about this error. + * Format is "debug![begin,end]: string \n msg" where: + *

+ * - debug is the debugging information if it exists (@link JexlEngine.setDebug) + * - begin, end are character offsets in the string for the precise location of the error + * - string is the string representation of the offending expression + * - msg is the actual explanation message for this error + * + * @return this error as a string + */ + @Override + public String getMessage() { + final StringBuilder msg = new StringBuilder(); + if (info != null) { + msg.append(info); + } else { + msg.append("?:"); + } + msg.append(' '); + msg.append(detailedMessage()); + final Throwable cause = getCause(); + if (cause instanceof JexlArithmetic.NullOperand) { + msg.append(" caused by null operand"); + } + return msg.toString(); + } +} diff --git a/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/JexlExpression.java b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/JexlExpression.java new file mode 100644 index 0000000..1839d7b --- /dev/null +++ b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/JexlExpression.java @@ -0,0 +1,74 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package aiyh.utils.tool.org.apache.commons.jexl3; + +import java.util.concurrent.Callable; + +/** + * Represents a single JEXL expression. + *

+ * This simple interface provides access to the underlying textual expression through + * {@link JexlExpression#getSourceText()}. + *

+ * + *

+ * An expression is different than a script - it is simply a reference to + * a single expression, not to multiple statements. + * This implies 'if','for','while','var' and blocks '{'... '}'are not allowed in expressions. + *

+ *

Do not create classes that implement this interface; delegate or compose instead.

+ * + * @since 1.0 + */ +public interface JexlExpression { + /** + * Evaluates the expression with the variables contained in the + * supplied {@link JexlContext}. + * + * @param context A JexlContext containing variables. + * @return The result of this evaluation + * @throws JexlException on any error + */ + Object evaluate(JexlContext context); + + /** + * Returns the source text of this expression. + * + * @return the source text + */ + String getSourceText(); + + /** + * Recreates the source text of this expression from the internal syntactic tree. + * + * @return the source text + */ + String getParsedText(); + + /** + * Creates a Callable from this expression. + * + *

This allows to submit it to an executor pool and provides support for asynchronous calls.

+ *

The interpreter will handle interruption/cancellation gracefully if needed.

+ * + * @param context the context + * @return the callable + * @since 3.1 + */ + Callable callable(JexlContext context); +} diff --git a/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/JexlFeatures.java b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/JexlFeatures.java new file mode 100644 index 0000000..ba7ec49 --- /dev/null +++ b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/JexlFeatures.java @@ -0,0 +1,540 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package aiyh.utils.tool.org.apache.commons.jexl3; + +import java.util.Collection; +import java.util.Collections; +import java.util.Set; +import java.util.TreeSet; +import java.util.Objects; +import java.util.function.Predicate; + +/** + * A set of language feature options. + * These control syntactical constructs that will throw JexlException.Feature exceptions (a + * subclass of JexlException.Parsing) when disabled. + *
    + *
  • Registers: register syntax (#number), used internally for {g,s}etProperty + *
  • Reserved Names: a set of reserved variable names that can not be used as local variable (or parameter) names + *
  • Global Side Effect : assigning/modifying values on global variables (=, += , -=, ...) + *
  • Lexical: lexical scope, prevents redefining local variables + *
  • Lexical Shade: local variables shade globals, prevents confusing a global variable with a local one + *
  • Side Effect : assigning/modifying values on any variables or left-value + *
  • Constant Array Reference: ensures array references only use constants;they should be statically solvable. + *
  • New Instance: creating an instance using new(...) + *
  • Loops: loop constructs (while(true), for(...)) + *
  • Lambda: function definitions (()->{...}, function(...) ). + *
  • Method calls: calling methods (obj.method(...) or obj['method'](...)); when disabled, leaves function calls + * - including namespace prefixes - available + *
  • Structured literals: arrays, lists, maps, sets, ranges + *
  • Pragmas: #pragma x y + *
  • Annotation: @annotation statement; + *
+ * @since 3.2 + */ +public final class JexlFeatures { + /** The feature flags. */ + private long flags; + /** The set of reserved names, aka global variables that can not be masked by local variables or parameters. */ + private Set reservedNames; + /** The namespace names. */ + private Predicate nameSpaces; + /** The false predicate. */ + public static final Predicate TEST_STR_FALSE = (s)->false; + /** Te feature names (for toString()). */ + private static final String[] F_NAMES = { + "register", "reserved variable", "local variable", "assign/modify", + "global assign/modify", "array reference", "create instance", "loop", "function", + "method call", "set/map/array literal", "pragma", "annotation", "script", "lexical", "lexicalShade" + }; + /** Registers feature ordinal. */ + private static final int REGISTER = 0; + /** Reserved name feature ordinal. */ + public static final int RESERVED = 1; + /** Locals feature ordinal. */ + public static final int LOCAL_VAR = 2; + /** Side-effects feature ordinal. */ + public static final int SIDE_EFFECT = 3; + /** Global side-effects feature ordinal. */ + public static final int SIDE_EFFECT_GLOBAL = 4; + /** Array get is allowed on expr. */ + public static final int ARRAY_REF_EXPR = 5; + /** New-instance feature ordinal. */ + public static final int NEW_INSTANCE = 6; + /** Loops feature ordinal. */ + public static final int LOOP = 7; + /** Lambda feature ordinal. */ + public static final int LAMBDA = 8; + /** Lambda feature ordinal. */ + public static final int METHOD_CALL = 9; + /** Structured literal feature ordinal. */ + public static final int STRUCTURED_LITERAL = 10; + /** Pragma feature ordinal. */ + public static final int PRAGMA = 11; + /** Annotation feature ordinal. */ + public static final int ANNOTATION = 12; + /** Script feature ordinal. */ + public static final int SCRIPT = 13; + /** Lexical feature ordinal. */ + public static final int LEXICAL = 14; + /** Lexical shade feature ordinal. */ + public static final int LEXICAL_SHADE = 15; + + /** + * Creates an all-features-enabled instance. + */ + public JexlFeatures() { + flags = (1L << LOCAL_VAR) + | (1L << SIDE_EFFECT) + | (1L << SIDE_EFFECT_GLOBAL) + | (1L << ARRAY_REF_EXPR) + | (1L << NEW_INSTANCE) + | (1L << LOOP) + | (1L << LAMBDA) + | (1L << METHOD_CALL) + | (1L << STRUCTURED_LITERAL) + | (1L << PRAGMA) + | (1L << ANNOTATION) + | (1L << SCRIPT); + reservedNames = Collections.emptySet(); + nameSpaces = TEST_STR_FALSE; + } + + /** + * Copy constructor. + * @param features the feature to copy from + */ + public JexlFeatures(final JexlFeatures features) { + this.flags = features.flags; + this.reservedNames = features.reservedNames; + this.nameSpaces = features.nameSpaces; + } + + @Override + public int hashCode() { //CSOFF: MagicNumber + int hash = 3; + hash = 53 * hash + (int) (this.flags ^ (this.flags >>> 32)); + hash = 53 * hash + (this.reservedNames != null ? this.reservedNames.hashCode() : 0); + return hash; + } + + @Override + public boolean equals(final Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final JexlFeatures other = (JexlFeatures) obj; + if (this.flags != other.flags) { + return false; + } + if (!Objects.equals(this.reservedNames, other.reservedNames)) { + return false; + } + return true; + } + + /** + * The text corresponding to a feature code. + * @param feature the feature number + * @return the feature name + */ + public static String stringify(final int feature) { + return feature >= 0 && feature < F_NAMES.length ? F_NAMES[feature] : "unsupported feature"; + } + + /** + * Sets a collection of reserved names precluding those to be used as local variables or parameter names. + * @param names the names to reserve + * @return this features instance + */ + public JexlFeatures reservedNames(final Collection names) { + if (names == null || names.isEmpty()) { + reservedNames = Collections.emptySet(); + } else { + reservedNames = Collections.unmodifiableSet(new TreeSet(names)); + } + setFeature(RESERVED, !reservedNames.isEmpty()); + return this; + } + + /** + * @return the (unmodifiable) set of reserved names. + */ + public Set getReservedNames() { + return reservedNames; + } + + /** + * Checks whether a name is reserved. + * @param name the name to check + * @return true if reserved, false otherwise + */ + public boolean isReservedName(final String name) { + return name != null && reservedNames.contains(name); + } + + /** + * Sets a test to determine namespace declaration. + * @param names the name predicate + * @return this features instance + */ + public JexlFeatures namespaceTest(final Predicate names) { + nameSpaces = names == null? TEST_STR_FALSE : names; + return this; + } + + /** + * @return the declared namespaces test. + */ + public Predicate namespaceTest() { + return nameSpaces; + } + + /** + * Sets a feature flag. + * @param feature the feature ordinal + * @param flag turn-on, turn off + */ + private void setFeature(final int feature, final boolean flag) { + if (flag) { + flags |= (1 << feature); + } else { + flags &= ~(1L << feature); + } + } + + /** + * Gets a feature flag value. + * @param feature feature ordinal + * @return true if on, false if off + */ + private boolean getFeature(final int feature) { + return (flags & (1L << feature)) != 0L; + } + + /** + * Sets whether register are enabled. + *

+ * This is mostly used internally during execution of JexlEngine.{g,s}etProperty. + *

+ * When disabled, parsing a script/expression using the register syntax will throw a parsing exception. + * @param flag true to enable, false to disable + * @return this features instance + */ + public JexlFeatures register(final boolean flag) { + setFeature(REGISTER, flag); + return this; + } + + /** + * @return true if register syntax is enabled + */ + public boolean supportsRegister() { + return getFeature(REGISTER); + } + + /** + * Sets whether local variables are enabled. + *

+ * When disabled, parsing a script/expression using a local variable or parameter syntax + * will throw a parsing exception. + * @param flag true to enable, false to disable + * @return this features instance + */ + public JexlFeatures localVar(final boolean flag) { + setFeature(LOCAL_VAR, flag); + return this; + } + + /** + * @return true if local variables syntax is enabled + */ + public boolean supportsLocalVar() { + return getFeature(LOCAL_VAR); + } + + /** + * Sets whether side effect expressions on global variables (aka non local) are enabled. + *

+ * When disabled, parsing a script/expression using syntactical constructs modifying variables + * including all potentially ant-ish variables will throw a parsing exception. + * @param flag true to enable, false to disable + * @return this features instance + */ + public JexlFeatures sideEffectGlobal(final boolean flag) { + setFeature(SIDE_EFFECT_GLOBAL, flag); + return this; + } + + /** + * @return true if global variables can be assigned + */ + public boolean supportsSideEffectGlobal() { + return getFeature(SIDE_EFFECT_GLOBAL); + } + + /** + * Sets whether side effect expressions are enabled. + *

+ * When disabled, parsing a script/expression using syntactical constructs modifying variables + * or members will throw a parsing exception. + * @param flag true to enable, false to disable + * @return this features instance + */ + public JexlFeatures sideEffect(final boolean flag) { + setFeature(SIDE_EFFECT, flag); + return this; + } + + /** + * @return true if side effects are enabled, false otherwise + */ + public boolean supportsSideEffect() { + return getFeature(SIDE_EFFECT); + } + + /** + * Sets whether array references expressions are enabled. + *

+ * When disabled, parsing a script/expression using 'obj[ ref ]' where ref is not a string or integer literal + * will throw a parsing exception; + * @param flag true to enable, false to disable + * @return this features instance + */ + public JexlFeatures arrayReferenceExpr(final boolean flag) { + setFeature(ARRAY_REF_EXPR, flag); + return this; + } + + /** + * @return true if array references can contain method call expressions, false otherwise + */ + public boolean supportsArrayReferenceExpr() { + return getFeature(ARRAY_REF_EXPR); + } + + /** + * Sets whether method calls expressions are enabled. + *

+ * When disabled, parsing a script/expression using 'obj.method()' + * will throw a parsing exception; + * @param flag true to enable, false to disable + * @return this features instance + */ + public JexlFeatures methodCall(final boolean flag) { + setFeature(METHOD_CALL, flag); + return this; + } + + /** + * @return true if array references can contain expressions, false otherwise + */ + public boolean supportsMethodCall() { + return getFeature(METHOD_CALL); + } + + /** + * Sets whether array/map/set literal expressions are enabled. + *

+ * When disabled, parsing a script/expression creating one of these literals + * will throw a parsing exception; + * @param flag true to enable, false to disable + * @return this features instance + */ + public JexlFeatures structuredLiteral(final boolean flag) { + setFeature(STRUCTURED_LITERAL, flag); + return this; + } + + /** + * @return true if array/map/set literal expressions are supported, false otherwise + */ + public boolean supportsStructuredLiteral() { + return getFeature(STRUCTURED_LITERAL); + } + + /** + * Sets whether creating new instances is enabled. + *

+ * When disabled, parsing a script/expression using 'new(...)' will throw a parsing exception; + * using a class as functor will fail at runtime. + * @param flag true to enable, false to disable + * @return this features instance + */ + public JexlFeatures newInstance(final boolean flag) { + setFeature(NEW_INSTANCE, flag); + return this; + } + + /** + * @return true if creating new instances is enabled, false otherwise + */ + public boolean supportsNewInstance() { + return getFeature(NEW_INSTANCE); + } + + /** + * Sets whether looping constructs are enabled. + *

+ * When disabled, parsing a script/expression using syntactic looping constructs (for,while) + * will throw a parsing exception. + * @param flag true to enable, false to disable + * @return this features instance + */ + public JexlFeatures loops(final boolean flag) { + setFeature(LOOP, flag); + return this; + } + + /** + * @return true if loops are enabled, false otherwise + */ + public boolean supportsLoops() { + return getFeature(LOOP); + } + + /** + * Sets whether lambda/function constructs are enabled. + *

+ * When disabled, parsing a script/expression using syntactic lambda constructs (->,function) + * will throw a parsing exception. + * @param flag true to enable, false to disable + * @return this features instance + */ + public JexlFeatures lambda(final boolean flag) { + setFeature(LAMBDA, flag); + return this; + } + + /** + * @return true if lambda are enabled, false otherwise + */ + public boolean supportsLambda() { + return getFeature(LAMBDA); + } + + /** + * Sets whether pragma constructs are enabled. + *

+ * When disabled, parsing a script/expression using syntactic pragma constructs (#pragma) + * will throw a parsing exception. + * @param flag true to enable, false to disable + * @return this features instance + */ + public JexlFeatures pragma(final boolean flag) { + setFeature(PRAGMA, flag); + return this; + } + + /** + * @return true if pragma are enabled, false otherwise + */ + public boolean supportsPragma() { + return getFeature(PRAGMA); + } + + /** + * Sets whether annotation constructs are enabled. + *

+ * When disabled, parsing a script/expression using syntactic annotation constructs (@annotation) + * will throw a parsing exception. + * @param flag true to enable, false to disable + * @return this features instance + */ + public JexlFeatures annotation(final boolean flag) { + setFeature(ANNOTATION, flag); + return this; + } + + /** + * @return true if annotation are enabled, false otherwise + */ + public boolean supportsAnnotation() { + return getFeature(ANNOTATION); + } + + /** + * Sets whether scripts constructs are enabled. + *

+ * When disabled, parsing a script using syntactic script constructs (statements, ...) + * will throw a parsing exception. + * @param flag true to enable, false to disable + * @return this features instance + */ + public JexlFeatures script(final boolean flag) { + setFeature(SCRIPT, flag); + return this; + } + + /** + * @return true if scripts are enabled, false otherwise + */ + public boolean supportsScript() { + return getFeature(SCRIPT); + } + + /** + * + * @return true if expressions (aka not scripts) are enabled, false otherwise + */ + public boolean supportsExpression() { + return !getFeature(SCRIPT); + } + + /** + * Sets whether syntactic lexical mode is enabled. + * + * @param flag true means syntactic lexical function scope is in effect, false implies non-lexical scoping + * @return this features instance + */ + public JexlFeatures lexical(final boolean flag) { + setFeature(LEXICAL, flag); + return this; + } + + + /** @return whether lexical scope feature is enabled */ + public boolean isLexical() { + return getFeature(LEXICAL); + } + + /** + * Sets whether syntactic lexical shade is enabled. + * + * @param flag true means syntactic lexical shade is in effect and implies lexical scope + * @return this features instance + */ + public JexlFeatures lexicalShade(final boolean flag) { + setFeature(LEXICAL_SHADE, flag); + if (flag) { + setFeature(LEXICAL, true); + } + return this; + } + + + /** @return whether lexical shade feature is enabled */ + public boolean isLexicalShade() { + return getFeature(LEXICAL_SHADE); + } +} diff --git a/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/JexlInfo.java b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/JexlInfo.java new file mode 100644 index 0000000..f2671d6 --- /dev/null +++ b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/JexlInfo.java @@ -0,0 +1,201 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package aiyh.utils.tool.org.apache.commons.jexl3; + +import aiyh.utils.tool.org.apache.commons.jexl3.internal.Script; + +/** + * Helper class to carry information such as a url/file name, line and column for + * debugging information reporting. + */ +public class JexlInfo { + + /** line number. */ + private final int line; + + /** column number. */ + private final int column; + + /** name. */ + private final String name; + + /** + * @return the detailed information in case of an error + */ + public Detail getDetail() { + return null; + } + + /** + * Describes errors more precisely. + */ + public interface Detail { + /** + * @return the start column on the line that triggered the error + */ + int start(); + + /** + * @return the end column on the line that triggered the error + */ + int end(); + + /** + * @return the actual part of code that triggered the error + */ + + @Override + String toString(); + } + + /** + * Create info. + * + * @param source source name + * @param l line number + * @param c column number + */ + public JexlInfo(final String source, final int l, final int c) { + name = source; + line = l; + column = c; + } + + /** + * Create an information structure for dynamic set/get/invoke/new. + *

This gathers the class, method and line number of the first calling method + * outside of o.a.c.jexl3.

+ */ + public JexlInfo() { + final StackTraceElement[] stack = new Throwable().getStackTrace(); + String cname = getClass().getName(); + final String pkgname = getClass().getPackage().getName(); + StackTraceElement se = null; + for (int s = 1; s < stack.length; ++s) { + se = stack[s]; + final String className = se.getClassName(); + if (!className.equals(cname)) { + // go deeper if called from jexl implementation classes + if (!className.startsWith(pkgname + ".internal.") && !className.startsWith(pkgname + ".Jexl") + && !className.startsWith(pkgname + ".parser")) { + break; + } + cname = className; + } + } + this.name = se != null ? se.getClassName() + "." + se.getMethodName() + ":" + se.getLineNumber() : "?"; + this.line = 0; + this.column = 0; + } + + /** + * Creates info reusing the name. + * + * @param l the line + * @param c the column + * @return a new info instance + */ + public JexlInfo at(final int l, final int c) { + return new JexlInfo(name, l, c); + } + + /** + * The copy constructor. + * + * @param copy the instance to copy + */ + protected JexlInfo(final JexlInfo copy) { + name = copy.getName(); + line = copy.getLine(); + column = copy.getColumn(); + } + + /** + * Formats this info in the form 'name@line:column'. + * + * @return the formatted info + */ + @Override + public String toString() { + final StringBuilder sb = new StringBuilder(name != null ? name : ""); + if (line > 0) { + sb.append("@"); + sb.append(line); + if (column > 0) { + sb.append(":"); + sb.append(column); + } + } + final Detail dbg = getDetail(); + if (dbg != null) { + sb.append("!["); + sb.append(dbg.start()); + sb.append(","); + sb.append(dbg.end()); + sb.append("]: '"); + sb.append(dbg); + sb.append("'"); + } + return sb.toString(); + } + + /** + * Gets the file/script/url name. + * + * @return template name + */ + public final String getName() { + return name; + } + + /** + * Gets the line number. + * + * @return line number. + */ + public final int getLine() { + return line; + } + + /** + * Gets the column number. + * + * @return the column. + */ + public final int getColumn() { + return column; + } + + /** + * @return this instance or a copy without any decorations + */ + public JexlInfo detach() { + return this; + } + + /** + * Gets the info from a script. + * + * @param script the script + * @return the info + */ + public static JexlInfo from(final JexlScript script) { + return script instanceof Script ? ((Script) script).getInfo() : null; + } +} + diff --git a/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/JexlOperator.java b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/JexlOperator.java new file mode 100644 index 0000000..93682a8 --- /dev/null +++ b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/JexlOperator.java @@ -0,0 +1,403 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package aiyh.utils.tool.org.apache.commons.jexl3; + +/** + * The JEXL operators. + * + * These are the operators that are executed by JexlArithmetic methods. + * + *

Each of them associates a symbol to a method signature. + * For instance, '+' is associated to 'T add(L x, R y)'.

+ * + *

The default JexlArithmetic implements generic versions of these methods using Object as arguments. + * You can use your own derived JexlArithmetic that override and/or overload those operator methods. + * Note that these are overloads by convention, not actual Java overloads. + * The following rules apply to operator methods:

+ *
    + *
  • Operator methods should be public
  • + *
  • Operators return type should be respected when primitive (int, boolean,...)
  • + *
  • Operators may be overloaded multiple times with different signatures
  • + *
  • Operators may return JexlEngine.TRY_AGAIN to fallback on default JEXL implementation
  • + *
+ * + * @since 3.0 + */ +public enum JexlOperator { + + /** + * Add operator. + *
Syntax: x + y + *
Method: T add(L x, R y);. + * @see JexlArithmetic#add + */ + ADD("+", "add", 2), + + /** + * Subtract operator. + *
Syntax: x - y + *
Method: T subtract(L x, R y);. + * @see JexlArithmetic#subtract + */ + SUBTRACT("-", "subtract", 2), + + /** + * Multiply operator. + *
Syntax: x * y + *
Method: T multiply(L x, R y);. + * @see JexlArithmetic#multiply + */ + MULTIPLY("*", "multiply", 2), + + /** + * Divide operator. + *
Syntax: x / y + *
Method: T divide(L x, R y);. + * @see JexlArithmetic#divide + */ + DIVIDE("/", "divide", 2), + + /** + * Modulo operator. + *
Syntax: x % y + *
Method: T mod(L x, R y);. + * @see JexlArithmetic#mod + */ + MOD("%", "mod", 2), + + /** + * Bitwise-and operator. + *
Syntax: x & y + *
Method: T and(L x, R y);. + * @see JexlArithmetic#and + */ + AND("&", "and", 2), + + /** + * Bitwise-or operator. + *
Syntax: x | y + *
Method: T or(L x, R y);. + * @see JexlArithmetic#or + */ + OR("|", "or", 2), + + /** + * Bitwise-xor operator. + *
Syntax: x ^ y + *
Method: T xor(L x, R y);. + * @see JexlArithmetic#xor + */ + XOR("^", "xor", 2), + + /** + * Equals operator. + *
Syntax: x == y + *
Method: boolean equals(L x, R y);. + * @see JexlArithmetic#equals + */ + EQ("==", "equals", 2), + + /** + * Less-than operator. + *
Syntax: x < y + *
Method: boolean lessThan(L x, R y);. + * @see JexlArithmetic#lessThan + */ + LT("<", "lessThan", 2), + + /** + * Less-than-or-equal operator. + *
Syntax: x <= y + *
Method: boolean lessThanOrEqual(L x, R y);. + * @see JexlArithmetic#lessThanOrEqual + */ + LTE("<=", "lessThanOrEqual", 2), + + /** + * Greater-than operator. + *
Syntax: x > y + *
Method: boolean greaterThan(L x, R y);. + * @see JexlArithmetic#greaterThan + */ + GT(">", "greaterThan", 2), + + /** + * Greater-than-or-equal operator. + *
Syntax: x >= y + *
Method: boolean greaterThanOrEqual(L x, R y);. + * @see JexlArithmetic#greaterThanOrEqual + */ + GTE(">=", "greaterThanOrEqual", 2), + + /** + * Contains operator. + *
Syntax: x =~ y + *
Method: boolean contains(L x, R y);. + * @see JexlArithmetic#contains + */ + CONTAINS("=~", "contains", 2), + + /** + * Starts-with operator. + *
Syntax: x =^ y + *
Method: boolean startsWith(L x, R y);. + * @see JexlArithmetic#startsWith + */ + STARTSWITH("=^", "startsWith", 2), + + /** + * Ends-with operator. + *
Syntax: x =$ y + *
Method: boolean endsWith(L x, R y);. + * @see JexlArithmetic#endsWith + */ + ENDSWITH("=$", "endsWith", 2), + + /** + * Not operator. + *
Syntax: !x + *
Method: T not(L x);. + * @see JexlArithmetic#not + */ + NOT("!", "not", 1), + + /** + * Complement operator. + *
Syntax: ~x + *
Method: T complement(L x);. + * @see JexlArithmetic#complement + */ + COMPLEMENT("~", "complement", 1), + + /** + * Negate operator. + *
Syntax: -x + *
Method: T negate(L x);. + * @see JexlArithmetic#negate + */ + NEGATE("-", "negate", 1), + + /** + * Positivize operator. + *
Syntax: +x + *
Method: T positivize(L x);. + * @see JexlArithmetic#positivize + */ + POSITIVIZE("+", "positivize", 1), + + /** + * Empty operator. + *
Syntax: empty x or empty(x) + *
Method: boolean empty(L x);. + * @see JexlArithmetic#empty + */ + EMPTY("empty", "empty", 1), + + /** + * Size operator. + *
Syntax: size x or size(x) + *
Method: int size(L x);. + * @see JexlArithmetic#size + */ + SIZE("size", "size", 1), + + /** + * Self-add operator. + *
Syntax: x += y + *
Method: T selfAdd(L x, R y);. + */ + SELF_ADD("+=", "selfAdd", ADD), + + /** + * Self-subtract operator. + *
Syntax: x -= y + *
Method: T selfSubtract(L x, R y);. + */ + SELF_SUBTRACT("-=", "selfSubtract", SUBTRACT), + + /** + * Self-multiply operator. + *
Syntax: x *= y + *
Method: T selfMultiply(L x, R y);. + */ + SELF_MULTIPLY("*=", "selfMultiply", MULTIPLY), + + /** + * Self-divide operator. + *
Syntax: x /= y + *
Method: T selfDivide(L x, R y);. + */ + SELF_DIVIDE("/=", "selfDivide", DIVIDE), + + /** + * Self-modulo operator. + *
Syntax: x %= y + *
Method: T selfMod(L x, R y);. + */ + SELF_MOD("%=", "selfMod", MOD), + + /** + * Self-and operator. + *
Syntax: x &= y + *
Method: T selfAnd(L x, R y);. + */ + SELF_AND("&=", "selfAnd", AND), + + /** + * Self-or operator. + *
Syntax: x |= y + *
Method: T selfOr(L x, R y);. + */ + SELF_OR("|=", "selfOr", OR), + + /** + * Self-xor operator. + *
Syntax: x ^= y + *
Method: T selfXor(L x, R y);. + */ + SELF_XOR("^=", "selfXor", XOR), + + /** + * Marker for side effect. + *
Returns this from 'self*' overload method to let the engine know the side effect has been performed and + * there is no need to assign the result. + */ + ASSIGN("=", null, null), + + /** + * Property get operator as in: x.y. + *
Syntax: x.y + *
Method: Object propertyGet(L x, R y);. + */ + PROPERTY_GET(".", "propertyGet", 2), + + /** + * Property set operator as in: x.y = z. + *
Syntax: x.y = z + *
Method: void propertySet(L x, R y, V z);. + */ + PROPERTY_SET(".=", "propertySet", 3), + + /** + * Array get operator as in: x[y]. + *
Syntax: x.y + *
Method: Object arrayGet(L x, R y);. + */ + ARRAY_GET("[]", "arrayGet", 2), + + /** + * Array set operator as in: x[y] = z. + *
Syntax: x[y] = z + *
Method: void arraySet(L x, R y, V z);. + */ + ARRAY_SET("[]=", "arraySet", 3), + + /** + * Iterator generator as in for(var x : y). + * If the returned Iterator is AutoCloseable, close will be called after the last execution of the loop block. + *
Syntax: for(var x : y){...} + *
Method: Iterator<Object> forEach(R y);. + * @since 3.1 + */ + FOR_EACH("for(...)", "forEach", 1); + + /** + * The operator symbol. + */ + private final String operator; + + /** + * The associated operator method name. + */ + private final String methodName; + + /** + * The method arity (ie number of arguments). + */ + private final int arity; + + /** + * The base operator. + */ + private final JexlOperator base; + + /** + * Creates a base operator. + * + * @param o the operator name + * @param m the method name associated to this operator in a JexlArithmetic + * @param argc the number of parameters for the method + */ + JexlOperator(final String o, final String m, final int argc) { + this.operator = o; + this.methodName = m; + this.arity = argc; + this.base = null; + } + + /** + * Creates a side-effect operator. + * + * @param o the operator name + * @param m the method name associated to this operator in a JexlArithmetic + * @param b the base operator, ie + for += + */ + JexlOperator(final String o, final String m, final JexlOperator b) { + this.operator = o; + this.methodName = m; + this.arity = 2; + this.base = b; + } + + /** + * Gets this operator symbol. + * + * @return the symbol + */ + public final String getOperatorSymbol() { + return operator; + } + + /** + * Gets this operator method name in a JexlArithmetic. + * + * @return the method name + */ + public final String getMethodName() { + return methodName; + } + + /** + * Gets this operator number of parameters. + * + * @return the method arity + */ + public int getArity() { + return arity; + } + + /** + * Gets the base operator. + * + * @return the base operator + */ + public final JexlOperator getBaseOperator() { + return base; + } + +} diff --git a/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/JexlOptions.java b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/JexlOptions.java new file mode 100644 index 0000000..29bb2a1 --- /dev/null +++ b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/JexlOptions.java @@ -0,0 +1,411 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package aiyh.utils.tool.org.apache.commons.jexl3; + +import java.math.MathContext; +import java.util.Collections; +import java.util.Map; +import aiyh.utils.tool.org.apache.commons.jexl3.internal.Engine; + +/** + * Flags and properties that can alter the evaluation behavior. + * The flags, briefly explained, are the following: + *
    + *
  • silent: whether errors throw exception
  • + *
  • safe: whether navigation through null is an error
  • + *
  • cancellable: whether thread interruption is an error
  • + *
  • lexical: whether redefining local variables is an error
  • + *
  • lexicalShade: whether local variables shade global ones even outside their scope
  • + *
  • strict: whether unknown or unsolvable identifiers are errors
  • + *
  • strictArithmetic: whether null as operand is an error
  • + *
  • sharedInstance: whether these options can be modified at runtime during execution (expert)
  • + *
+ * The sensible default is cancellable, strict and strictArithmetic. + *

This interface replaces the now deprecated JexlEngine.Options. + * @since 3.2 + */ +public final class JexlOptions { + /** The shared instance bit. */ + private static final int SHARED = 7; + /** The local shade bit. */ + private static final int SHADE = 6; + /** The antish var bit. */ + private static final int ANTISH = 5; + /** The lexical scope bit. */ + private static final int LEXICAL = 4; + /** The safe bit. */ + private static final int SAFE = 3; + /** The silent bit. */ + private static final int SILENT = 2; + /** The strict bit. */ + private static final int STRICT = 1; + /** The cancellable bit. */ + private static final int CANCELLABLE = 0; + /** The flags names ordered. */ + private static final String[] NAMES = { + "cancellable", "strict", "silent", "safe", "lexical", "antish", "lexicalShade", "sharedInstance" + }; + /** Default mask .*/ + private static int DEFAULT = 1 /*<< CANCELLABLE*/ | 1 << STRICT | 1 << ANTISH | 1 << SAFE; + /** The arithmetic math context. */ + private MathContext mathContext = null; + /** The arithmetic math scale. */ + private int mathScale = Integer.MIN_VALUE; + /** The arithmetic strict math flag. */ + private boolean strictArithmetic = true; + /** The default flags, all but safe. */ + private int flags = DEFAULT; + /** The namespaces .*/ + private Map namespaces = Collections.emptyMap(); + + /** + * Sets the value of a flag in a mask. + * @param ordinal the flag ordinal + * @param mask the flags mask + * @param value true or false + * @return the new flags mask value + */ + private static int set(final int ordinal, final int mask, final boolean value) { + return value? mask | (1 << ordinal) : mask & ~(1 << ordinal); + } + + /** + * Checks the value of a flag in the mask. + * @param ordinal the flag ordinal + * @param mask the flags mask + * @return the mask value with this flag or-ed in + */ + private static boolean isSet(final int ordinal, final int mask) { + return (mask & 1 << ordinal) != 0; + } + + /** + * Default ctor. + */ + public JexlOptions() {} + + /** + * Sets the default (static, shared) option flags. + *

+ * Whenever possible, we recommend using JexlBuilder methods to unambiguously instantiate a JEXL + * engine; this method should only be used for testing / validation. + *

A '+flag' or 'flag' will set the option named 'flag' as true, '-flag' set as false. + * The possible flag names are: + * cancellable, strict, silent, safe, lexical, antish, lexicalShade + *

Calling JexlBuilder.setDefaultOptions("+safe") once before JEXL engine creation + * may ease validating JEXL3.2 in your environment. + * @param flags the flags to set + */ + public static void setDefaultFlags(final String...flags) { + DEFAULT = parseFlags(DEFAULT, flags); + } + + /** + * Parses flags by name. + *

A '+flag' or 'flag' will set flag as true, '-flag' set as false. + * The possible flag names are: + * cancellable, strict, silent, safe, lexical, antish, lexicalShade + * @param mask the initial mask state + * @param flags the flags to set + * @return the flag mask updated + */ + public static int parseFlags(int mask, final String...flags) { + for(String name : flags) { + boolean b = true; + if (name.charAt(0) == '+') { + name = name.substring(1); + } else if (name.charAt(0) == '-') { + name = name.substring(1); + b = false; + } + for(int flag = 0; flag < NAMES.length; ++flag) { + if (NAMES[flag].equals(name)) { + if (b) { + mask |= (1 << flag); + } else { + mask &= ~(1 << flag); + } + break; + } + } + } + return mask; + } + + /** + * Sets this option flags using the +/- syntax. + * @param opts the option flags + */ + public void setFlags(final String[] opts) { + flags = parseFlags(flags, opts); + } + + /** + * The MathContext instance used for +,-,/,*,% operations on big decimals. + * @return the math context + */ + public MathContext getMathContext() { + return mathContext; + } + + /** + * The BigDecimal scale used for comparison and coercion operations. + * @return the scale + */ + public int getMathScale() { + return mathScale; + } + + /** + * Checks whether evaluation will attempt resolving antish variable names. + * @return true if antish variables are solved, false otherwise + */ + public boolean isAntish() { + return isSet(ANTISH, flags); + } + + /** + * Checks whether evaluation will throw JexlException.Cancel (true) or + * return null (false) if interrupted. + * @return true when cancellable, false otherwise + */ + public boolean isCancellable() { + return isSet(CANCELLABLE, flags); + } + + /** + * Checks whether runtime variable scope is lexical. + *

If true, lexical scope applies to local variables and parameters. + * Redefining a variable in the same lexical unit will generate errors. + * @return true if scope is lexical, false otherwise + */ + public boolean isLexical() { + return isSet(LEXICAL, flags); + } + + /** + * Checks whether local variables shade global ones. + *

After a symbol is defined as local, dereferencing it outside its + * scope will trigger an error instead of seeking a global variable of the + * same name. To further reduce potential naming ambiguity errors, + * global variables (ie non local) must be declared to be assigned (@link JexlContext#has(String) ) + * when this flag is on; attempting to set an undeclared global variables will + * raise an error. + * @return true if lexical shading is applied, false otherwise + */ + public boolean isLexicalShade() { + return isSet(SHADE, flags); + } + + /** + * Checks whether the engine considers null in navigation expression as + * errors during evaluation.. + * @return true if safe, false otherwise + */ + public boolean isSafe() { + return isSet(SAFE, flags); + } + + /** + * Checks whether the engine will throw a {@link JexlException} when an + * error is encountered during evaluation. + * @return true if silent, false otherwise + */ + public boolean isSilent() { + return isSet(SILENT, flags); + } + + /** + * Checks whether the engine considers unknown variables, methods and + * constructors as errors during evaluation. + * @return true if strict, false otherwise + */ + public boolean isStrict() { + return isSet(STRICT, flags); + } + + /** + * Checks whether the arithmetic triggers errors during evaluation when null + * is used as an operand. + * @return true if strict, false otherwise + */ + public boolean isStrictArithmetic() { + return strictArithmetic; + } + + /** + * Sets whether the engine will attempt solving antish variable names from + * context. + * @param flag true if antish variables are solved, false otherwise + */ + public void setAntish(final boolean flag) { + flags = set(ANTISH, flags, flag); + } + + /** + * Sets whether the engine will throw JexlException.Cancel (true) or return + * null (false) when interrupted during evaluation. + * @param flag true when cancellable, false otherwise + */ + public void setCancellable(final boolean flag) { + flags = set(CANCELLABLE, flags, flag); + } + + /** + * Sets whether the engine uses a strict block lexical scope during + * evaluation. + * @param flag true if lexical scope is used, false otherwise + */ + public void setLexical(final boolean flag) { + flags = set(LEXICAL, flags, flag); + } + + /** + * Sets whether the engine strictly shades global variables. + * Local symbols shade globals after definition and creating global + * variables is prohibited during evaluation. + * If setting to lexical shade, lexical scope is also set. + * @param flag true if creation is allowed, false otherwise + */ + public void setLexicalShade(final boolean flag) { + flags = set(SHADE, flags, flag); + if (flag) { + flags = set(LEXICAL, flags, true); + } + } + + /** + * Sets the arithmetic math context. + * @param mcontext the context + */ + public void setMathContext(final MathContext mcontext) { + this.mathContext = mcontext; + } + + /** + * Sets the arithmetic math scale. + * @param mscale the scale + */ + public void setMathScale(final int mscale) { + this.mathScale = mscale; + } + + /** + * Sets whether the engine considers null in navigation expression as errors + * during evaluation. + * @param flag true if safe, false otherwise + */ + public void setSafe(final boolean flag) { + flags = set(SAFE, flags, flag); + } + + /** + * Sets whether the engine will throw a {@link JexlException} when an error + * is encountered during evaluation. + * @param flag true if silent, false otherwise + */ + public void setSilent(final boolean flag) { + flags = set(SILENT, flags, flag); + } + + /** + * Sets whether the engine considers unknown variables, methods and + * constructors as errors during evaluation. + * @param flag true if strict, false otherwise + */ + public void setStrict(final boolean flag) { + flags = set(STRICT, flags, flag); + } + + /** + * Sets the strict arithmetic flag. + * @param stricta true or false + */ + public void setStrictArithmetic(final boolean stricta) { + this.strictArithmetic = stricta; + } + + /** + * Whether these options are immutable at runtime. + *

Expert mode; allows instance handled through context to be shared + * instead of copied. + * @param flag true if shared, false if not + */ + public void setSharedInstance(final boolean flag) { + flags = set(SHARED, flags, flag); + } + + /** + * @return false if a copy of these options is used during execution, + * true if those can potentially be modified + */ + public boolean isSharedInstance() { + return isSet(SHARED, flags); + } + + /** + * Set options from engine. + * @param jexl the engine + * @return this instance + */ + public JexlOptions set(final JexlEngine jexl) { + if (jexl instanceof Engine) { + ((Engine) jexl).optionsSet(this); + } + return this; + } + + /** + * Set options from options. + * @param src the options + * @return this instance + */ + public JexlOptions set(final JexlOptions src) { + mathContext = src.mathContext; + mathScale = src.mathScale; + strictArithmetic = src.strictArithmetic; + flags = src.flags; + namespaces = src.namespaces; + return this; + } + + /** + * Gets the optional map of namespaces. + * @return the map of namespaces, may be empty, not null + */ + public Map getNamespaces() { + return namespaces; + } + + /** + * Sets the optional map of namespaces. + * @param ns a namespaces map + */ + public void setNamespaces(final Map ns) { + this.namespaces = ns == null? Collections.emptyMap() : ns; + } + + /** + * Creates a copy of this instance. + * @return a copy + */ + public JexlOptions copy() { + return new JexlOptions().set(this); + } + +} \ No newline at end of file diff --git a/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/JexlScript.java b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/JexlScript.java new file mode 100644 index 0000000..61e649b --- /dev/null +++ b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/JexlScript.java @@ -0,0 +1,163 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package aiyh.utils.tool.org.apache.commons.jexl3; + +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.Callable; + +/** + * A JEXL Script. + * + *

A script is some valid JEXL syntax to be executed with a given set of {@link JexlContext} variables.

+ * + *

A script is a group of statements, separated by semicolons.

+ * + *

The statements can be blocks (curly braces containing code), + * Control statements such as if and while + * as well as expressions and assignment statements.

+ * + *

Do not create classes that implement this interface; delegate or compose instead.

+ * + * @since 1.1 + */ +public interface JexlScript { + + /** + * Returns the source text of this expression. + * + * @return the source text + */ + String getSourceText(); + + /** + * Recreates the source text of this expression from the internal syntactic tree. + * + * @return the source text + */ + String getParsedText(); + + /** + * Recreates the source text of this expression from the internal syntactic tree. + * + * @param indent the number of spaces for indentation, 0 meaning no indentation + * @return the source text + */ + String getParsedText(int indent); + + /** + * Executes the script with the variables contained in the + * supplied {@link JexlContext}. + * + * @param context A JexlContext containing variables. + * @return The result of this script, usually the result of + * the last statement. + */ + Object execute(JexlContext context); + + /** + * Executes the script with the variables contained in the + * supplied {@link JexlContext} and a set of arguments corresponding to the + * parameters used during parsing. + * + * @param context A JexlContext containing variables. + * @param args the arguments + * @return The result of this script, usually the result of + * the last statement. + * @since 2.1 + */ + Object execute(JexlContext context, Object... args); + + /** + * Gets this script parameters. + * + * @return the parameters or null + * @since 2.1 + */ + String[] getParameters(); + + /** + * Gets this script unbound parameters. + *

Parameters that haven't been bound by a previous call to curry().

+ * @return the parameters or null + * @since 3.2 + */ + String[] getUnboundParameters(); + + /** + * Gets this script local variables. + * + * @return the local variables or null + * @since 2.1 + */ + String[] getLocalVariables(); + + /** + * Gets this script variables. + *

Note that since variables can be in an ant-ish form (ie foo.bar.quux), each variable is returned as + * a list of strings where each entry is a fragment of the variable ({"foo", "bar", "quux"} in the example.

+ * + * @return the variables or null + * @since 2.1 + */ + Set> getVariables(); + + /** + * Gets this script pragmas. + * + * @return the (non null, may be empty) pragmas map + */ + Map getPragmas(); + + /** + * Creates a Callable from this script. + * + *

This allows to submit it to an executor pool and provides support for asynchronous calls.

+ *

The interpreter will handle interruption/cancellation gracefully if needed.

+ * + * @param context the context + * @return the callable + * @since 2.1 + */ + Callable callable(JexlContext context); + + /** + * Creates a Callable from this script. + * + *

This allows to submit it to an executor pool and provides support for asynchronous calls.

+ *

The interpreter will handle interruption/cancellation gracefully if needed.

+ * + * @param context the context + * @param args the script arguments + * @return the callable + * @since 2.1 + */ + Callable callable(JexlContext context, Object... args); + + /** + * Curries this script, returning a script with bound arguments. + * + *

If this script does not declare parameters or if all of them are already bound, + * no error is generated and this script is returned.

+ * + * @param args the arguments to bind + * @return the curried script or this script if no binding can occur + */ + JexlScript curry(Object... args); +} diff --git a/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/JxltEngine.java b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/JxltEngine.java new file mode 100644 index 0000000..0c2201e --- /dev/null +++ b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/JxltEngine.java @@ -0,0 +1,417 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package aiyh.utils.tool.org.apache.commons.jexl3; + +import java.io.Reader; +import java.io.StringReader; +import java.io.Writer; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * A simple "JeXL Template" engine. + * + *

At the base is an evaluator similar to the Unified EL evaluator used in JSP/JSF based on JEXL. + * At the top is a template engine inspired by Velocity that uses JEXL (instead of OGNL/VTL) as the scripting + * language.

+ * + *

The evaluator is intended to be used in configuration modules, XML based frameworks or JSP taglibs + * and facilitate the implementation of expression evaluation.

+ * + *

The template engine is intended to output any form of text; html, XML, CSV...

+ * + * @since 3.0 + */ +public abstract class JxltEngine { + + /** + * The sole type of (runtime) exception the JxltEngine can throw. + */ + public static class Exception extends JexlException { + + /** Serial version UID. */ + private static final long serialVersionUID = 201112030113L; + + /** + * Creates an Exception. + * + * @param info the contextual information + * @param msg the exception message + * @param cause the exception cause + */ + public Exception(final JexlInfo info, final String msg, final Throwable cause) { + super(info, msg, cause); + } + } + + /** + * A unified expression that can mix immediate, deferred and nested sub-expressions as well as string constants; + *
    + *
  • The "immediate" syntax is of the form "...${jexl-expr}..."
  • + *
  • The "deferred" syntax is of the form "...#{jexl-expr}..."
  • + *
  • The "nested" syntax is of the form "...#{...${jexl-expr0}...}..."
  • + *
  • The "composite" syntax is of the form "...${jexl-expr0}... #{jexl-expr1}..."
  • + *
+ * + *

Deferred and immediate expression carry different intentions:

+ * + *
    + *
  • An immediate expression indicate that evaluation is intended to be performed close to + * the definition/parsing point.
  • + *
  • A deferred expression indicate that evaluation is intended to occur at a later stage.
  • + *
+ * + *

For instance: "Hello ${name}, now is #{time}" is a composite "deferred" expression since one + * of its subexpressions is deferred. Furthermore, this (composite) expression intent is + * to perform two evaluations; one close to its definition and another one in a later + * phase.

+ * + *

The API reflects this feature in 2 methods, prepare and evaluate. The prepare method + * will evaluate the immediate subexpression and return an expression that contains only + * the deferred subexpressions (and constants), a prepared expression. Such a prepared expression + * is suitable for a later phase evaluation that may occur with a different JexlContext. + * Note that it is valid to call evaluate without prepare in which case the same JexlContext + * is used for the 2 evaluation phases.

+ * + *

In the most common use-case where deferred expressions are to be kept around as properties of objects, + * one should createExpression and prepare an expression before storing it and evaluate it each time + * the property storing it is accessed.

+ * + *

Note that nested expression use the JEXL syntax as in:

+ * + *
"#{${bar}+'.charAt(2)'}"
+ * + *

The most common mistake leading to an invalid expression being the following:

+ * + *
"#{${bar}charAt(2)}"
+ * + *

Also note that methods that createExpression evaluate expressions may throw unchecked exceptions; + * The {@link Exception} are thrown when the engine instance is in "non-silent" mode + * but since these are RuntimeException, user-code should catch them where appropriate.

+ * + * @since 2.0 + */ + public interface Expression { + + /** + * Generates this expression's string representation. + * + * @return the string representation + */ + String asString(); + + /** + * Adds this expression's string representation to a StringBuilder. + * + * @param strb the builder to fill + * @return the builder argument + */ + StringBuilder asString(StringBuilder strb); + + /** + * Evaluates this expression. + * + *

If the underlying JEXL engine is silent, errors will be logged through its logger as warning.

+ * + * @param context the variable context + * @return the result of this expression evaluation or null if an error occurs and the {@link JexlEngine} is + * running in silent mode + * @throws Exception if an error occurs and the {@link JexlEngine} + * is not silent + */ + Object evaluate(JexlContext context); + + /** + * Retrieves this expression's source expression. + *

+ * If this expression was prepared, this allows to retrieve the + * original expression that lead to it.

+ *

Other expressions return themselves.

+ * + * @return the source expression + */ + Expression getSource(); + + /** + * Gets the list of variables accessed by this expression. + *

This method will visit all nodes of the sub-expressions and extract all variables whether they + * are written in 'dot' or 'bracketed' notation. (a.b is equivalent to a['b']).

+ * + * @return the set of variables, each as a list of strings (ant-ish variables use more than 1 string) + * or the empty set if no variables are used + */ + Set> getVariables(); + + /** + * Checks whether this expression is deferred. + * + * @return true if deferred, false otherwise + */ + boolean isDeferred(); + + /** + * Checks whether this expression is immediate. + * + * @return true if immediate, false otherwise + */ + boolean isImmediate(); + + /** + * Evaluates the immediate sub-expressions. + * + *

When the expression is dependant upon immediate and deferred sub-expressions, + * evaluates the immediate sub-expressions with the context passed as parameter + * and returns this expression deferred form.

+ * + *

In effect, this binds the result of the immediate sub-expressions evaluation in the + * context, allowing to differ evaluation of the remaining (deferred) expression within another context. + * This only has an effect to nested and composite expressions that contain differed and + * immediate sub-expressions.

+ * + *

If the underlying JEXL engine is silent, errors will be logged through its logger as warning.*

+ * + * @param context the context to use for immediate expression evaluations + * @return an {@link Expression} or null if an error occurs and the {@link JexlEngine} is running + * in silent mode + * @throws Exception if an error occurs and the {@link JexlEngine} is not in silent mode + */ + Expression prepare(JexlContext context); + + /** + * Formats this expression, adding its source string representation in + * comments if available: 'expression /*= source *\/'' . + * + * @return the formatted expression string + */ + @Override + String toString(); + } + + /** + * Creates a a {@link Expression} from an expression string. + * Uses and fills up the expression cache if any. + * + *

If the underlying JEXL engine is silent, errors will be logged through its logger as warnings.

+ * + * @param expression the {@link Template} string expression + * @return the {@link Expression}, null if silent and an error occurred + * @throws Exception if an error occurs and the {@link JexlEngine} is not silent + */ + public Expression createExpression(final String expression) { + return createExpression(null, expression); + } + + /** + * Creates a a {@link Expression} from an expression string. + * Uses and fills up the expression cache if any. + * + *

If the underlying JEXL engine is silent, errors will be logged through its logger as warnings.

+ * + * @param info the {@link JexlInfo} source information + * @param expression the {@link Template} string expression + * @return the {@link Expression}, null if silent and an error occurred + * @throws Exception if an error occurs and the {@link JexlEngine} is not silent + */ + public abstract Expression createExpression(JexlInfo info, String expression); + + /** + * A template is a JEXL script that evaluates by writing its content through a Writer. + *

+ * The source text is parsed considering each line beginning with '$$' (as default pattern) as JEXL script code + * and all others as Unified JEXL expressions; those expressions will be invoked from the script during + * evaluation and their output gathered through a writer. + * It is thus possible to use looping or conditional construct "around" expressions generating output. + *

+ * For instance: + *
+	 * $$ for(var x : [1, 3, 5, 42, 169]) {
+	 * $$   if (x == 42) {
+	 * Life, the universe, and everything
+	 * $$   } else if (x > 42) {
+	 * The value $(x} is over forty-two
+	 * $$   } else {
+	 * The value ${x} is under forty-two
+	 * $$   }
+	 * $$ }
+	 * 
+ * + *

Will evaluate as:

+ * + *
+	 * The value 1 is under forty-two
+	 * The value 3 is under forty-two
+	 * The value 5 is under forty-two
+	 * Life, the universe, and everything
+	 * The value 169 is over forty-two
+	 * 
+ * + *

During evaluation, the template context exposes its writer as '$jexl' which is safe to use in this case. + * This allows writing directly through the writer without adding new-lines as in:

+ * + *
+	 * $$ for(var cell : cells) { $jexl.print(cell); $jexl.print(';') }
+	 * 
+ * + *

A template is expanded as one JEXL script and a list of template expressions; each template expression is + * being replaced in the script by a call to jexl:print(expr) (the expr is in fact the expr number in the template). + * This integration uses a specialized JexlContext (TemplateContext) that serves as a namespace (for jexl:) + * and stores the template expression array and the writer (java.io.Writer) that the 'jexl:print(...)' + * delegates the output generation to.

+ * + * @since 3.0 + */ + public interface Template { + + /** + * Recreate the template source from its inner components. + * + * @return the template source rewritten + */ + String asString(); + + /** + * Evaluates this template. + * + * @param context the context to use during evaluation + * @param writer the writer to use for output + */ + void evaluate(JexlContext context, Writer writer); + + /** + * Evaluates this template. + * + * @param context the context to use during evaluation + * @param writer the writer to use for output + * @param args the arguments + */ + void evaluate(JexlContext context, Writer writer, Object... args); + + /** + * Prepares this template by expanding any contained deferred TemplateExpression. + * + * @param context the context to prepare against + * @return the prepared version of the template + */ + Template prepare(JexlContext context); + + /** + * Gets the list of variables accessed by this template. + *

This method will visit all nodes of the sub-expressions and extract all variables whether they + * are written in 'dot' or 'bracketed' notation. (a.b is equivalent to a['b']).

+ * + * @return the set of variables, each as a list of strings (ant-ish variables use more than 1 string) + * or the empty set if no variables are used + */ + Set> getVariables(); + + /** + * Gets the list of parameters expected by this template. + * + * @return the parameter names array + */ + String[] getParameters(); + + /** + * Gets this script pragmas. + * + * @return the (non null, may be empty) pragmas map + * @since 3.1 + */ + Map getPragmas(); + } + + /** + * Creates a new template. + * + * @param info the jexl info (file, line, column) + * @param prefix the directive prefix + * @param source the source + * @param parms the parameter names + * @return the template + */ + public abstract Template createTemplate(JexlInfo info, String prefix, Reader source, String... parms); + + /** + * Creates a new template. + * + * @param info the source info + * @param parms the parameter names + * @param source the source + * @return the template + */ + public Template createTemplate(final JexlInfo info, final String source, final String... parms) { + return createTemplate(info, "$$", new StringReader(source), parms); + } + + /** + * Creates a new template. + * + * @param info the source info + * @param source the source + * @return the template + */ + public Template createTemplate(final JexlInfo info, final String source) { + return createTemplate(info, "$$", new StringReader(source), (String[]) null); + } + + /** + * Creates a new template. + * + * @param prefix the directive prefix + * @param source the source + * @param parms the parameter names + * @return the template + */ + public Template createTemplate(final String prefix, final Reader source, final String... parms) { + return createTemplate(null, prefix, source, parms); + } + + /** + * Creates a new template. + * + * @param source the source + * @param parms the parameter names + * @return the template + */ + public Template createTemplate(final String source, final String... parms) { + return createTemplate(null, source, parms); + } + + /** + * Creates a new template. + * + * @param source the source + * @return the template + */ + public Template createTemplate(final String source) { + return createTemplate(null, source); + } + + /** + * Gets the {@link JexlEngine} underlying this template engine. + * + * @return the JexlEngine + */ + public abstract JexlEngine getEngine(); + + /** + * Clears the cache. + */ + public abstract void clearCache(); +} diff --git a/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/MapContext.java b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/MapContext.java new file mode 100644 index 0000000..8b7f2b8 --- /dev/null +++ b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/MapContext.java @@ -0,0 +1,71 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package aiyh.utils.tool.org.apache.commons.jexl3; + +import java.util.HashMap; +import java.util.Map; + +/** + * Wraps a map in a context. + *

Each entry in the map is considered a variable name, value pair.

+ */ +public class MapContext implements JexlContext { + + /** + * The wrapped variable map. + */ + private final Map map; + + /** + * Creates a MapContext on an automatically allocated underlying HashMap. + */ + public MapContext() { + this(null); + } + + /** + * Creates a MapContext wrapping an existing user provided map. + * + * @param vars the variable map + */ + public MapContext(final Map vars) { + map = vars == null ? new HashMap() : vars; + } + + @Override + public boolean has(final String name) { + return map.containsKey(name); + } + + @Override + public Object get(final String name) { + return map.get(name); + } + + @Override + public void set(final String name, final Object value) { + map.put(name, value); + } + + /** + * Clears all variables. + */ + public void clear() { + map.clear(); + } +} diff --git a/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/ObjectContext.java b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/ObjectContext.java new file mode 100644 index 0000000..b9fa659 --- /dev/null +++ b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/ObjectContext.java @@ -0,0 +1,108 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package aiyh.utils.tool.org.apache.commons.jexl3; + +import aiyh.utils.tool.org.apache.commons.jexl3.introspection.JexlPropertySet; +import aiyh.utils.tool.org.apache.commons.jexl3.introspection.JexlPropertyGet; + +/** + * Wraps an Object as a JEXL context and NamespaceResolver. + * + * @param the wrapped object type to use + * @since 3.0 + */ +public class ObjectContext implements JexlContext, JexlContext.NamespaceResolver { + + /** The property solving jexl engine. */ + private final JexlEngine jexl; + + /** The object serving as context provider. */ + private final T object; + + /** + * @return the Jexl engine + */ + protected JexlEngine getJexl() { + return jexl; + } + + /** + * @return the object exposed by this context + */ + protected T getObject() { + return object; + } + + /** + * Creates a new ObjectContext. + * + * @param engine the jexl engine to use to solve properties + * @param wrapped the object to wrap in this context + */ + public ObjectContext(final JexlEngine engine, final T wrapped) { + this.jexl = engine; + this.object = wrapped; + } + + @Override + public Object get(final String name) { + final JexlPropertyGet jget = jexl.getUberspect().getPropertyGet(object, name); + if (jget != null) { + try { + return jget.invoke(object); + } catch (final Exception xany) { + if (jexl.isStrict()) { + throw new JexlException.Property(null, name, true, xany); + } + } + } + return null; + } + + @Override + public void set(final String name, final Object value) { + final JexlPropertySet jset = jexl.getUberspect().getPropertySet(object, name, value); + if (jset != null) { + try { + jset.invoke(object, value); + } catch (final Exception xany) { + // ignore + if (jexl.isStrict()) { + throw new JexlException.Property(null, name, true, xany); + } + } + } + } + + @Override + public boolean has(final String name) { + final JexlPropertyGet jget = jexl.getUberspect().getPropertyGet(object, name); + try { + return jget != null && jget.invoke(object) != null; + } catch (final Exception xany) { + return false; + } + } + + @Override + public Object resolveNamespace(final String name) { + if (name == null || name.isEmpty()) { + return object; + } + return null; + } +} diff --git a/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/annotations/NoJexl.java b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/annotations/NoJexl.java new file mode 100644 index 0000000..e2cd813 --- /dev/null +++ b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/annotations/NoJexl.java @@ -0,0 +1,41 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package aiyh.utils.tool.org.apache.commons.jexl3.annotations; + +import aiyh.utils.tool.org.apache.commons.jexl3.introspection.JexlSandbox; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Indicates JEXL Introspection should not see this element. + *

+ * This allows to completely hide a package, class, interface, constructor, method or field from + * JEXL; a NoJexl annotated element will not be usable through any kind of JEXL expression or script. + *

+ * See {@link JexlSandbox} for another way to restrict JEXL access. + */ +@Documented +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.TYPE, ElementType.METHOD, ElementType.CONSTRUCTOR, ElementType.FIELD, ElementType.PACKAGE}) +public @interface NoJexl { + +} diff --git a/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/annotations/package.html b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/annotations/package.html new file mode 100644 index 0000000..2748ab8 --- /dev/null +++ b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/annotations/package.html @@ -0,0 +1,27 @@ + + + + Package Documentation for org.apache.commons.jexl3.annotations Package + + +

Provides annotation for introspection services.

+

The only annotation in this package allows to restrict what JEXL + can introspect and expose through scripting. +

+ + diff --git a/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/internal/ArrayBuilder.java b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/internal/ArrayBuilder.java new file mode 100644 index 0000000..a621b1b --- /dev/null +++ b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/internal/ArrayBuilder.java @@ -0,0 +1,139 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package aiyh.utils.tool.org.apache.commons.jexl3.internal; + +import aiyh.utils.tool.org.apache.commons.jexl3.JexlArithmetic; + +import java.lang.reflect.Array; +import java.util.ArrayList; +import java.util.IdentityHashMap; +import java.util.List; +import java.util.Map; +import java.util.Arrays; + +/** + * Helper class to create typed arrays. + */ +public class ArrayBuilder implements JexlArithmetic.ArrayBuilder { + /** The number of primitive types. */ + private static final int PRIMITIVE_SIZE = 8; + /** The boxing types to primitive conversion map. */ + private static final Map, Class> BOXING_CLASSES; + static { + BOXING_CLASSES = new IdentityHashMap, Class>(PRIMITIVE_SIZE); + BOXING_CLASSES.put(Boolean.class, Boolean.TYPE); + BOXING_CLASSES.put(Byte.class, Byte.TYPE); + BOXING_CLASSES.put(Character.class, Character.TYPE); + BOXING_CLASSES.put(Double.class, Double.TYPE); + BOXING_CLASSES.put(Float.class, Float.TYPE); + BOXING_CLASSES.put(Integer.class, Integer.TYPE); + BOXING_CLASSES.put(Long.class, Long.TYPE); + BOXING_CLASSES.put(Short.class, Short.TYPE); + } + + /** + * Gets the primitive type of a given class (when it exists). + * @param parm a class + * @return the primitive type or null it the argument is not unboxable + */ + protected static Class unboxingClass(final Class parm) { + final Class prim = BOXING_CLASSES.get(parm); + return prim == null ? parm : prim; + } + + /** The intended class array. */ + protected Class commonClass = null; + /** Whether the array stores numbers. */ + protected boolean isNumber = true; + /** Whether we can try unboxing. */ + protected boolean unboxing = true; + /** The untyped list of items being added. */ + protected final Object[] untyped; + /** Number of added items. */ + protected int added = 0; + + /** + * Creates a new builder. + * @param size the exact array size + */ + public ArrayBuilder(final int size) { + untyped = new Object[size]; + } + + @Override + public void add(final Object value) { + // for all children after first... + if (!Object.class.equals(commonClass)) { + if (value == null) { + isNumber = false; + unboxing = false; + } else { + Class eclass = value.getClass(); + // base common class on first non-null entry + if (commonClass == null) { + commonClass = eclass; + isNumber = isNumber && Number.class.isAssignableFrom(commonClass); + } else if (!commonClass.equals(eclass)) { + // if both are numbers... + if (isNumber && Number.class.isAssignableFrom(eclass)) { + commonClass = Number.class; + } else { + // attempt to find valid superclass + do { + eclass = eclass.getSuperclass(); + if (eclass == null) { + commonClass = Object.class; + break; + } + } while (!commonClass.isAssignableFrom(eclass)); + } + } + } + } + if (added >= untyped.length) { + throw new IllegalArgumentException("add() over size"); + } + untyped[added++] = value; + } + + @Override + public Object create(final boolean extended) { + if (untyped == null) { + return new Object[0]; + } + if (extended) { + final List list = new ArrayList(added); + list.addAll(Arrays.asList(untyped).subList(0, added)); + return list; + } + // convert untyped array to the common class if not Object.class + if ((commonClass == null) || Object.class.equals(commonClass)) { + return untyped.clone(); + } + final int size = added; + // if the commonClass is a number, it has an equivalent primitive type, get it + if (unboxing) { + commonClass = unboxingClass(commonClass); + } + // allocate and fill up the typed array + final Object typed = Array.newInstance(commonClass, size); + for (int i = 0; i < size; ++i) { + Array.set(typed, i, untyped[i]); + } + return typed; + } +} diff --git a/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/internal/Closure.java b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/internal/Closure.java new file mode 100644 index 0000000..0ed2801 --- /dev/null +++ b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/internal/Closure.java @@ -0,0 +1,140 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package aiyh.utils.tool.org.apache.commons.jexl3.internal; + +import aiyh.utils.tool.org.apache.commons.jexl3.JexlContext; +import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTJexlLambda; + +import java.util.Objects; + +/** + * A Script closure. + */ +public class Closure extends Script { + /** The frame. */ + protected final Frame frame; + + /** + * Creates a closure. + * @param theCaller the calling interpreter + * @param lambda the lambda + */ + protected Closure(final Interpreter theCaller, final ASTJexlLambda lambda) { + super(theCaller.jexl, null, lambda); + frame = lambda.createFrame(theCaller.frame); + } + + /** + * Creates a curried version of a script. + * @param base the base script + * @param args the script arguments + */ + protected Closure(final Script base, final Object[] args) { + super(base.jexl, base.source, base.script); + final Frame sf = (base instanceof Closure) ? ((Closure) base).frame : null; + frame = sf == null + ? script.createFrame(args) + : sf.assign(args); + } + + @Override + public int hashCode() { + // CSOFF: Magic number + int hash = 17; + hash = 31 * hash + (this.jexl != null ? this.jexl.hashCode() : 0); + hash = 31 * hash + (this.source != null ? this.source.hashCode() : 0); + hash = 31 * hash + (this.frame != null ? this.frame.hashCode() : 0); + // CSON: Magic number + return hash; + } + + @Override + public boolean equals(final Object obj) { + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final Closure other = (Closure) obj; + if (this.jexl != other.jexl) { + return false; + } + if (!Objects.equals(this.source, other.source)) { + return false; + } + if (!Objects.equals(this.frame, other.frame)) { + return false; + } + return true; + } + + @Override + public String[] getUnboundParameters() { + return frame.getUnboundParameters(); + } + + /** + * Sets the captured index of a given symbol, ie the target index of a parent + * captured symbol in this closure's frame. + *

This is meant to allow a locally defined function to "see" and call + * itself as a local (captured) variable; + * in other words, this allows recursive call of a function. + * @param symbol the symbol index (in the caller of this closure) + * @param value the value to set in the local frame + */ + public void setCaptured(final int symbol, final Object value) { + if (script instanceof ASTJexlLambda) { + final ASTJexlLambda lambda = (ASTJexlLambda) script; + final Scope scope = lambda.getScope(); + if (scope != null) { + final Integer reg = scope.getCaptured(symbol); + if (reg != null) { + frame.set(reg, value); + } + } + } + } + + @Override + public Object evaluate(final JexlContext context) { + return execute(context, (Object[])null); + } + + @Override + public Object execute(final JexlContext context) { + return execute(context, (Object[])null); + } + + @Override + public Object execute(final JexlContext context, final Object... args) { + final Frame local = frame != null? frame.assign(args) : null; + final Interpreter interpreter = createInterpreter(context, local); + return interpreter.runClosure(this, null); + } + + @Override + public Callable callable(final JexlContext context, final Object... args) { + final Frame local = frame != null? frame.assign(args) : null; + return new Callable(createInterpreter(context, local)) { + @Override + public Object interpret() { + return interpreter.runClosure(Closure.this, null); + } + }; + } +} diff --git a/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/internal/Debugger.java b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/internal/Debugger.java new file mode 100644 index 0000000..d80a437 --- /dev/null +++ b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/internal/Debugger.java @@ -0,0 +1,1082 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package aiyh.utils.tool.org.apache.commons.jexl3.internal; + + +import aiyh.utils.tool.org.apache.commons.jexl3.JexlExpression; +import aiyh.utils.tool.org.apache.commons.jexl3.JexlInfo; +import aiyh.utils.tool.org.apache.commons.jexl3.JexlScript; +import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTAddNode; +import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTAndNode; +import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTArguments; +import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTArrayAccess; +import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTArrayLiteral; +import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTAssignment; +import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTBitwiseAndNode; +import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTBitwiseComplNode; +import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTBitwiseOrNode; +import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTBitwiseXorNode; +import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTBlock; +import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTBreak; +import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTConstructorNode; +import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTContinue; +import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTDivNode; +import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTDoWhileStatement; +import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTEQNode; +import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTERNode; +import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTEWNode; +import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTEmptyFunction; +import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTExtendedLiteral; +import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTFalseNode; +import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTForeachStatement; +import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTFunctionNode; +import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTGENode; +import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTGTNode; +import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTIdentifier; +import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTIdentifierAccess; +import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTIfStatement; +import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTJexlLambda; +import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTJexlScript; +import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTJxltLiteral; +import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTLENode; +import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTLTNode; +import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTMapEntry; +import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTMapLiteral; +import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTMethodNode; +import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTModNode; +import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTMulNode; +import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTNENode; +import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTNEWNode; +import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTNRNode; +import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTNSWNode; +import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTNotNode; +import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTNullLiteral; +import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTNumberLiteral; +import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTOrNode; +import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTRangeNode; +import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTReference; +import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTReferenceExpression; +import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTRegexLiteral; +import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTReturnStatement; +import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTSWNode; +import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTSetAddNode; +import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTSetAndNode; +import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTSetDivNode; +import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTSetLiteral; +import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTSetModNode; +import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTSetMultNode; +import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTSetOrNode; +import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTSetSubNode; +import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTSetXorNode; +import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTSizeFunction; +import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTStringLiteral; +import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTSubNode; +import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTTernaryNode; +import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTTrueNode; +import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTUnaryMinusNode; +import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTVar; +import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTWhileStatement; +import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTAnnotatedStatement; +import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTAnnotation; +import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTNullpNode; + +import aiyh.utils.tool.org.apache.commons.jexl3.parser.JexlNode; +import aiyh.utils.tool.org.apache.commons.jexl3.parser.ParserVisitor; + +import java.util.regex.Pattern; +import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTUnaryPlusNode; +import aiyh.utils.tool.org.apache.commons.jexl3.parser.StringParser; + +/** + * Helps pinpoint the cause of problems in expressions that fail during evaluation. + *

+ * It rebuilds an expression string from the tree and the start/end offsets of the cause in that string. + * This implies that exceptions during evaluation do always carry the node that's causing the error. + *

+ * @since 2.0 + */ +public class Debugger extends ParserVisitor implements JexlInfo.Detail { + /** The builder to compose messages. */ + protected final StringBuilder builder = new StringBuilder(); + /** The cause of the issue to debug. */ + protected JexlNode cause = null; + /** The starting character location offset of the cause in the builder. */ + protected int start = 0; + /** The ending character location offset of the cause in the builder. */ + protected int end = 0; + /** The indentation level. */ + protected int indentLevel = 0; + /** Perform indentation?. */ + protected int indent = 2; + /** accept() relative depth. */ + protected int depth = Integer.MAX_VALUE; + + /** + * Creates a Debugger. + */ + public Debugger() { + } + + /** + * Resets this debugger state. + */ + public void reset() { + builder.setLength(0); + cause = null; + start = 0; + end = 0; + indentLevel = 0; + indent = 2; + depth = Integer.MAX_VALUE; + } + + /** + * Position the debugger on the root of an expression. + * @param jscript the expression + * @return true if the expression was a {@link Script} instance, false otherwise + */ + public boolean debug(final JexlExpression jscript) { + if (jscript instanceof Script) { + return debug(((Script) jscript).script); + } + return false; + } + + /** + * Position the debugger on the root of a script. + * @param jscript the script + * @return true if the script was a {@link Script} instance, false otherwise + */ + public boolean debug(final JexlScript jscript) { + if (jscript instanceof Script) { + return debug(((Script) jscript).script); + } + return false; + } + + /** + * Seeks the location of an error cause (a node) in an expression. + * @param node the node to debug + * @return true if the cause was located, false otherwise + */ + public boolean debug(final JexlNode node) { + return debug(node, true); + } + + /** + * Seeks the location of an error cause (a node) in an expression. + * @param node the node to debug + * @param r whether we should actively find the root node of the debugged node + * @return true if the cause was located, false otherwise + */ + public boolean debug(final JexlNode node, final boolean r) { + start = 0; + end = 0; + indentLevel = 0; + if (node != null) { + builder.setLength(0); + cause = node; + // make arg cause become the root cause + JexlNode walk = node; + if (r) { + while (walk.jjtGetParent() != null) { + walk = walk.jjtGetParent(); + } + } + accept(walk, null); + } + return end > 0; + } + + /** + * @return The rebuilt expression + */ + @Override + public String toString() { + return builder.toString(); + } + + /** + * Rebuilds an expression from a JEXL node. + * @param node the node to rebuilt from + * @return the rebuilt expression + * @since 3.0 + */ + public String data(final JexlNode node) { + start = 0; + end = 0; + indentLevel = 0; + if (node != null) { + builder.setLength(0); + cause = node; + accept(node, null); + } + return builder.toString(); + } + + /** + * @return The starting offset location of the cause in the expression + */ + @Override + public int start() { + return start; + } + + /** + * @return The end offset location of the cause in the expression + */ + @Override + public int end() { + return end; + } + + /** + * Sets the indentation level. + * @param level the number of spaces for indentation, none if less or equal to zero + */ + public void setIndentation(final int level) { + indentation(level); + } + + /** + * Sets the indentation level. + * @param level the number of spaces for indentation, none if less or equal to zero + * @return this debugger instance + */ + public Debugger indentation(final int level) { + indent = Math.max(level, 0); + indentLevel = 0; + return this; + } + + /** + * Sets this debugger relative maximum depth. + * @param rdepth the maximum relative depth from the debugged node + * @return this debugger instance + */ + public Debugger depth(final int rdepth) { + this.depth = rdepth; + return this; + } + + /** + * Checks if a child node is the cause to debug & adds its representation to the rebuilt expression. + * @param node the child node + * @param data visitor pattern argument + * @return visitor pattern value + */ + protected Object accept(final JexlNode node, final Object data) { + if (depth <= 0) { + builder.append("..."); + return data; + } + if (node == cause) { + start = builder.length(); + } + depth -= 1; + final Object value = node.jjtAccept(this, data); + depth += 1; + if (node == cause) { + end = builder.length(); + } + return value; + } + + /** + * Adds a statement node to the rebuilt expression. + * @param child the child node + * @param data visitor pattern argument + * @return visitor pattern value + */ + protected Object acceptStatement(final JexlNode child, final Object data) { + final JexlNode parent = child.jjtGetParent(); + if (indent > 0 && (parent instanceof ASTBlock || parent instanceof ASTJexlScript)) { + for (int i = 0; i < indentLevel; ++i) { + for(int s = 0; s < indent; ++s) { + builder.append(' '); + } + } + } + depth -= 1; + final Object value = accept(child, data); + depth += 1; + // blocks, if, for & while don't need a ';' at end + if (!(child instanceof ASTJexlScript + || child instanceof ASTBlock + || child instanceof ASTIfStatement + || child instanceof ASTForeachStatement + || child instanceof ASTWhileStatement + || child instanceof ASTDoWhileStatement + || child instanceof ASTAnnotation)) { + builder.append(';'); + if (indent > 0) { + builder.append('\n'); + } else { + builder.append(' '); + } + } + return value; + } + + /** + * Checks if a terminal node is the cause to debug & adds its representation to the rebuilt expression. + * @param node the child node + * @param image the child node token image (may be null) + * @param data visitor pattern argument + * @return visitor pattern value + */ + protected Object check(final JexlNode node, final String image, final Object data) { + if (node == cause) { + start = builder.length(); + } + if (image != null) { + builder.append(image); + } else { + builder.append(node.toString()); + } + if (node == cause) { + end = builder.length(); + } + return data; + } + + /** + * Checks if the children of a node using infix notation is the cause to debug, adds their representation to the + * rebuilt expression. + * @param node the child node + * @param infix the child node token + * @param paren whether the child should be parenthesized + * @param data visitor pattern argument + * @return visitor pattern value + */ + protected Object infixChildren(final JexlNode node, final String infix, final boolean paren, final Object data) { + final int num = node.jjtGetNumChildren(); //child.jjtGetNumChildren() > 1; + if (paren) { + builder.append('('); + } + for (int i = 0; i < num; ++i) { + if (i > 0) { + builder.append(infix); + } + accept(node.jjtGetChild(i), data); + } + if (paren) { + builder.append(')'); + } + return data; + } + + /** + * Checks if the child of a node using prefix notation is the cause to debug, adds their representation to the + * rebuilt expression. + * @param node the node + * @param prefix the node token + * @param data visitor pattern argument + * @return visitor pattern value + */ + protected Object prefixChild(final JexlNode node, final String prefix, final Object data) { + final boolean paren = node.jjtGetChild(0).jjtGetNumChildren() > 1; + builder.append(prefix); + if (paren) { + builder.append('('); + } + accept(node.jjtGetChild(0), data); + if (paren) { + builder.append(')'); + } + return data; + } + + @Override + protected Object visit(final ASTAddNode node, final Object data) { + return additiveNode(node, " + ", data); + } + + @Override + protected Object visit(final ASTSubNode node, final Object data) { + return additiveNode(node, " - ", data); + } + + /** + * Rebuilds an additive expression. + * @param node the node + * @param op the operator + * @param data visitor pattern argument + * @return visitor pattern value + */ + protected Object additiveNode(final JexlNode node, final String op, final Object data) { + // need parenthesis if not in operator precedence order + final boolean paren = node.jjtGetParent() instanceof ASTMulNode + || node.jjtGetParent() instanceof ASTDivNode + || node.jjtGetParent() instanceof ASTModNode; + final int num = node.jjtGetNumChildren(); + if (paren) { + builder.append('('); + } + accept(node.jjtGetChild(0), data); + for (int i = 1; i < num; ++i) { + builder.append(op); + accept(node.jjtGetChild(i), data); + } + if (paren) { + builder.append(')'); + } + return data; + } + + @Override + protected Object visit(final ASTAndNode node, final Object data) { + return infixChildren(node, " && ", false, data); + } + + @Override + protected Object visit(final ASTArrayAccess node, final Object data) { + final int num = node.jjtGetNumChildren(); + for (int i = 0; i < num; ++i) { + builder.append('['); + accept(node.jjtGetChild(i), data); + builder.append(']'); + } + return data; + } + + @Override + protected Object visit(final ASTExtendedLiteral node, final Object data) { + builder.append("..."); + return data; + } + + @Override + protected Object visit(final ASTArrayLiteral node, final Object data) { + final int num = node.jjtGetNumChildren(); + builder.append("[ "); + if (num > 0) { + accept(node.jjtGetChild(0), data); + for (int i = 1; i < num; ++i) { + builder.append(", "); + accept(node.jjtGetChild(i), data); + } + } + builder.append(" ]"); + return data; + } + + @Override + protected Object visit(final ASTRangeNode node, final Object data) { + return infixChildren(node, " .. ", false, data); + } + + @Override + protected Object visit(final ASTAssignment node, final Object data) { + return infixChildren(node, " = ", false, data); + } + + @Override + protected Object visit(final ASTBitwiseAndNode node, final Object data) { + return infixChildren(node, " & ", false, data); + } + + @Override + protected Object visit(final ASTBitwiseComplNode node, final Object data) { + return prefixChild(node, "~", data); + } + + @Override + protected Object visit(final ASTBitwiseOrNode node, final Object data) { + final boolean paren = node.jjtGetParent() instanceof ASTBitwiseAndNode; + return infixChildren(node, " | ", paren, data); + } + + @Override + protected Object visit(final ASTBitwiseXorNode node, final Object data) { + final boolean paren = node.jjtGetParent() instanceof ASTBitwiseAndNode; + return infixChildren(node, " ^ ", paren, data); + } + + @Override + protected Object visit(final ASTBlock node, final Object data) { + builder.append('{'); + if (indent > 0) { + indentLevel += 1; + builder.append('\n'); + } else { + builder.append(' '); + } + final int num = node.jjtGetNumChildren(); + for (int i = 0; i < num; ++i) { + final JexlNode child = node.jjtGetChild(i); + acceptStatement(child, data); + } + if (indent > 0) { + indentLevel -= 1; + for (int i = 0; i < indentLevel; ++i) { + for(int s = 0; s < indent; ++s) { + builder.append(' '); + } + } + } + builder.append('}'); + return data; + } + + @Override + protected Object visit(final ASTDivNode node, final Object data) { + return infixChildren(node, " / ", false, data); + } + + @Override + protected Object visit(final ASTEmptyFunction node, final Object data) { + builder.append("empty "); + accept(node.jjtGetChild(0), data); + return data; + } + + @Override + protected Object visit(final ASTEQNode node, final Object data) { + return infixChildren(node, " == ", false, data); + } + + @Override + protected Object visit(final ASTERNode node, final Object data) { + return infixChildren(node, " =~ ", false, data); + } + + @Override + protected Object visit(final ASTSWNode node, final Object data) { + return infixChildren(node, " =^ ", false, data); + } + + @Override + protected Object visit(final ASTEWNode node, final Object data) { + return infixChildren(node, " =$ ", false, data); + } + + @Override + protected Object visit(final ASTNSWNode node, final Object data) { + return infixChildren(node, " !^ ", false, data); + } + + @Override + protected Object visit(final ASTNEWNode node, final Object data) { + return infixChildren(node, " !$ ", false, data); + } + + @Override + protected Object visit(final ASTFalseNode node, final Object data) { + return check(node, "false", data); + } + + @Override + protected Object visit(final ASTContinue node, final Object data) { + return check(node, "continue", data); + } + + @Override + protected Object visit(final ASTBreak node, final Object data) { + return check(node, "break", data); + } + + @Override + protected Object visit(final ASTForeachStatement node, final Object data) { + builder.append("for("); + accept(node.jjtGetChild(0), data); + builder.append(" : "); + accept(node.jjtGetChild(1), data); + builder.append(") "); + if (node.jjtGetNumChildren() > 2) { + acceptStatement(node.jjtGetChild(2), data); + } else { + builder.append(';'); + } + return data; + } + + @Override + protected Object visit(final ASTGENode node, final Object data) { + return infixChildren(node, " >= ", false, data); + } + + @Override + protected Object visit(final ASTGTNode node, final Object data) { + return infixChildren(node, " > ", false, data); + } + + /** Checks identifiers that contain spaces or punctuation + * (but underscore, at-sign, sharp-sign and dollar). + */ + protected static final Pattern QUOTED_IDENTIFIER = + Pattern.compile("[\\s]|[\\p{Punct}&&[^@#\\$_]]"); + + /** + * Checks whether an identifier should be quoted or not. + * @param str the identifier + * @return true if needing quotes, false otherwise + */ + protected boolean needQuotes(final String str) { + return QUOTED_IDENTIFIER.matcher(str).find() + || "size".equals(str) + || "empty".equals(str); + } + + @Override + protected Object visit(final ASTIdentifier node, final Object data) { + final String ns = node.getNamespace(); + final String image = StringParser.escapeIdentifier(node.getName()); + if (ns == null) { + return check(node, image, data); + } + final String nsid = StringParser.escapeIdentifier(ns) + ":" + image; + return check(node, nsid, data); + } + + @Override + protected Object visit(final ASTIdentifierAccess node, final Object data) { + builder.append(node.isSafe() ? "?." : "."); + final String image = node.getName(); + if (node.isExpression()) { + builder.append('`'); + builder.append(image.replace("`", "\\`")); + builder.append('`'); + } else if (needQuotes(image)) { + // quote it + builder.append('\''); + builder.append(image.replace("'", "\\'")); + builder.append('\''); + } else { + builder.append(image); + } + return data; + } + + @Override + protected Object visit(final ASTIfStatement node, final Object data) { + final int numChildren = node.jjtGetNumChildren(); + // if (...) ... + builder.append("if ("); + accept(node.jjtGetChild(0), data); + builder.append(") "); + acceptStatement(node.jjtGetChild(1), data); + //.. else if (...) ... + for(int c = 2; c < numChildren - 1; c += 2) { + builder.append(" else if ("); + accept(node.jjtGetChild(c), data); + builder.append(") "); + acceptStatement(node.jjtGetChild(c + 1), data); + } + // else... (if odd) + if ((numChildren & 1) == 1) { + builder.append(" else "); + acceptStatement(node.jjtGetChild(numChildren - 1), data); + } + return data; + } + + @Override + protected Object visit(final ASTNumberLiteral node, final Object data) { + return check(node, node.toString(), data); + } + + /** + * A pseudo visitor for parameters. + * @param p the parameter name + * @param data the visitor argument + * @return the parameter name to use + */ + protected String visitParameter(final String p, final Object data) { + return p; + } + + @Override + protected Object visit(final ASTJexlScript node, Object data) { + // if lambda, produce parameters + if (node instanceof ASTJexlLambda) { + final JexlNode parent = node.jjtGetParent(); + // use lambda syntax if not assigned + final boolean named = parent instanceof ASTAssignment; + if (named) { + builder.append("function"); + } + builder.append('('); + final String[] params = node.getParameters(); + if (params != null && params.length > 0) { + builder.append(visitParameter(params[0], data)); + for (int p = 1; p < params.length; ++p) { + builder.append(", "); + builder.append(visitParameter(params[p], data)); + } + } + builder.append(')'); + if (named) { + builder.append(' '); + } else { + builder.append("->"); + } + // we will need a block... + } + // no parameters or done with them + final int num = node.jjtGetNumChildren(); + if (num == 1 && !(node instanceof ASTJexlLambda)) { + data = accept(node.jjtGetChild(0), data); + } else { + for (int i = 0; i < num; ++i) { + final JexlNode child = node.jjtGetChild(i); + acceptStatement(child, data); + } + } + return data; + } + + @Override + protected Object visit(final ASTLENode node, final Object data) { + return infixChildren(node, " <= ", false, data); + } + + @Override + protected Object visit(final ASTLTNode node, final Object data) { + return infixChildren(node, " < ", false, data); + } + + @Override + protected Object visit(final ASTMapEntry node, final Object data) { + accept(node.jjtGetChild(0), data); + builder.append(" : "); + accept(node.jjtGetChild(1), data); + return data; + } + + @Override + protected Object visit(final ASTSetLiteral node, final Object data) { + final int num = node.jjtGetNumChildren(); + builder.append("{ "); + if (num > 0) { + accept(node.jjtGetChild(0), data); + for (int i = 1; i < num; ++i) { + builder.append(","); + accept(node.jjtGetChild(i), data); + } + } + builder.append(" }"); + return data; + } + + @Override + protected Object visit(final ASTMapLiteral node, final Object data) { + final int num = node.jjtGetNumChildren(); + builder.append("{ "); + if (num > 0) { + accept(node.jjtGetChild(0), data); + for (int i = 1; i < num; ++i) { + builder.append(","); + accept(node.jjtGetChild(i), data); + } + } else { + builder.append(':'); + } + builder.append(" }"); + return data; + } + + @Override + protected Object visit(final ASTConstructorNode node, final Object data) { + final int num = node.jjtGetNumChildren(); + builder.append("new("); + if (num > 0) { + accept(node.jjtGetChild(0), data); + for (int i = 1; i < num; ++i) { + builder.append(", "); + accept(node.jjtGetChild(i), data); + } + } + builder.append(")"); + return data; + } + + @Override + protected Object visit(final ASTFunctionNode node, final Object data) { + final int num = node.jjtGetNumChildren(); + if (num == 3) { + accept(node.jjtGetChild(0), data); + builder.append(":"); + accept(node.jjtGetChild(1), data); + accept(node.jjtGetChild(2), data); + } else if (num == 2) { + accept(node.jjtGetChild(0), data); + accept(node.jjtGetChild(1), data); + } + return data; + } + + @Override + protected Object visit(final ASTMethodNode node, final Object data) { + final int num = node.jjtGetNumChildren(); + if (num == 2) { + accept(node.jjtGetChild(0), data); + accept(node.jjtGetChild(1), data); + } + return data; + } + + @Override + protected Object visit(final ASTArguments node, final Object data) { + final int num = node.jjtGetNumChildren(); + builder.append("("); + if (num > 0) { + accept(node.jjtGetChild(0), data); + for (int i = 1; i < num; ++i) { + builder.append(", "); + accept(node.jjtGetChild(i), data); + } + } + builder.append(")"); + return data; + } + + @Override + protected Object visit(final ASTModNode node, final Object data) { + return infixChildren(node, " % ", false, data); + } + + @Override + protected Object visit(final ASTMulNode node, final Object data) { + return infixChildren(node, " * ", false, data); + } + + @Override + protected Object visit(final ASTNENode node, final Object data) { + return infixChildren(node, " != ", false, data); + } + + @Override + protected Object visit(final ASTNRNode node, final Object data) { + return infixChildren(node, " !~ ", false, data); + } + + @Override + protected Object visit(final ASTNotNode node, final Object data) { + builder.append("!"); + accept(node.jjtGetChild(0), data); + return data; + } + + @Override + protected Object visit(final ASTNullLiteral node, final Object data) { + check(node, "null", data); + return data; + } + + @Override + protected Object visit(final ASTOrNode node, final Object data) { + // need parenthesis if not in operator precedence order + final boolean paren = node.jjtGetParent() instanceof ASTAndNode; + return infixChildren(node, " || ", paren, data); + } + + @Override + protected Object visit(final ASTReference node, final Object data) { + final int num = node.jjtGetNumChildren(); + for (int i = 0; i < num; ++i) { + accept(node.jjtGetChild(i), data); + } + return data; + } + + @Override + protected Object visit(final ASTReferenceExpression node, final Object data) { + final JexlNode first = node.jjtGetChild(0); + builder.append('('); + accept(first, data); + builder.append(')'); + final int num = node.jjtGetNumChildren(); + for (int i = 1; i < num; ++i) { + builder.append("["); + accept(node.jjtGetChild(i), data); + builder.append("]"); + } + return data; + } + + @Override + protected Object visit(final ASTReturnStatement node, final Object data) { + builder.append("return "); + accept(node.jjtGetChild(0), data); + return data; + } + + @Override + protected Object visit(final ASTSizeFunction node, final Object data) { + builder.append("size "); + accept(node.jjtGetChild(0), data); + return data; + } + + @Override + protected Object visit(final ASTStringLiteral node, final Object data) { + final String img = node.getLiteral().replace("'", "\\'"); + return check(node, "'" + img + "'", data); + } + + @Override + protected Object visit(final ASTRegexLiteral node, final Object data) { + final String img = node.toString().replace("/", "\\/"); + return check(node, "~/" + img + "/", data); + } + + @Override + protected Object visit(final ASTTernaryNode node, final Object data) { + accept(node.jjtGetChild(0), data); + if (node.jjtGetNumChildren() > 2) { + builder.append("? "); + accept(node.jjtGetChild(1), data); + builder.append(" : "); + accept(node.jjtGetChild(2), data); + } else { + builder.append("?: "); + accept(node.jjtGetChild(1), data); + + } + return data; + } + + @Override + protected Object visit(final ASTNullpNode node, final Object data) { + accept(node.jjtGetChild(0), data); + builder.append("??"); + accept(node.jjtGetChild(1), data); + return data; + } + + @Override + protected Object visit(final ASTTrueNode node, final Object data) { + check(node, "true", data); + return data; + } + + @Override + protected Object visit(final ASTUnaryMinusNode node, final Object data) { + return prefixChild(node, "-", data); + } + + @Override + protected Object visit(final ASTUnaryPlusNode node, final Object data) { + return prefixChild(node, "+", data); + } + + @Override + protected Object visit(final ASTVar node, final Object data) { + builder.append("var "); + check(node, node.getName(), data); + return data; + } + + @Override + protected Object visit(final ASTWhileStatement node, final Object data) { + builder.append("while ("); + accept(node.jjtGetChild(0), data); + builder.append(") "); + if (node.jjtGetNumChildren() > 1) { + acceptStatement(node.jjtGetChild(1), data); + } else { + builder.append(';'); + } + return data; + } + + @Override + protected Object visit(final ASTDoWhileStatement node, final Object data) { + builder.append("do "); + final int nc = node.jjtGetNumChildren(); + if (nc > 1) { + acceptStatement(node.jjtGetChild(0), data); + } else { + builder.append(";"); + } + builder.append(" while ("); + accept(node.jjtGetChild(nc - 1), data); + builder.append(")"); + return data; + } + + @Override + protected Object visit(final ASTSetAddNode node, final Object data) { + return infixChildren(node, " += ", false, data); + } + + @Override + protected Object visit(final ASTSetSubNode node, final Object data) { + return infixChildren(node, " -= ", false, data); + } + + @Override + protected Object visit(final ASTSetMultNode node, final Object data) { + return infixChildren(node, " *= ", false, data); + } + + @Override + protected Object visit(final ASTSetDivNode node, final Object data) { + return infixChildren(node, " /= ", false, data); + } + + @Override + protected Object visit(final ASTSetModNode node, final Object data) { + return infixChildren(node, " %= ", false, data); + } + + @Override + protected Object visit(final ASTSetAndNode node, final Object data) { + return infixChildren(node, " &= ", false, data); + } + + @Override + protected Object visit(final ASTSetOrNode node, final Object data) { + return infixChildren(node, " |= ", false, data); + } + + @Override + protected Object visit(final ASTSetXorNode node, final Object data) { + return infixChildren(node, " ^= ", false, data); + } + + @Override + protected Object visit(final ASTJxltLiteral node, final Object data) { + final String img = node.getLiteral().replace("`", "\\`"); + return check(node, "`" + img + "`", data); + } + + @Override + protected Object visit(final ASTAnnotation node, final Object data) { + final int num = node.jjtGetNumChildren(); + builder.append('@'); + builder.append(node.getName()); + if (num > 0) { + accept(node.jjtGetChild(0), data); // zut + } + return null; + } + + @Override + protected Object visit(final ASTAnnotatedStatement node, final Object data) { + final int num = node.jjtGetNumChildren(); + for (int i = 0; i < num; ++i) { + if (i > 0) {// && child instanceof ASTBlock) { + builder.append(' '); + } + final JexlNode child = node.jjtGetChild(i); + acceptStatement(child, data); + } + return data; + } +} \ No newline at end of file diff --git a/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/internal/Engine.java b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/internal/Engine.java new file mode 100644 index 0000000..0a622e1 --- /dev/null +++ b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/internal/Engine.java @@ -0,0 +1,925 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package aiyh.utils.tool.org.apache.commons.jexl3.internal; + +import aiyh.utils.tool.org.apache.commons.jexl3.*; +import aiyh.utils.tool.org.apache.commons.jexl3.internal.introspection.SandboxUberspect; +import aiyh.utils.tool.org.apache.commons.jexl3.internal.introspection.Uberspect; +import aiyh.utils.tool.org.apache.commons.jexl3.introspection.JexlMethod; +import aiyh.utils.tool.org.apache.commons.jexl3.introspection.JexlSandbox; +import aiyh.utils.tool.org.apache.commons.jexl3.introspection.JexlUberspect; +import aiyh.utils.tool.org.apache.commons.jexl3.parser.*; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import java.nio.charset.Charset; +import java.util.*; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.function.Predicate; + +/** + * A JexlEngine implementation. + * + * @since 2.0 + */ +public class Engine extends JexlEngine { + /** + * Gets the default instance of Uberspect. + *

This is lazily initialized to avoid building a default instance if there + * is no use for it. The main reason for not using the default Uberspect instance is to + * be able to use a (low level) introspector created with a given logger + * instead of the default one.

+ *

Implemented as on demand holder idiom.

+ */ + private static final class UberspectHolder { + /** The default uberspector that handles all introspection patterns. */ + private static final Uberspect UBERSPECT = + new Uberspect(LogFactory.getLog(JexlEngine.class), JexlUberspect.JEXL_STRATEGY); + + /** Non-instantiable. */ + private UberspectHolder() { + } + } + + /** + * The name of the options pragma. + */ + protected static final String PRAGMA_OPTIONS = "jexl.options"; + /** + * The prefix of a namespace pragma. + */ + protected static final String PRAGMA_JEXLNS = "jexl.namespace."; + /** + * The Log to which all JexlEngine messages will be logged. + */ + protected final Log logger; + /** + * The JexlUberspect instance. + */ + protected final JexlUberspect uberspect; + /** + * The {@link JexlArithmetic} instance. + */ + protected final JexlArithmetic arithmetic; + /** + * The map of 'prefix:function' to object implementing the namespaces. + */ + protected final Map functions; + /** + * The maximum stack height. + */ + protected final int stackOverflow; + /** + * Whether this engine considers unknown variables, methods and constructors as errors. + */ + protected final boolean strict; + /** + * Whether this engine considers null in navigation expression as errors. + */ + protected final boolean safe; + /** + * Whether expressions evaluated by this engine will throw exceptions (false) or return null (true) on errors. + * Default is false. + */ + protected final boolean silent; + /** + * Whether expressions evaluated by this engine will throw JexlException.Cancel (true) or return null (false) when + * interrupted. + * Default is true when not silent and strict. + */ + protected final boolean cancellable; + /** + * Whether error messages will carry debugging information. + */ + protected final boolean debug; + /** + * The set of default script parsing features. + */ + protected final JexlFeatures scriptFeatures; + /** + * The set of default expression parsing features. + */ + protected final JexlFeatures expressionFeatures; + /** + * The default charset. + */ + protected final Charset charset; + /** + * The atomic parsing flag; true whilst parsing. + */ + protected final AtomicBoolean parsing = new AtomicBoolean(false); + /** + * The {@link Parser}; when parsing expressions, this engine uses the parser if it + * is not already in use otherwise it will create a new temporary one. + */ + protected final Parser parser = new Parser(new StringProvider(";")); //$NON-NLS-1$ + /** + * The expression max length to hit the cache. + */ + protected final int cacheThreshold; + /** + * The expression cache. + */ + protected final SoftCache cache; + /** + * The default jxlt engine. + */ + protected volatile TemplateEngine jxlt = null; + /** + * Collect all or only dot references. + */ + protected final int collectMode; + /** + * A cached version of the options. + */ + protected final JexlOptions options; + + /** + * Creates an engine with default arguments. + */ + public Engine() { + this(new JexlBuilder()); + } + + /** + * Creates a JEXL engine using the provided {@link JexlBuilder}. + * + * @param conf the builder + */ + public Engine(final JexlBuilder conf) { + // options: + this.options = conf.options().copy(); + this.strict = options.isStrict(); + this.safe = options.isSafe(); + this.silent = options.isSilent(); + this.cancellable = option(conf.cancellable(), !silent && strict); + options.setCancellable(cancellable); + this.debug = option(conf.debug(), true); + this.collectMode = conf.collectMode(); + this.stackOverflow = conf.stackOverflow() > 0 ? conf.stackOverflow() : Integer.MAX_VALUE; + // core properties: + final JexlUberspect uber = conf.uberspect() == null ? getUberspect(conf.logger(), conf.strategy()) : conf.uberspect(); + final ClassLoader loader = conf.loader(); + if (loader != null) { + uber.setClassLoader(loader); + } + final JexlSandbox sandbox = conf.sandbox(); + if (sandbox == null) { + this.uberspect = uber; + } else { + this.uberspect = new SandboxUberspect(uber, sandbox); + } + this.logger = conf.logger() == null ? LogFactory.getLog(JexlEngine.class) : conf.logger(); + this.arithmetic = conf.arithmetic() == null ? new JexlArithmetic(this.strict) : conf.arithmetic(); + options.setMathContext(arithmetic.getMathContext()); + options.setMathScale(arithmetic.getMathScale()); + options.setStrictArithmetic(arithmetic.isStrict()); + this.functions = conf.namespaces() == null ? Collections.emptyMap() : conf.namespaces(); + // parsing & features: + final JexlFeatures features = conf.features() == null ? DEFAULT_FEATURES : conf.features(); + Predicate nsTest = features.namespaceTest(); + final Set nsNames = functions.keySet(); + if (!nsNames.isEmpty()) { + nsTest = nsTest == JexlFeatures.TEST_STR_FALSE ? nsNames::contains : nsTest.or(nsNames::contains); + } + this.expressionFeatures = new JexlFeatures(features).script(false).namespaceTest(nsTest); + this.scriptFeatures = new JexlFeatures(features).script(true).namespaceTest(nsTest); + this.charset = conf.charset(); + // caching: + this.cache = conf.cache() <= 0 ? null : new SoftCache(conf.cache()); + this.cacheThreshold = conf.cacheThreshold(); + if (uberspect == null) { + throw new IllegalArgumentException("uberspect can not be null"); + } + } + + + /** + * Gets the default instance of Uberspect. + *

This is lazily initialized to avoid building a default instance if there + * is no use for it. The main reason for not using the default Uberspect instance is to + * be able to use a (low level) introspector created with a given logger + * instead of the default one.

+ * + * @param logger the logger to use for the underlying Uberspect + * @param strategy the property resolver strategy + * @return Uberspect the default uberspector instance. + */ + public static Uberspect getUberspect(final Log logger, final JexlUberspect.ResolverStrategy strategy) { + if ((logger == null || logger.equals(LogFactory.getLog(JexlEngine.class))) + && (strategy == null || strategy == JexlUberspect.JEXL_STRATEGY)) { + return UberspectHolder.UBERSPECT; + } + return new Uberspect(logger, strategy); + } + + @Override + public JexlUberspect getUberspect() { + return uberspect; + } + + @Override + public JexlArithmetic getArithmetic() { + return arithmetic; + } + + @Override + public boolean isDebug() { + return this.debug; + } + + @Override + public boolean isSilent() { + return this.silent; + } + + @Override + public boolean isStrict() { + return this.strict; + } + + @Override + public boolean isCancellable() { + return this.cancellable; + } + + @Override + public void setClassLoader(final ClassLoader loader) { + jxlt = null; + uberspect.setClassLoader(loader); + if (functions != null) { + final List names = new ArrayList(functions.keySet()); + for (final String name : names) { + final Object functor = functions.get(name); + if (functor instanceof Class) { + final Class fclass = ((Class) functor); + try { + final Class nclass = loader.loadClass(fclass.getName()); + if (nclass != fclass) { + functions.put(name, nclass); + } + } catch (final ClassNotFoundException xany) { + functions.put(name, fclass.getName()); + } + } + } + } + if (cache != null) { + cache.clear(); + } + } + + @Override + public Charset getCharset() { + return charset; + } + + /** + * Solves an optional option. + * + * @param conf the option as configured, may be null + * @param def the default value if null, shall not be null + * @param the option type + * @return conf or def + */ + private static T option(final T conf, final T def) { + return conf == null ? def : conf; + } + + /** + * Extracts the engine evaluation options from context if available, the engine + * options otherwise. + *

If the context is a options handle and the handled options shared instance flag + * is false, this method creates a copy of the options making them immutable during execution. + * + * @param context the context + * @return the options if any + */ + protected JexlOptions options(final JexlContext context) { + // Make a copy of the handled options if any + if (context instanceof JexlContext.OptionsHandle) { + final JexlOptions jexlo = ((JexlContext.OptionsHandle) context).getEngineOptions(); + if (jexlo != null) { + return jexlo.isSharedInstance() ? jexlo : jexlo.copy(); + } + } else if (context instanceof Options) { + // This condition and block for compatibility between 3.1 and 3.2 + final JexlOptions jexlo = options.copy(); + final JexlEngine jexl = this; + final Options opts = (Options) context; + jexlo.setCancellable(option(opts.isCancellable(), jexl.isCancellable())); + jexlo.setSilent(option(opts.isSilent(), jexl.isSilent())); + jexlo.setStrict(option(opts.isStrict(), jexl.isStrict())); + final JexlArithmetic jexla = jexl.getArithmetic(); + jexlo.setStrictArithmetic(option(opts.isStrictArithmetic(), jexla.isStrict())); + jexlo.setMathContext(opts.getArithmeticMathContext()); + jexlo.setMathScale(opts.getArithmeticMathScale()); + return jexlo; + } + return options; + } + + /** + * Compute a script options for evaluation. + *

This calls processPragma(...). + * + * @param script the script + * @param context the context + * @return the options + */ + protected JexlOptions options(final ASTJexlScript script, final JexlContext context) { + final JexlOptions opts = options(context); + if (opts != options) { + // when feature lexical, try hard to run lexical + if (scriptFeatures.isLexical()) { + opts.setLexical(true); + } + if (scriptFeatures.isLexicalShade()) { + opts.setLexicalShade(true); + } + } + if (script != null) { + // process script pragmas if any + processPragmas(script, context, opts); + } + return opts; + } + + /** + * Processes a script pragmas. + *

Only called from options(...) + * + * @param script the script + * @param context the context + * @param opts the options + */ + protected void processPragmas(final ASTJexlScript script, final JexlContext context, final JexlOptions opts) { + final Map pragmas = script.getPragmas(); + if (pragmas != null && !pragmas.isEmpty()) { + final JexlContext.PragmaProcessor processor = + context instanceof JexlContext.PragmaProcessor + ? (JexlContext.PragmaProcessor) context + : null; + Map ns = null; + for (final Map.Entry pragma : pragmas.entrySet()) { + final String key = pragma.getKey(); + final Object value = pragma.getValue(); + if (value instanceof String) { + if (PRAGMA_OPTIONS.equals(key)) { + // jexl.options + final String[] vs = value.toString().split(" "); + opts.setFlags(vs); + } else if (key.startsWith(PRAGMA_JEXLNS)) { + // jexl.namespace.*** + final String nsname = key.substring(PRAGMA_JEXLNS.length()); + if (nsname != null && !nsname.isEmpty()) { + if (ns == null) { + ns = new HashMap<>(functions); + } + final String nsclass = value.toString(); + try { + ns.put(nsname, uberspect.getClassLoader().loadClass(nsclass)); + } catch (final ClassNotFoundException e) { + ns.put(nsname, nsclass); + } + } + } + } + if (processor != null) { + processor.processPragma(key, value); + } + } + if (ns != null) { + opts.setNamespaces(ns); + } + } + } + + /** + * Sets options from this engine options. + * + * @param opts the options to set + * @return the options + */ + public JexlOptions optionsSet(final JexlOptions opts) { + if (opts != null) { + opts.set(options); + } + return opts; + } + + @Override + public TemplateEngine createJxltEngine(final boolean noScript, final int cacheSize, final char immediate, final char deferred) { + return new TemplateEngine(this, noScript, cacheSize, immediate, deferred); + } + + @Override + public void clearCache() { + if (cache != null) { + cache.clear(); + } + } + + /** + * Creates an interpreter. + * + * @param context a JexlContext; if null, the empty context is used instead. + * @param frame the interpreter frame + * @param opts the evaluation options + * @return an Interpreter + */ + protected Interpreter createInterpreter(final JexlContext context, final Frame frame, final JexlOptions opts) { + return new Interpreter(this, opts, context, frame); + } + + + @Override + public Script createExpression(final JexlInfo info, final String expression) { + return createScript(expressionFeatures, info, expression, null); + } + + @Override + public Script createScript(final JexlFeatures features, final JexlInfo info, final String scriptText, final String... names) { + if (scriptText == null) { + throw new NullPointerException("source is null"); + } + final String source = trimSource(scriptText); + final Scope scope = names == null || names.length == 0 ? null : new Scope(null, names); + final JexlFeatures ftrs = features == null ? scriptFeatures : features; + final ASTJexlScript tree = parse(info, ftrs, source, scope); + return new Script(this, source, tree); + } + + /** + * The features allowed for property set/get methods. + */ + protected static final JexlFeatures PROPERTY_FEATURES = new JexlFeatures() + .localVar(false) + .loops(false) + .lambda(false) + .script(false) + .arrayReferenceExpr(false) + .methodCall(false) + .register(true); + + @Override + public Object getProperty(final Object bean, final String expr) { + return getProperty(null, bean, expr); + } + + @Override + public Object getProperty(JexlContext context, final Object bean, final String expr) { + if (context == null) { + context = EMPTY_CONTEXT; + } + // synthesize expr using register + String src = trimSource(expr); + src = "#0" + (src.charAt(0) == '[' ? "" : ".") + src; + try { + final Scope scope = new Scope(null, "#0"); + final ASTJexlScript script = parse(null, PROPERTY_FEATURES, src, scope); + final JexlNode node = script.jjtGetChild(0); + final Frame frame = script.createFrame(bean); + final Interpreter interpreter = createInterpreter(context, frame, options); + return interpreter.visitLexicalNode(node, null); + } catch (final JexlException xjexl) { + if (silent) { + logger.warn(xjexl.getMessage(), xjexl.getCause()); + return null; + } + throw xjexl.clean(); + } + } + + @Override + public void setProperty(final Object bean, final String expr, final Object value) { + setProperty(null, bean, expr, value); + } + + @Override + public void setProperty(JexlContext context, final Object bean, final String expr, final Object value) { + if (context == null) { + context = EMPTY_CONTEXT; + } + // synthesize expr using register + String src = trimSource(expr); + src = "#0" + (src.charAt(0) == '[' ? "" : ".") + src + "=" + "#1"; + try { + final Scope scope = new Scope(null, "#0", "#1"); + final ASTJexlScript script = parse(null, PROPERTY_FEATURES, src, scope); + final JexlNode node = script.jjtGetChild(0); + final Frame frame = script.createFrame(bean, value); + final Interpreter interpreter = createInterpreter(context, frame, options); + interpreter.visitLexicalNode(node, null); + } catch (final JexlException xjexl) { + if (silent) { + logger.warn(xjexl.getMessage(), xjexl.getCause()); + return; + } + throw xjexl.clean(); + } + } + + @Override + public Object invokeMethod(final Object obj, final String meth, final Object... args) { + JexlException xjexl = null; + Object result = null; + final JexlInfo info = debug ? createInfo() : null; + try { + JexlMethod method = uberspect.getMethod(obj, meth, args); + if (method == null && arithmetic.narrowArguments(args)) { + method = uberspect.getMethod(obj, meth, args); + } + if (method != null) { + result = method.invoke(obj, args); + } else { + xjexl = new JexlException.Method(info, meth, args); + } + } catch (final JexlException xany) { + xjexl = xany; + } catch (final Exception xany) { + xjexl = new JexlException.Method(info, meth, args, xany); + } + if (xjexl != null) { + if (!silent) { + throw xjexl.clean(); + } + logger.warn(xjexl.getMessage(), xjexl.getCause()); + result = null; + } + return result; + } + + @Override + public T newInstance(final Class clazz, final Object... args) { + return clazz.cast(doCreateInstance(clazz, args)); + } + + @Override + public Object newInstance(final String clazz, final Object... args) { + return doCreateInstance(clazz, args); + } + + /** + * Creates a new instance of an object using the most appropriate constructor + * based on the arguments. + * + * @param clazz the class to instantiate + * @param args the constructor arguments + * @return the created object instance or null on failure when silent + */ + protected Object doCreateInstance(final Object clazz, final Object... args) { + JexlException xjexl = null; + Object result = null; + final JexlInfo info = debug ? createInfo() : null; + try { + JexlMethod ctor = uberspect.getConstructor(clazz, args); + if (ctor == null && arithmetic.narrowArguments(args)) { + ctor = uberspect.getConstructor(clazz, args); + } + if (ctor != null) { + result = ctor.invoke(clazz, args); + } else { + xjexl = new JexlException.Method(info, clazz.toString(), args); + } + } catch (final JexlException xany) { + xjexl = xany; + } catch (final Exception xany) { + xjexl = new JexlException.Method(info, clazz.toString(), args, xany); + } + if (xjexl != null) { + if (silent) { + logger.warn(xjexl.getMessage(), xjexl.getCause()); + return null; + } + throw xjexl.clean(); + } + return result; + } + + /** + * Swaps the current thread local context. + * + * @param tls the context or null + * @return the previous thread local context + */ + protected JexlContext.ThreadLocal putThreadLocal(final JexlContext.ThreadLocal tls) { + final JexlContext.ThreadLocal local = CONTEXT.get(); + CONTEXT.set(tls); + return local; + } + + /** + * Swaps the current thread local engine. + * + * @param jexl the engine or null + * @return the previous thread local engine + */ + protected JexlEngine putThreadEngine(final JexlEngine jexl) { + final JexlEngine pjexl = ENGINE.get(); + ENGINE.set(jexl); + return pjexl; + } + + /** + * Gets the list of variables accessed by a script. + *

This method will visit all nodes of a script and extract all variables whether they + * are written in 'dot' or 'bracketed' notation. (a.b is equivalent to a['b']).

+ * + * @param script the script + * @return the set of variables, each as a list of strings (ant-ish variables use more than 1 string) + * or the empty set if no variables are used + */ + protected Set> getVariables(final ASTJexlScript script) { + final VarCollector collector = varCollector(); + getVariables(script, script, collector); + return collector.collected(); + } + + /** + * Creates a collector instance. + * + * @return a collector instance + */ + protected VarCollector varCollector() { + return new VarCollector(this.collectMode); + } + + /** + * Utility class to collect variables. + */ + protected static class VarCollector { + /** + * The collected variables represented as a set of list of strings. + */ + private final Set> refs = new LinkedHashSet>(); + /** + * The current variable being collected. + */ + private List ref = new ArrayList(); + /** + * The node that started the collect. + */ + private JexlNode root = null; + /** + * Whether constant array-access is considered equivalent to dot-access; + * if so, > 1 means collect any constant (set,map,...) instead of just + * strings and numbers. + */ + private int mode = 1; + + /** + * Constructor. + * + * @param constaa whether constant array-access is considered equivalent to dot-access + */ + protected VarCollector(final int constaa) { + mode = constaa; + } + + /** + * Starts/stops a variable collect. + * + * @param node starts if not null, stop if null + */ + public void collect(final JexlNode node) { + if (!ref.isEmpty()) { + refs.add(ref); + ref = new ArrayList(); + } + root = node; + } + + /** + * @return true if currently collecting a variable, false otherwise + */ + public boolean isCollecting() { + return root instanceof ASTIdentifier; + } + + /** + * Adds a 'segment' to the variable being collected. + * + * @param name the name + */ + public void add(final String name) { + ref.add(name); + } + + /** + * @return the collected variables + */ + public Set> collected() { + return refs; + } + } + + /** + * Fills up the list of variables accessed by a node. + * + * @param script the owning script + * @param node the node + * @param collector the variable collector + */ + protected void getVariables(final ASTJexlScript script, final JexlNode node, final VarCollector collector) { + if (node instanceof ASTIdentifier) { + final JexlNode parent = node.jjtGetParent(); + if (parent instanceof ASTMethodNode || parent instanceof ASTFunctionNode) { + // skip identifiers for methods and functions + collector.collect(null); + return; + } + final ASTIdentifier identifier = (ASTIdentifier) node; + final int symbol = identifier.getSymbol(); + // symbols that are captured are considered "global" variables + if (symbol >= 0 && script != null && !script.isCapturedSymbol(symbol)) { + collector.collect(null); + } else { + // start collecting from identifier + collector.collect(identifier); + collector.add(identifier.getName()); + } + } else if (node instanceof ASTIdentifierAccess) { + final JexlNode parent = node.jjtGetParent(); + if (parent instanceof ASTMethodNode || parent instanceof ASTFunctionNode) { + // skip identifiers for methods and functions + collector.collect(null); + return; + } + // belt and suspender since an identifier should have been seen first + if (collector.isCollecting()) { + collector.add(((ASTIdentifierAccess) node).getName()); + } + } else if (node instanceof ASTArrayAccess && collector.mode > 0) { + final int num = node.jjtGetNumChildren(); + // collect only if array access is const and follows an identifier + boolean collecting = collector.isCollecting(); + for (int i = 0; i < num; ++i) { + final JexlNode child = node.jjtGetChild(i); + if (collecting && child.isConstant()) { + // collect all constants or only string and number literals + final boolean collect = collector.mode > 1 + || (child instanceof ASTStringLiteral || child instanceof ASTNumberLiteral); + if (collect) { + final String image = child.toString(); + collector.add(image); + } + } else { + collecting = false; + collector.collect(null); + getVariables(script, child, collector); + collector.collect(null); + } + } + } else { + final int num = node.jjtGetNumChildren(); + for (int i = 0; i < num; ++i) { + getVariables(script, node.jjtGetChild(i), collector); + } + collector.collect(null); + } + } + + /** + * Gets the array of parameters from a script. + * + * @param script the script + * @return the parameters which may be empty (but not null) if no parameters were defined + * @since 3.0 + */ + protected String[] getParameters(final JexlScript script) { + return script.getParameters(); + } + + /** + * Gets the array of local variable from a script. + * + * @param script the script + * @return the local variables array which may be empty (but not null) if no local variables were defined + * @since 3.0 + */ + protected String[] getLocalVariables(final JexlScript script) { + return script.getLocalVariables(); + } + + /** + * Parses an expression. + * + * @param info information structure + * @param expr whether we parse an expression or a feature + * @param src the expression to parse + * @param scope the script frame + * @return the parsed tree + * @throws JexlException if any error occurred during parsing + */ + protected ASTJexlScript parse(final JexlInfo info, final boolean expr, final String src, final Scope scope) { + return parse(info, expr ? this.expressionFeatures : this.scriptFeatures, src, scope); + } + + /** + * Parses an expression. + * + * @param info information structure + * @param parsingf the set of parsing features + * @param src the expression to parse + * @param scope the script frame + * @return the parsed tree + * @throws JexlException if any error occurred during parsing + */ + protected ASTJexlScript parse(final JexlInfo info, final JexlFeatures parsingf, final String src, final Scope scope) { + final boolean cached = src.length() < cacheThreshold && cache != null; + JexlFeatures features = parsingf != null ? parsingf : DEFAULT_FEATURES; + // if (features.getNameSpaces().isEmpty() && !functions.isEmpty()) { + // features = new JexlFeatures(features).nameSpaces(functions.keySet()); + // } + final Source source = cached ? new Source(features, src) : null; + ASTJexlScript script = null; + if (source != null) { + script = cache.get(source); + if (script != null) { + final Scope f = script.getScope(); + if ((f == null && scope == null) || (f != null && f.equals(scope))) { + return script; + } + } + } + final JexlInfo ninfo = info == null && debug ? createInfo() : info; + // if parser not in use... + if (parsing.compareAndSet(false, true)) { + try { + // lets parse + script = parser.parse(ninfo, features, src, scope); + } finally { + // no longer in use + parsing.set(false); + } + } else { + // ...otherwise parser was in use, create a new temporary one + final Parser lparser = new Parser(new StringProvider(";")); + script = lparser.parse(ninfo, features, src, scope); + } + if (source != null) { + cache.put(source, script); + } + return script; + } + + /** + * Trims the source from front and ending spaces. + * + * @param str expression to clean + * @return trimmed expression ending in a semicolon + */ + protected String trimSource(final CharSequence str) { + if (str != null) { + int start = 0; + int end = str.length(); + if (end > 0) { + // trim front spaces + while (start < end && Character.isSpaceChar(str.charAt(start))) { + ++start; + } + // trim ending spaces; end is > 0 since start >= 0 + while (end > start && Character.isSpaceChar(str.charAt(end - 1))) { + --end; + } + return str.subSequence(start, end).toString(); + } + return ""; + } + return null; + } + + /** + * Gets and/or creates a default template engine. + * + * @return a template engine + */ + protected TemplateEngine jxlt() { + TemplateEngine e = jxlt; + if (e == null) { + synchronized (this) { + e = jxlt; + if (e == null) { + e = new TemplateEngine(this, true, 0, '$', '#'); + jxlt = e; + } + } + } + return e; + } +} diff --git a/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/internal/Frame.java b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/internal/Frame.java new file mode 100644 index 0000000..3b2cdd2 --- /dev/null +++ b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/internal/Frame.java @@ -0,0 +1,126 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package aiyh.utils.tool.org.apache.commons.jexl3.internal; + +import java.util.Arrays; + +/** + * A call frame, created from a scope, stores the arguments and local variables in a "stack frame" (sic). + * @since 3.0 + */ +public final class Frame { + /** The scope. */ + private final Scope scope; + /** The actual stack frame. */ + private final Object[] stack; + /** Number of curried parameters. */ + private final int curried; + + /** + * Creates a new frame. + * @param s the scope + * @param r the stack frame + * @param c the number of curried parameters + */ + Frame(final Scope s, final Object[] r, final int c) { + scope = s; + stack = r; + curried = c; + } + + /** + * Gets this script unbound parameters, i.e. parameters not bound through curry(). + * @return the parameter names + */ + public String[] getUnboundParameters() { + return scope.getParameters(curried); + } + + /** + * Gets the scope. + * @return this frame scope + */ + public Scope getScope() { + return scope; + } + + @Override + public int hashCode() { + return Arrays.deepHashCode(this.stack); + } + + @Override + public boolean equals(final Object obj) { + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final Frame other = (Frame) obj; + return Arrays.deepEquals(this.stack, other.stack); + } + + /** + * Gets a value. + * @param s the offset in this frame + * @return the stacked value + */ + Object get(final int s) { + return stack[s]; + } + + /** + * Whether this frame defines a symbol, ie declared it and assigned it a value. + * @param s the offset in this frame + * @return true if this symbol has been assigned a value, false otherwise + */ + boolean has(final int s) { + return s >= 0 && s < stack.length && stack[s] != Scope.UNDECLARED; + } + + /** + * Sets a value. + * @param r the offset in this frame + * @param value the value to set in this frame + */ + void set(final int r, final Object value) { + stack[r] = value; + } + + /** + * Assign values to this frame. + * @param values the values + * @return this frame + */ + Frame assign(final Object... values) { + if (stack != null) { + final int nparm = scope.getArgCount(); + final Object[] copy = stack.clone(); + int ncopy = 0; + if (values != null && values.length > 0) { + ncopy = Math.min(nparm - curried, Math.min(nparm, values.length)); + System.arraycopy(values, 0, copy, curried, ncopy); + } + // unbound parameters are defined as null + Arrays.fill(copy, curried + ncopy, nparm, null); + return new Frame(scope, copy, curried + ncopy); + } + return this; + } + +} diff --git a/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/internal/IntegerRange.java b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/internal/IntegerRange.java new file mode 100644 index 0000000..30191f3 --- /dev/null +++ b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/internal/IntegerRange.java @@ -0,0 +1,318 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package aiyh.utils.tool.org.apache.commons.jexl3.internal; + +import java.lang.reflect.Array; +import java.util.Collection; +import java.util.Iterator; +import java.util.NoSuchElementException; + +/** + * A range of integers. + */ +public abstract class IntegerRange implements Collection { + /** The lower boundary. */ + protected final int min; + /** The upper boundary. */ + protected final int max; + + /** + * Creates a range, ascending or descending depending on boundaries order. + * + * @param from the lower inclusive boundary + * @param to the higher inclusive boundary + * @return a range + */ + public static IntegerRange create(final int from, final int to) { + if (from <= to) { + return new Ascending(from, to); + } + return new Descending(to, from); + } + + /** + * Creates a new range. + * + * @param from the lower inclusive boundary + * @param to the higher inclusive boundary + */ + public IntegerRange(final int from, final int to) { + min = from; + max = to; + } + + /** + * Gets the interval minimum value. + * + * @return the low boundary + */ + public int getMin() { + return min; + } + + /** + * Gets the interval maximum value. + * + * @return the high boundary + */ + public int getMax() { + return max; + } + + + @Override + public int hashCode() { + int hash = getClass().hashCode(); + // CSOFF: MagicNumber + hash = 13 * hash + this.min; + hash = 13 * hash + this.max; + // CSON: MagicNumber + return hash; + } + + @Override + public boolean equals(final Object obj) { + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final IntegerRange other = (IntegerRange) obj; + if (this.min != other.min) { + return false; + } + return this.max == other.max; + } + + @Override + public abstract Iterator iterator(); + + @Override + public int size() { + return max - min + 1; + } + + @Override + public boolean isEmpty() { + return false; + } + + @Override + public boolean contains(final Object o) { + if (o instanceof Number) { + final long v = ((Number) o).intValue(); + return min <= v && v <= max; + } + return false; + } + + @Override + public Object[] toArray() { + final int size = size(); + final Object[] array = new Object[size]; + for (int a = 0; a < size; ++a) { + array[a] = min + a; + } + return array; + } + + @Override + @SuppressWarnings("unchecked") + public T[] toArray(final T[] array) { + final Class ct = array.getClass().getComponentType(); + final int length = size(); + T[] copy = array; + if (ct.isAssignableFrom(Integer.class)) { + if (array.length < length) { + copy = (T[]) Array.newInstance(ct, length); + } + for (int a = 0; a < length; ++a) { + Array.set(copy, a, min + a); + } + if (length < copy.length) { + copy[length] = null; + } + return copy; + } + throw new UnsupportedOperationException(); + } + + @Override + public boolean containsAll(final Collection c) { + for (final Object cc : c) { + if (!contains(cc)) { + return false; + } + } + return true; + } + + @Override + public boolean add(final Integer e) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean remove(final Object o) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean addAll(final Collection c) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean removeAll(final Collection c) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean retainAll(final Collection c) { + throw new UnsupportedOperationException(); + } + + @Override + public void clear() { + throw new UnsupportedOperationException(); + } + + /** + * Ascending integer range. + */ + public static class Ascending extends IntegerRange { + /** + * Constructor. + * + * @param from lower boundary + * @param to upper boundary + */ + protected Ascending(final int from, final int to) { + super(from, to); + } + + @Override + public Iterator iterator() { + return new AscIntegerIterator(min, max); + } + } + + /** + * Descending integer range. + */ + public static class Descending extends IntegerRange { + /** + * Constructor. + * + * @param from upper boundary + * @param to lower boundary + */ + protected Descending(final int from, final int to) { + super(from, to); + } + + @Override + public Iterator iterator() { + return new DescIntegerIterator(min, max); + } + } +} + +/** + * An ascending iterator on an integer range. + */ +class AscIntegerIterator implements Iterator { + /** The lower boundary. */ + private final int min; + /** The upper boundary. */ + private final int max; + /** The current value. */ + private int cursor; + + /** + * Creates a iterator on the range. + * + * @param l low boundary + * @param h high boundary + */ + public AscIntegerIterator(final int l, final int h) { + min = l; + max = h; + cursor = min; + } + + @Override + public boolean hasNext() { + return cursor <= max; + } + + @Override + public Integer next() { + if (cursor <= max) { + return cursor++; + } + throw new NoSuchElementException(); + } + + @Override + public void remove() { + throw new UnsupportedOperationException("Not supported."); + } +} + +/** + * A descending iterator on an integer range. + */ +class DescIntegerIterator implements Iterator { + /** The lower boundary. */ + private final int min; + /** The upper boundary. */ + private final int max; + /** The current value. */ + private int cursor; + + /** + * Creates a iterator on the range. + * + * @param l low boundary + * @param h high boundary + */ + public DescIntegerIterator(final int l, final int h) { + min = l; + max = h; + cursor = max; + } + + @Override + public boolean hasNext() { + return cursor >= min; + } + + @Override + public Integer next() { + if (cursor >= min) { + return cursor--; + } + throw new NoSuchElementException(); + } + + @Override + public void remove() { + throw new UnsupportedOperationException("Not supported."); + } +} diff --git a/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/internal/Interpreter.java b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/internal/Interpreter.java new file mode 100644 index 0000000..a5ad945 --- /dev/null +++ b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/internal/Interpreter.java @@ -0,0 +1,1855 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +// CSOFF: FileLength +package aiyh.utils.tool.org.apache.commons.jexl3.internal; + +import aiyh.utils.tool.org.apache.commons.jexl3.*; +import aiyh.utils.tool.org.apache.commons.jexl3.introspection.JexlMethod; +import aiyh.utils.tool.org.apache.commons.jexl3.introspection.JexlPropertyGet; +import aiyh.utils.tool.org.apache.commons.jexl3.parser.*; + +import java.util.Iterator; +import java.util.concurrent.Callable; + +/** + * An interpreter of JEXL syntax. + * + * @since 2.0 + */ +public class Interpreter extends InterpreterBase { + /** Frame height. */ + protected int fp = 0; + /** Symbol values. */ + protected final Frame frame; + /** Block micro-frames. */ + protected LexicalFrame block = null; + + /** + * The thread local interpreter. + */ + protected static final ThreadLocal INTER = + new ThreadLocal(); + + /** + * Creates an interpreter. + * + * @param engine the engine creating this interpreter + * @param aContext the evaluation context, global variables, methods and functions + * @param opts the evaluation options, flags modifying evaluation behavior + * @param eFrame the evaluation frame, arguments and local variables + */ + protected Interpreter(final Engine engine, final JexlOptions opts, final JexlContext aContext, final Frame eFrame) { + super(engine, opts, aContext); + this.frame = eFrame; + } + + /** + * Copy constructor. + * + * @param ii the interpreter to copy + * @param jexla the arithmetic instance to use (or null) + */ + protected Interpreter(final Interpreter ii, final JexlArithmetic jexla) { + super(ii, jexla); + frame = ii.frame; + block = ii.block != null ? new LexicalFrame(ii.block) : null; + } + + /** + * Swaps the current thread local interpreter. + * + * @param inter the interpreter or null + * @return the previous thread local interpreter + */ + protected Interpreter putThreadInterpreter(final Interpreter inter) { + final Interpreter pinter = INTER.get(); + INTER.set(inter); + return pinter; + } + + /** + * Interpret the given script/expression. + *

+ * If the underlying JEXL engine is silent, errors will be logged through + * its logger as warning. + * + * @param node the script or expression to interpret. + * @return the result of the interpretation. + * @throws JexlException if any error occurs during interpretation. + */ + public Object interpret(final JexlNode node) { + JexlContext.ThreadLocal tcontext = null; + JexlEngine tjexl = null; + Interpreter tinter = null; + try { + tinter = putThreadInterpreter(this); + if (tinter != null) { + fp = tinter.fp + 1; + } + if (context instanceof JexlContext.ThreadLocal) { + tcontext = jexl.putThreadLocal((JexlContext.ThreadLocal) context); + } + tjexl = jexl.putThreadEngine(jexl); + if (fp > jexl.stackOverflow) { + throw new JexlException.StackOverflow(node.jexlInfo(), "jexl (" + jexl.stackOverflow + ")", null); + } + cancelCheck(node); + return node.jjtAccept(this, null); + } catch (final StackOverflowError xstack) { + final JexlException xjexl = new JexlException.StackOverflow(node.jexlInfo(), "jvm", xstack); + if (!isSilent()) { + throw xjexl.clean(); + } + if (logger.isWarnEnabled()) { + logger.warn(xjexl.getMessage(), xjexl.getCause()); + } + } catch (final JexlException.Return xreturn) { + return xreturn.getValue(); + } catch (final JexlException.Cancel xcancel) { + // cancelled |= Thread.interrupted(); + cancelled.weakCompareAndSet(false, Thread.interrupted()); + if (isCancellable()) { + throw xcancel.clean(); + } + } catch (final JexlException xjexl) { + if (!isSilent()) { + throw xjexl.clean(); + } + if (logger.isWarnEnabled()) { + logger.warn(xjexl.getMessage(), xjexl.getCause()); + } + } finally { + synchronized (this) { + if (functors != null) { + for (final Object functor : functors.values()) { + closeIfSupported(functor); + } + functors.clear(); + functors = null; + } + } + jexl.putThreadEngine(tjexl); + if (context instanceof JexlContext.ThreadLocal) { + jexl.putThreadLocal(tcontext); + } + if (tinter != null) { + fp = tinter.fp - 1; + } + putThreadInterpreter(tinter); + } + return null; + } + + /** + * Gets an attribute of an object. + * + * @param object to retrieve value from + * @param attribute the attribute of the object, e.g. an index (1, 0, 2) or key for a map + * @return the attribute value + */ + public Object getAttribute(final Object object, final Object attribute) { + return getAttribute(object, attribute, null); + } + + /** + * Sets an attribute of an object. + * + * @param object to set the value to + * @param attribute the attribute of the object, e.g. an index (1, 0, 2) or key for a map + * @param value the value to assign to the object's attribute + */ + public void setAttribute(final Object object, final Object attribute, final Object value) { + setAttribute(object, attribute, value, null); + } + + @Override + protected Object visit(final ASTAddNode node, final Object data) { + final Object left = node.jjtGetChild(0).jjtAccept(this, data); + final Object right = node.jjtGetChild(1).jjtAccept(this, data); + try { + final Object result = operators.tryOverload(node, JexlOperator.ADD, left, right); + return result != JexlEngine.TRY_FAILED ? result : arithmetic.add(left, right); + } catch (final ArithmeticException xrt) { + throw new JexlException(node, "+ error", xrt); + } + } + + @Override + protected Object visit(final ASTSubNode node, final Object data) { + final Object left = node.jjtGetChild(0).jjtAccept(this, data); + final Object right = node.jjtGetChild(1).jjtAccept(this, data); + try { + final Object result = operators.tryOverload(node, JexlOperator.SUBTRACT, left, right); + return result != JexlEngine.TRY_FAILED ? result : arithmetic.subtract(left, right); + } catch (final ArithmeticException xrt) { + throw new JexlException(node, "- error", xrt); + } + } + + @Override + protected Object visit(final ASTMulNode node, final Object data) { + final Object left = node.jjtGetChild(0).jjtAccept(this, data); + final Object right = node.jjtGetChild(1).jjtAccept(this, data); + try { + final Object result = operators.tryOverload(node, JexlOperator.MULTIPLY, left, right); + return result != JexlEngine.TRY_FAILED ? result : arithmetic.multiply(left, right); + } catch (final ArithmeticException xrt) { + final JexlNode xnode = findNullOperand(xrt, node, left, right); + throw new JexlException(xnode, "* error", xrt); + } + } + + @Override + protected Object visit(final ASTDivNode node, final Object data) { + final Object left = node.jjtGetChild(0).jjtAccept(this, data); + final Object right = node.jjtGetChild(1).jjtAccept(this, data); + try { + final Object result = operators.tryOverload(node, JexlOperator.DIVIDE, left, right); + return result != JexlEngine.TRY_FAILED ? result : arithmetic.divide(left, right); + } catch (final ArithmeticException xrt) { + if (!arithmetic.isStrict()) { + return 0.0d; + } + final JexlNode xnode = findNullOperand(xrt, node, left, right); + throw new JexlException(xnode, "/ error", xrt); + } + } + + @Override + protected Object visit(final ASTModNode node, final Object data) { + final Object left = node.jjtGetChild(0).jjtAccept(this, data); + final Object right = node.jjtGetChild(1).jjtAccept(this, data); + try { + final Object result = operators.tryOverload(node, JexlOperator.MOD, left, right); + return result != JexlEngine.TRY_FAILED ? result : arithmetic.mod(left, right); + } catch (final ArithmeticException xrt) { + if (!arithmetic.isStrict()) { + return 0.0d; + } + final JexlNode xnode = findNullOperand(xrt, node, left, right); + throw new JexlException(xnode, "% error", xrt); + } + } + + @Override + protected Object visit(final ASTBitwiseAndNode node, final Object data) { + final Object left = node.jjtGetChild(0).jjtAccept(this, data); + final Object right = node.jjtGetChild(1).jjtAccept(this, data); + try { + final Object result = operators.tryOverload(node, JexlOperator.AND, left, right); + return result != JexlEngine.TRY_FAILED ? result : arithmetic.and(left, right); + } catch (final ArithmeticException xrt) { + throw new JexlException(node, "& error", xrt); + } + } + + @Override + protected Object visit(final ASTBitwiseOrNode node, final Object data) { + final Object left = node.jjtGetChild(0).jjtAccept(this, data); + final Object right = node.jjtGetChild(1).jjtAccept(this, data); + try { + final Object result = operators.tryOverload(node, JexlOperator.OR, left, right); + return result != JexlEngine.TRY_FAILED ? result : arithmetic.or(left, right); + } catch (final ArithmeticException xrt) { + throw new JexlException(node, "| error", xrt); + } + } + + @Override + protected Object visit(final ASTBitwiseXorNode node, final Object data) { + final Object left = node.jjtGetChild(0).jjtAccept(this, data); + final Object right = node.jjtGetChild(1).jjtAccept(this, data); + try { + final Object result = operators.tryOverload(node, JexlOperator.XOR, left, right); + return result != JexlEngine.TRY_FAILED ? result : arithmetic.xor(left, right); + } catch (final ArithmeticException xrt) { + throw new JexlException(node, "^ error", xrt); + } + } + + @Override + protected Object visit(final ASTEQNode node, final Object data) { + final Object left = node.jjtGetChild(0).jjtAccept(this, data); + final Object right = node.jjtGetChild(1).jjtAccept(this, data); + try { + final Object result = operators.tryOverload(node, JexlOperator.EQ, left, right); + return result != JexlEngine.TRY_FAILED ? result : arithmetic.equals(left, right); + } catch (final ArithmeticException xrt) { + throw new JexlException(node, "== error", xrt); + } + } + + @Override + protected Object visit(final ASTNENode node, final Object data) { + final Object left = node.jjtGetChild(0).jjtAccept(this, data); + final Object right = node.jjtGetChild(1).jjtAccept(this, data); + try { + final Object result = operators.tryOverload(node, JexlOperator.EQ, left, right); + return result != JexlEngine.TRY_FAILED + ? !arithmetic.toBoolean(result) + : !arithmetic.equals(left, right); + } catch (final ArithmeticException xrt) { + final JexlNode xnode = findNullOperand(xrt, node, left, right); + throw new JexlException(xnode, "!= error", xrt); + } + } + + @Override + protected Object visit(final ASTGENode node, final Object data) { + final Object left = node.jjtGetChild(0).jjtAccept(this, data); + final Object right = node.jjtGetChild(1).jjtAccept(this, data); + try { + final Object result = operators.tryOverload(node, JexlOperator.GTE, left, right); + return result != JexlEngine.TRY_FAILED + ? result + : arithmetic.greaterThanOrEqual(left, right); + } catch (final ArithmeticException xrt) { + throw new JexlException(node, ">= error", xrt); + } + } + + @Override + protected Object visit(final ASTGTNode node, final Object data) { + final Object left = node.jjtGetChild(0).jjtAccept(this, data); + final Object right = node.jjtGetChild(1).jjtAccept(this, data); + try { + final Object result = operators.tryOverload(node, JexlOperator.GT, left, right); + return result != JexlEngine.TRY_FAILED + ? result + : arithmetic.greaterThan(left, right); + } catch (final ArithmeticException xrt) { + throw new JexlException(node, "> error", xrt); + } + } + + @Override + protected Object visit(final ASTLENode node, final Object data) { + final Object left = node.jjtGetChild(0).jjtAccept(this, data); + final Object right = node.jjtGetChild(1).jjtAccept(this, data); + try { + final Object result = operators.tryOverload(node, JexlOperator.LTE, left, right); + return result != JexlEngine.TRY_FAILED + ? result + : arithmetic.lessThanOrEqual(left, right); + } catch (final ArithmeticException xrt) { + throw new JexlException(node, "<= error", xrt); + } + } + + @Override + protected Object visit(final ASTLTNode node, final Object data) { + final Object left = node.jjtGetChild(0).jjtAccept(this, data); + final Object right = node.jjtGetChild(1).jjtAccept(this, data); + try { + final Object result = operators.tryOverload(node, JexlOperator.LT, left, right); + return result != JexlEngine.TRY_FAILED + ? result + : arithmetic.lessThan(left, right); + } catch (final ArithmeticException xrt) { + throw new JexlException(node, "< error", xrt); + } + } + + @Override + protected Object visit(final ASTSWNode node, final Object data) { + final Object left = node.jjtGetChild(0).jjtAccept(this, data); + final Object right = node.jjtGetChild(1).jjtAccept(this, data); + return operators.startsWith(node, "^=", left, right); + } + + @Override + protected Object visit(final ASTNSWNode node, final Object data) { + final Object left = node.jjtGetChild(0).jjtAccept(this, data); + final Object right = node.jjtGetChild(1).jjtAccept(this, data); + return !operators.startsWith(node, "^!", left, right); + } + + @Override + protected Object visit(final ASTEWNode node, final Object data) { + final Object left = node.jjtGetChild(0).jjtAccept(this, data); + final Object right = node.jjtGetChild(1).jjtAccept(this, data); + return operators.endsWith(node, "$=", left, right); + } + + @Override + protected Object visit(final ASTNEWNode node, final Object data) { + final Object left = node.jjtGetChild(0).jjtAccept(this, data); + final Object right = node.jjtGetChild(1).jjtAccept(this, data); + return !operators.endsWith(node, "$!", left, right); + } + + @Override + protected Object visit(final ASTERNode node, final Object data) { + final Object left = node.jjtGetChild(0).jjtAccept(this, data); + final Object right = node.jjtGetChild(1).jjtAccept(this, data); + return operators.contains(node, "=~", right, left); + } + + @Override + protected Object visit(final ASTNRNode node, final Object data) { + final Object left = node.jjtGetChild(0).jjtAccept(this, data); + final Object right = node.jjtGetChild(1).jjtAccept(this, data); + return !operators.contains(node, "!~", right, left); + } + + @Override + protected Object visit(final ASTRangeNode node, final Object data) { + final Object left = node.jjtGetChild(0).jjtAccept(this, data); + final Object right = node.jjtGetChild(1).jjtAccept(this, data); + try { + return arithmetic.createRange(left, right); + } catch (final ArithmeticException xrt) { + final JexlNode xnode = findNullOperand(xrt, node, left, right); + throw new JexlException(xnode, ".. error", xrt); + } + } + + @Override + protected Object visit(final ASTUnaryMinusNode node, final Object data) { + // use cached value if literal + final Object value = node.jjtGetValue(); + if (value != null && !(value instanceof JexlMethod)) { + return value; + } + final JexlNode valNode = node.jjtGetChild(0); + final Object val = valNode.jjtAccept(this, data); + try { + final Object result = operators.tryOverload(node, JexlOperator.NEGATE, val); + if (result != JexlEngine.TRY_FAILED) { + return result; + } + Object number = arithmetic.negate(val); + // attempt to recoerce to literal class + // cache if number literal and negate is idempotent + if (number instanceof Number && valNode instanceof ASTNumberLiteral) { + number = arithmetic.narrowNumber((Number) number, ((ASTNumberLiteral) valNode).getLiteralClass()); + if (arithmetic.isNegateStable()) { + node.jjtSetValue(number); + } + } + return number; + } catch (final ArithmeticException xrt) { + throw new JexlException(valNode, "- error", xrt); + } + } + + @Override + protected Object visit(final ASTUnaryPlusNode node, final Object data) { + // use cached value if literal + final Object value = node.jjtGetValue(); + if (value != null && !(value instanceof JexlMethod)) { + return value; + } + final JexlNode valNode = node.jjtGetChild(0); + final Object val = valNode.jjtAccept(this, data); + try { + final Object result = operators.tryOverload(node, JexlOperator.POSITIVIZE, val); + if (result != JexlEngine.TRY_FAILED) { + return result; + } + final Object number = arithmetic.positivize(val); + if (valNode instanceof ASTNumberLiteral + && number instanceof Number + && arithmetic.isPositivizeStable()) { + node.jjtSetValue(number); + } + return number; + } catch (final ArithmeticException xrt) { + throw new JexlException(valNode, "- error", xrt); + } + } + + @Override + protected Object visit(final ASTBitwiseComplNode node, final Object data) { + final Object arg = node.jjtGetChild(0).jjtAccept(this, data); + try { + final Object result = operators.tryOverload(node, JexlOperator.COMPLEMENT, arg); + return result != JexlEngine.TRY_FAILED ? result : arithmetic.complement(arg); + } catch (final ArithmeticException xrt) { + throw new JexlException(node, "~ error", xrt); + } + } + + @Override + protected Object visit(final ASTNotNode node, final Object data) { + final Object val = node.jjtGetChild(0).jjtAccept(this, data); + try { + final Object result = operators.tryOverload(node, JexlOperator.NOT, val); + return result != JexlEngine.TRY_FAILED ? result : arithmetic.not(val); + } catch (final ArithmeticException xrt) { + throw new JexlException(node, "! error", xrt); + } + } + + @Override + protected Object visit(final ASTIfStatement node, final Object data) { + final int n = 0; + final int numChildren = node.jjtGetNumChildren(); + try { + Object result = null; + // pairs of { conditions , 'then' statement } + for (int ifElse = 0; ifElse < (numChildren - 1); ifElse += 2) { + final Object condition = node.jjtGetChild(ifElse).jjtAccept(this, null); + if (arithmetic.toBoolean(condition)) { + // first objectNode is true statement + return node.jjtGetChild(ifElse + 1).jjtAccept(this, null); + } + } + // if odd... + if ((numChildren & 1) == 1) { + // if there is an else, there are an odd number of children in the statement and it is the last child, + // execute it. + result = node.jjtGetChild(numChildren - 1).jjtAccept(this, null); + } + return result; + } catch (final ArithmeticException xrt) { + throw new JexlException(node.jjtGetChild(n), "if error", xrt); + } + } + + @Override + protected Object visit(final ASTVar node, final Object data) { + final int symbol = node.getSymbol(); + // if we have a var, we have a scope thus a frame + if (!options.isLexical()) { + if (frame.has(symbol)) { + return frame.get(symbol); + } + } else if (!defineVariable(node, block)) { + return redefinedVariable(node, node.getName()); + } + frame.set(symbol, null); + return null; + } + + @Override + protected Object visit(final ASTBlock node, final Object data) { + final int cnt = node.getSymbolCount(); + if (!options.isLexical() || cnt <= 0) { + return visitBlock(node, data); + } + try { + block = new LexicalFrame(frame, block); + return visitBlock(node, data); + } finally { + block = block.pop(); + } + } + + /** + * Base visitation for blocks. + * + * @param node the block + * @param data the usual data + * @return the result of the last expression evaluation + */ + private Object visitBlock(final ASTBlock node, final Object data) { + final int numChildren = node.jjtGetNumChildren(); + Object result = null; + for (int i = 0; i < numChildren; i++) { + cancelCheck(node); + result = node.jjtGetChild(i).jjtAccept(this, data); + } + return result; + } + + @Override + protected Object visit(final ASTReturnStatement node, final Object data) { + final Object val = node.jjtGetChild(0).jjtAccept(this, data); + cancelCheck(node); + throw new JexlException.Return(node, null, val); + } + + @Override + protected Object visit(final ASTContinue node, final Object data) { + throw new JexlException.Continue(node); + } + + @Override + protected Object visit(final ASTBreak node, final Object data) { + throw new JexlException.Break(node); + } + + @Override + protected Object visit(final ASTForeachStatement node, final Object data) { + Object result = null; + /* first objectNode is the loop variable */ + final ASTReference loopReference = (ASTReference) node.jjtGetChild(0); + final ASTIdentifier loopVariable = (ASTIdentifier) loopReference.jjtGetChild(0); + final int symbol = loopVariable.getSymbol(); + final boolean lexical = options.isLexical();// && node.getSymbolCount() > 0; + final LexicalFrame locals = lexical ? new LexicalFrame(frame, block) : null; + final boolean loopSymbol = symbol >= 0 && loopVariable instanceof ASTVar; + if (lexical) { + // create lexical frame + // it may be a local previously declared + if (loopSymbol && !defineVariable((ASTVar) loopVariable, locals)) { + return redefinedVariable(node, loopVariable.getName()); + } + block = locals; + } + Object forEach = null; + try { + /* second objectNode is the variable to iterate */ + final Object iterableValue = node.jjtGetChild(1).jjtAccept(this, data); + // make sure there is a value to iterate upon + if (iterableValue != null) { + /* third objectNode is the statement to execute */ + final JexlNode statement = node.jjtGetNumChildren() >= 3 ? node.jjtGetChild(2) : null; + // get an iterator for the collection/array etc via the introspector. + forEach = operators.tryOverload(node, JexlOperator.FOR_EACH, iterableValue); + final Iterator itemsIterator = forEach instanceof Iterator + ? (Iterator) forEach + : uberspect.getIterator(iterableValue); + if (itemsIterator != null) { + int cnt = 0; + while (itemsIterator.hasNext()) { + cancelCheck(node); + // reset loop variable + if (lexical && cnt++ > 0) { + // clean up but remain current + block.pop(); + // unlikely to fail + if (loopSymbol && !defineVariable((ASTVar) loopVariable, locals)) { + return redefinedVariable(node, loopVariable.getName()); + } + } + // set loopVariable to value of iterator + final Object value = itemsIterator.next(); + if (symbol < 0) { + setContextVariable(node, loopVariable.getName(), value); + } else { + frame.set(symbol, value); + } + if (statement != null) { + try { + // execute statement + result = statement.jjtAccept(this, data); + } catch (final JexlException.Break stmtBreak) { + break; + } catch (final JexlException.Continue stmtContinue) { + // continue; + } + } + } + } + } + } finally { + // closeable iterator handling + closeIfSupported(forEach); + // restore lexical frame + if (lexical) { + block = block.pop(); + } + } + return result; + } + + @Override + protected Object visit(final ASTWhileStatement node, final Object data) { + Object result = null; + /* first objectNode is the condition */ + final Node condition = node.jjtGetChild(0); + while (arithmetic.toBoolean(condition.jjtAccept(this, data))) { + cancelCheck(node); + if (node.jjtGetNumChildren() > 1) { + try { + // execute statement + result = node.jjtGetChild(1).jjtAccept(this, data); + } catch (final JexlException.Break stmtBreak) { + break; + } catch (final JexlException.Continue stmtContinue) { + // continue; + } + } + } + return result; + } + + @Override + protected Object visit(final ASTDoWhileStatement node, final Object data) { + Object result = null; + final int nc = node.jjtGetNumChildren(); + /* last objectNode is the condition */ + final Node condition = node.jjtGetChild(nc - 1); + do { + cancelCheck(node); + if (nc > 1) { + try { + // execute statement + result = node.jjtGetChild(0).jjtAccept(this, data); + } catch (final JexlException.Break stmtBreak) { + break; + } catch (final JexlException.Continue stmtContinue) { + // continue; + } + } + } while (arithmetic.toBoolean(condition.jjtAccept(this, data))); + return result; + } + + @Override + protected Object visit(final ASTAndNode node, final Object data) { + /* + * The pattern for exception mgmt is to let the child*.jjtAccept out of the try/catch loop so that if one fails, + * the ex will traverse up to the interpreter. In cases where this is not convenient/possible, JexlException + * must be caught explicitly and rethrown. + */ + final Object left = node.jjtGetChild(0).jjtAccept(this, data); + try { + final boolean leftValue = arithmetic.toBoolean(left); + if (!leftValue) { + return Boolean.FALSE; + } + } catch (final ArithmeticException xrt) { + throw new JexlException(node.jjtGetChild(0), "boolean coercion error", xrt); + } + final Object right = node.jjtGetChild(1).jjtAccept(this, data); + try { + final boolean rightValue = arithmetic.toBoolean(right); + if (!rightValue) { + return Boolean.FALSE; + } + } catch (final ArithmeticException xrt) { + throw new JexlException(node.jjtGetChild(1), "boolean coercion error", xrt); + } + return Boolean.TRUE; + } + + @Override + protected Object visit(final ASTOrNode node, final Object data) { + final Object left = node.jjtGetChild(0).jjtAccept(this, data); + try { + final boolean leftValue = arithmetic.toBoolean(left); + if (leftValue) { + return Boolean.TRUE; + } + } catch (final ArithmeticException xrt) { + throw new JexlException(node.jjtGetChild(0), "boolean coercion error", xrt); + } + final Object right = node.jjtGetChild(1).jjtAccept(this, data); + try { + final boolean rightValue = arithmetic.toBoolean(right); + if (rightValue) { + return Boolean.TRUE; + } + } catch (final ArithmeticException xrt) { + throw new JexlException(node.jjtGetChild(1), "boolean coercion error", xrt); + } + return Boolean.FALSE; + } + + @Override + protected Object visit(final ASTNullLiteral node, final Object data) { + return null; + } + + @Override + protected Object visit(final ASTTrueNode node, final Object data) { + return Boolean.TRUE; + } + + @Override + protected Object visit(final ASTFalseNode node, final Object data) { + return Boolean.FALSE; + } + + @Override + protected Object visit(final ASTNumberLiteral node, final Object data) { + if (data != null && node.isInteger()) { + return getAttribute(data, node.getLiteral(), node); + } + return node.getLiteral(); + } + + @Override + protected Object visit(final ASTStringLiteral node, final Object data) { + if (data != null) { + return getAttribute(data, node.getLiteral(), node); + } + return node.getLiteral(); + } + + @Override + protected Object visit(final ASTRegexLiteral node, final Object data) { + return node.getLiteral(); + } + + @Override + protected Object visit(final ASTArrayLiteral node, final Object data) { + final int childCount = node.jjtGetNumChildren(); + final JexlArithmetic.ArrayBuilder ab = arithmetic.arrayBuilder(childCount); + boolean extended = false; + for (int i = 0; i < childCount; i++) { + cancelCheck(node); + final JexlNode child = node.jjtGetChild(i); + if (child instanceof ASTExtendedLiteral) { + extended = true; + } else { + final Object entry = node.jjtGetChild(i).jjtAccept(this, data); + ab.add(entry); + } + } + return ab.create(extended); + } + + @Override + protected Object visit(final ASTExtendedLiteral node, final Object data) { + return node; + } + + @Override + protected Object visit(final ASTSetLiteral node, final Object data) { + final int childCount = node.jjtGetNumChildren(); + final JexlArithmetic.SetBuilder mb = arithmetic.setBuilder(childCount); + for (int i = 0; i < childCount; i++) { + cancelCheck(node); + final Object entry = node.jjtGetChild(i).jjtAccept(this, data); + mb.add(entry); + } + return mb.create(); + } + + @Override + protected Object visit(final ASTMapLiteral node, final Object data) { + final int childCount = node.jjtGetNumChildren(); + final JexlArithmetic.MapBuilder mb = arithmetic.mapBuilder(childCount); + for (int i = 0; i < childCount; i++) { + cancelCheck(node); + final Object[] entry = (Object[]) (node.jjtGetChild(i)).jjtAccept(this, data); + mb.put(entry[0], entry[1]); + } + return mb.create(); + } + + @Override + protected Object visit(final ASTMapEntry node, final Object data) { + final Object key = node.jjtGetChild(0).jjtAccept(this, data); + final Object value = node.jjtGetChild(1).jjtAccept(this, data); + return new Object[]{key, value}; + } + + @Override + protected Object visit(final ASTTernaryNode node, final Object data) { + Object condition; + try { + condition = node.jjtGetChild(0).jjtAccept(this, data); + } catch (final JexlException xany) { + if (!(xany.getCause() instanceof JexlArithmetic.NullOperand)) { + throw xany; + } + condition = null; + } + // ternary as in "x ? y : z" + if (node.jjtGetNumChildren() == 3) { + if (condition != null && arithmetic.toBoolean(condition)) { + return node.jjtGetChild(1).jjtAccept(this, data); + } + return node.jjtGetChild(2).jjtAccept(this, data); + } + // elvis as in "x ?: z" + if (condition != null && arithmetic.toBoolean(condition)) { + return condition; + } + return node.jjtGetChild(1).jjtAccept(this, data); + } + + @Override + protected Object visit(final ASTNullpNode node, final Object data) { + Object lhs; + try { + lhs = node.jjtGetChild(0).jjtAccept(this, data); + } catch (final JexlException xany) { + if (!(xany.getCause() instanceof JexlArithmetic.NullOperand)) { + throw xany; + } + lhs = null; + } + // null elision as in "x ?? z" + return lhs != null ? lhs : node.jjtGetChild(1).jjtAccept(this, data); + } + + @Override + protected Object visit(final ASTSizeFunction node, final Object data) { + try { + final Object val = node.jjtGetChild(0).jjtAccept(this, data); + return operators.size(node, val); + } catch (final JexlException xany) { + return 0; + } + } + + @Override + protected Object visit(final ASTEmptyFunction node, final Object data) { + try { + final Object value = node.jjtGetChild(0).jjtAccept(this, data); + return operators.empty(node, value); + } catch (final JexlException xany) { + return true; + } + } + + /** + * Runs a node. + * + * @param node the node + * @param data the usual data + * @return the return value + */ + protected Object visitLexicalNode(final JexlNode node, final Object data) { + block = new LexicalFrame(frame, null); + try { + return node.jjtAccept(this, data); + } finally { + block = block.pop(); + } + } + + /** + * Runs a closure. + * + * @param closure the closure + * @param data the usual data + * @return the closure return value + */ + protected Object runClosure(final Closure closure, final Object data) { + final ASTJexlScript script = closure.getScript(); + block = new LexicalFrame(frame, block).defineArgs(); + try { + final JexlNode body = script.jjtGetChild(script.jjtGetNumChildren() - 1); + return interpret(body); + } finally { + block = block.pop(); + } + } + + @Override + protected Object visit(final ASTJexlScript script, final Object data) { + if (script instanceof ASTJexlLambda && !((ASTJexlLambda) script).isTopLevel()) { + return new Closure(this, (ASTJexlLambda) script); + } + block = new LexicalFrame(frame, block).defineArgs(); + try { + final int numChildren = script.jjtGetNumChildren(); + Object result = null; + for (int i = 0; i < numChildren; i++) { + final JexlNode child = script.jjtGetChild(i); + result = child.jjtAccept(this, data); + cancelCheck(child); + } + return result; + } finally { + block = block.pop(); + } + } + + @Override + protected Object visit(final ASTReferenceExpression node, final Object data) { + return node.jjtGetChild(0).jjtAccept(this, data); + } + + @Override + protected Object visit(final ASTIdentifier identifier, final Object data) { + cancelCheck(identifier); + return data != null + ? getAttribute(data, identifier.getName(), identifier) + : getVariable(frame, block, identifier); + } + + @Override + protected Object visit(final ASTArrayAccess node, final Object data) { + // first objectNode is the identifier + Object object = data; + // can have multiple nodes - either an expression, integer literal or reference + final int numChildren = node.jjtGetNumChildren(); + for (int i = 0; i < numChildren; i++) { + final JexlNode nindex = node.jjtGetChild(i); + if (object == null) { + return unsolvableProperty(nindex, stringifyProperty(nindex), false, null); + } + final Object index = nindex.jjtAccept(this, null); + cancelCheck(node); + object = getAttribute(object, index, nindex); + } + return object; + } + + /** + * Evaluates an access identifier based on the 2 main implementations; + * static (name or numbered identifier) or dynamic (jxlt). + * + * @param node the identifier access node + * @return the evaluated identifier + */ + private Object evalIdentifier(final ASTIdentifierAccess node) { + if (!(node instanceof ASTIdentifierAccessJxlt)) { + return node.getIdentifier(); + } + final ASTIdentifierAccessJxlt accessJxlt = (ASTIdentifierAccessJxlt) node; + final String src = node.getName(); + Throwable cause = null; + TemplateEngine.TemplateExpression expr = (TemplateEngine.TemplateExpression) accessJxlt.getExpression(); + try { + if (expr == null) { + final TemplateEngine jxlt = jexl.jxlt(); + expr = jxlt.parseExpression(node.jexlInfo(), src, frame != null ? frame.getScope() : null); + accessJxlt.setExpression(expr); + } + if (expr != null) { + final Object name = expr.evaluate(frame, context); + if (name != null) { + final Integer id = ASTIdentifierAccess.parseIdentifier(name.toString()); + return id != null ? id : name; + } + } + } catch (final JxltEngine.Exception xjxlt) { + cause = xjxlt; + } + return node.isSafe() ? null : unsolvableProperty(node, src, true, cause); + } + + @Override + protected Object visit(final ASTIdentifierAccess node, final Object data) { + if (data == null) { + return null; + } + final Object id = evalIdentifier(node); + return getAttribute(data, id, node); + } + + @Override + protected Object visit(final ASTReference node, final Object data) { + cancelCheck(node); + final int numChildren = node.jjtGetNumChildren(); + final JexlNode parent = node.jjtGetParent(); + // pass first piece of data in and loop through children + Object object = null; + JexlNode objectNode = null; + JexlNode ptyNode = null; + StringBuilder ant = null; + boolean antish = !(parent instanceof ASTReference); + int v = 1; + main: + for (int c = 0; c < numChildren; c++) { + objectNode = node.jjtGetChild(c); + if (objectNode instanceof ASTMethodNode) { + antish = false; + if (object == null) { + // we may be performing a method call on an antish var + if (ant != null) { + final JexlNode child = objectNode.jjtGetChild(0); + if (child instanceof ASTIdentifierAccess) { + final int alen = ant.length(); + ant.append('.'); + ant.append(((ASTIdentifierAccess) child).getName()); + object = context.get(ant.toString()); + if (object != null) { + object = visit((ASTMethodNode) objectNode, object, context); + continue; + } + // remove method name from antish + ant.delete(alen, ant.length()); + ptyNode = objectNode; + } + } + break; + } + } else if (objectNode instanceof ASTArrayAccess) { + antish = false; + if (object == null) { + ptyNode = objectNode; + break; + } + } + // attempt to evaluate the property within the object (visit(ASTIdentifierAccess node)) + object = objectNode.jjtAccept(this, object); + cancelCheck(node); + if (object != null) { + // disallow mixing antish variable & bean with same root; avoid ambiguity + antish = false; + } else if (antish) { + // create first from first node + if (ant == null) { + // if we still have a null object, check for an antish variable + final JexlNode first = node.jjtGetChild(0); + if (!(first instanceof ASTIdentifier)) { + // not an identifier, not antish + ptyNode = objectNode; + break; + } + final ASTIdentifier afirst = (ASTIdentifier) first; + ant = new StringBuilder(afirst.getName()); + // skip the else...* + // *... and continue + if (!options.isAntish()) { + antish = false; + continue; + } + // skip the first node case since it was trialed in jjtAccept above and returned null + if (c == 0) { + continue; + } + } + // catch up to current node + for (; v <= c; ++v) { + final JexlNode child = node.jjtGetChild(v); + if (!(child instanceof ASTIdentifierAccess)) { + // not an identifier, not antish + ptyNode = objectNode; + break main; + } + final ASTIdentifierAccess achild = (ASTIdentifierAccess) child; + if (achild.isSafe() || achild.isExpression()) { + break main; + } + ant.append('.'); + ant.append(achild.getName()); + } + // solve antish + object = context.get(ant.toString()); + } else if (c != numChildren - 1) { + // only the last one may be null + ptyNode = objectNode; + break; // + } + } + // dealing with null + if (object == null) { + if (ptyNode != null) { + if (ptyNode.isSafeLhs(isSafe())) { + return null; + } + if (ant != null) { + final String aname = ant.toString(); + final boolean defined = isVariableDefined(frame, block, aname); + return unsolvableVariable(node, aname, !defined); + } + return unsolvableProperty(node, + stringifyProperty(ptyNode), ptyNode == objectNode, null); + } + if (antish) { + if (node.isSafeLhs(isSafe())) { + return null; + } + final String aname = ant != null ? ant.toString() : "?"; + final boolean defined = isVariableDefined(frame, block, aname); + // defined but null; arg of a strict operator? + if (defined && (!arithmetic.isStrict() || !node.jjtGetParent().isStrictOperator())) { + return null; + } + return unsolvableVariable(node, aname, !defined); + } + } + return object; + } + + @Override + protected Object visit(final ASTAssignment node, final Object data) { + return executeAssign(node, null, data); + } + + @Override + protected Object visit(final ASTSetAddNode node, final Object data) { + return executeAssign(node, JexlOperator.SELF_ADD, data); + } + + @Override + protected Object visit(final ASTSetSubNode node, final Object data) { + return executeAssign(node, JexlOperator.SELF_SUBTRACT, data); + } + + @Override + protected Object visit(final ASTSetMultNode node, final Object data) { + return executeAssign(node, JexlOperator.SELF_MULTIPLY, data); + } + + @Override + protected Object visit(final ASTSetDivNode node, final Object data) { + return executeAssign(node, JexlOperator.SELF_DIVIDE, data); + } + + @Override + protected Object visit(final ASTSetModNode node, final Object data) { + return executeAssign(node, JexlOperator.SELF_MOD, data); + } + + @Override + protected Object visit(final ASTSetAndNode node, final Object data) { + return executeAssign(node, JexlOperator.SELF_AND, data); + } + + @Override + protected Object visit(final ASTSetOrNode node, final Object data) { + return executeAssign(node, JexlOperator.SELF_OR, data); + } + + @Override + protected Object visit(final ASTSetXorNode node, final Object data) { + return executeAssign(node, JexlOperator.SELF_XOR, data); + } + + /** + * Executes an assignment with an optional side-effect operator. + * + * @param node the node + * @param assignop the assignment operator or null if simply assignment + * @param data the data + * @return the left hand side + */ + protected Object executeAssign(final JexlNode node, final JexlOperator assignop, final Object data) { // CSOFF: MethodLength + cancelCheck(node); + // left contains the reference to assign to + final JexlNode left = node.jjtGetChild(0); + ASTIdentifier var = null; + Object object = null; + int symbol = -1; + // check var decl with assign is ok + if (left instanceof ASTIdentifier) { + var = (ASTIdentifier) left; + symbol = var.getSymbol(); + if (symbol >= 0 && options.isLexical()) { + if (var instanceof ASTVar) { + if (!defineVariable((ASTVar) var, block)) { + return redefinedVariable(var, var.getName()); + } + } else if (options.isLexicalShade() && var.isShaded()) { + return undefinedVariable(var, var.getName()); + } + } + } + boolean antish = options.isAntish(); + // 0: determine initial object & property: + final int last = left.jjtGetNumChildren() - 1; + // right is the value expression to assign + Object right = node.jjtGetChild(1).jjtAccept(this, data); + // a (var?) v = ... expression + if (var != null) { + if (symbol >= 0) { + // check we are not assigning a symbol itself + if (last < 0) { + if (assignop != null) { + final Object self = getVariable(frame, block, var); + right = operators.tryAssignOverload(node, assignop, self, right); + if (right == JexlOperator.ASSIGN) { + return self; + } + } + frame.set(symbol, right); + // make the closure accessible to itself, ie capture the currently set variable after frame creation + if (right instanceof Closure) { + ((Closure) right).setCaptured(symbol, right); + } + return right; // 1 + } + object = getVariable(frame, block, var); + // top level is a symbol, can not be an antish var + antish = false; + } else { + // check we are not assigning direct global + if (last < 0) { + if (assignop != null) { + final Object self = context.get(var.getName()); + right = operators.tryAssignOverload(node, assignop, self, right); + if (right == JexlOperator.ASSIGN) { + return self; + } + } + setContextVariable(node, var.getName(), right); + return right; // 2 + } + object = context.get(var.getName()); + // top level accesses object, can not be an antish var + if (object != null) { + antish = false; + } + } + } else if (!(left instanceof ASTReference)) { + throw new JexlException(left, "illegal assignment form 0"); + } + // 1: follow children till penultimate, resolve dot/array + JexlNode objectNode = null; + StringBuilder ant = null; + int v = 1; + // start at 1 if symbol + main: + for (int c = symbol >= 0 ? 1 : 0; c < last; ++c) { + objectNode = left.jjtGetChild(c); + object = objectNode.jjtAccept(this, object); + if (object != null) { + // disallow mixing antish variable & bean with same root; avoid ambiguity + antish = false; + } else if (antish) { + // initialize if first time + if (ant == null) { + final JexlNode first = left.jjtGetChild(0); + final ASTIdentifier firstId = first instanceof ASTIdentifier + ? (ASTIdentifier) first + : null; + if ((firstId == null) || (firstId.getSymbol() >= 0)) { + // ant remains null, object is null, stop solving + antish = false; + break; + } + ant = new StringBuilder(firstId.getName()); + } + // catch up to current child + for (; v <= c; ++v) { + final JexlNode child = left.jjtGetChild(v); + final ASTIdentifierAccess aid = child instanceof ASTIdentifierAccess + ? (ASTIdentifierAccess) child + : null; + // remain antish only if unsafe navigation + if ((aid == null) || aid.isSafe() || aid.isExpression()) { + antish = false; + break main; + } + ant.append('.'); + ant.append(aid.getName()); + } + // solve antish + object = context.get(ant.toString()); + } else { + throw new JexlException(objectNode, "illegal assignment form"); + } + } + // 2: last objectNode will perform assignement in all cases + Object property = null; + JexlNode propertyNode = left.jjtGetChild(last); + final ASTIdentifierAccess propertyId = propertyNode instanceof ASTIdentifierAccess + ? (ASTIdentifierAccess) propertyNode + : null; + if (propertyId != null) { + // deal with creating/assignining antish variable + if (antish && ant != null && object == null && !propertyId.isSafe() && !propertyId.isExpression()) { + if (last > 0) { + ant.append('.'); + } + ant.append(propertyId.getName()); + if (assignop != null) { + final Object self = context.get(ant.toString()); + right = operators.tryAssignOverload(node, assignop, self, right); + if (right == JexlOperator.ASSIGN) { + return self; + } + } + setContextVariable(propertyNode, ant.toString(), right); + return right; // 3 + } + // property of an object ? + property = evalIdentifier(propertyId); + } else if (propertyNode instanceof ASTArrayAccess) { + // can have multiple nodes - either an expression, integer literal or reference + final int numChildren = propertyNode.jjtGetNumChildren() - 1; + for (int i = 0; i < numChildren; i++) { + final JexlNode nindex = propertyNode.jjtGetChild(i); + final Object index = nindex.jjtAccept(this, null); + object = getAttribute(object, index, nindex); + } + propertyNode = propertyNode.jjtGetChild(numChildren); + property = propertyNode.jjtAccept(this, null); + } else { + throw new JexlException(objectNode, "illegal assignment form"); + } + // we may have a null property as in map[null], no check needed. + // we can not *have* a null object though. + if (object == null) { + // no object, we fail + return unsolvableProperty(objectNode, ".", true, null); + } + // 3: one before last, assign + if (assignop != null) { + final Object self = getAttribute(object, property, propertyNode); + right = operators.tryAssignOverload(node, assignop, self, right); + if (right == JexlOperator.ASSIGN) { + return self; + } + } + setAttribute(object, property, right, propertyNode); + return right; // 4 + } + + @Override + protected Object[] visit(final ASTArguments node, final Object data) { + final int argc = node.jjtGetNumChildren(); + final Object[] argv = new Object[argc]; + for (int i = 0; i < argc; i++) { + argv[i] = node.jjtGetChild(i).jjtAccept(this, data); + } + return argv; + } + + @Override + protected Object visit(final ASTMethodNode node, final Object data) { + return visit(node, null, data); + } + + /** + * Execute a method call, ie syntactically written as name.call(...). + * + * @param node the actual method call node + * @param object non null when name.call is an antish variable + * @param data the context + * @return the method call result + */ + private Object visit(final ASTMethodNode node, Object object, final Object data) { + // left contains the reference to the method + final JexlNode methodNode = node.jjtGetChild(0); + Object method; + // 1: determine object and method or functor + if (methodNode instanceof ASTIdentifierAccess) { + method = methodNode; + if (object == null) { + object = data; + if (object == null) { + // no object, we fail + return node.isSafeLhs(isSafe()) + ? null + : unsolvableMethod(methodNode, ".(...)"); + } + } else { + // edge case of antish var used as functor + method = object; + } + } else { + method = methodNode.jjtAccept(this, data); + } + Object result = method; + for (int a = 1; a < node.jjtGetNumChildren(); ++a) { + if (result == null) { + // no method, we fail// variable unknown in context and not a local + return node.isSafeLhs(isSafe()) + ? null + : unsolvableMethod(methodNode, ".(...)"); + } + final ASTArguments argNode = (ASTArguments) node.jjtGetChild(a); + result = call(node, object, result, argNode); + object = result; + } + return result; + } + + @Override + protected Object visit(final ASTFunctionNode node, final Object data) { + final ASTIdentifier functionNode = (ASTIdentifier) node.jjtGetChild(0); + final String nsid = functionNode.getNamespace(); + final Object namespace = (nsid != null) ? resolveNamespace(nsid, node) : context; + final ASTArguments argNode = (ASTArguments) node.jjtGetChild(1); + return call(node, namespace, functionNode, argNode); + } + + /** + * Calls a method (or function). + *

+ * Method resolution is a follows: + * 1 - attempt to find a method in the target passed as parameter; + * 2 - if this fails, seeks a JexlScript or JexlMethod or a duck-callable* as a property of that target; + * 3 - if this fails, narrow the arguments and try again 1 + * 4 - if this fails, seeks a context or arithmetic method with the proper name taking the target as first argument; + *

+ * *duck-callable: an object where a "call" function exists + * + * @param node the method node + * @param target the target of the method, what it should be invoked upon + * @param functor the object carrying the method or function or the method identifier + * @param argNode the node carrying the arguments + * @return the result of the method invocation + */ + protected Object call(final JexlNode node, final Object target, Object functor, final ASTArguments argNode) { + cancelCheck(node); + // evaluate the arguments + final Object[] argv = visit(argNode, null); + // get the method name if identifier + final int symbol; + final String methodName; + boolean cacheable = cache; + boolean isavar = false; + if (functor instanceof ASTIdentifier) { + // function call, target is context or namespace (if there was one) + final ASTIdentifier methodIdentifier = (ASTIdentifier) functor; + symbol = methodIdentifier.getSymbol(); + methodName = methodIdentifier.getName(); + functor = null; + // is it a global or local variable ? + if (target == context) { + if (frame != null && frame.has(symbol)) { + functor = frame.get(symbol); + isavar = functor != null; + } else if (context.has(methodName)) { + functor = context.get(methodName); + isavar = functor != null; + } + // name is a variable, can't be cached + cacheable &= !isavar; + } + } else if (functor instanceof ASTIdentifierAccess) { + // a method call on target + methodName = ((ASTIdentifierAccess) functor).getName(); + symbol = -1; + functor = null; + cacheable = true; + } else if (functor != null) { + // ...(x)(y) + symbol = -1 - 1; // -2; + methodName = null; + cacheable = false; + } else if (!node.isSafeLhs(isSafe())) { + return unsolvableMethod(node, "?(...)"); + } else { + // safe lhs + return null; + } + + // solving the call site + final CallDispatcher call = new CallDispatcher(node, cacheable); + try { + // do we have a cached version method/function name ? + final Object eval = call.tryEval(target, methodName, argv); + if (JexlEngine.TRY_FAILED != eval) { + return eval; + } + boolean functorp = false; + boolean narrow = false; + // pseudo loop to try acquiring methods without and with argument narrowing + while (true) { + call.narrow = narrow; + // direct function or method call + if (functor == null || functorp) { + // try a method or function from context + if (call.isTargetMethod(target, methodName, argv)) { + return call.eval(methodName); + } + if (target == context) { + // solve 'null' namespace + final Object namespace = resolveNamespace(null, node); + if (namespace != null + && namespace != context + && call.isTargetMethod(namespace, methodName, argv)) { + return call.eval(methodName); + } + // do not try context function since this was attempted + // 10 lines above...; solve as an arithmetic function + if (call.isArithmeticMethod(methodName, argv)) { + return call.eval(methodName); + } + // could not find a method, try as a property of a non-context target (performed once) + } else { + // try prepending target to arguments and look for + // applicable method in context... + final Object[] pargv = functionArguments(target, narrow, argv); + if (call.isContextMethod(methodName, pargv)) { + return call.eval(methodName); + } + // ...or arithmetic + if (call.isArithmeticMethod(methodName, pargv)) { + return call.eval(methodName); + } + // the method may also be a functor stored in a property of the target + if (!narrow) { + final JexlPropertyGet get = uberspect.getPropertyGet(target, methodName); + if (get != null) { + functor = get.tryInvoke(target, methodName); + functorp = functor != null; + } + } + } + } + // this may happen without the above when we are chaining call like x(a)(b) + // or when a var/symbol or antish var is used as a "function" name + if (functor != null) { + // lambda, script or jexl method will do + if (functor instanceof JexlScript) { + return ((JexlScript) functor).execute(context, argv); + } + if (functor instanceof JexlMethod) { + return ((JexlMethod) functor).invoke(target, argv); + } + final String mCALL = "call"; + // may be a generic callable, try a 'call' method + if (call.isTargetMethod(functor, mCALL, argv)) { + return call.eval(mCALL); + } + // functor is a var, may be method is a global one ? + if (isavar && target == context) { + if (call.isContextMethod(methodName, argv)) { + return call.eval(methodName); + } + if (call.isArithmeticMethod(methodName, argv)) { + return call.eval(methodName); + } + } + // try prepending functor to arguments and look for + // context or arithmetic function called 'call' + final Object[] pargv = functionArguments(functor, narrow, argv); + if (call.isContextMethod(mCALL, pargv)) { + return call.eval(mCALL); + } + if (call.isArithmeticMethod(mCALL, pargv)) { + return call.eval(mCALL); + } + } + // if we did not find an exact method by name and we haven't tried yet, + // attempt to narrow the parameters and if this succeeds, try again in next loop + if (narrow || !arithmetic.narrowArguments(argv)) { + break; + } + narrow = true; + // continue; + } + } catch (JexlException.Method xmethod) { + // ignore and handle at end; treat as an inner discover that fails + } catch (final JexlException.TryFailed xany) { + throw invocationException(node, methodName, xany); + } catch (final JexlException xthru) { + throw xthru; + } catch (final Exception xany) { + throw invocationException(node, methodName, xany); + } + // we have either evaluated and returned or no method was found + return node.isSafeLhs(isSafe()) + ? null + : unsolvableMethod(node, methodName, argv); + } + + @Override + protected Object visit(final ASTConstructorNode node, final Object data) { + if (isCancelled()) { + throw new JexlException.Cancel(node); + } + // first child is class or class name + final Object target = node.jjtGetChild(0).jjtAccept(this, data); + // get the ctor args + final int argc = node.jjtGetNumChildren() - 1; + Object[] argv = new Object[argc]; + for (int i = 0; i < argc; i++) { + argv[i] = node.jjtGetChild(i + 1).jjtAccept(this, data); + } + + try { + final boolean cacheable = cache; + // attempt to reuse last funcall cached in volatile JexlNode.value + if (cacheable) { + final Object cached = node.jjtGetValue(); + if (cached instanceof Funcall) { + final Object eval = ((Funcall) cached).tryInvoke(this, null, target, argv); + if (JexlEngine.TRY_FAILED != eval) { + return eval; + } + } + } + boolean narrow = false; + JexlMethod ctor = null; + Funcall funcall = null; + while (true) { + // try as stated + ctor = uberspect.getConstructor(target, argv); + if (ctor != null) { + if (cacheable && ctor.isCacheable()) { + funcall = new Funcall(ctor, narrow); + } + break; + } + // try with prepending context as first argument + final Object[] nargv = callArguments(context, narrow, argv); + ctor = uberspect.getConstructor(target, nargv); + if (ctor != null) { + if (cacheable && ctor.isCacheable()) { + funcall = new ContextualCtor(ctor, narrow); + } + argv = nargv; + break; + } + // if we did not find an exact method by name and we haven't tried yet, + // attempt to narrow the parameters and if this succeeds, try again in next loop + if (!narrow && arithmetic.narrowArguments(argv)) { + narrow = true; + continue; + } + // we are done trying + break; + } + // we have either evaluated and returned or might have found a ctor + if (ctor != null) { + final Object eval = ctor.invoke(target, argv); + // cache executor in volatile JexlNode.value + if (funcall != null) { + node.jjtSetValue(funcall); + } + return eval; + } + final String tstr = target != null ? target.toString() : "?"; + return unsolvableMethod(node, tstr, argv); + } catch (final JexlException.Method xmethod) { + throw xmethod; + } catch (final Exception xany) { + final String tstr = target != null ? target.toString() : "?"; + throw invocationException(node, tstr, xany); + } + } + + @Override + protected Object visit(final ASTJxltLiteral node, final Object data) { + TemplateEngine.TemplateExpression tp = (TemplateEngine.TemplateExpression) node.jjtGetValue(); + if (tp == null) { + final TemplateEngine jxlt = jexl.jxlt(); + JexlInfo info = node.jexlInfo(); + if (this.block != null) { + info = new JexlNode.Info(node, info); + } + tp = jxlt.parseExpression(info, node.getLiteral(), frame != null ? frame.getScope() : null); + node.jjtSetValue(tp); + } + if (tp != null) { + return tp.evaluate(frame, context); + } + return null; + } + + @Override + protected Object visit(final ASTAnnotation node, final Object data) { + throw new UnsupportedOperationException(ASTAnnotation.class.getName() + ": Not supported."); + } + + @Override + protected Object visit(final ASTAnnotatedStatement node, final Object data) { + return processAnnotation(node, 0, data); + } + + /** + * An annotated call. + */ + public class AnnotatedCall implements Callable { + /** The statement. */ + private final ASTAnnotatedStatement stmt; + /** The child index. */ + private final int index; + /** The data. */ + private final Object data; + /** Tracking whether we processed the annotation. */ + private boolean processed = false; + + /** + * Simple ctor. + * + * @param astmt the statement + * @param aindex the index + * @param adata the data + */ + AnnotatedCall(final ASTAnnotatedStatement astmt, final int aindex, final Object adata) { + stmt = astmt; + index = aindex; + data = adata; + } + + + @Override + public Object call() throws Exception { + processed = true; + try { + return processAnnotation(stmt, index, data); + } catch (JexlException.Return | JexlException.Break | JexlException.Continue xreturn) { + return xreturn; + } + } + + /** + * @return whether the statement has been processed + */ + public boolean isProcessed() { + return processed; + } + + /** + * @return the actual statement. + */ + public Object getStatement() { + return stmt; + } + } + + /** + * Processes an annotated statement. + * + * @param stmt the statement + * @param index the index of the current annotation being processed + * @param data the contextual data + * @return the result of the statement block evaluation + */ + protected Object processAnnotation(final ASTAnnotatedStatement stmt, final int index, final Object data) { + // are we evaluating the block ? + final int last = stmt.jjtGetNumChildren() - 1; + if (index == last) { + final JexlNode cblock = stmt.jjtGetChild(last); + // if the context has changed, might need a new interpreter + final JexlArithmetic jexla = arithmetic.options(context); + if (jexla == arithmetic) { + return cblock.jjtAccept(Interpreter.this, data); + } + if (!arithmetic.getClass().equals(jexla.getClass())) { + logger.warn("expected arithmetic to be " + arithmetic.getClass().getSimpleName() + + ", got " + jexla.getClass().getSimpleName() + ); + } + final Interpreter ii = new Interpreter(Interpreter.this, jexla); + final Object r = cblock.jjtAccept(ii, data); + if (ii.isCancelled()) { + Interpreter.this.cancel(); + } + return r; + } + // tracking whether we processed the annotation + final AnnotatedCall jstmt = new AnnotatedCall(stmt, index + 1, data); + // the annotation node and name + final ASTAnnotation anode = (ASTAnnotation) stmt.jjtGetChild(index); + final String aname = anode.getName(); + // evaluate the arguments + final Object[] argv = anode.jjtGetNumChildren() > 0 + ? visit((ASTArguments) anode.jjtGetChild(0), null) : null; + // wrap the future, will recurse through annotation processor + Object result; + try { + result = processAnnotation(aname, argv, jstmt); + // not processing an annotation is an error + if (!jstmt.isProcessed()) { + return annotationError(anode, aname, null); + } + } catch (final JexlException xany) { + throw xany; + } catch (final Exception xany) { + return annotationError(anode, aname, xany); + } + // the caller may return a return, break or continue + if (result instanceof JexlException) { + throw (JexlException) result; + } + return result; + } + + /** + * Delegates the annotation processing to the JexlContext if it is an AnnotationProcessor. + * + * @param annotation the annotation name + * @param args the annotation arguments + * @param stmt the statement / block that was annotated + * @return the result of statement.call() + * @throws Exception if anything goes wrong + */ + protected Object processAnnotation(final String annotation, final Object[] args, final Callable stmt) throws Exception { + return context instanceof JexlContext.AnnotationProcessor + ? ((JexlContext.AnnotationProcessor) context).processAnnotation(annotation, args, stmt) + : stmt.call(); + } +} diff --git a/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/internal/InterpreterBase.java b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/internal/InterpreterBase.java new file mode 100644 index 0000000..92c16bb --- /dev/null +++ b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/internal/InterpreterBase.java @@ -0,0 +1,1061 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package aiyh.utils.tool.org.apache.commons.jexl3.internal; + + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.atomic.AtomicBoolean; + +import aiyh.utils.tool.org.apache.commons.jexl3.introspection.JexlMethod; +import aiyh.utils.tool.org.apache.commons.jexl3.introspection.JexlPropertyGet; +import aiyh.utils.tool.org.apache.commons.jexl3.introspection.JexlPropertySet; +import aiyh.utils.tool.org.apache.commons.jexl3.introspection.JexlUberspect; +import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTVar; +import aiyh.utils.tool.org.apache.commons.jexl3.parser.JexlNode; +import aiyh.utils.tool.org.apache.commons.jexl3.JexlArithmetic; +import aiyh.utils.tool.org.apache.commons.jexl3.JexlContext; +import aiyh.utils.tool.org.apache.commons.jexl3.JexlEngine; +import aiyh.utils.tool.org.apache.commons.jexl3.JexlException; +import aiyh.utils.tool.org.apache.commons.jexl3.JexlOperator; +import aiyh.utils.tool.org.apache.commons.jexl3.JexlOptions; +import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTArrayAccess; +import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTAssignment; +import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTFunctionNode; +import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTIdentifier; +import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTIdentifierAccess; +import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTMethodNode; +import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTReference; +import aiyh.utils.tool.org.apache.commons.jexl3.parser.ParserVisitor; + + +import org.apache.commons.logging.Log; + +/** + * The helper base of an interpreter of JEXL syntax. + * @since 3.0 + */ +public abstract class InterpreterBase extends ParserVisitor { + /** The JEXL engine. */ + protected final Engine jexl; + /** The logger. */ + protected final Log logger; + /** The uberspect. */ + protected final JexlUberspect uberspect; + /** The arithmetic handler. */ + protected final JexlArithmetic arithmetic; + /** The context to store/retrieve variables. */ + protected final JexlContext context; + /** The options. */ + protected final JexlOptions options; + /** Cache executors. */ + protected final boolean cache; + /** Cancellation support. */ + protected final AtomicBoolean cancelled; + /** Empty parameters for method matching. */ + protected static final Object[] EMPTY_PARAMS = new Object[0]; + /** The namespace resolver. */ + protected final JexlContext.NamespaceResolver ns; + /** The operators evaluation delegate. */ + protected final Operators operators; + /** The map of 'prefix:function' to object resolving as namespaces. */ + protected final Map functions; + /** The map of dynamically creates namespaces, NamespaceFunctor or duck-types of those. */ + protected Map functors; + + /** + * Creates an interpreter base. + * @param engine the engine creating this interpreter + * @param opts the evaluation options + * @param aContext the evaluation context + */ + protected InterpreterBase(final Engine engine, final JexlOptions opts, final JexlContext aContext) { + this.jexl = engine; + this.logger = jexl.logger; + this.uberspect = jexl.uberspect; + this.context = aContext != null ? aContext : Engine.EMPTY_CONTEXT; + this.cache = engine.cache != null; + final JexlArithmetic jexla = jexl.arithmetic; + this.options = opts == null? engine.options(aContext) : opts; + this.arithmetic = jexla.options(options); + if (arithmetic != jexla && !arithmetic.getClass().equals(jexla.getClass())) { + logger.warn("expected arithmetic to be " + jexla.getClass().getSimpleName() + + ", got " + arithmetic.getClass().getSimpleName() + ); + } + if (this.context instanceof JexlContext.NamespaceResolver) { + ns = ((JexlContext.NamespaceResolver) context); + } else { + ns = Engine.EMPTY_NS; + } + AtomicBoolean acancel = null; + if (this.context instanceof JexlContext.CancellationHandle) { + acancel = ((JexlContext.CancellationHandle) context).getCancellation(); + } + this.cancelled = acancel != null? acancel : new AtomicBoolean(false); + final Map ons = options.getNamespaces(); + this.functions = ons.isEmpty()? jexl.functions : ons; + this.functors = null; + this.operators = new Operators(this); + } + + /** + * Copy constructor. + * @param ii the base to copy + * @param jexla the arithmetic instance to use (or null) + */ + protected InterpreterBase(final InterpreterBase ii, final JexlArithmetic jexla) { + jexl = ii.jexl; + logger = ii.logger; + uberspect = ii.uberspect; + arithmetic = jexla; + context = ii.context; + options = ii.options.copy(); + cache = ii.cache; + ns = ii.ns; + operators = ii.operators; + cancelled = ii.cancelled; + functions = ii.functions; + functors = ii.functors; + } + + /** + * Attempt to call close() if supported. + *

This is used when dealing with auto-closeable (duck-like) objects + * @param closeable the object we'd like to close + */ + protected void closeIfSupported(final Object closeable) { + if (closeable != null) { + final JexlMethod mclose = uberspect.getMethod(closeable, "close", EMPTY_PARAMS); + if (mclose != null) { + try { + mclose.invoke(closeable, EMPTY_PARAMS); + } catch (final Exception xignore) { + logger.warn(xignore); + } + } + } + } + + /** + * Resolves a namespace, eventually allocating an instance using context as constructor argument. + *

+ * The lifetime of such instances span the current expression or script evaluation.

+ * @param prefix the prefix name (may be null for global namespace) + * @param node the AST node + * @return the namespace instance + */ + protected Object resolveNamespace(final String prefix, final JexlNode node) { + Object namespace; + // check whether this namespace is a functor + synchronized (this) { + if (functors != null) { + namespace = functors.get(prefix); + if (namespace != null) { + return namespace; + } + } + } + // check if namespace is a resolver + namespace = ns.resolveNamespace(prefix); + if (namespace == null) { + namespace = functions.get(prefix); + if (prefix != null && namespace == null) { + throw new JexlException(node, "no such function namespace " + prefix, null); + } + } + // shortcut if ns is known to be not-a-functor + final boolean cacheable = cache; + final Object cached = cacheable ? node.jjtGetValue() : null; + if (cached != JexlContext.NamespaceFunctor.class) { + // allow namespace to instantiate a functor with context if possible, not an error otherwise + Object functor = null; + if (namespace instanceof JexlContext.NamespaceFunctor) { + functor = ((JexlContext.NamespaceFunctor) namespace).createFunctor(context); + } else if (namespace instanceof Class || namespace instanceof String) { + // attempt to reuse last ctor cached in volatile JexlNode.value + if (cached instanceof JexlMethod) { + try { + final Object eval = ((JexlMethod) cached).tryInvoke(null, context); + if (JexlEngine.TRY_FAILED != eval) { + functor = eval; + } + } catch (final JexlException.TryFailed xtry) { + throw new JexlException(node, "unable to instantiate namespace " + prefix, xtry.getCause()); + } + } + // find a ctor with that context class + if (functor == null) { + JexlMethod ctor = uberspect.getConstructor(namespace, context); + if (ctor != null) { + try { + functor = ctor.invoke(namespace, context); + if (cacheable && ctor.isCacheable()) { + node.jjtSetValue(ctor); + } + } catch (final Exception xinst) { + throw new JexlException(node, "unable to instantiate namespace " + prefix, xinst); + } + } + // try again; find a ctor with no arg + if (functor == null) { + ctor = uberspect.getConstructor(namespace); + if (ctor != null) { + try { + functor = ctor.invoke(namespace); + } catch (final Exception xinst) { + throw new JexlException(node, "unable to instantiate namespace " + prefix, xinst); + } + } + // try again; use a class, namespace of static methods + // try to find a class with that name + if (functor == null && namespace instanceof String) { + try { + namespace = uberspect.getClassLoader().loadClass((String) namespace); + } catch (final ClassNotFoundException xignore) { + // not a class + namespace = null; + } + } // we know it's a class + } + } + } + // got a functor, store it and return it + if (functor != null) { + synchronized (this) { + if (functors == null) { + functors = new HashMap<>(); + } + functors.put(prefix, functor); + } + return functor; + } + // use the NamespaceFunctor class to tag this node as not-a-functor + node.jjtSetValue(JexlContext.NamespaceFunctor.class); + } + return namespace; + } + + /** + * Defines a variable. + * @param var the variable to define + * @param frame the frame in which it will be defined + * @return true if definition succeeded, false otherwise + */ + protected boolean defineVariable(final ASTVar var, final LexicalFrame frame) { + final int symbol = var.getSymbol(); + if (symbol < 0) { + return false; + } + if (var.isRedefined()) { + return false; + } + return frame.defineSymbol(symbol, var.isCaptured()); + } + + /** + * Checks whether a variable is defined. + *

The var may be either a local variable declared in the frame and + * visible from the block or defined in the context. + * @param frame the frame + * @param block the block + * @param name the variable name + * @return true if variable is defined, false otherwise + */ + protected boolean isVariableDefined(final Frame frame, final LexicalScope block, final String name) { + if (frame != null && block != null) { + final Integer ref = frame.getScope().getSymbol(name); + final int symbol = ref != null? ref : -1; + if (symbol >= 0 && block.hasSymbol(symbol)) { + final Object value = frame.get(symbol); + return value != Scope.UNDEFINED && value != Scope.UNDECLARED; + } + } + return context.has(name); + } + + /** + * Gets a value of a defined local variable or from the context. + * @param frame the local frame + * @param block the lexical block if any + * @param identifier the variable node + * @return the value + */ + protected Object getVariable(final Frame frame, final LexicalScope block, final ASTIdentifier identifier) { + final int symbol = identifier.getSymbol(); + final String name = identifier.getName(); + // if we have a symbol, we have a scope thus a frame + if (options.isLexicalShade() && identifier.isShaded()) { + return undefinedVariable(identifier, name); + } + // a local var ? + if ((symbol >= 0) && frame.has(symbol)) { + final Object value = frame.get(symbol); + // not out of scope with no lexical shade ? + if (value != Scope.UNDEFINED) { + // null argument of an arithmetic operator ? + if (value == null && arithmetic.isStrict() && identifier.jjtGetParent().isStrictOperator()) { + return unsolvableVariable(identifier, name, false); // defined but null + } + return value; + } + } + // consider global + final Object value = context.get(name); + // is it null ? + if (value == null) { + // is it defined ? + if (!context.has(name)) { + // not defined, ignore in some cases... + final boolean ignore = + (isSafe() && (symbol >= 0 || identifier.jjtGetParent() instanceof ASTAssignment)) + || (identifier.jjtGetParent() instanceof ASTReference); + if (!ignore) { + return undefinedVariable(identifier, name); // undefined + } + } else if (arithmetic.isStrict() && identifier.jjtGetParent().isStrictOperator()) { + return unsolvableVariable(identifier, name, false); // defined but null + } + } + return value; + } + + /** + * Sets a variable in the global context. + *

If interpretation applies lexical shade, the variable must exist (ie + * the context has(...) method returns true) otherwise an error occurs. + * @param node the node + * @param name the variable name + * @param value the variable value + */ + protected void setContextVariable(final JexlNode node, final String name, final Object value) { + if (options.isLexicalShade() && !context.has(name)) { + throw new JexlException.Variable(node, name, true); + } + try { + context.set(name, value); + } catch (final UnsupportedOperationException xsupport) { + throw new JexlException(node, "context is readonly", xsupport); + } + } + + /** + * Whether this interpreter is currently evaluating with a strict engine flag. + * @return true if strict engine, false otherwise + */ + protected boolean isStrictEngine() { + return options.isStrict(); + } + + /** + * Whether this interpreter ignores null in navigation expression as errors. + * @return true if safe, false otherwise + */ + protected boolean isSafe() { + return options.isSafe(); + } + + /** + * Whether this interpreter is currently evaluating with a silent mode. + * @return true if silent, false otherwise + */ + protected boolean isSilent() { + return options.isSilent(); + } + + /** + * @return true if interrupt throws a JexlException.Cancel. + */ + protected boolean isCancellable() { + return options.isCancellable(); + } + + /** + * Finds the node causing a NPE for diadic operators. + * @param xrt the RuntimeException + * @param node the parent node + * @param left the left argument + * @param right the right argument + * @return the left, right or parent node + */ + protected JexlNode findNullOperand(final RuntimeException xrt, final JexlNode node, final Object left, final Object right) { + if (xrt instanceof JexlArithmetic.NullOperand) { + if (left == null) { + return node.jjtGetChild(0); + } + if (right == null) { + return node.jjtGetChild(1); + } + } + return node; + } + + /** + * Triggered when a variable can not be resolved. + * @param node the node where the error originated from + * @param var the variable name + * @param undef whether the variable is undefined or null + * @return throws JexlException if strict and not silent, null otherwise + */ + protected Object unsolvableVariable(final JexlNode node, final String var, final boolean undef) { + return variableError(node, var, undef? JexlException.VariableIssue.UNDEFINED : JexlException.VariableIssue.NULLVALUE); + } + + /** + * Triggered when a variable is lexically known as undefined. + * @param node the node where the error originated from + * @param var the variable name + * @return throws JexlException if strict and not silent, null otherwise + */ + protected Object undefinedVariable(final JexlNode node, final String var) { + return variableError(node, var, JexlException.VariableIssue.UNDEFINED); + } + + /** + * Triggered when a variable is lexically known as being redefined. + * @param node the node where the error originated from + * @param var the variable name + * @return throws JexlException if strict and not silent, null otherwise + */ + protected Object redefinedVariable(final JexlNode node, final String var) { + return variableError(node, var, JexlException.VariableIssue.REDEFINED); + } + + /** + * Triggered when a variable generates an issue. + * @param node the node where the error originated from + * @param var the variable name + * @param issue the issue type + * @return throws JexlException if strict and not silent, null otherwise + */ + protected Object variableError(final JexlNode node, final String var, final JexlException.VariableIssue issue) { + if (isStrictEngine() && !node.isTernaryProtected()) { + throw new JexlException.Variable(node, var, issue); + } + if (logger.isDebugEnabled()) { + logger.debug(JexlException.variableError(node, var, issue)); + } + return null; + } + /** + * Triggered when a method can not be resolved. + * @param node the node where the error originated from + * @param method the method name + * @return throws JexlException if strict and not silent, null otherwise + */ + protected Object unsolvableMethod(final JexlNode node, final String method) { + return unsolvableMethod(node, method, null); + } + + /** + * Triggered when a method can not be resolved. + * @param node the node where the error originated from + * @param method the method name + * @param args the method arguments + * @return throws JexlException if strict and not silent, null otherwise + */ + protected Object unsolvableMethod(final JexlNode node, final String method, final Object[] args) { + if (isStrictEngine()) { + throw new JexlException.Method(node, method, args); + } + if (logger.isDebugEnabled()) { + logger.debug(JexlException.methodError(node, method, args)); + } + return null; + } + + /** + * Triggered when a property can not be resolved. + * @param node the node where the error originated from + * @param property the property node + * @param cause the cause if any + * @param undef whether the property is undefined or null + * @return throws JexlException if strict and not silent, null otherwise + */ + protected Object unsolvableProperty(final JexlNode node, final String property, final boolean undef, final Throwable cause) { + if (isStrictEngine() && !node.isTernaryProtected()) { + throw new JexlException.Property(node, property, undef, cause); + } + if (logger.isDebugEnabled()) { + logger.debug(JexlException.propertyError(node, property, undef)); + } + return null; + } + + /** + * Checks whether a reference child node holds a local variable reference. + * @param node the reference node + * @param which the child we are checking + * @return true if child is local variable, false otherwise + */ + protected boolean isLocalVariable(final ASTReference node, final int which) { + return (node.jjtGetNumChildren() > which + && node.jjtGetChild(which) instanceof ASTIdentifier + && ((ASTIdentifier) node.jjtGetChild(which)).getSymbol() >= 0); + } + + /** + * Checks whether a reference child node holds a function call. + * @param node the reference node + * @return true if child is function call, false otherwise + */ + protected boolean isFunctionCall(final ASTReference node) { + return (node.jjtGetNumChildren() > 0 + && node.jjtGetChild(0) instanceof ASTFunctionNode); + } + + /** + * Pretty-prints a failing property (de)reference. + *

Used by calls to unsolvableProperty(...).

+ * @param node the property node + * @return the (pretty) string + */ + protected String stringifyProperty(final JexlNode node) { + if (node instanceof ASTArrayAccess) { + return "[" + + stringifyPropertyValue(node.jjtGetChild(0)) + + "]"; + } + if (node instanceof ASTMethodNode) { + return stringifyPropertyValue(node.jjtGetChild(0)); + } + if (node instanceof ASTFunctionNode) { + return stringifyPropertyValue(node.jjtGetChild(0)); + } + if (node instanceof ASTIdentifier) { + return ((ASTIdentifier) node).getName(); + } + if (node instanceof ASTReference) { + return stringifyProperty(node.jjtGetChild(0)); + } + return stringifyPropertyValue(node); + } + + /** + * Pretty-prints a failing property value (de)reference. + *

Used by calls to unsolvableProperty(...).

+ * @param node the property node + * @return the (pretty) string value + */ + protected static String stringifyPropertyValue(final JexlNode node) { + return node != null? new Debugger().depth(1).data(node) : "???"; + } + + /** + * Triggered when an operator fails. + * @param node the node where the error originated from + * @param operator the method name + * @param cause the cause of error (if any) + * @return throws JexlException if strict and not silent, null otherwise + */ + protected Object operatorError(final JexlNode node, final JexlOperator operator, final Throwable cause) { + if (isStrictEngine()) { + throw new JexlException.Operator(node, operator.getOperatorSymbol(), cause); + } + if (logger.isDebugEnabled()) { + logger.debug(JexlException.operatorError(node, operator.getOperatorSymbol()), cause); + } + return null; + } + + /** + * Triggered when an annotation processing fails. + * @param node the node where the error originated from + * @param annotation the annotation name + * @param cause the cause of error (if any) + * @return throws a JexlException if strict and not silent, null otherwise + */ + protected Object annotationError(final JexlNode node, final String annotation, final Throwable cause) { + if (isStrictEngine()) { + throw new JexlException.Annotation(node, annotation, cause); + } + if (logger.isDebugEnabled()) { + logger.debug(JexlException.annotationError(node, annotation), cause); + } + return null; + } + + /** + * Triggered when method, function or constructor invocation fails with an exception. + * @param node the node triggering the exception + * @param methodName the method/function name + * @param xany the cause + * @return a JexlException that will be thrown + */ + protected JexlException invocationException(final JexlNode node, final String methodName, final Throwable xany) { + final Throwable cause = xany.getCause(); + if (cause instanceof JexlException) { + return (JexlException) cause; + } + if (cause instanceof InterruptedException) { + return new JexlException.Cancel(node); + } + return new JexlException(node, methodName, xany); + } + + /** + * Cancels this evaluation, setting the cancel flag that will result in a JexlException.Cancel to be thrown. + * @return false if already cancelled, true otherwise + */ + protected boolean cancel() { + return cancelled.compareAndSet(false, true); + } + + /** + * Checks whether this interpreter execution was cancelled due to thread interruption. + * @return true if cancelled, false otherwise + */ + protected boolean isCancelled() { + return cancelled.get() | Thread.currentThread().isInterrupted(); + } + + /** + * Throws a JexlException.Cancel if script execution was cancelled. + * @param node the node being evaluated + */ + protected void cancelCheck(final JexlNode node) { + if (isCancelled()) { + throw new JexlException.Cancel(node); + } + } + + /** + * Concatenate arguments in call(...). + *

When target == context, we are dealing with a global namespace function call + * @param target the pseudo-method owner, first to-be argument + * @param narrow whether we should attempt to narrow number arguments + * @param args the other (non null) arguments + * @return the arguments array + */ + protected Object[] functionArguments(final Object target, final boolean narrow, final Object[] args) { + // when target == context, we are dealing with the null namespace + if (target == null || target == context) { + if (narrow) { + arithmetic.narrowArguments(args); + } + return args; + } + // makes target 1st args, copy others - optionally narrow numbers + final Object[] nargv = new Object[args.length + 1]; + if (narrow) { + nargv[0] = functionArgument(true, target); + for (int a = 1; a <= args.length; ++a) { + nargv[a] = functionArgument(true, args[a - 1]); + } + } else { + nargv[0] = target; + System.arraycopy(args, 0, nargv, 1, args.length); + } + return nargv; + } + + /** + * Concatenate arguments in call(...). + * @param target the pseudo-method owner, first to-be argument + * @param narrow whether we should attempt to narrow number arguments + * @param args the other (non null) arguments + * @return the arguments array + */ + protected Object[] callArguments(final Object target, final boolean narrow, final Object[] args) { + // makes target 1st args, copy others - optionally narrow numbers + final Object[] nargv = new Object[args.length + 1]; + if (narrow) { + nargv[0] = functionArgument(true, target); + for (int a = 1; a <= args.length; ++a) { + nargv[a] = functionArgument(true, args[a - 1]); + } + } else { + nargv[0] = target; + System.arraycopy(args, 0, nargv, 1, args.length); + } + return nargv; + } + + /** + * Optionally narrows an argument for a function call. + * @param narrow whether narrowing should occur + * @param arg the argument + * @return the narrowed argument + */ + protected Object functionArgument(final boolean narrow, final Object arg) { + return narrow && arg instanceof Number ? arithmetic.narrow((Number) arg) : arg; + } + + /** + * Cached function call. + */ + protected static class Funcall implements JexlNode.Funcall { + /** Whether narrow should be applied to arguments. */ + protected final boolean narrow; + /** The JexlMethod to delegate the call to. */ + protected final JexlMethod me; + /** + * Constructor. + * @param jme the method + * @param flag the narrow flag + */ + protected Funcall(final JexlMethod jme, final boolean flag) { + this.me = jme; + this.narrow = flag; + } + + /** + * Try invocation. + * @param ii the interpreter + * @param name the method name + * @param target the method target + * @param args the method arguments + * @return the method invocation result (or JexlEngine.TRY_FAILED) + */ + protected Object tryInvoke(final InterpreterBase ii, final String name, final Object target, final Object[] args) { + return me.tryInvoke(name, target, ii.functionArguments(null, narrow, args)); + } + } + + /** + * Cached arithmetic function call. + */ + protected static class ArithmeticFuncall extends Funcall { + /** + * Constructor. + * @param jme the method + * @param flag the narrow flag + */ + protected ArithmeticFuncall(final JexlMethod jme, final boolean flag) { + super(jme, flag); + } + + @Override + protected Object tryInvoke(final InterpreterBase ii, final String name, final Object target, final Object[] args) { + return me.tryInvoke(name, ii.arithmetic, ii.functionArguments(target, narrow, args)); + } + } + + /** + * Cached context function call. + */ + protected static class ContextFuncall extends Funcall { + /** + * Constructor. + * @param jme the method + * @param flag the narrow flag + */ + protected ContextFuncall(final JexlMethod jme, final boolean flag) { + super(jme, flag); + } + + @Override + protected Object tryInvoke(final InterpreterBase ii, final String name, final Object target, final Object[] args) { + return me.tryInvoke(name, ii.context, ii.functionArguments(target, narrow, args)); + } + } + + /** + * A ctor that needs a context as 1st argument. + */ + protected static class ContextualCtor extends Funcall { + /** + * Constructor. + * @param jme the method + * @param flag the narrow flag + */ + protected ContextualCtor(final JexlMethod jme, final boolean flag) { + super(jme, flag); + } + + @Override + protected Object tryInvoke(final InterpreterBase ii, final String name, final Object target, final Object[] args) { + return me.tryInvoke(name, target, ii.callArguments(ii.context, narrow, args)); + } + } + + /** + * Helping dispatch function calls. + */ + protected class CallDispatcher { + /** + * The syntactic node. + */ + final JexlNode node; + /** + * Whether solution is cacheable. + */ + boolean cacheable = true; + /** + * Whether arguments have been narrowed. + */ + boolean narrow = false; + /** + * The method to call. + */ + JexlMethod vm = null; + /** + * The method invocation target. + */ + Object target = null; + /** + * The actual arguments. + */ + Object[] argv = null; + /** + * The cacheable funcall if any. + */ + Funcall funcall = null; + + /** + * Dispatcher ctor. + * + * @param anode the syntactic node. + * @param acacheable whether resolution can be cached + */ + CallDispatcher(final JexlNode anode, final boolean acacheable) { + this.node = anode; + this.cacheable = acacheable; + } + + /** + * Whether the method is a target method. + * + * @param ntarget the target instance + * @param mname the method name + * @param arguments the method arguments + * @return true if arithmetic, false otherwise + */ + protected boolean isTargetMethod(final Object ntarget, final String mname, final Object[] arguments) { + // try a method + vm = uberspect.getMethod(ntarget, mname, arguments); + if (vm != null) { + argv = arguments; + target = ntarget; + if (cacheable && vm.isCacheable()) { + funcall = new Funcall(vm, narrow); + } + return true; + } + return false; + } + + /** + * Whether the method is a context method. + * + * @param mname the method name + * @param arguments the method arguments + * @return true if arithmetic, false otherwise + */ + protected boolean isContextMethod(final String mname, final Object[] arguments) { + vm = uberspect.getMethod(context, mname, arguments); + if (vm != null) { + argv = arguments; + target = context; + if (cacheable && vm.isCacheable()) { + funcall = new ContextFuncall(vm, narrow); + } + return true; + } + return false; + } + + /** + * Whether the method is an arithmetic method. + * + * @param mname the method name + * @param arguments the method arguments + * @return true if arithmetic, false otherwise + */ + protected boolean isArithmeticMethod(final String mname, final Object[] arguments) { + vm = uberspect.getMethod(arithmetic, mname, arguments); + if (vm != null) { + argv = arguments; + target = arithmetic; + if (cacheable && vm.isCacheable()) { + funcall = new ArithmeticFuncall(vm, narrow); + } + return true; + } + return false; + } + + /** + * Attempt to reuse last funcall cached in volatile JexlNode.value (if + * it was cacheable). + * + * @param ntarget the target instance + * @param mname the method name + * @param arguments the method arguments + * @return TRY_FAILED if invocation was not possible or failed, the + * result otherwise + */ + protected Object tryEval(final Object ntarget, final String mname, final Object[] arguments) { + // do we have a method/function name ? + // attempt to reuse last funcall cached in volatile JexlNode.value (if it was not a variable) + if (mname != null && cacheable && ntarget != null) { + final Object cached = node.jjtGetValue(); + if (cached instanceof Funcall) { + return ((Funcall) cached).tryInvoke(InterpreterBase.this, mname, ntarget, arguments); + } + } + return JexlEngine.TRY_FAILED; + } + + /** + * Evaluates the method previously dispatched. + * + * @param mname the method name + * @return the method invocation result + * @throws Exception when invocation fails + */ + protected Object eval(final String mname) throws Exception { + // we have either evaluated and returned or might have found a method + if (vm != null) { + // vm cannot be null if xjexl is null + final Object eval = vm.invoke(target, argv); + // cache executor in volatile JexlNode.value + if (funcall != null) { + node.jjtSetValue(funcall); + } + return eval; + } + return unsolvableMethod(node, mname, argv); + } + } + + /** + * Gets an attribute of an object. + * + * @param object to retrieve value from + * @param attribute the attribute of the object, e.g. an index (1, 0, 2) or key for a map + * @param node the node that evaluated as the object + * @return the attribute value + */ + protected Object getAttribute(final Object object, final Object attribute, final JexlNode node) { + if (object == null) { + throw new JexlException(node, "object is null"); + } + cancelCheck(node); + final JexlOperator operator = node != null && node.jjtGetParent() instanceof ASTArrayAccess + ? JexlOperator.ARRAY_GET : JexlOperator.PROPERTY_GET; + final Object result = operators.tryOverload(node, operator, object, attribute); + if (result != JexlEngine.TRY_FAILED) { + return result; + } + Exception xcause = null; + try { + // attempt to reuse last executor cached in volatile JexlNode.value + if (node != null && cache) { + final Object cached = node.jjtGetValue(); + if (cached instanceof JexlPropertyGet) { + final JexlPropertyGet vg = (JexlPropertyGet) cached; + final Object value = vg.tryInvoke(object, attribute); + if (!vg.tryFailed(value)) { + return value; + } + } + } + // resolve that property + final List resolvers = uberspect.getResolvers(operator, object); + final JexlPropertyGet vg = uberspect.getPropertyGet(resolvers, object, attribute); + if (vg != null) { + final Object value = vg.invoke(object); + // cache executor in volatile JexlNode.value + if (node != null && cache && vg.isCacheable()) { + node.jjtSetValue(vg); + } + return value; + } + } catch (final Exception xany) { + xcause = xany; + } + // lets fail + if (node == null) { + // direct call + final String error = "unable to get object property" + + ", class: " + object.getClass().getName() + + ", property: " + attribute; + throw new UnsupportedOperationException(error, xcause); + } + final boolean safe = (node instanceof ASTIdentifierAccess) && ((ASTIdentifierAccess) node).isSafe(); + if (safe) { + return null; + } + final String attrStr = attribute != null ? attribute.toString() : null; + return unsolvableProperty(node, attrStr, true, xcause); + } + + /** + * Sets an attribute of an object. + * + * @param object to set the value to + * @param attribute the attribute of the object, e.g. an index (1, 0, 2) or key for a map + * @param value the value to assign to the object's attribute + * @param node the node that evaluated as the object + */ + protected void setAttribute(final Object object, final Object attribute, final Object value, final JexlNode node) { + cancelCheck(node); + final JexlOperator operator = node != null && node.jjtGetParent() instanceof ASTArrayAccess + ? JexlOperator.ARRAY_SET : JexlOperator.PROPERTY_SET; + final Object result = operators.tryOverload(node, operator, object, attribute, value); + if (result != JexlEngine.TRY_FAILED) { + return; + } + Exception xcause = null; + try { + // attempt to reuse last executor cached in volatile JexlNode.value + if (node != null && cache) { + final Object cached = node.jjtGetValue(); + if (cached instanceof JexlPropertySet) { + final JexlPropertySet setter = (JexlPropertySet) cached; + final Object eval = setter.tryInvoke(object, attribute, value); + if (!setter.tryFailed(eval)) { + return; + } + } + } + final List resolvers = uberspect.getResolvers(operator, object); + JexlPropertySet vs = uberspect.getPropertySet(resolvers, object, attribute, value); + // if we can't find an exact match, narrow the value argument and try again + if (vs == null) { + // replace all numbers with the smallest type that will fit + final Object[] narrow = {value}; + if (arithmetic.narrowArguments(narrow)) { + vs = uberspect.getPropertySet(resolvers, object, attribute, narrow[0]); + } + } + if (vs != null) { + // cache executor in volatile JexlNode.value + vs.invoke(object, value); + if (node != null && cache && vs.isCacheable()) { + node.jjtSetValue(vs); + } + return; + } + } catch (final Exception xany) { + xcause = xany; + } + // lets fail + if (node == null) { + // direct call + final String error = "unable to set object property" + + ", class: " + object.getClass().getName() + + ", property: " + attribute + + ", argument: " + value.getClass().getSimpleName(); + throw new UnsupportedOperationException(error, xcause); + } + final String attrStr = attribute != null ? attribute.toString() : null; + unsolvableProperty(node, attrStr, true, xcause); + } +} diff --git a/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/internal/LexicalFrame.java b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/internal/LexicalFrame.java new file mode 100644 index 0000000..68f0919 --- /dev/null +++ b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/internal/LexicalFrame.java @@ -0,0 +1,125 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package aiyh.utils.tool.org.apache.commons.jexl3.internal; + +import java.util.ArrayDeque; +import java.util.Deque; + +/** + * The set of valued symbols defined in a lexical frame. + *

The symbol identifiers are determined by the functional scope. + */ +public class LexicalFrame extends LexicalScope { + /** + * The script frame. + */ + private final Frame frame; + /** + * Previous frame. + */ + protected final LexicalFrame previous; + /** + * The stack of values in the lexical frame. + */ + private Deque stack = null; + + /** + * Lexical frame ctor. + * + * @param scriptf the script frame + * @param outerf the previous lexical frame + */ + public LexicalFrame(final Frame scriptf, final LexicalFrame outerf) { + this.previous = outerf; + this.frame = scriptf; + } + + /** + * Copy ctor. + * + * @param src the frame to copy + */ + public LexicalFrame(final LexicalFrame src) { + super(src.symbols, src.moreSymbols); + frame = src.frame; + previous = src.previous; + stack = src.stack != null ? new ArrayDeque<>(src.stack) : null; + } + + /** + * Define the arguments. + * + * @return this frame + */ + public LexicalFrame defineArgs() { + if (frame != null) { + final int argc = frame.getScope().getArgCount(); + for (int a = 0; a < argc; ++a) { + super.addSymbol(a); + } + } + return this; + } + + /** + * Defines a symbol. + * + * @param symbol the symbol to define + * @param capture whether this redefines a captured symbol + * @return true if symbol is defined, false otherwise + */ + public boolean defineSymbol(final int symbol, final boolean capture) { + final boolean declared = addSymbol(symbol); + if (declared && capture) { + if (stack == null) { + stack = new ArrayDeque<>(); + } + stack.push(symbol); + Object value = frame.get(symbol); + if (value == null) { + value = this; + } + stack.push(value); + } + return declared; + } + + /** + * Pops back values and lexical frame. + * + * @return the previous frame + */ + public LexicalFrame pop() { + // undefine all symbols + clearSymbols(s -> frame.set(s, Scope.UNDEFINED) ); + // restore values of captured symbols that were overwritten + if (stack != null) { + while (!stack.isEmpty()) { + Object value = stack.pop(); + if (value == Scope.UNDECLARED) { + value = Scope.UNDEFINED; + } else if (value == this) { + value = null; + } + final int symbol = (Integer) stack.pop(); + frame.set(symbol, value); + } + } + return previous; + } + +} diff --git a/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/internal/LexicalScope.java b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/internal/LexicalScope.java new file mode 100644 index 0000000..3436594 --- /dev/null +++ b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/internal/LexicalScope.java @@ -0,0 +1,136 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package aiyh.utils.tool.org.apache.commons.jexl3.internal; + +import java.util.BitSet; + +/** + * The set of symbols declared in a lexical scope. + *

The symbol identifiers are determined by the functional scope. + */ +public class LexicalScope { + /** + * Number of bits in a long. + */ + protected static final int LONGBITS = 64; + /** + * The mask of symbols in the frame. + */ + protected long symbols = 0L; + /** + * Symbols after 64. + */ + protected BitSet moreSymbols = null; + + /** + * Create a scope. + */ + public LexicalScope() { + } + + /** + * Frame copy ctor base. + * + * @param s the symbols mask + * @param ms the more symbols bitset + */ + protected LexicalScope(final long s, final BitSet ms) { + symbols = s; + moreSymbols = ms != null ? (BitSet) ms.clone() : null; + } + + /** + * Ensure more symbpls can be stored. + * + * @return the set of more symbols + */ + protected final BitSet moreSymbols() { + if (moreSymbols == null) { + moreSymbols = new BitSet(); + } + return moreSymbols; + } + + /** + * Checks whether a symbol has already been declared. + * + * @param symbol the symbol + * @return true if declared, false otherwise + */ + public boolean hasSymbol(final int symbol) { + if (symbol < LONGBITS) { + return (symbols & (1L << symbol)) != 0L; + } + return moreSymbols != null && moreSymbols.get(symbol - LONGBITS); + } + + /** + * Adds a symbol in this scope. + * + * @param symbol the symbol + * @return true if registered, false if symbol was already registered + */ + public boolean addSymbol(final int symbol) { + if (symbol < LONGBITS) { + if ((symbols & (1L << symbol)) != 0L) { + return false; + } + symbols |= (1L << symbol); + } else { + final int s = symbol - LONGBITS; + final BitSet ms = moreSymbols(); + if (ms.get(s)) { + return false; + } + ms.set(s, true); + } + return true; + } + + /** + * Clear all symbols. + * + * @param cleanSymbol a (optional, may be null) functor to call for each cleaned symbol + */ + public final void clearSymbols(final java.util.function.IntConsumer cleanSymbol) { + // undefine symbols getting out of scope + if (cleanSymbol != null) { + long clean = symbols; + while (clean != 0L) { + final int s = Long.numberOfTrailingZeros(clean); + clean &= ~(1L << s); + cleanSymbol.accept(s); + } + } + symbols = 0L; + if (moreSymbols != null) { + if (cleanSymbol != null) { + for (int s = moreSymbols.nextSetBit(0); s != -1; s = moreSymbols.nextSetBit(s + 1)) { + cleanSymbol.accept(s + LONGBITS); + } + } + moreSymbols.clear(); + } + } + + /** + * @return the number of symbols defined in this scope. + */ + public int getSymbolCount() { + return Long.bitCount(symbols) + (moreSymbols == null ? 0 : moreSymbols.cardinality()); + } +} diff --git a/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/internal/LongRange.java b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/internal/LongRange.java new file mode 100644 index 0000000..85e689f --- /dev/null +++ b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/internal/LongRange.java @@ -0,0 +1,319 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package aiyh.utils.tool.org.apache.commons.jexl3.internal; + +import java.lang.reflect.Array; +import java.util.Collection; +import java.util.Iterator; +import java.util.NoSuchElementException; + +/** + * A range of longs. + *

+ * Behaves as a readonly collection of longs. + */ +public abstract class LongRange implements Collection { + /** The lower boundary. */ + protected final long min; + /** The upper boundary. */ + protected final long max; + + /** + * Creates a range, ascending or descending depending on boundaries order. + * + * @param from the lower inclusive boundary + * @param to the higher inclusive boundary + * @return a range + */ + public static LongRange create(final long from, final long to) { + if (from <= to) { + return new Ascending(from, to); + } + return new Descending(to, from); + } + + /** + * Creates a new range. + * + * @param from the lower inclusive boundary + * @param to the higher inclusive boundary + */ + protected LongRange(final long from, final long to) { + min = from; + max = to; + } + + /** + * Gets the interval minimum value. + * + * @return the low boundary + */ + public long getMin() { + return min; + } + + /** + * Gets the interval maximum value. + * + * @return the high boundary + */ + public long getMax() { + return max; + } + + @Override + public int hashCode() { + int hash = getClass().hashCode(); + // CSOFF: MagicNumber + hash = 13 * hash + (int) (this.min ^ (this.min >>> 32)); + hash = 13 * hash + (int) (this.max ^ (this.max >>> 32)); + // CSON: MagicNumber + return hash; + } + + @Override + public boolean equals(final Object obj) { + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final LongRange other = (LongRange) obj; + if (this.min != other.min) { + return false; + } + return this.max == other.max; + } + + @Override + public abstract Iterator iterator(); + + @Override + public int size() { + return (int) (max - min + 1); + } + + @Override + public boolean isEmpty() { + return false; + } + + @Override + public boolean contains(final Object o) { + if (o instanceof Number) { + final long v = ((Number) o).longValue(); + return min <= v && v <= max; + } + return false; + } + + @Override + public Object[] toArray() { + final int size = size(); + final Object[] array = new Object[size]; + for (int a = 0; a < size; ++a) { + array[a] = min + a; + } + return array; + } + + @Override + @SuppressWarnings("unchecked") + public T[] toArray(final T[] array) { + final Class ct = array.getClass().getComponentType(); + final int length = size(); + T[] copy = array; + if (ct.isAssignableFrom(Long.class)) { + if (array.length < length) { + copy = (T[]) Array.newInstance(ct, length); + } + for (int a = 0; a < length; ++a) { + Array.set(copy, a, min + a); + } + if (length < copy.length) { + copy[length] = null; + } + return copy; + } + throw new UnsupportedOperationException(); + } + + @Override + public boolean containsAll(final Collection c) { + for (final Object cc : c) { + if (!contains(cc)) { + return false; + } + } + return true; + } + + @Override + public boolean add(final Long e) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean remove(final Object o) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean addAll(final Collection c) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean removeAll(final Collection c) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean retainAll(final Collection c) { + throw new UnsupportedOperationException(); + } + + @Override + public void clear() { + throw new UnsupportedOperationException(); + } + + /** + * Ascending long range. + */ + public static class Ascending extends LongRange { + /** + * Constructor. + * + * @param from lower boundary + * @param to upper boundary + */ + protected Ascending(final long from, final long to) { + super(from, to); + } + + @Override + public Iterator iterator() { + return new AscLongIterator(min, max); + } + } + + /** + * Descending long range. + */ + public static class Descending extends LongRange { + /** + * Constructor. + * + * @param from upper boundary + * @param to lower boundary + */ + protected Descending(final long from, final long to) { + super(from, to); + } + + @Override + public Iterator iterator() { + return new DescLongIterator(min, max); + } + } +} + +/** + * An iterator on a long range. + */ +class AscLongIterator implements Iterator { + /** The lower boundary. */ + private final long min; + /** The upper boundary. */ + private final long max; + /** The current value. */ + private long cursor; + + /** + * Creates a iterator on the range. + * + * @param l low boundary + * @param h high boundary + */ + AscLongIterator(final long l, final long h) { + min = l; + max = h; + cursor = min; + } + + @Override + public boolean hasNext() { + return cursor <= max; + } + + @Override + public Long next() { + if (cursor <= max) { + return cursor++; + } + throw new NoSuchElementException(); + } + + @Override + public void remove() { + throw new UnsupportedOperationException(); + } +} + +/** + * An iterator on a long range. + */ +class DescLongIterator implements Iterator { + /** The lower boundary. */ + private final long min; + /** The upper boundary. */ + private final long max; + /** The current value. */ + private long cursor; + + /** + * Creates a iterator on the range. + * + * @param l low boundary + * @param h high boundary + */ + DescLongIterator(final long l, final long h) { + min = l; + max = h; + cursor = max; + } + + @Override + public boolean hasNext() { + return cursor >= min; + } + + @Override + public Long next() { + if (cursor >= min) { + return cursor--; + } + throw new NoSuchElementException(); + } + + @Override + public void remove() { + throw new UnsupportedOperationException(); + } +} diff --git a/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/internal/MapBuilder.java b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/internal/MapBuilder.java new file mode 100644 index 0000000..709face --- /dev/null +++ b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/internal/MapBuilder.java @@ -0,0 +1,48 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package aiyh.utils.tool.org.apache.commons.jexl3.internal; + +import java.util.HashMap; +import java.util.Map; + +import aiyh.utils.tool.org.apache.commons.jexl3.JexlArithmetic; + +/** + * Helper class to create map literals. + */ +public class MapBuilder implements JexlArithmetic.MapBuilder { + /** The map being created. */ + protected final Map map; + + /** + * Creates a new builder. + * @param size the expected map size + */ + public MapBuilder(final int size) { + map = new HashMap(size); + } + + @Override + public void put(final Object key, final Object value) { + map.put(key, value); + } + + @Override + public Map create() { + return map; + } +} diff --git a/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/internal/Operators.java b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/internal/Operators.java new file mode 100644 index 0000000..93d950a --- /dev/null +++ b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/internal/Operators.java @@ -0,0 +1,410 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package aiyh.utils.tool.org.apache.commons.jexl3.internal; + +import java.lang.reflect.Method; + +import aiyh.utils.tool.org.apache.commons.jexl3.JexlArithmetic; +import aiyh.utils.tool.org.apache.commons.jexl3.JexlEngine; +import aiyh.utils.tool.org.apache.commons.jexl3.JexlException; +import aiyh.utils.tool.org.apache.commons.jexl3.JexlOperator; +import aiyh.utils.tool.org.apache.commons.jexl3.introspection.JexlMethod; +import aiyh.utils.tool.org.apache.commons.jexl3.introspection.JexlUberspect; +import aiyh.utils.tool.org.apache.commons.jexl3.parser.JexlNode; +import aiyh.utils.tool.org.apache.commons.jexl3.internal.introspection.MethodExecutor; + +/** + * Helper class to deal with operator overloading and specifics. + * @since 3.0 + */ +public class Operators { + /** The owner. */ + protected final InterpreterBase interpreter; + /** The overloaded arithmetic operators. */ + protected final JexlArithmetic.Uberspect operators; + + /** + * Constructor. + * @param owner the owning interpreter + */ + protected Operators(final InterpreterBase owner) { + final JexlArithmetic arithmetic = owner.arithmetic; + final JexlUberspect uberspect = owner.uberspect; + this.interpreter = owner; + this.operators = uberspect.getArithmetic(arithmetic); + } + + /** + * Checks whether a method returns a boolean or a Boolean. + * @param vm the JexlMethod (may be null) + * @return true of false + */ + private boolean returnsBoolean(final JexlMethod vm) { + if (vm !=null) { + final Class rc = vm.getReturnType(); + return Boolean.TYPE.equals(rc) || Boolean.class.equals(rc); + } + return false; + } + + /** + * Checks whether a method returns an int or an Integer. + * @param vm the JexlMethod (may be null) + * @return true of false + */ + private boolean returnsInteger(final JexlMethod vm) { + if (vm !=null) { + final Class rc = vm.getReturnType(); + return Integer.TYPE.equals(rc) || Integer.class.equals(rc); + } + return false; + } + + /** + * Checks whether a method is a JexlArithmetic method. + * @param vm the JexlMethod (may be null) + * @return true of false + */ + private boolean isArithmetic(final JexlMethod vm) { + if (vm instanceof MethodExecutor) { + final Method method = ((MethodExecutor) vm).getMethod(); + return JexlArithmetic.class.equals(method.getDeclaringClass()); + } + return false; + } + + /** + * Attempts to call an operator. + *

+ * This takes care of finding and caching the operator method when appropriate + * @param node the syntactic node + * @param operator the operator + * @param args the arguments + * @return the result of the operator evaluation or TRY_FAILED + */ + protected Object tryOverload(final JexlNode node, final JexlOperator operator, final Object... args) { + if (operators != null && operators.overloads(operator)) { + final JexlArithmetic arithmetic = interpreter.arithmetic; + final boolean cache = interpreter.cache; + try { + if (cache) { + final Object cached = node.jjtGetValue(); + if (cached instanceof JexlMethod) { + final JexlMethod me = (JexlMethod) cached; + final Object eval = me.tryInvoke(operator.getMethodName(), arithmetic, args); + if (!me.tryFailed(eval)) { + return eval; + } + } + } + final JexlMethod vm = operators.getOperator(operator, args); + if (vm != null && !isArithmetic(vm)) { + final Object result = vm.invoke(arithmetic, args); + if (cache) { + node.jjtSetValue(vm); + } + return result; + } + } catch (final Exception xany) { + return interpreter.operatorError(node, operator, xany); + } + } + return JexlEngine.TRY_FAILED; + } + + /** + * Evaluates an assign operator. + *

+ * This takes care of finding and caching the operator method when appropriate. + * If an overloads returns Operator.ASSIGN, it means the side-effect is complete. + * Otherwise, a += b <=> a = a + b + *

+ * @param node the syntactic node + * @param operator the operator + * @param args the arguments, the first one being the target of assignment + * @return JexlOperator.ASSIGN if operation assignment has been performed, + * JexlEngine.TRY_FAILED if no operation was performed, + * the value to use as the side effect argument otherwise + */ + protected Object tryAssignOverload(final JexlNode node, final JexlOperator operator, final Object...args) { + final JexlArithmetic arithmetic = interpreter.arithmetic; + if (args.length != operator.getArity()) { + return JexlEngine.TRY_FAILED; + } + // try to call overload with side effect + Object result = tryOverload(node, operator, args); + if (result != JexlEngine.TRY_FAILED) { + return result; + } + // call base operator + final JexlOperator base = operator.getBaseOperator(); + if (base == null) { + throw new IllegalArgumentException("must be called with a side-effect operator"); + } + if (operators != null && operators.overloads(base)) { + // in case there is an overload on the base operator + try { + final JexlMethod vm = operators.getOperator(base, args); + if (vm != null) { + result = vm.invoke(arithmetic, args); + if (result != JexlEngine.TRY_FAILED) { + return result; + } + } + } catch (final Exception xany) { + interpreter.operatorError(node, base, xany); + } + } + // base eval + try { + switch (operator) { + case SELF_ADD: + return arithmetic.add(args[0], args[1]); + case SELF_SUBTRACT: + return arithmetic.subtract(args[0], args[1]); + case SELF_MULTIPLY: + return arithmetic.multiply(args[0], args[1]); + case SELF_DIVIDE: + return arithmetic.divide(args[0], args[1]); + case SELF_MOD: + return arithmetic.mod(args[0], args[1]); + case SELF_AND: + return arithmetic.and(args[0], args[1]); + case SELF_OR: + return arithmetic.or(args[0], args[1]); + case SELF_XOR: + return arithmetic.xor(args[0], args[1]); + default: + // unexpected, new operator added? + throw new UnsupportedOperationException(operator.getOperatorSymbol()); + } + } catch (final Exception xany) { + interpreter.operatorError(node, base, xany); + } + return JexlEngine.TRY_FAILED; + } + + /** + * The 'startsWith' operator implementation. + * @param node the node + * @param operator the calling operator, $= or $! + * @param left the left operand + * @param right the right operand + * @return true if left starts with right, false otherwise + */ + protected boolean startsWith(final JexlNode node, final String operator, final Object left, final Object right) { + final JexlArithmetic arithmetic = interpreter.arithmetic; + final JexlUberspect uberspect = interpreter.uberspect; + try { + // try operator overload + final Object result = tryOverload(node, JexlOperator.STARTSWITH, left, right); + if (result instanceof Boolean) { + return (Boolean) result; + } + // use arithmetic / pattern matching ? + final Boolean matched = arithmetic.startsWith(left, right); + if (matched != null) { + return matched; + } + // try a startsWith method (duck type) + try { + final Object[] argv = {right}; + JexlMethod vm = uberspect.getMethod(left, "startsWith", argv); + if (returnsBoolean(vm)) { + return (Boolean) vm.invoke(left, argv); + } + if (arithmetic.narrowArguments(argv)) { + vm = uberspect.getMethod(left, "startsWith", argv); + if (returnsBoolean(vm)) { + return (Boolean) vm.invoke(left, argv); + } + } + } catch (final Exception e) { + throw new JexlException(node, operator + " error", e); + } + // defaults to equal + return arithmetic.equals(left, right); + } catch (final ArithmeticException xrt) { + throw new JexlException(node, operator + " error", xrt); + } + } + + /** + * The 'endsWith' operator implementation. + * @param node the node + * @param operator the calling operator, ^= or ^! + * @param left the left operand + * @param right the right operand + * @return true if left ends with right, false otherwise + */ + protected boolean endsWith(final JexlNode node, final String operator, final Object left, final Object right) { + final JexlArithmetic arithmetic = interpreter.arithmetic; + final JexlUberspect uberspect = interpreter.uberspect; + try { + // try operator overload + final Object result = tryOverload(node, JexlOperator.ENDSWITH, left, right); + if (result instanceof Boolean) { + return (Boolean) result; + } + // use arithmetic / pattern matching ? + final Boolean matched = arithmetic.endsWith(left, right); + if (matched != null) { + return matched; + } + // try a endsWith method (duck type) + try { + final Object[] argv = {right}; + JexlMethod vm = uberspect.getMethod(left, "endsWith", argv); + if (returnsBoolean(vm)) { + return (Boolean) vm.invoke(left, argv); + } + if (arithmetic.narrowArguments(argv)) { + vm = uberspect.getMethod(left, "endsWith", argv); + if (returnsBoolean(vm)) { + return (Boolean) vm.invoke(left, argv); + } + } + } catch (final Exception e) { + throw new JexlException(node, operator + " error", e); + } + // defaults to equal + return arithmetic.equals(left, right); + } catch (final ArithmeticException xrt) { + throw new JexlException(node, operator + " error", xrt); + } + } + + /** + * The 'match'/'in' operator implementation. + *

+ * Note that 'x in y' or 'x matches y' means 'y contains x' ; + * the JEXL operator arguments order syntax is the reverse of this method call. + *

+ * @param node the node + * @param op the calling operator, =~ or !~ + * @param right the left operand + * @param left the right operand + * @return true if left matches right, false otherwise + */ + protected boolean contains(final JexlNode node, final String op, final Object left, final Object right) { + final JexlArithmetic arithmetic = interpreter.arithmetic; + final JexlUberspect uberspect = interpreter.uberspect; + try { + // try operator overload + final Object result = tryOverload(node, JexlOperator.CONTAINS, left, right); + if (result instanceof Boolean) { + return (Boolean) result; + } + // use arithmetic / pattern matching ? + final Boolean matched = arithmetic.contains(left, right); + if (matched != null) { + return matched; + } + // try a contains method (duck type set) + try { + final Object[] argv = {right}; + JexlMethod vm = uberspect.getMethod(left, "contains", argv); + if (returnsBoolean(vm)) { + return (Boolean) vm.invoke(left, argv); + } + if (arithmetic.narrowArguments(argv)) { + vm = uberspect.getMethod(left, "contains", argv); + if (returnsBoolean(vm)) { + return (Boolean) vm.invoke(left, argv); + } + } + } catch (final Exception e) { + throw new JexlException(node, op + " error", e); + } + // defaults to equal + return arithmetic.equals(left, right); + } catch (final ArithmeticException xrt) { + throw new JexlException(node, op + " error", xrt); + } + } + + /** + * Check for emptyness of various types: Collection, Array, Map, String, and anything that has a boolean isEmpty() + * method. + *

Note that the result may not be a boolean. + * + * @param node the node holding the object + * @param object the object to check the emptyness of + * @return the evaluation result + */ + protected Object empty(final JexlNode node, final Object object) { + if (object == null) { + return true; + } + Object result = tryOverload(node, JexlOperator.EMPTY, object); + if (result != JexlEngine.TRY_FAILED) { + return result; + } + final JexlArithmetic arithmetic = interpreter.arithmetic; + result = arithmetic.isEmpty(object, null); + if (result == null) { + final JexlUberspect uberspect = interpreter.uberspect; + result = false; + // check if there is an isEmpty method on the object that returns a + // boolean and if so, just use it + final JexlMethod vm = uberspect.getMethod(object, "isEmpty", Interpreter.EMPTY_PARAMS); + if (returnsBoolean(vm)) { + try { + result = vm.invoke(object, Interpreter.EMPTY_PARAMS); + } catch (final Exception xany) { + interpreter.operatorError(node, JexlOperator.EMPTY, xany); + } + } + } + return !(result instanceof Boolean) || (Boolean) result; + } + + /** + * Calculate the size of various types: + * Collection, Array, Map, String, and anything that has a int size() method. + *

Note that the result may not be an integer. + * + * @param node the node that gave the value to size + * @param object the object to get the size of + * @return the evaluation result + */ + protected Object size(final JexlNode node, final Object object) { + if (object == null) { + return 0; + } + Object result = tryOverload(node, JexlOperator.SIZE, object); + if (result != JexlEngine.TRY_FAILED) { + return result; + } + final JexlArithmetic arithmetic = interpreter.arithmetic; + result = arithmetic.size(object, null); + if (result == null) { + final JexlUberspect uberspect = interpreter.uberspect; + // check if there is a size method on the object that returns an + // integer and if so, just use it + final JexlMethod vm = uberspect.getMethod(object, "size", Interpreter.EMPTY_PARAMS); + if (returnsInteger(vm)) { + try { + result = vm.invoke(object, Interpreter.EMPTY_PARAMS); + } catch (final Exception xany) { + interpreter.operatorError(node, JexlOperator.SIZE, xany); + } + } + } + return result instanceof Number ? ((Number) result).intValue() : 0; + } +} diff --git a/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/internal/Scope.java b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/internal/Scope.java new file mode 100644 index 0000000..b295e89 --- /dev/null +++ b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/internal/Scope.java @@ -0,0 +1,318 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package aiyh.utils.tool.org.apache.commons.jexl3.internal; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +/** + * A script scope, stores the declaration of parameters and local variables as symbols. + *

This also acts as the functional scope and variable definition store. + * @since 3.0 + */ +public final class Scope { + /** + * The value of an as-yet undeclared but variable, for instance: x; before var x;. + */ + static final Object UNDECLARED = new Object() { + @Override public String toString() { + return "??"; + } + }; + /** + * The value of a declared but undefined variable, for instance: var x;. + */ + static final Object UNDEFINED = new Object() { + @Override public String toString() { + return "?"; + } + }; + /** + * The parent scope. + */ + private final Scope parent; + /** + * The number of parameters. + */ + private int parms; + /** + * The number of local variables. + */ + private int vars; + /** + * The map of named variables aka script parameters and local variables. + * Each parameter is associated to a symbol and is materialized as an offset in the stacked array used + * during evaluation. + */ + private Map namedVariables = null; + /** + * The map of local captured variables to parent scope variables, ie closure. + */ + private Map capturedVariables = null; + /** + * The empty string array. + */ + private static final String[] EMPTY_STRS = new String[0]; + + /** + * Creates a new scope with a list of parameters. + * @param scope the parent scope if any + * @param parameters the list of parameters + */ + public Scope(final Scope scope, final String... parameters) { + if (parameters != null) { + parms = parameters.length; + namedVariables = new LinkedHashMap(); + for (int p = 0; p < parms; ++p) { + namedVariables.put(parameters[p], p); + } + } else { + parms = 0; + } + vars = 0; + parent = scope; + } + + @Override + public int hashCode() { + return namedVariables == null ? 0 : parms ^ namedVariables.hashCode(); + } + + @Override + public boolean equals(final Object o) { + if (this == o) { + return true; + } + if (!(o instanceof Scope)) { + return false; + } + final Scope scope = (Scope) o; + if (parms != scope.parms) { + return false; + } + if (namedVariables == null) { + return scope.namedVariables == null; + } + return namedVariables.equals(scope.namedVariables); + } + + /** + * Checks whether an identifier is a local variable or argument, ie a symbol. + * If this fails, look in parents for symbol that can be captured. + * @param name the symbol name + * @return the symbol index + */ + public Integer getSymbol(final String name) { + return getSymbol(name, true); + } + + /** + * Checks whether an identifier is a local variable or argument, ie a symbol. + * @param name the symbol name + * @param capture whether solving by capturing a parent symbol is allowed + * @return the symbol index + */ + private Integer getSymbol(final String name, final boolean capture) { + Integer register = namedVariables != null ? namedVariables.get(name) : null; + if (register == null && capture && parent != null) { + final Integer pr = parent.getSymbol(name, true); + if (pr != null) { + if (capturedVariables == null) { + capturedVariables = new LinkedHashMap(); + } + if (namedVariables == null) { + namedVariables = new LinkedHashMap(); + } + register = namedVariables.size(); + namedVariables.put(name, register); + capturedVariables.put(register, pr); + } + } + return register; + } + + /** + * Checks whether a given symbol is captured. + * @param symbol the symbol number + * @return true if captured, false otherwise + */ + public boolean isCapturedSymbol(final int symbol) { + return capturedVariables != null && capturedVariables.containsKey(symbol); + } + + /** + * Declares a parameter. + *

+ * This method creates an new entry in the symbol map. + *

+ * @param name the parameter name + * @return the register index storing this variable + */ + public int declareParameter(final String name) { + if (namedVariables == null) { + namedVariables = new LinkedHashMap(); + } else if (vars > 0) { + throw new IllegalStateException("cant declare parameters after variables"); + } + Integer register = namedVariables.get(name); + if (register == null) { + register = namedVariables.size(); + namedVariables.put(name, register); + parms += 1; + } + return register; + } + + /** + * Declares a local variable. + *

+ * This method creates an new entry in the symbol map. + *

+ * @param name the variable name + * @return the register index storing this variable + */ + public int declareVariable(final String name) { + if (namedVariables == null) { + namedVariables = new LinkedHashMap(); + } + Integer register = namedVariables.get(name); + if (register == null) { + register = namedVariables.size(); + namedVariables.put(name, register); + vars += 1; + // check if local is redefining captured + if (parent != null) { + final Integer pr = parent.getSymbol(name, true); + if (pr != null) { + if (capturedVariables == null) { + capturedVariables = new LinkedHashMap(); + } + capturedVariables.put(register, pr); + } + } + } + return register; + } + + /** + * Creates a frame by copying values up to the number of parameters. + *

This captures the captured variables values.

+ * @param frame the caller frame + * @param args the arguments + * @return the arguments array + */ + public Frame createFrame(final Frame frame, final Object...args) { + if (namedVariables == null) { + return null; + } + final Object[] arguments = new Object[namedVariables.size()]; + Arrays.fill(arguments, UNDECLARED); + if (frame != null && capturedVariables != null && parent != null) { + for (final Map.Entry capture : capturedVariables.entrySet()) { + final Integer target = capture.getKey(); + final Integer source = capture.getValue(); + final Object arg = frame.get(source); + arguments[target] = arg; + } + } + return new Frame(this, arguments, 0).assign(args); + } + + /** + * Gets the captured index of a given symbol, ie the target index of a symbol in a child frame. + * @param symbol the symbol index + * @return the target symbol index or null if the symbol is not captured + */ + public Integer getCaptured(final int symbol) { + if (capturedVariables != null) { + for (final Map.Entry capture : capturedVariables.entrySet()) { + final Integer source = capture.getValue(); + if (source == symbol) { + return capture.getKey(); + } + } + } + return null; + } + + /** + * Gets the (maximum) number of arguments this script expects. + * @return the number of parameters + */ + public int getArgCount() { + return parms; + } + + /** + * Gets this script symbols names, i.e. parameters and local variables. + * @return the symbol names + */ + public String[] getSymbols() { + return namedVariables != null ? namedVariables.keySet().toArray(new String[0]) : EMPTY_STRS; + } + + /** + * Gets this script parameters, i.e. symbols assigned before creating local variables. + * @return the parameter names + */ + public String[] getParameters() { + return getParameters(0); + } + + /** + * Gets this script parameters. + * @param bound number of known bound parameters (curry) + * @return the parameter names + */ + protected String[] getParameters(final int bound) { + final int unbound = parms - bound; + if ((namedVariables == null) || (unbound <= 0)) { + return EMPTY_STRS; + } + final String[] pa = new String[unbound]; + int p = 0; + for (final Map.Entry entry : namedVariables.entrySet()) { + final int argn = entry.getValue(); + if (argn >= bound && argn < parms) { + pa[p++] = entry.getKey(); + } + } + return pa; + } + + /** + * Gets this script local variable, i.e. symbols assigned to local variables excluding captured variables. + * @return the local variable names + */ + public String[] getLocalVariables() { + if ((namedVariables == null) || (vars <= 0)) { + return EMPTY_STRS; + } + final List locals = new ArrayList(vars); + for (final Map.Entry entry : namedVariables.entrySet()) { + final int symnum = entry.getValue(); + if (symnum >= parms && (capturedVariables == null || !capturedVariables.containsKey(symnum))) { + locals.add(entry.getKey()); + } + } + return locals.toArray(new String[locals.size()]); + } + +} diff --git a/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/internal/Script.java b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/internal/Script.java new file mode 100644 index 0000000..6a77ce8 --- /dev/null +++ b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/internal/Script.java @@ -0,0 +1,342 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package aiyh.utils.tool.org.apache.commons.jexl3.internal; + +import aiyh.utils.tool.org.apache.commons.jexl3.JexlContext; +import aiyh.utils.tool.org.apache.commons.jexl3.JexlFeatures; +import aiyh.utils.tool.org.apache.commons.jexl3.JexlInfo; +import aiyh.utils.tool.org.apache.commons.jexl3.JexlOptions; +import aiyh.utils.tool.org.apache.commons.jexl3.JexlEngine; +import aiyh.utils.tool.org.apache.commons.jexl3.JexlScript; +import aiyh.utils.tool.org.apache.commons.jexl3.JexlExpression; +import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTJexlScript; + +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Set; +/** + *

A JexlScript implementation.

+ * @since 1.1 + */ +public class Script implements JexlScript, JexlExpression { + /** + * The engine for this expression. + */ + protected final Engine jexl; + /** + * Original expression stripped from leading and trailing spaces. + */ + protected final String source; + /** + * The resulting AST we can interpret. + */ + protected final ASTJexlScript script; + /** + * The engine version (as class loader change count) that last evaluated this script. + */ + protected int version; + + /** + * @return the script AST + */ + protected ASTJexlScript getScript() { + return script; + } + + /** + * Do not let this be generally instantiated with a 'new'. + * + * @param engine the interpreter to evaluate the expression + * @param expr the expression source. + * @param ref the parsed expression. + */ + protected Script(final Engine engine, final String expr, final ASTJexlScript ref) { + jexl = engine; + source = expr; + script = ref; + version = jexl.getUberspect().getVersion(); + } + + /** + * Checks that this script cached methods (wrt introspection) matches the engine version. + *

+ * If the engine class loader has changed since we last evaluated this script, the script local cache + * is invalidated to drop references to obsolete methods. It is not strictly necessary since the tryExecute + * will fail because the class won't match but it seems cleaner nevertheless. + *

+ */ + protected void checkCacheVersion() { + final int uberVersion = jexl.getUberspect().getVersion(); + if (version != uberVersion) { + // version 0 of the uberSpect is an illusion due to order of construction; no need to clear cache + if (version > 0) { + script.clearCache(); + } + version = uberVersion; + } + } + + /** + * Creates this script frame for evaluation. + * @param args the arguments to bind to parameters + * @return the frame (may be null) + */ + protected Frame createFrame(final Object[] args) { + return script.createFrame(args); + } + + /** + * Creates this script interpreter. + * @param context the context + * @param frame the calling frame + * @return the interpreter + */ + protected Interpreter createInterpreter(final JexlContext context, final Frame frame) { + final JexlOptions opts = jexl.options(script, context); + return jexl.createInterpreter(context, frame, opts); + } + + /** + * @return the engine that created this script + */ + public JexlEngine getEngine() { + return jexl; + } + + @Override + public String getSourceText() { + return source; + } + + @Override + public String getParsedText() { + return getParsedText(2); + } + + @Override + public String getParsedText(final int indent) { + final Debugger debug = new Debugger(); + debug.setIndentation(indent); + debug.debug(script, false); + return debug.toString(); + } + + @Override + public String toString() { + CharSequence src = source; + if (src == null) { + final Debugger debug = new Debugger(); + debug.debug(script, false); + src = debug.toString(); + } + return src.toString(); + } + + @Override + public int hashCode() { + // CSOFF: Magic number + int hash = 17; + hash = 31 * hash + (this.jexl != null ? this.jexl.hashCode() : 0); + hash = 31 * hash + (this.source != null ? this.source.hashCode() : 0); + return hash; + // CSON: Magic number + } + + @Override + public boolean equals(final Object obj) { + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final Script other = (Script) obj; + if (this.jexl != other.jexl) { + return false; + } + if (!Objects.equals(this.source, other.source)) { + return false; + } + return true; + } + + @Override + public Object evaluate(final JexlContext context) { + return execute(context); + } + + @Override + public Object execute(final JexlContext context) { + checkCacheVersion(); + final Frame frame = createFrame(null); + final Interpreter interpreter = createInterpreter(context, frame); + return interpreter.interpret(script); + } + + @Override + public Object execute(final JexlContext context, final Object... args) { + checkCacheVersion(); + final Frame frame = createFrame(args != null && args.length > 0 ? args : null); + final Interpreter interpreter = createInterpreter(context, frame); + return interpreter.interpret(script); + } + + @Override + public JexlScript curry(final Object... args) { + final String[] parms = script.getParameters(); + if (parms == null || parms.length == 0) { + return this; + } + return new Closure(this, args); + } + + @Override + public String[] getParameters() { + return script.getParameters(); + } + + @Override + public String[] getUnboundParameters() { + return getParameters(); + } + + @Override + public String[] getLocalVariables() { + return script.getLocalVariables(); + } + + /** + * @return the info + */ + public JexlInfo getInfo() { + return script.jexlInfo(); + } + + /** + * @return the script features + */ + public JexlFeatures getFeatures() { + return script.getFeatures(); + } + + /** + * Gets this script variables. + *

Note that since variables can be in an ant-ish form (ie foo.bar.quux), each variable is returned as + * a list of strings where each entry is a fragment of the variable ({"foo", "bar", "quux"} in the example.

+ * @return the variables or null + */ + @Override + public Set> getVariables() { + return jexl.getVariables(script); + } + + /** + * Get this script pragmas + *

Pragma keys are ant-ish variables, their values are scalar literals.. + * @return the pragmas + */ + @Override + public Map getPragmas() { + return script.getPragmas(); + } + + /** + * Creates a Callable from this script. + *

This allows to submit it to an executor pool and provides support for asynchronous calls.

+ *

The interpreter will handle interruption/cancellation gracefully if needed.

+ * @param context the context + * @return the callable + */ + @Override + public Callable callable(final JexlContext context) { + return callable(context, (Object[]) null); + } + + /** + * Creates a Callable from this script. + *

This allows to submit it to an executor pool and provides support for asynchronous calls.

+ *

The interpreter will handle interruption/cancellation gracefully if needed.

+ * @param context the context + * @param args the script arguments + * @return the callable + */ + @Override + public Callable callable(final JexlContext context, final Object... args) { + return new Callable(createInterpreter(context, script.createFrame(args))); + } + + /** + * Implements the Future and Callable interfaces to help delegation. + */ + public class Callable implements java.util.concurrent.Callable { + /** The actual interpreter. */ + protected final Interpreter interpreter; + /** Use interpreter as marker for not having run. */ + protected volatile Object result; + + /** + * The base constructor. + * @param intrprtr the interpreter to use + */ + protected Callable(final Interpreter intrprtr) { + this.interpreter = intrprtr; + this.result = intrprtr; + } + + /** + * Run the interpreter. + * @return the evaluation result + */ + protected Object interpret() { + return interpreter.interpret(script); + } + + @Override + public Object call() throws Exception { + synchronized(this) { + if (result == interpreter) { + checkCacheVersion(); + result = interpret(); + } + return result; + } + } + + /** + * Soft cancel the execution. + * @return true if cancel was successful, false otherwise + */ + public boolean cancel() { + return interpreter.cancel(); + } + + /** + * @return true if evaluation was cancelled, false otherwise + */ + public boolean isCancelled() { + return interpreter.isCancelled(); + } + + /** + * @return true if interruption will throw a JexlException.Cancel, false otherwise + */ + public boolean isCancellable() { + return interpreter.isCancellable(); + } + } +} \ No newline at end of file diff --git a/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/internal/ScriptVisitor.java b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/internal/ScriptVisitor.java new file mode 100644 index 0000000..3442d82 --- /dev/null +++ b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/internal/ScriptVisitor.java @@ -0,0 +1,502 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package aiyh.utils.tool.org.apache.commons.jexl3.internal; + +import aiyh.utils.tool.org.apache.commons.jexl3.JexlExpression; +import aiyh.utils.tool.org.apache.commons.jexl3.JexlScript; +import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTAddNode; +import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTAndNode; +import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTAnnotatedStatement; +import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTAnnotation; +import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTArguments; +import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTArrayAccess; +import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTArrayLiteral; +import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTAssignment; +import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTBitwiseAndNode; +import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTBitwiseComplNode; +import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTBitwiseOrNode; +import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTBitwiseXorNode; +import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTBlock; +import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTBreak; +import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTConstructorNode; +import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTContinue; +import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTDivNode; +import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTDoWhileStatement; +import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTEQNode; +import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTERNode; +import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTEWNode; +import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTEmptyFunction; +import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTExtendedLiteral; +import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTFalseNode; +import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTForeachStatement; +import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTFunctionNode; +import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTGENode; +import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTGTNode; +import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTIdentifier; +import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTIdentifierAccess; +import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTIfStatement; +import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTJexlScript; +import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTJxltLiteral; +import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTLENode; +import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTLTNode; +import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTMapEntry; +import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTMapLiteral; +import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTMethodNode; +import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTModNode; +import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTMulNode; +import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTNENode; +import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTNEWNode; +import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTNRNode; +import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTNSWNode; +import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTNotNode; +import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTNullLiteral; +import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTNullpNode; +import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTNumberLiteral; +import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTOrNode; +import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTRangeNode; +import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTReference; +import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTReferenceExpression; +import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTRegexLiteral; +import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTReturnStatement; +import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTSWNode; +import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTSetAddNode; +import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTSetAndNode; +import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTSetDivNode; +import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTSetLiteral; +import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTSetModNode; +import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTSetMultNode; +import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTSetOrNode; +import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTSetSubNode; +import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTSetXorNode; +import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTSizeFunction; +import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTStringLiteral; +import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTSubNode; +import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTTernaryNode; +import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTTrueNode; +import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTUnaryMinusNode; +import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTUnaryPlusNode; +import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTVar; +import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTWhileStatement; +import aiyh.utils.tool.org.apache.commons.jexl3.parser.JexlNode; +import aiyh.utils.tool.org.apache.commons.jexl3.parser.ParserVisitor; + +/** + * Fully abstract to avoid public interface exposition. + */ +public class ScriptVisitor extends ParserVisitor { + /** + * Visits all AST constituents of a JEXL expression. + * @param jscript the expression + * @param data some data context + * @return the visit result or null if jscript was not a Script implementation + */ + public Object visitExpression (final JexlExpression jscript, final Object data) { + if (jscript instanceof Script) { + return ((Script) jscript).getScript().jjtAccept(this, data); + } + return null; + } + + /** + * Visits all AST constituents of a JEXL script. + * @param jscript the expression + * @param data some data context + * @return the visit result or null if jscript was not a Script implementation + */ + public Object visitScript(final JexlScript jscript, final Object data) { + if (jscript instanceof Script) { + return ((Script) jscript).getScript().jjtAccept(this, data); + } + return null; + } + + /** + * Visits a node. + * Default implementation visits all its children. + * @param node the node to visit + * @param data visitor pattern argument + * @return visitor pattern value + */ + protected Object visitNode(final JexlNode node, final Object data) { + return node.childrenAccept(this, data); + } + + @Override + protected Object visit(final ASTJexlScript node, final Object data) { + return visitNode(node, data); + } + + @Override + protected Object visit(final ASTBlock node, final Object data) { + return visitNode(node, data); + } + + @Override + protected Object visit(final ASTIfStatement node, final Object data) { + return visitNode(node, data); + } + + @Override + protected Object visit(final ASTWhileStatement node, final Object data) { + return visitNode(node, data); + } + + @Override + protected Object visit(final ASTDoWhileStatement node, final Object data) { + return visitNode(node, data); + } + + @Override + protected Object visit(final ASTContinue node, final Object data) { + return visitNode(node, data); + } + + @Override + protected Object visit(final ASTBreak node, final Object data) { + return visitNode(node, data); + } + + @Override + protected Object visit(final ASTForeachStatement node, final Object data) { + return visitNode(node, data); + } + + @Override + protected Object visit(final ASTReturnStatement node, final Object data) { + return visitNode(node, data); + } + + @Override + protected Object visit(final ASTAssignment node, final Object data) { + return visitNode(node, data); + } + + @Override + protected Object visit(final ASTVar node, final Object data) { + return visitNode(node, data); + } + + @Override + protected Object visit(final ASTReference node, final Object data) { + return visitNode(node, data); + } + + @Override + protected Object visit(final ASTTernaryNode node, final Object data) { + return visitNode(node, data); + } + + @Override + protected Object visit(final ASTNullpNode node, final Object data) { + return visitNode(node, data); + } + + @Override + protected Object visit(final ASTOrNode node, final Object data) { + return visitNode(node, data); + } + + @Override + protected Object visit(final ASTAndNode node, final Object data) { + return visitNode(node, data); + } + + @Override + protected Object visit(final ASTBitwiseOrNode node, final Object data) { + return visitNode(node, data); + } + + @Override + protected Object visit(final ASTBitwiseXorNode node, final Object data) { + return visitNode(node, data); + } + + @Override + protected Object visit(final ASTBitwiseAndNode node, final Object data) { + return visitNode(node, data); + } + + @Override + protected Object visit(final ASTEQNode node, final Object data) { + return visitNode(node, data); + } + + @Override + protected Object visit(final ASTNENode node, final Object data) { + return visitNode(node, data); + } + + @Override + protected Object visit(final ASTLTNode node, final Object data) { + return visitNode(node, data); + } + + @Override + protected Object visit(final ASTGTNode node, final Object data) { + return visitNode(node, data); + } + + @Override + protected Object visit(final ASTLENode node, final Object data) { + return visitNode(node, data); + } + + @Override + protected Object visit(final ASTGENode node, final Object data) { + return visitNode(node, data); + } + + @Override + protected Object visit(final ASTERNode node, final Object data) { + return visitNode(node, data); + } + + @Override + protected Object visit(final ASTNRNode node, final Object data) { + return visitNode(node, data); + } + + @Override + protected Object visit(final ASTSWNode node, final Object data) { + return visitNode(node, data); + } + + @Override + protected Object visit(final ASTNSWNode node, final Object data) { + return visitNode(node, data); + } + + @Override + protected Object visit(final ASTEWNode node, final Object data) { + return visitNode(node, data); + } + + @Override + protected Object visit(final ASTNEWNode node, final Object data) { + return visitNode(node, data); + } + + @Override + protected Object visit(final ASTAddNode node, final Object data) { + return visitNode(node, data); + } + + @Override + protected Object visit(final ASTSubNode node, final Object data) { + return visitNode(node, data); + } + + @Override + protected Object visit(final ASTMulNode node, final Object data) { + return visitNode(node, data); + } + + @Override + protected Object visit(final ASTDivNode node, final Object data) { + return visitNode(node, data); + } + + @Override + protected Object visit(final ASTModNode node, final Object data) { + return visitNode(node, data); + } + + @Override + protected Object visit(final ASTUnaryMinusNode node, final Object data) { + return visitNode(node, data); + } + + @Override + protected Object visit(final ASTUnaryPlusNode node, final Object data) { + return visitNode(node, data); + } + + @Override + protected Object visit(final ASTBitwiseComplNode node, final Object data) { + return visitNode(node, data); + } + + @Override + protected Object visit(final ASTNotNode node, final Object data) { + return visitNode(node, data); + } + + @Override + protected Object visit(final ASTIdentifier node, final Object data) { + return visitNode(node, data); + } + + @Override + protected Object visit(final ASTNullLiteral node, final Object data) { + return visitNode(node, data); + } + + @Override + protected Object visit(final ASTTrueNode node, final Object data) { + return visitNode(node, data); + } + + @Override + protected Object visit(final ASTFalseNode node, final Object data) { + return visitNode(node, data); + } + + @Override + protected Object visit(final ASTNumberLiteral node, final Object data) { + return visitNode(node, data); + } + + @Override + protected Object visit(final ASTStringLiteral node, final Object data) { + return visitNode(node, data); + } + + @Override + protected Object visit(final ASTRegexLiteral node, final Object data) { + return visitNode(node, data); + } + + @Override + protected Object visit(final ASTSetLiteral node, final Object data) { + return visitNode(node, data); + } + + @Override + protected Object visit(final ASTExtendedLiteral node, final Object data) { + return visitNode(node, data); + } + + @Override + protected Object visit(final ASTArrayLiteral node, final Object data) { + return visitNode(node, data); + } + + @Override + protected Object visit(final ASTRangeNode node, final Object data) { + return visitNode(node, data); + } + + @Override + protected Object visit(final ASTMapLiteral node, final Object data) { + return visitNode(node, data); + } + + @Override + protected Object visit(final ASTMapEntry node, final Object data) { + return visitNode(node, data); + } + + @Override + protected Object visit(final ASTEmptyFunction node, final Object data) { + return visitNode(node, data); + } + + @Override + protected Object visit(final ASTSizeFunction node, final Object data) { + return visitNode(node, data); + } + + @Override + protected Object visit(final ASTFunctionNode node, final Object data) { + return visitNode(node, data); + } + + @Override + protected Object visit(final ASTMethodNode node, final Object data) { + return visitNode(node, data); + } + + @Override + protected Object visit(final ASTConstructorNode node, final Object data) { + return visitNode(node, data); + } + + @Override + protected Object visit(final ASTArrayAccess node, final Object data) { + return visitNode(node, data); + } + + @Override + protected Object visit(final ASTIdentifierAccess node, final Object data) { + return visitNode(node, data); + } + + @Override + protected Object visit(final ASTArguments node, final Object data) { + return visitNode(node, data); + } + + @Override + protected Object visit(final ASTReferenceExpression node, final Object data) { + return visitNode(node, data); + } + + @Override + protected Object visit(final ASTSetAddNode node, final Object data) { + return visitNode(node, data); + } + + @Override + protected Object visit(final ASTSetSubNode node, final Object data) { + return visitNode(node, data); + } + + @Override + protected Object visit(final ASTSetMultNode node, final Object data) { + return visitNode(node, data); + } + + @Override + protected Object visit(final ASTSetDivNode node, final Object data) { + return visitNode(node, data); + } + + @Override + protected Object visit(final ASTSetModNode node, final Object data) { + return visitNode(node, data); + } + + @Override + protected Object visit(final ASTSetAndNode node, final Object data) { + return visitNode(node, data); + } + + @Override + protected Object visit(final ASTSetOrNode node, final Object data) { + return visitNode(node, data); + } + + @Override + protected Object visit(final ASTSetXorNode node, final Object data) { + return visitNode(node, data); + } + + @Override + protected Object visit(final ASTJxltLiteral node, final Object data) { + return visitNode(node, data); + } + + @Override + protected Object visit(final ASTAnnotation node, final Object data) { + return visitNode(node, data); + } + + @Override + protected Object visit(final ASTAnnotatedStatement node, final Object data) { + return visitNode(node, data); + } +} diff --git a/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/internal/SetBuilder.java b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/internal/SetBuilder.java new file mode 100644 index 0000000..5dfa477 --- /dev/null +++ b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/internal/SetBuilder.java @@ -0,0 +1,48 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package aiyh.utils.tool.org.apache.commons.jexl3.internal; + +import aiyh.utils.tool.org.apache.commons.jexl3.JexlArithmetic; + +import java.util.HashSet; +import java.util.Set; + +/** + * Helper class to create set literals. + */ +public class SetBuilder implements JexlArithmetic.SetBuilder { + /** The set being created. */ + protected final Set set; + + /** + * Creates a new builder. + * @param size the expected set size + */ + public SetBuilder(final int size) { + set = new HashSet(size); + } + + @Override + public void add(final Object value) { + set.add(value); + } + + @Override + public Object create() { + return set; + } +} diff --git a/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/internal/SoftCache.java b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/internal/SoftCache.java new file mode 100644 index 0000000..c754849 --- /dev/null +++ b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/internal/SoftCache.java @@ -0,0 +1,211 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package aiyh.utils.tool.org.apache.commons.jexl3.internal; + +import java.lang.ref.SoftReference; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.locks.ReadWriteLock; +import java.util.concurrent.locks.ReentrantReadWriteLock; + +/** + * A soft referenced cache. + *

+ * The actual cache is held through a soft reference, allowing it to be GCed + * under memory pressure.

+ * + * @param the cache key entry type + * @param the cache key value type + */ +public class SoftCache { + /** + * The default cache load factor. + */ + private static final float LOAD_FACTOR = 0.75f; + /** + * The cache size. + */ + private final int size; + /** + * The soft reference to the cache map. + */ + private SoftReference> ref = null; + /** + * The cache r/w lock. + */ + private final ReadWriteLock lock; + + /** + * Creates a new instance of a soft cache. + * + * @param theSize the cache size + */ + SoftCache(final int theSize) { + size = theSize; + lock = new ReentrantReadWriteLock(); + } + + /** + * Returns the cache size. + * + * @return the cache size + */ + public int size() { + return size; + } + + /** + * Clears the cache. + */ + public void clear() { + lock.writeLock().lock(); + try { + ref = null; + } finally { + lock.writeLock().unlock(); + } + } + + /** + * Gets a value from cache. + * + * @param key the cache entry key + * @return the cache entry value + */ + public V get(final K key) { + lock.readLock().lock(); + try { + final Map map = ref != null ? ref.get() : null; + return map != null ? map.get(key) : null; + } finally { + lock.readLock().unlock(); + } + } + + /** + * Puts a value in cache. + * + * @param key the cache entry key + * @param script the cache entry value + */ + public void put(final K key, final V script) { + lock.writeLock().lock(); + try { + Map map = ref != null ? ref.get() : null; + if (map == null) { + map = createCache(size); + ref = new SoftReference>(map); + } + map.put(key, script); + } finally { + lock.writeLock().unlock(); + } + } + + /** + * Produces the cache entry set. + *

+ * For testing only, perform deep copy of cache entries + * + * @return the cache entry list + */ + public List> entries() { + lock.readLock().lock(); + try { + final Map map = ref != null ? ref.get() : null; + if (map == null) { + return Collections.emptyList(); + } + final Set> set = map.entrySet(); + final List> entries = new ArrayList>(set.size()); + for (final Map.Entry e : set) { + entries.add(new SoftCacheEntry(e)); + } + return entries; + } finally { + lock.readLock().unlock(); + } + } + + /** + * Creates the cache store. + * + * @param the key type + * @param the value type + * @param cacheSize the cache size, must be > 0 + * @return a Map usable as a cache bounded to the given size + */ + public Map createCache(final int cacheSize) { + return new java.util.LinkedHashMap(cacheSize, LOAD_FACTOR, true) { + /** + * Serial version UID. + */ + private static final long serialVersionUID = 1L; + + @Override + protected boolean removeEldestEntry(final Map.Entry eldest) { + return super.size() > cacheSize; + } + }; + } +} + +/** + * A soft cache entry. + * + * @param key type + * @param value type + */ +class SoftCacheEntry implements Map.Entry { + /** + * The entry key. + */ + private final K key; + /** + * The entry value. + */ + private final V value; + + /** + * Creates an entry clone. + * + * @param e the entry to clone + */ + SoftCacheEntry(final Map.Entry e) { + key = e.getKey(); + value = e.getValue(); + } + + @Override + public K getKey() { + return key; + } + + @Override + public V getValue() { + return value; + } + + @Override + public V setValue(final V v) { + throw new UnsupportedOperationException("Not supported."); + } +} + diff --git a/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/internal/Source.java b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/internal/Source.java new file mode 100644 index 0000000..7e0091f --- /dev/null +++ b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/internal/Source.java @@ -0,0 +1,95 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package aiyh.utils.tool.org.apache.commons.jexl3.internal; + +import aiyh.utils.tool.org.apache.commons.jexl3.JexlFeatures; + +import java.util.Objects; + +/** + * Maintains the set of allowed features associated with a script/expression source. + *

This is meant for caching scripts using their 'source' as key but still distinguishing + * scripts with different features and prevent false sharing. + */ +public final class Source { + /** The hash code, pre-computed for fast op. */ + private final int hashCode; + /** The set of features. */ + private final JexlFeatures features; + /** The actual source script/expression. */ + private final String str; + + /** + * Default constructor. + * @param theFeatures the features + * @param theStr the script source + */ + Source(final JexlFeatures theFeatures, final String theStr) { // CSOFF: MagicNumber + this.features = theFeatures; + this.str = theStr; + int hash = 3; + hash = 37 * hash + features.hashCode(); + hash = 37 * hash + str.hashCode() ; + this.hashCode = hash; + } + + /** + * @return the length of the script source + */ + int length() { + return str.length(); + } + + @Override + public int hashCode() { + return hashCode; + } + + @Override + public boolean equals(final Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final Source other = (Source) obj; + if (!Objects.equals(this.features, other.features)) { + return false; + } + if (!Objects.equals(this.str, other.str)) { + return false; + } + return true; + } + + @Override + public String toString() { + return str; + } + + /** + * @return the features associated with the source + */ + public JexlFeatures getFeatures() { + return features; + } + +} diff --git a/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/internal/TemplateDebugger.java b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/internal/TemplateDebugger.java new file mode 100644 index 0000000..a07eb5b --- /dev/null +++ b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/internal/TemplateDebugger.java @@ -0,0 +1,314 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package aiyh.utils.tool.org.apache.commons.jexl3.internal; + +import aiyh.utils.tool.org.apache.commons.jexl3.JxltEngine; +import aiyh.utils.tool.org.apache.commons.jexl3.internal.TemplateEngine.*; +import aiyh.utils.tool.org.apache.commons.jexl3.parser.*; + +/** + * A visitor for templates. + *

A friend (ala C++) of template engine. + */ +public class TemplateDebugger extends Debugger { + /** The outer script. */ + private ASTJexlScript script; + /** The expressions called by the script through jexl:print. */ + private TemplateExpression[] exprs; + + /** + * Default ctor. + */ + public TemplateDebugger() { + } + + @Override + public void reset() { + super.reset(); + // so we can use it more than one time + exprs = null; + script = null; + } + + /** + * Position the debugger on the root of a template expression. + * + * @param je the expression + * @return true if the expression was a {@link TemplateExpression} instance, false otherwise + */ + public boolean debug(final JxltEngine.Expression je) { + if (je instanceof TemplateExpression) { + final TemplateExpression te = (TemplateExpression) je; + return visit(te, this) != null; + } + return false; + } + + /** + * Position the debugger on the root of a template script. + * + * @param jt the template + * @return true if the template was a {@link TemplateScript} instance, false otherwise + */ + public boolean debug(final JxltEngine.Template jt) { + if (!(jt instanceof TemplateScript)) { + return false; + } + final TemplateScript ts = (TemplateScript) jt; + // ensure expr is not null for templates + this.exprs = ts.getExpressions() == null ? new TemplateExpression[0] : ts.getExpressions(); + this.script = ts.getScript(); + start = 0; + end = 0; + indentLevel = 0; + builder.setLength(0); + cause = script; + final int num = script.jjtGetNumChildren(); + for (int i = 0; i < num; ++i) { + final JexlNode child = script.jjtGetChild(i); + acceptStatement(child, null); + } + // the last line + if (builder.length() > 0 && builder.charAt(builder.length() - 1) != '\n') { + builder.append('\n'); + } + end = builder.length(); + return end > 0; + } + + + @Override + protected Object visit(final ASTBlock node, final Object data) { + // if not really a template, must use super impl + if (exprs == null) { + return super.visit(node, data); + } + // open the block + builder.append('{'); + if (indent > 0) { + indentLevel += 1; + builder.append('\n'); + } else { + builder.append(' '); + } + final int num = node.jjtGetNumChildren(); + for (int i = 0; i < num; ++i) { + final JexlNode child = node.jjtGetChild(i); + acceptStatement(child, data); + } + // before we close this block node, $$ might be needed + newJexlLine(); + if (indent > 0) { + indentLevel -= 1; + for (int i = 0; i < indentLevel; ++i) { + for (int s = 0; s < indent; ++s) { + builder.append(' '); + } + } + } + builder.append('}'); + // closed the block + return data; + } + + @Override + protected Object acceptStatement(final JexlNode child, final Object data) { + // if not really a template, must use super impl + if (exprs == null) { + return super.acceptStatement(child, data); + } + final TemplateExpression te = getPrintStatement(child); + if (te != null) { + // if statement is a jexl:print(...), may need to prepend '\n' + newJxltLine(); + return visit(te, data); + } + // if statement is not a jexl:print(...), need to prepend '$$' + newJexlLine(); + return super.acceptStatement(child, data); + } + + /** + * In a template, any statement that is not 'jexl:print(n)' must be prefixed by "$$". + * + * @param child the node to check + * @return the expression number or -1 if the node is not a jexl:print + */ + private TemplateExpression getPrintStatement(final JexlNode child) { + if (exprs != null && child instanceof ASTFunctionNode) { + final ASTFunctionNode node = (ASTFunctionNode) child; + final ASTIdentifier ns = (ASTIdentifier) node.jjtGetChild(0); + final JexlNode args = node.jjtGetChild(1); + if ("jexl".equals(ns.getNamespace()) + && "print".equals(ns.getName()) + && args.jjtGetNumChildren() == 1 + && args.jjtGetChild(0) instanceof ASTNumberLiteral) { + final ASTNumberLiteral exprn = (ASTNumberLiteral) args.jjtGetChild(0); + final int n = exprn.getLiteral().intValue(); + if (n >= 0 && n < exprs.length) { + return exprs[n]; + } + } + } + return null; + } + + /** + * Insert $$ and \n when needed. + */ + private void newJexlLine() { + final int length = builder.length(); + if (length == 0) { + builder.append("$$ "); + } else { + for (int i = length - 1; i >= 0; --i) { + final char c = builder.charAt(i); + switch (c) { + case '\n': + builder.append("$$ "); + return; + case '}': + builder.append("\n$$ "); + return; + case ' ': + case ';': + return; + default: // continue + } + } + } + } + + /** + * Insert \n when needed. + */ + private void newJxltLine() { + final int length = builder.length(); + for (int i = length - 1; i >= 0; --i) { + final char c = builder.charAt(i); + switch (c) { + case '\n': + case ';': + return; + case '}': + builder.append('\n'); + return; + default: // continue + } + } + } + + /** + * Visit a template expression. + * + * @param expr the constant expression + * @param data the visitor argument + * @return the visitor argument + */ + private Object visit(final TemplateExpression expr, final Object data) { + Object r; + switch (expr.getType()) { + case CONSTANT: + r = visit((ConstantExpression) expr, data); + break; + case IMMEDIATE: + r = visit((ImmediateExpression) expr, data); + break; + case DEFERRED: + r = visit((DeferredExpression) expr, data); + break; + case NESTED: + r = visit((NestedExpression) expr, data); + break; + case COMPOSITE: + r = visit((CompositeExpression) expr, data); + break; + default: + r = null; + } + return r; + } + + /** + * Visit a constant expression. + * + * @param expr the constant expression + * @param data the visitor argument + * @return the visitor argument + */ + private Object visit(final ConstantExpression expr, final Object data) { + expr.asString(builder); + return data; + } + + /** + * Visit an immediate expression. + * + * @param expr the immediate expression + * @param data the visitor argument + * @return the visitor argument + */ + private Object visit(final ImmediateExpression expr, final Object data) { + builder.append(expr.isImmediate() ? '$' : '#'); + builder.append('{'); + super.accept(expr.node, data); + builder.append('}'); + return data; + } + + /** + * Visit a deferred expression. + * + * @param expr the deferred expression + * @param data the visitor argument + * @return the visitor argument + */ + private Object visit(final DeferredExpression expr, final Object data) { + builder.append(expr.isImmediate() ? '$' : '#'); + builder.append('{'); + super.accept(expr.node, data); + builder.append('}'); + return data; + } + + /** + * Visit a nested expression. + * + * @param expr the nested expression + * @param data the visitor argument + * @return the visitor argument + */ + private Object visit(final NestedExpression expr, final Object data) { + super.accept(expr.node, data); + return data; + } + + /** + * Visit a composite expression. + * + * @param expr the composite expression + * @param data the visitor argument + * @return the visitor argument + */ + private Object visit(final CompositeExpression expr, final Object data) { + for (final TemplateExpression ce : expr.exprs) { + visit(ce, data); + } + return data; + } + +} diff --git a/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/internal/TemplateEngine.java b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/internal/TemplateEngine.java new file mode 100644 index 0000000..4fb869b --- /dev/null +++ b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/internal/TemplateEngine.java @@ -0,0 +1,1232 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package aiyh.utils.tool.org.apache.commons.jexl3.internal; + +import aiyh.utils.tool.org.apache.commons.jexl3.*; +import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTJexlScript; +import aiyh.utils.tool.org.apache.commons.jexl3.parser.JexlNode; +import aiyh.utils.tool.org.apache.commons.jexl3.parser.StringParser; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.Reader; +import java.io.StringReader; +import java.util.*; + +/** + * A JxltEngine implementation. + * + * @since 3.0 + */ +public final class TemplateEngine extends JxltEngine { + /** The TemplateExpression cache. */ + private final SoftCache cache; + /** The JEXL engine instance. */ + private final Engine jexl; + /** The first character for immediate expressions. */ + private final char immediateChar; + /** The first character for deferred expressions. */ + private final char deferredChar; + /** Whether expressions can use JEXL script or only expressions (ie, no for, var, etc). */ + private boolean noscript = true; + + /** + * Creates a new instance of {@link JxltEngine} creating a local cache. + * + * @param aJexl the JexlEngine to use. + * @param noScript whether this engine only allows JEXL expressions or scripts + * @param cacheSize the number of expressions in this cache, default is 256 + * @param immediate the immediate template expression character, default is '$' + * @param deferred the deferred template expression character, default is '#' + */ + public TemplateEngine(final Engine aJexl, + final boolean noScript, + final int cacheSize, + final char immediate, + final char deferred) { + this.jexl = aJexl; + this.cache = new SoftCache<>(cacheSize); + immediateChar = immediate; + deferredChar = deferred; + noscript = noScript; + } + + /** + * @return the immediate character + */ + char getImmediateChar() { + return immediateChar; + } + + /** + * @return the deferred character + */ + char getDeferredChar() { + return deferredChar; + } + + /** + * Types of expressions. + * Each instance carries a counter index per (composite sub-) template expression type. + * + * @see ExpressionBuilder + */ + enum ExpressionType { + /** Constant TemplateExpression, count index 0. */ + CONSTANT(0), + /** Immediate TemplateExpression, count index 1. */ + IMMEDIATE(1), + /** Deferred TemplateExpression, count index 2. */ + DEFERRED(2), + /** + * Nested (which are deferred) expressions, count + * index 2. + */ + NESTED(2), + /** Composite expressions are not counted, index -1. */ + COMPOSITE(-1); + /** The index in arrays of TemplateExpression counters for composite expressions. */ + private final int index; + + /** + * Creates an ExpressionType. + * + * @param idx the index for this type in counters arrays. + */ + ExpressionType(final int idx) { + this.index = idx; + } + } + + /** + * A helper class to build expressions. + * Keeps count of sub-expressions by type. + */ + static final class ExpressionBuilder { + /** Per TemplateExpression type counters. */ + private final int[] counts; + /** The list of expressions. */ + private final ArrayList expressions; + + /** + * Creates a builder. + * + * @param size the initial TemplateExpression array size + */ + private ExpressionBuilder(final int size) { + counts = new int[]{0, 0, 0}; + expressions = new ArrayList<>(size <= 0 ? 3 : size); + } + + /** + * Adds an TemplateExpression to the list of expressions, maintain per-type counts. + * + * @param expr the TemplateExpression to add + */ + private void add(final TemplateExpression expr) { + counts[expr.getType().index] += 1; + expressions.add(expr); + } + + @Override + public String toString() { + return toString(new StringBuilder()).toString(); + } + + /** + * Base for to-string. + * + * @param error the builder to fill + * @return the builder + */ + private StringBuilder toString(final StringBuilder error) { + error.append("exprs{"); + error.append(expressions.size()); + error.append(", constant:"); + error.append(counts[ExpressionType.CONSTANT.index]); + error.append(", immediate:"); + error.append(counts[ExpressionType.IMMEDIATE.index]); + error.append(", deferred:"); + error.append(counts[ExpressionType.DEFERRED.index]); + error.append("}"); + return error; + } + + /** + * Builds an TemplateExpression from a source, performs checks. + * + * @param el the unified el instance + * @param source the source TemplateExpression + * @return an TemplateExpression + */ + private TemplateExpression build(final TemplateEngine el, final TemplateExpression source) { + int sum = 0; + for (final int count : counts) { + sum += count; + } + if (expressions.size() != sum) { + final StringBuilder error = new StringBuilder("parsing algorithm error: "); + throw new IllegalStateException(toString(error).toString()); + } + // if only one sub-expr, no need to create a composite + if (expressions.size() == 1) { + return expressions.get(0); + } + return el.new CompositeExpression(counts, expressions, source); + } + } + + /** + * Gets the JexlEngine underlying this JxltEngine. + * + * @return the JexlEngine + */ + @Override + public Engine getEngine() { + return jexl; + } + + /** + * Clears the cache. + */ + @Override + public void clearCache() { + synchronized (cache) { + cache.clear(); + } + } + + /** + * The abstract base class for all unified expressions, immediate '${...}' and deferred '#{...}'. + */ + abstract class TemplateExpression implements Expression { + /** The source of this template expression(see {@link TemplateExpression#prepare}). */ + protected final TemplateExpression source; + + /** + * Creates an TemplateExpression. + * + * @param src the source TemplateExpression if any + */ + TemplateExpression(final TemplateExpression src) { + this.source = src != null ? src : this; + } + + @Override + public boolean isImmediate() { + return true; + } + + @Override + public final boolean isDeferred() { + return !isImmediate(); + } + + /** + * Gets this TemplateExpression type. + * + * @return its type + */ + abstract ExpressionType getType(); + + /** @return the info */ + JexlInfo getInfo() { + return null; + } + + @Override + public final String toString() { + final StringBuilder strb = new StringBuilder(); + asString(strb); + if (source != this) { + strb.append(" /*= "); + strb.append(source); + strb.append(" */"); + } + return strb.toString(); + } + + @Override + public String asString() { + final StringBuilder strb = new StringBuilder(); + asString(strb); + return strb.toString(); + } + + @Override + public Set> getVariables() { + return Collections.emptySet(); + } + + @Override + public final TemplateExpression getSource() { + return source; + } + + /** + * Fills up the list of variables accessed by this unified expression. + * + * @param collector the variable collector + */ + protected void getVariables(final Engine.VarCollector collector) { + // nothing to do + } + + @Override + public final TemplateExpression prepare(final JexlContext context) { + return prepare(null, context); + } + + /** + * Prepares this expression. + * + * @param frame the frame storing parameters and local variables + * @param context the context storing global variables + * @return the expression value + * @throws JexlException + */ + protected final TemplateExpression prepare(final Frame frame, final JexlContext context) { + try { + final Interpreter interpreter = jexl.createInterpreter(context, frame, jexl.options(context)); + return prepare(interpreter); + } catch (final JexlException xjexl) { + final JexlException xuel = createException(xjexl.getInfo(), "prepare", this, xjexl); + if (jexl.isSilent()) { + jexl.logger.warn(xuel.getMessage(), xuel.getCause()); + return null; + } + throw xuel; + } + } + + /** + * Prepares a sub-expression for interpretation. + * + * @param interpreter a JEXL interpreter + * @return a prepared unified expression + * @throws JexlException (only for nested and composite) + */ + protected TemplateExpression prepare(final Interpreter interpreter) { + return this; + } + + @Override + public final Object evaluate(final JexlContext context) { + return evaluate(null, context); + } + + /** + * The options to use during evaluation. + * + * @param context the context + * @return the options + */ + protected JexlOptions options(final JexlContext context) { + return jexl.options(null, context); + } + + /** + * Evaluates this expression. + * + * @param frame the frame storing parameters and local variables + * @param context the context storing global variables + * @return the expression value + * @throws JexlException + */ + protected final Object evaluate(final Frame frame, final JexlContext context) { + try { + final JexlOptions options = options(context); + final TemplateInterpreter.Arguments args = new TemplateInterpreter + .Arguments(jexl) + .context(context) + .options(options) + .frame(frame); + final Interpreter interpreter = new TemplateInterpreter(args); + return evaluate(interpreter); + } catch (final JexlException xjexl) { + final JexlException xuel = createException(xjexl.getInfo(), "evaluate", this, xjexl); + if (jexl.isSilent()) { + jexl.logger.warn(xuel.getMessage(), xuel.getCause()); + return null; + } + throw xuel; + } + } + + /** + * Interprets a sub-expression. + * + * @param interpreter a JEXL interpreter + * @return the result of interpretation + * @throws JexlException (only for nested and composite) + */ + protected abstract Object evaluate(Interpreter interpreter); + + } + + /** A constant unified expression. */ + class ConstantExpression extends TemplateExpression { + /** The constant held by this unified expression. */ + private final Object value; + + /** + * Creates a constant unified expression. + *

+ * If the wrapped constant is a string, it is treated + * as a JEXL strings with respect to escaping. + *

+ * + * @param val the constant value + * @param source the source TemplateExpression if any + */ + ConstantExpression(Object val, final TemplateExpression source) { + super(source); + if (val == null) { + throw new NullPointerException("constant can not be null"); + } + if (val instanceof String) { + val = StringParser.buildTemplate((String) val, false); + } + this.value = val; + } + + @Override + ExpressionType getType() { + return ExpressionType.CONSTANT; + } + + @Override + public StringBuilder asString(final StringBuilder strb) { + if (value != null) { + strb.append(value); + } + return strb; + } + + @Override + protected Object evaluate(final Interpreter interpreter) { + return value; + } + } + + /** The base for JEXL based unified expressions. */ + abstract class JexlBasedExpression extends TemplateExpression { + /** The JEXL string for this unified expression. */ + protected final CharSequence expr; + /** The JEXL node for this unified expression. */ + protected final JexlNode node; + + /** + * Creates a JEXL interpretable unified expression. + * + * @param theExpr the unified expression as a string + * @param theNode the unified expression as an AST + * @param theSource the source unified expression if any + */ + protected JexlBasedExpression(final CharSequence theExpr, final JexlNode theNode, final TemplateExpression theSource) { + super(theSource); + this.expr = theExpr; + this.node = theNode; + } + + @Override + public StringBuilder asString(final StringBuilder strb) { + strb.append(isImmediate() ? immediateChar : deferredChar); + strb.append("{"); + strb.append(expr); + strb.append("}"); + return strb; + } + + @Override + protected JexlOptions options(final JexlContext context) { + return jexl.options(node instanceof ASTJexlScript ? (ASTJexlScript) node : null, context); + } + + @Override + protected Object evaluate(final Interpreter interpreter) { + return interpreter.interpret(node); + } + + @Override + public Set> getVariables() { + final Engine.VarCollector collector = jexl.varCollector(); + getVariables(collector); + return collector.collected(); + } + + @Override + protected void getVariables(final Engine.VarCollector collector) { + jexl.getVariables(node instanceof ASTJexlScript ? (ASTJexlScript) node : null, node, collector); + } + + @Override + JexlInfo getInfo() { + return node.jexlInfo(); + } + } + + /** An immediate unified expression: ${jexl}. */ + class ImmediateExpression extends JexlBasedExpression { + /** + * Creates an immediate unified expression. + * + * @param expr the unified expression as a string + * @param node the unified expression as an AST + * @param source the source unified expression if any + */ + ImmediateExpression(final CharSequence expr, final JexlNode node, final TemplateExpression source) { + super(expr, node, source); + } + + @Override + ExpressionType getType() { + return ExpressionType.IMMEDIATE; + } + + @Override + protected TemplateExpression prepare(final Interpreter interpreter) { + // evaluate immediate as constant + final Object value = evaluate(interpreter); + return value != null ? new ConstantExpression(value, source) : null; + } + } + + /** A deferred unified expression: #{jexl}. */ + class DeferredExpression extends JexlBasedExpression { + /** + * Creates a deferred unified expression. + * + * @param expr the unified expression as a string + * @param node the unified expression as an AST + * @param source the source unified expression if any + */ + DeferredExpression(final CharSequence expr, final JexlNode node, final TemplateExpression source) { + super(expr, node, source); + } + + @Override + public boolean isImmediate() { + return false; + } + + @Override + ExpressionType getType() { + return ExpressionType.DEFERRED; + } + + @Override + protected TemplateExpression prepare(final Interpreter interpreter) { + return new ImmediateExpression(expr, node, source); + } + + @Override + protected void getVariables(final Engine.VarCollector collector) { + // noop + } + } + + /** + * An immediate unified expression nested into a deferred unified expression. + * #{...${jexl}...} + * Note that the deferred syntax is JEXL's. + */ + class NestedExpression extends JexlBasedExpression { + /** + * Creates a nested unified expression. + * + * @param expr the unified expression as a string + * @param node the unified expression as an AST + * @param source the source unified expression if any + */ + NestedExpression(final CharSequence expr, final JexlNode node, final TemplateExpression source) { + super(expr, node, source); + if (this.source != this) { + throw new IllegalArgumentException("Nested TemplateExpression can not have a source"); + } + } + + @Override + public StringBuilder asString(final StringBuilder strb) { + strb.append(expr); + return strb; + } + + @Override + public boolean isImmediate() { + return false; + } + + @Override + ExpressionType getType() { + return ExpressionType.NESTED; + } + + @Override + protected TemplateExpression prepare(final Interpreter interpreter) { + final String value = interpreter.interpret(node).toString(); + final JexlNode dnode = jexl.parse(node.jexlInfo(), noscript, value, null); + return new ImmediateExpression(value, dnode, this); + } + + @Override + protected Object evaluate(final Interpreter interpreter) { + return prepare(interpreter).evaluate(interpreter); + } + } + + /** A composite unified expression: "... ${...} ... #{...} ...". */ + class CompositeExpression extends TemplateExpression { + /** Bit encoded (deferred count > 0) bit 1, (immediate count > 0) bit 0. */ + private final int meta; + /** The list of sub-expression resulting from parsing. */ + protected final TemplateExpression[] exprs; + + /** + * Creates a composite expression. + * + * @param counters counters of expressions per type + * @param list the sub-expressions + * @param src the source for this expression if any + */ + CompositeExpression(final int[] counters, final ArrayList list, final TemplateExpression src) { + super(src); + this.exprs = list.toArray(new TemplateExpression[list.size()]); + this.meta = (counters[ExpressionType.DEFERRED.index] > 0 ? 2 : 0) + | (counters[ExpressionType.IMMEDIATE.index] > 0 ? 1 : 0); + } + + @Override + public boolean isImmediate() { + // immediate if no deferred + return (meta & 2) == 0; + } + + @Override + ExpressionType getType() { + return ExpressionType.COMPOSITE; + } + + @Override + public StringBuilder asString(final StringBuilder strb) { + for (final TemplateExpression e : exprs) { + e.asString(strb); + } + return strb; + } + + @Override + public Set> getVariables() { + final Engine.VarCollector collector = jexl.varCollector(); + for (final TemplateExpression expr : exprs) { + expr.getVariables(collector); + } + return collector.collected(); + } + + /** + * Fills up the list of variables accessed by this unified expression. + * + * @param collector the variable collector + */ + @Override + protected void getVariables(final Engine.VarCollector collector) { + for (final TemplateExpression expr : exprs) { + expr.getVariables(collector); + } + } + + @Override + protected TemplateExpression prepare(final Interpreter interpreter) { + // if this composite is not its own source, it is already prepared + if (source != this) { + return this; + } + // we need to prepare all sub-expressions + final int size = exprs.length; + final ExpressionBuilder builder = new ExpressionBuilder(size); + // tracking whether prepare will return a different expression + boolean eq = true; + for (final TemplateExpression expr : exprs) { + final TemplateExpression prepared = expr.prepare(interpreter); + // add it if not null + if (prepared != null) { + builder.add(prepared); + } + // keep track of TemplateExpression equivalence + eq &= expr == prepared; + } + return eq ? this : builder.build(TemplateEngine.this, this); + } + + @Override + protected Object evaluate(final Interpreter interpreter) { + Object value; + // common case: evaluate all expressions & concatenate them as a string + final StringBuilder strb = new StringBuilder(); + for (final TemplateExpression expr : exprs) { + value = expr.evaluate(interpreter); + if (value != null) { + strb.append(value); + } + } + value = strb.toString(); + return value; + } + } + + + @Override + public Expression createExpression(JexlInfo info, final String expression) { + if (info == null) { + info = jexl.createInfo(); + } + Exception xuel = null; + TemplateExpression stmt = null; + try { + stmt = cache.get(expression); + if (stmt == null) { + stmt = parseExpression(info, expression, null); + cache.put(expression, stmt); + } + } catch (final JexlException xjexl) { + xuel = new Exception(xjexl.getInfo(), "failed to parse '" + expression + "'", xjexl); + } + if (xuel != null) { + if (!jexl.isSilent()) { + throw xuel; + } + jexl.logger.warn(xuel.getMessage(), xuel.getCause()); + stmt = null; + } + return stmt; + } + + /** + * Creates a JxltEngine.Exception from a JexlException. + * + * @param info the source info + * @param action createExpression, prepare, evaluate + * @param expr the template expression + * @param xany the exception + * @return an exception containing an explicit error message + */ + static Exception createException(final JexlInfo info, + final String action, + final TemplateExpression expr, + final java.lang.Exception xany) { + final StringBuilder strb = new StringBuilder("failed to "); + strb.append(action); + if (expr != null) { + strb.append(" '"); + strb.append(expr); + strb.append("'"); + } + final Throwable cause = xany.getCause(); + if (cause != null) { + final String causeMsg = cause.getMessage(); + if (causeMsg != null) { + strb.append(", "); + strb.append(causeMsg); + } + } + return new Exception(info, strb.toString(), xany); + } + + /** The different parsing states. */ + private enum ParseState { + /** Parsing a constant. */ + CONST, + /** Parsing after $ . */ + IMMEDIATE0, + /** Parsing after # . */ + DEFERRED0, + /** Parsing after ${ . */ + IMMEDIATE1, + /** Parsing after #{ . */ + DEFERRED1, + /** Parsing after \ . */ + ESCAPE + } + + /** + * Helper for expression dealing with embedded strings. + * + * @param strb the expression buffer to copy characters into + * @param expr the source + * @param position the offset into the source + * @param c the separator character + * @return the new position to read the source from + */ + private static int append(final StringBuilder strb, final CharSequence expr, final int position, final char c) { + strb.append(c); + if (c != '"' && c != '\'') { + return position; + } + // read thru strings + final int end = expr.length(); + boolean escape = false; + int index = position + 1; + for (; index < end; ++index) { + final char ec = expr.charAt(index); + strb.append(ec); + if (ec == '\\') { + escape = !escape; + } else if (escape) { + escape = false; + } else if (ec == c) { + break; + } + } + return index; + } + + /** + * Parses a unified expression. + * + * @param info the source info + * @param expr the string expression + * @param scope the template scope + * @return the unified expression instance + * @throws JexlException if an error occur during parsing + */ + TemplateExpression parseExpression(final JexlInfo info, final String expr, final Scope scope) { // CSOFF: MethodLength + final int size = expr.length(); + final ExpressionBuilder builder = new ExpressionBuilder(0); + final StringBuilder strb = new StringBuilder(size); + ParseState state = ParseState.CONST; + int immediate1 = 0; + int deferred1 = 0; + int inner1 = 0; + boolean nested = false; + int inested = -1; + int lineno = info.getLine(); + for (int column = 0; column < size; ++column) { + final char c = expr.charAt(column); + switch (state) { + default: // in case we ever add new unified expression type + throw new UnsupportedOperationException("unexpected unified expression type"); + case CONST: + if (c == immediateChar) { + state = ParseState.IMMEDIATE0; + } else if (c == deferredChar) { + inested = column; + state = ParseState.DEFERRED0; + } else if (c == '\\') { + state = ParseState.ESCAPE; + } else { + // do buildup expr + strb.append(c); + } + break; + case IMMEDIATE0: // $ + if (c == '{') { + state = ParseState.IMMEDIATE1; + // if chars in buffer, create constant + if (strb.length() > 0) { + final TemplateExpression cexpr = new ConstantExpression(strb.toString(), null); + builder.add(cexpr); + strb.delete(0, Integer.MAX_VALUE); + } + } else { + // revert to CONST + strb.append(immediateChar); + strb.append(c); + state = ParseState.CONST; + } + break; + case DEFERRED0: // # + if (c == '{') { + state = ParseState.DEFERRED1; + // if chars in buffer, create constant + if (strb.length() > 0) { + final TemplateExpression cexpr = new ConstantExpression(strb.toString(), null); + builder.add(cexpr); + strb.delete(0, Integer.MAX_VALUE); + } + } else { + // revert to CONST + strb.append(deferredChar); + strb.append(c); + state = ParseState.CONST; + } + break; + case IMMEDIATE1: // ${... + if (c == '}') { + if (immediate1 > 0) { + immediate1 -= 1; + strb.append(c); + } else { + // materialize the immediate expr + final String src = strb.toString(); + final TemplateExpression iexpr = new ImmediateExpression( + src, + jexl.parse(info.at(lineno, column), noscript, src, scope), + null); + builder.add(iexpr); + strb.delete(0, Integer.MAX_VALUE); + state = ParseState.CONST; + } + } else { + if (c == '{') { + immediate1 += 1; + } + // do buildup expr + column = append(strb, expr, column, c); + } + break; + case DEFERRED1: // #{... + // skip inner strings (for '}') + if (c == '"' || c == '\'') { + strb.append(c); + column = StringParser.readString(strb, expr, column + 1, c); + continue; + } + // nested immediate in deferred; need to balance count of '{' & '}' + if (c == '{') { + if (expr.charAt(column - 1) == immediateChar) { + inner1 += 1; + strb.deleteCharAt(strb.length() - 1); + nested = true; + } else { + deferred1 += 1; + strb.append(c); + } + continue; + } + // closing '}' + if (c == '}') { + // balance nested immediate + if (deferred1 > 0) { + deferred1 -= 1; + strb.append(c); + } else if (inner1 > 0) { + inner1 -= 1; + } else { + // materialize the nested/deferred expr + final String src = strb.toString(); + TemplateExpression dexpr; + if (nested) { + dexpr = new NestedExpression( + expr.substring(inested, column + 1), + jexl.parse(info.at(lineno, column), noscript, src, scope), + null); + } else { + dexpr = new DeferredExpression( + strb.toString(), + jexl.parse(info.at(lineno, column), noscript, src, scope), + null); + } + builder.add(dexpr); + strb.delete(0, Integer.MAX_VALUE); + nested = false; + state = ParseState.CONST; + } + } else { + // do buildup expr + column = append(strb, expr, column, c); + } + break; + case ESCAPE: + if (c == deferredChar) { + strb.append(deferredChar); + } else if (c == immediateChar) { + strb.append(immediateChar); + } else { + strb.append('\\'); + strb.append(c); + } + state = ParseState.CONST; + } + if (c == '\n') { + lineno += 1; + } + } + // we should be in that state + if (state != ParseState.CONST) { + // otherwise, we ended a line with a \, $ or # + switch (state) { + case ESCAPE: + strb.append('\\'); + strb.append('\\'); + break; + case DEFERRED0: + strb.append(deferredChar); + break; + case IMMEDIATE0: + strb.append(immediateChar); + break; + default: + throw new Exception(info.at(lineno, 0), "malformed expression: " + expr, null); + } + } + // if any chars were buffered, add them as a constant + if (strb.length() > 0) { + final TemplateExpression cexpr = new ConstantExpression(strb.toString(), null); + builder.add(cexpr); + } + return builder.build(this, null); + } + + /** + * The enum capturing the difference between verbatim and code source fragments. + */ + enum BlockType { + /** Block is to be output "as is" but may be a unified expression. */ + VERBATIM, + /** Block is a directive, ie a fragment of JEXL code. */ + DIRECTIVE + } + + /** + * Abstract the source fragments, verbatim or immediate typed text blocks. + */ + static final class Block { + /** The type of block, verbatim or directive. */ + private final BlockType type; + /** The block start line info. */ + private final int line; + /** The actual content. */ + private final String body; + + /** + * Creates a new block. + * + * @param theType the block type + * @param theLine the line number + * @param theBlock the content + */ + Block(final BlockType theType, final int theLine, final String theBlock) { + type = theType; + line = theLine; + body = theBlock; + } + + /** + * @return type + */ + BlockType getType() { + return type; + } + + /** + * @return line + */ + int getLine() { + return line; + } + + /** + * @return body + */ + String getBody() { + return body; + } + + @Override + public String toString() { + if (BlockType.VERBATIM.equals(type)) { + return body; + } + // CHECKSTYLE:OFF + final StringBuilder strb = new StringBuilder(64); // CSOFF: MagicNumber + // CHECKSTYLE:ON + final Iterator lines = readLines(new StringReader(body)); + while (lines.hasNext()) { + strb.append("$$").append(lines.next()); + } + return strb.toString(); + } + + /** + * Appends this block string representation to a builder. + * + * @param strb the string builder to append to + * @param prefix the line prefix (immediate or deferred) + */ + void toString(final StringBuilder strb, final String prefix) { + if (BlockType.VERBATIM.equals(type)) { + strb.append(body); + } else { + final Iterator lines = readLines(new StringReader(body)); + while (lines.hasNext()) { + strb.append(prefix).append(lines.next()); + } + } + } + } + + /** + * Whether a sequence starts with a given set of characters (following spaces). + *

Space characters at beginning of line before the pattern are discarded.

+ * + * @param sequence the sequence + * @param pattern the pattern to match at start of sequence + * @return the first position after end of pattern if it matches, -1 otherwise + */ + private int startsWith(CharSequence sequence, final CharSequence pattern) { + final int length = sequence.length(); + int s = 0; + while (s < length && Character.isSpaceChar(sequence.charAt(s))) { + s += 1; + } + if (s < length && pattern.length() <= (length - s)) { + sequence = sequence.subSequence(s, length); + if (sequence.subSequence(0, pattern.length()).equals(pattern)) { + return s + pattern.length(); + } + } + return -1; + } + + /** + * Read lines from a (buffered / mark-able) reader keeping all new-lines and line-feeds. + * + * @param reader the reader + * @return the line iterator + */ + private static Iterator readLines(final Reader reader) { + if (!reader.markSupported()) { + throw new IllegalArgumentException("mark support in reader required"); + } + return new Iterator() { + private CharSequence next = doNext(); + + private CharSequence doNext() { + final StringBuffer strb = new StringBuffer(64); // CSOFF: MagicNumber + int c; + boolean eol = false; + try { + while ((c = reader.read()) >= 0) { + if (eol) {// && (c != '\n' && c != '\r')) { + reader.reset(); + break; + } + if (c == '\n') { + eol = true; + } + strb.append((char) c); + reader.mark(1); + } + } catch (final IOException xio) { + return null; + } + return strb.length() > 0 ? strb : null; + } + + @Override + public boolean hasNext() { + return next != null; + } + + @Override + public CharSequence next() { + final CharSequence current = next; + if (current != null) { + next = doNext(); + } + return current; + } + + @Override + public void remove() { + throw new UnsupportedOperationException("Not supported."); + } + }; + } + + /** + * Reads lines of a template grouping them by typed blocks. + * + * @param prefix the directive prefix + * @param source the source reader + * @return the list of blocks + */ + List readTemplate(final String prefix, final Reader source) { + final ArrayList blocks = new ArrayList(); + final BufferedReader reader; + if (source instanceof BufferedReader) { + reader = (BufferedReader) source; + } else { + reader = new BufferedReader(source); + } + final StringBuilder strb = new StringBuilder(); + BlockType type = null; + int prefixLen; + final Iterator lines = readLines(reader); + int lineno = 1; + int start = 0; + while (lines.hasNext()) { + final CharSequence line = lines.next(); + if (line == null) { + break; + } + if (type == null) { + // determine starting type if not known yet + prefixLen = startsWith(line, prefix); + if (prefixLen >= 0) { + type = BlockType.DIRECTIVE; + strb.append(line.subSequence(prefixLen, line.length())); + } else { + type = BlockType.VERBATIM; + strb.append(line.subSequence(0, line.length())); + } + start = lineno; + } else if (type == BlockType.DIRECTIVE) { + // switch to verbatim if necessary + prefixLen = startsWith(line, prefix); + if (prefixLen < 0) { + final Block directive = new Block(BlockType.DIRECTIVE, start, strb.toString()); + strb.delete(0, Integer.MAX_VALUE); + blocks.add(directive); + type = BlockType.VERBATIM; + strb.append(line.subSequence(0, line.length())); + start = lineno; + } else { + // still a directive + strb.append(line.subSequence(prefixLen, line.length())); + } + } else if (type == BlockType.VERBATIM) { + // switch to directive if necessary + prefixLen = startsWith(line, prefix); + if (prefixLen >= 0) { + final Block verbatim = new Block(BlockType.VERBATIM, start, strb.toString()); + strb.delete(0, Integer.MAX_VALUE); + blocks.add(verbatim); + type = BlockType.DIRECTIVE; + strb.append(line.subSequence(prefixLen, line.length())); + start = lineno; + } else { + strb.append(line.subSequence(0, line.length())); + } + } + lineno += 1; + } + // input may be null + if (type != null && strb.length() > 0) { + final Block block = new Block(type, start, strb.toString()); + blocks.add(block); + } + blocks.trimToSize(); + return blocks; + } + + @Override + public TemplateScript createTemplate(final JexlInfo info, final String prefix, final Reader source, final String... parms) { + return new TemplateScript(this, info, prefix, source, parms); + } +} diff --git a/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/internal/TemplateInterpreter.java b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/internal/TemplateInterpreter.java new file mode 100644 index 0000000..9d11802 --- /dev/null +++ b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/internal/TemplateInterpreter.java @@ -0,0 +1,305 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package aiyh.utils.tool.org.apache.commons.jexl3.internal; + +import aiyh.utils.tool.org.apache.commons.jexl3.JexlContext; +import aiyh.utils.tool.org.apache.commons.jexl3.JexlInfo; +import aiyh.utils.tool.org.apache.commons.jexl3.JexlOptions; +import aiyh.utils.tool.org.apache.commons.jexl3.JxltEngine; +import aiyh.utils.tool.org.apache.commons.jexl3.introspection.JexlMethod; +import aiyh.utils.tool.org.apache.commons.jexl3.introspection.JexlUberspect; +import aiyh.utils.tool.org.apache.commons.jexl3.parser.*; + +import java.io.Writer; +import java.util.Arrays; + +/** + * The type of interpreter to use during evaluation of templates. + *

This context exposes its writer as '$jexl' to the scripts.

+ *

public for introspection purpose.

+ */ +public class TemplateInterpreter extends Interpreter { + /** The array of template expressions. */ + private final TemplateEngine.TemplateExpression[] exprs; + /** The writer used to output. */ + private final Writer writer; + + /** + * Helper ctor. + *

Stores the different properties required to create a Template interpreter. + */ + static class Arguments { + /** The engine. */ + Engine jexl; + /** The options. */ + JexlOptions options; + /** The context. */ + JexlContext jcontext; + /** The frame. */ + Frame jframe; + /** The expressions. */ + TemplateEngine.TemplateExpression[] expressions; + /** The writer. */ + Writer out; + + /** + * Sole ctor. + * + * @param e the JEXL engine + */ + Arguments(final Engine e) { + this.jexl = e; + } + + /** + * Sets the options. + * + * @param o the options + * @return this instance + */ + Arguments options(final JexlOptions o) { + this.options = o; + return this; + } + + /** + * Sets the context. + * + * @param j the context + * @return this instance + */ + Arguments context(final JexlContext j) { + this.jcontext = j; + return this; + } + + /** + * Sets the frame. + * + * @param f the frame + * @return this instance + */ + Arguments frame(final Frame f) { + this.jframe = f; + return this; + } + + /** + * Sets the expressions. + * + * @param e the expressions + * @return this instance + */ + Arguments expressions(final TemplateEngine.TemplateExpression[] e) { + this.expressions = e; + return this; + } + + /** + * Sets the writer. + * + * @param o the writer + * @return this instance + */ + Arguments writer(final Writer o) { + this.out = o; + return this; + } + } + + /** + * Creates a template interpreter instance. + * + * @param args the template interpreter arguments + */ + TemplateInterpreter(final Arguments args) { + super(args.jexl, args.options, args.jcontext, args.jframe); + exprs = args.expressions; + writer = args.out; + block = new LexicalFrame(frame, null); + } + + /** + * Includes a call to another template. + *

+ * Includes another template using this template initial context and writer.

+ * + * @param script the TemplateScript to evaluate + * @param args the arguments + */ + public void include(final JxltEngine.Template script, final Object... args) { + script.evaluate(context, writer, args); + } + + /** + * Prints a unified expression evaluation result. + * + * @param e the expression number + */ + public void print(final int e) { + if (e < 0 || e >= exprs.length) { + return; + } + TemplateEngine.TemplateExpression expr = exprs[e]; + if (expr.isDeferred()) { + expr = expr.prepare(frame, context); + } + if (expr instanceof TemplateEngine.CompositeExpression) { + printComposite((TemplateEngine.CompositeExpression) expr); + } else { + doPrint(expr.getInfo(), expr.evaluate(this)); + } + } + + /** + * Prints a composite expression. + * + * @param composite the composite expression + */ + private void printComposite(final TemplateEngine.CompositeExpression composite) { + final TemplateEngine.TemplateExpression[] cexprs = composite.exprs; + Object value; + for (final TemplateEngine.TemplateExpression cexpr : cexprs) { + value = cexpr.evaluate(this); + doPrint(cexpr.getInfo(), value); + } + } + + /** + * Prints to output. + *

+ * This will dynamically try to find the best suitable method in the writer through uberspection. + * Subclassing Writer by adding 'print' methods should be the preferred way to specialize output. + *

+ * + * @param info the source info + * @param arg the argument to print out + */ + private void doPrint(final JexlInfo info, final Object arg) { + try { + if (writer != null) { + if (arg instanceof CharSequence) { + writer.write(arg.toString()); + } else if (arg != null) { + final Object[] value = {arg}; + final JexlUberspect uber = jexl.getUberspect(); + final JexlMethod method = uber.getMethod(writer, "print", value); + if (method != null) { + method.invoke(writer, value); + } else { + writer.write(arg.toString()); + } + } + } + } catch (final java.io.IOException xio) { + throw TemplateEngine.createException(info, "call print", null, xio); + } catch (final Exception xany) { + throw TemplateEngine.createException(info, "invoke print", null, xany); + } + } + + @Override + protected Object resolveNamespace(final String prefix, final JexlNode node) { + return "jexl".equals(prefix) ? this : super.resolveNamespace(prefix, node); + } + + @Override + protected Object visit(final ASTIdentifier node, final Object data) { + final String name = node.getName(); + if ("$jexl".equals(name)) { + return writer; + } + return super.visit(node, data); + } + + /** + * Interprets a function node. + * print() and include() must be decoded by this interpreter since delegating to the Uberspect + * may be sandboxing the interpreter itself making it unable to call the function. + * + * @param node the function node + * @param data the data + * @return the function evaluation result. + */ + @Override + protected Object visit(final ASTFunctionNode node, Object data) { + final int argc = node.jjtGetNumChildren(); + if (argc == 2) { + final ASTIdentifier functionNode = (ASTIdentifier) node.jjtGetChild(0); + if ("jexl".equals(functionNode.getNamespace())) { + final String functionName = functionNode.getName(); + final ASTArguments argNode = (ASTArguments) node.jjtGetChild(1); + if ("print".equals(functionName)) { + // evaluate the arguments + Object[] argv = visit(argNode, null); + if (argv != null && argv.length > 0 && argv[0] instanceof Number) { + print(((Number) argv[0]).intValue()); + return null; + } + } + if ("include".equals(functionName)) { + // evaluate the arguments + Object[] argv = visit(argNode, null); + if (argv != null && argv.length > 0) { + if (argv[0] instanceof TemplateScript) { + TemplateScript script = (TemplateScript) argv[0]; + if (argv.length > 1) { + argv = Arrays.copyOfRange(argv, 1, argv.length); + } else { + argv = null; + } + include(script, argv); + return null; + } + } + } + // fail safe + throw new JxltEngine.Exception(node.jexlInfo(), "no callable template function " + functionName, null); + } + } + return super.visit(node, data); + } + + @Override + protected Object visit(final ASTJexlScript script, final Object data) { + if (script instanceof ASTJexlLambda && !((ASTJexlLambda) script).isTopLevel()) { + return new Closure(this, (ASTJexlLambda) script) { + @Override + protected Interpreter createInterpreter(final JexlContext context, final Frame local) { + final JexlOptions opts = jexl.options(script, context); + final Arguments targs = new Arguments(jexl) + .context(context) + .options(opts) + .frame(local) + .expressions(exprs) + .writer(writer); + return new TemplateInterpreter(targs); + } + }; + } + // otherwise... + final int numChildren = script.jjtGetNumChildren(); + Object result = null; + for (int i = 0; i < numChildren; i++) { + final JexlNode child = script.jjtGetChild(i); + result = child.jjtAccept(this, data); + cancelCheck(child); + } + return result; + } + +} diff --git a/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/internal/TemplateScript.java b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/internal/TemplateScript.java new file mode 100644 index 0000000..63b8de3 --- /dev/null +++ b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/internal/TemplateScript.java @@ -0,0 +1,322 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package aiyh.utils.tool.org.apache.commons.jexl3.internal; + +import aiyh.utils.tool.org.apache.commons.jexl3.parser.JexlNode; +import aiyh.utils.tool.org.apache.commons.jexl3.JexlContext; +import aiyh.utils.tool.org.apache.commons.jexl3.JexlInfo; +import aiyh.utils.tool.org.apache.commons.jexl3.JxltEngine; +import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTJexlScript; +import aiyh.utils.tool.org.apache.commons.jexl3.JexlException; +import aiyh.utils.tool.org.apache.commons.jexl3.JexlOptions; +import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTArguments; +import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTFunctionNode; +import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTIdentifier; +import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTNumberLiteral; + +import java.io.Reader; +import java.io.Writer; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.TreeMap; + +/** + * A Template instance. + */ +public final class TemplateScript implements JxltEngine.Template { + /** The prefix marker. */ + private final String prefix; + /** The array of source blocks. */ + private final TemplateEngine.Block[] source; + /** The resulting script. */ + private final ASTJexlScript script; + /** The TemplateEngine expressions called by the script. */ + private final TemplateEngine.TemplateExpression[] exprs; + /** The engine. */ + private final TemplateEngine jxlt; + + /** + * Creates a new template from an character input. + * @param engine the template engine + * @param info the source info + * @param directive the prefix for lines of code; can not be "$", "${", "#" or "#{" + since this would preclude being able to differentiate directives and jxlt expressions + * @param reader the input reader + * @param parms the parameter names + * @throws NullPointerException if either the directive prefix or input is null + * @throws IllegalArgumentException if the directive prefix is invalid + */ + public TemplateScript(final TemplateEngine engine, + JexlInfo info, + final String directive, + final Reader reader, + final String... parms) { + if (directive == null) { + throw new NullPointerException("null prefix"); + } + final String engineImmediateCharString = Character.toString(engine.getImmediateChar()); + final String engineDeferredCharString = Character.toString(engine.getDeferredChar()); + + if (engineImmediateCharString.equals(directive) + || engineDeferredCharString.equals(directive) + || (engineImmediateCharString + "{").equals(directive) + || (engineDeferredCharString + "{").equals(directive)) { + throw new IllegalArgumentException(directive + ": is not a valid directive pattern"); + } + if (reader == null) { + throw new NullPointerException("null input"); + } + this.jxlt = engine; + this.prefix = directive; + final List blocks = jxlt.readTemplate(prefix, reader); + final List uexprs = new ArrayList<>(); + final StringBuilder strb = new StringBuilder(); + int nuexpr = 0; + int codeStart = -1; + int line = 1; + for (int b = 0; b < blocks.size(); ++b) { + final TemplateEngine.Block block = blocks.get(b); + final int bl = block.getLine(); + while(line < bl) { + strb.append("//\n"); + line += 1; + } + if (block.getType() == TemplateEngine.BlockType.VERBATIM) { + strb.append("jexl:print("); + strb.append(nuexpr++); + strb.append(");\n"); + line += 1; + } else { + // keep track of first block of code, the frame creator + if (codeStart < 0) { + codeStart = b; + } + final String body = block.getBody(); + strb.append(body); + for(int c = 0; c < body.length(); ++c) { + if (body.charAt(c) == '\n') { + line += 1; + } + } + } + } + // create the script + if (info == null) { + info = jxlt.getEngine().createInfo(); + } + // allow lambda defining params + final Scope scope = parms == null ? null : new Scope(null, parms); + script = jxlt.getEngine().parse(info.at(1, 1), false, strb.toString(), scope).script(); + // seek the map of expression number to scope so we can parse Unified + // expression blocks with the appropriate symbols + final Map minfo = new TreeMap<>(); + collectPrintScope(script.script(), minfo); + // jexl:print(...) expression counter + int jpe = 0; + // create the exprs using the intended scopes + for (final TemplateEngine.Block block : blocks) { + if (block.getType() == TemplateEngine.BlockType.VERBATIM) { + final JexlNode.Info ji = minfo.get(jpe); + TemplateEngine.TemplateExpression te; + // no node info means this verbatim is surrounded by comments markers; + // expr at this index is never called + if (ji != null) { + te = jxlt.parseExpression(ji, block.getBody(), scopeOf(ji)); + } else { + te = jxlt.new ConstantExpression(block.getBody(), null); + } + uexprs.add(te); + jpe += 1; + } + } + source = blocks.toArray(new TemplateEngine.Block[blocks.size()]); + exprs = uexprs.toArray(new TemplateEngine.TemplateExpression[uexprs.size()]); + } + + /** + * Private ctor used to expand deferred expressions during prepare. + * @param engine the template engine + * @param thePrefix the directive prefix + * @param theSource the source + * @param theScript the script + * @param theExprs the expressions + */ + TemplateScript(final TemplateEngine engine, + final String thePrefix, + final TemplateEngine.Block[] theSource, + final ASTJexlScript theScript, + final TemplateEngine.TemplateExpression[] theExprs) { + jxlt = engine; + prefix = thePrefix; + source = theSource; + script = theScript; + exprs = theExprs; + } + + /** + * Gets the scope from an info. + * @param info the node info + * @return the scope + */ + private static Scope scopeOf(final JexlNode.Info info) { + JexlNode walk = info.getNode(); + while(walk != null) { + if (walk instanceof ASTJexlScript) { + return ((ASTJexlScript) walk).getScope(); + } + walk = walk.jjtGetParent(); + } + return null; + } + + /** + * Collects the scope surrounding a call to jexl:print(i). + *

This allows to later parse the blocks with the known symbols + * in the frame visible to the parser. + * @param node the visited node + * @param minfo the map of printed expression number to node info + */ + private static void collectPrintScope(final JexlNode node, final Map minfo) { + final int nc = node.jjtGetNumChildren(); + if (node instanceof ASTFunctionNode && nc == 2) { + // 0 must be the prefix jexl: + final ASTIdentifier nameNode = (ASTIdentifier) node.jjtGetChild(0); + if ("print".equals(nameNode.getName()) && "jexl".equals(nameNode.getNamespace())) { + final ASTArguments argNode = (ASTArguments) node.jjtGetChild(1); + if (argNode.jjtGetNumChildren() == 1) { + // seek the epression number + final JexlNode arg0 = argNode.jjtGetChild(0); + if (arg0 instanceof ASTNumberLiteral) { + final int exprNumber = ((ASTNumberLiteral) arg0).getLiteral().intValue(); + minfo.put(exprNumber, new JexlNode.Info(nameNode)); + return; + } + } + } + } + for (int c = 0; c < nc; ++c) { + collectPrintScope(node.jjtGetChild(c), minfo); + } + } + + /** + * @return script + */ + ASTJexlScript getScript() { + return script; + } + + /** + * @return exprs + */ + TemplateEngine.TemplateExpression[] getExpressions() { + return exprs; + } + + @Override + public String toString() { + final StringBuilder strb = new StringBuilder(); + for (final TemplateEngine.Block block : source) { + block.toString(strb, prefix); + } + return strb.toString(); + } + + @Override + public String asString() { + final StringBuilder strb = new StringBuilder(); + int e = 0; + for (final TemplateEngine.Block block : source) { + if (block.getType() == TemplateEngine.BlockType.DIRECTIVE) { + strb.append(prefix); + strb.append(block.getBody()); + } else { + exprs[e++].asString(strb); + } + } + return strb.toString(); + } + + @Override + public TemplateScript prepare(final JexlContext context) { + final Engine jexl = jxlt.getEngine(); + final JexlOptions options = jexl.options(script, context); + final Frame frame = script.createFrame((Object[]) null); + final TemplateInterpreter.Arguments targs = new TemplateInterpreter + .Arguments(jxlt.getEngine()) + .context(context) + .options(options) + .frame(frame); + final Interpreter interpreter = new TemplateInterpreter(targs); + final TemplateEngine.TemplateExpression[] immediates = new TemplateEngine.TemplateExpression[exprs.length]; + for (int e = 0; e < exprs.length; ++e) { + try { + immediates[e] = exprs[e].prepare(interpreter); + } catch (final JexlException xjexl) { + final JexlException xuel = TemplateEngine.createException(xjexl.getInfo(), "prepare", exprs[e], xjexl); + if (jexl.isSilent()) { + jexl.logger.warn(xuel.getMessage(), xuel.getCause()); + return null; + } + throw xuel; + } + } + return new TemplateScript(jxlt, prefix, source, script, immediates); + } + + @Override + public void evaluate(final JexlContext context, final Writer writer) { + evaluate(context, writer, (Object[]) null); + } + + @Override + public void evaluate(final JexlContext context, final Writer writer, final Object... args) { + final Engine jexl = jxlt.getEngine(); + final JexlOptions options = jexl.options(script, context); + final Frame frame = script.createFrame(args); + final TemplateInterpreter.Arguments targs = new TemplateInterpreter + .Arguments(jexl) + .context(context) + .options(options) + .frame(frame) + .expressions(exprs) + .writer(writer); + final Interpreter interpreter = new TemplateInterpreter(targs); + interpreter.interpret(script); + } + + @Override + public Set> getVariables() { + final Engine.VarCollector collector = jxlt.getEngine().varCollector(); + for (final TemplateEngine.TemplateExpression expr : exprs) { + expr.getVariables(collector); + } + return collector.collected(); + } + + @Override + public String[] getParameters() { + return script.getParameters(); + } + + @Override + public Map getPragmas() { + return script.getPragmas(); + } +} diff --git a/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/internal/introspection/AbstractExecutor.java b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/internal/introspection/AbstractExecutor.java new file mode 100644 index 0000000..6f5ac8e --- /dev/null +++ b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/internal/introspection/AbstractExecutor.java @@ -0,0 +1,256 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package aiyh.utils.tool.org.apache.commons.jexl3.internal.introspection; + +import aiyh.utils.tool.org.apache.commons.jexl3.JexlEngine; +import aiyh.utils.tool.org.apache.commons.jexl3.introspection.JexlMethod; +import aiyh.utils.tool.org.apache.commons.jexl3.introspection.JexlPropertyGet; +import aiyh.utils.tool.org.apache.commons.jexl3.introspection.JexlPropertySet; + +/** + * Abstract class that is used to execute an arbitrary + * method that is introspected. This is the superclass + * for all other AbstractExecutor classes. + * + * @since 1.0 + */ +abstract class AbstractExecutor { + /** A marker for invocation failures in tryInvoke. */ + public static final Object TRY_FAILED = JexlEngine.TRY_FAILED; + + /** + * A helper to initialize the marker methods (array.get, list.get, etc...). + * @param clazz the class to introspect + * @param name the name of the method + * @param parms the parameters + * @return the method + */ + static java.lang.reflect.Method initMarker(final Class clazz, final String name, final Class... parms) { + try { + return clazz.getMethod(name, parms); + } catch (final Exception xnever) { + throw new Error(xnever); + } + } + + /** + * Coerce an Object which must be a number to an Integer. + * @param arg the Object to coerce + * @return an Integer if it can be converted, null otherwise + */ + static Integer castInteger(final Object arg) { + return arg instanceof Number? ((Number) arg).intValue() : null; + } + + /** + * Coerce an Object to a String. + * @param arg the Object to coerce + * @return a String if it can be converted, null otherwise + */ + static String castString(final Object arg) { + return arg instanceof CharSequence || arg instanceof Integer ? arg.toString() : null; + } + + /** + * Creates an arguments array. + * @param args the list of arguments + * @return the arguments array + */ + static Object[] makeArgs(final Object... args) { + return args; + } + + /** + * Gets the class of an object or Object if null. + * @param instance the instance + * @return the class + */ + static Class classOf(final Object instance) { + return instance == null? Object.class : instance.getClass(); + } + + /** The class this executor applies to. */ + protected final Class objectClass; + /** Method to be executed. */ + protected final java.lang.reflect.Method method; + + /** + * Default and sole constructor. + * @param theClass the class this executor applies to + * @param theMethod the method held by this executor + */ + protected AbstractExecutor(final Class theClass, final java.lang.reflect.Method theMethod) { + objectClass = theClass; + method = theMethod; + } + + @Override + public boolean equals(final Object obj) { + return this == obj || (obj instanceof AbstractExecutor && equals((AbstractExecutor) obj)); + } + + @Override + public int hashCode() { + return method.hashCode(); + } + + /** + * Indicates whether some other executor is equivalent to this one. + * @param arg the other executor to check + * @return true if both executors are equivalent, false otherwise + */ + public boolean equals(final AbstractExecutor arg) { + // common equality check + if (!this.getClass().equals(arg.getClass())) { + return false; + } + if (!this.getMethod().equals(arg.getMethod())) { + return false; + } + if (!this.getTargetClass().equals(arg.getTargetClass())) { + return false; + } + // specific equality check + final Object lhsp = this.getTargetProperty(); + final Object rhsp = arg.getTargetProperty(); + if (lhsp == null && rhsp == null) { + return true; + } + if (lhsp != null && rhsp != null) { + return lhsp.equals(rhsp); + } + return false; + } + + /** + * Tell whether the executor is alive by looking + * at the value of the method. + * + * @return boolean Whether the executor is alive. + */ + public final boolean isAlive() { + return (method != null); + } + + /** + * Specifies if this executor is cacheable and able to be reused for this + * class of object it was returned for. + * + * @return true if can be reused for this class, false if not + */ + public boolean isCacheable() { + return method != null; + } + + /** + * Gets the method to be executed or used as a marker. + * @return Method The method used by execute in derived classes. + */ + public final java.lang.reflect.Method getMethod() { + return method; + } + + /** + * Gets the object class targeted by this executor. + * @return the target object class + */ + public final Class getTargetClass() { + return objectClass; + } + + /** + * Gets the property targeted by this executor. + * @return the target property + */ + public Object getTargetProperty() { + return null; + } + + /** + * Gets the method name used. + * @return method name + */ + public final String getMethodName() { + return method.getName(); + } + + /** + * Checks whether a tryExecute failed or not. + * @param exec the value returned by tryExecute + * @return true if tryExecute failed, false otherwise + */ + public final boolean tryFailed(final Object exec) { + return exec == JexlEngine.TRY_FAILED; + } + + /** + * Abstract class that is used to execute an arbitrary 'get' method. + */ + public abstract static class Get extends AbstractExecutor implements JexlPropertyGet { + /** + * Default and sole constructor. + * @param theClass the class this executor applies to + * @param theMethod the method held by this executor + */ + protected Get(final Class theClass, final java.lang.reflect.Method theMethod) { + super(theClass, theMethod); + } + } + + /** + * Abstract class that is used to execute an arbitrary 'set' method. + */ + public abstract static class Set extends AbstractExecutor implements JexlPropertySet { + /** + * Default and sole constructor. + * @param theClass the class this executor applies to + * @param theMethod the method held by this executor + */ + protected Set(final Class theClass, final java.lang.reflect.Method theMethod) { + super(theClass, theMethod); + } + } + + /** + * Abstract class that is used to execute an arbitrary method. + */ + public abstract static class Method extends AbstractExecutor implements JexlMethod { + /** The method key discovered from the arguments. */ + protected final MethodKey key; + + /** + * Creates a new instance. + * @param c the class this executor applies to + * @param m the method + * @param k the MethodKey + */ + protected Method(final Class c, final java.lang.reflect.Method m, final MethodKey k) { + super(c, m); + key = k; + } + + @Override + public Object getTargetProperty() { + return key; + } + + @Override + public final Class getReturnType() { + return method.getReturnType(); + } + } +} diff --git a/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/internal/introspection/ArrayIterator.java b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/internal/introspection/ArrayIterator.java new file mode 100644 index 0000000..670e59f --- /dev/null +++ b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/internal/introspection/ArrayIterator.java @@ -0,0 +1,98 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package aiyh.utils.tool.org.apache.commons.jexl3.internal.introspection; + +import java.lang.reflect.Array; +import java.util.Iterator; +import java.util.NoSuchElementException; + +/** + *

+ * An Iterator wrapper for an Object[]. This will + * allow us to deal with all array like structures + * in a consistent manner. + *

+ *

+ * WARNING : this class's operations are NOT synchronized. + * It is meant to be used in a single thread, newly created + * for each use in the #foreach() directive. + * If this is used or shared, synchronize in the + * next() method. + *

+ * + * @since 1.0 + */ +public class ArrayIterator implements Iterator { + /** The objects to iterate over. */ + private final Object array; + /** The size of the array. */ + private final int size; + /** The current position and size in the array. */ + private int pos; + + /** + * Creates a new iterator instance for the specified array. + * @param arr The array for which an iterator is desired. + */ + public ArrayIterator(final Object arr) { + if (arr == null) { + array = null; + pos = 0; + size = 0; + } else if (!arr.getClass().isArray()) { + throw new IllegalArgumentException(arr.getClass() + " is not an array"); + } else { + array = arr; + pos = 0; + size = Array.getLength(array); + } + } + + /** + * Move to next element in the array. + * + * @return The next object in the array. + */ + @Override + public Object next() { + if (pos < size) { + return Array.get(array, pos++); + } + // we screwed up... + throw new NoSuchElementException("No more elements: " + pos + + " / " + size); + } + + /** + * Check to see if there is another element in the array. + * + * @return Whether there is another element. + */ + @Override + public boolean hasNext() { + return (pos < size); + } + + /** + * No op--merely added to satify the Iterator interface. + */ + @Override + public void remove() { + throw new UnsupportedOperationException(); + } +} \ No newline at end of file diff --git a/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/internal/introspection/ArrayListWrapper.java b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/internal/introspection/ArrayListWrapper.java new file mode 100644 index 0000000..450d0c5 --- /dev/null +++ b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/internal/introspection/ArrayListWrapper.java @@ -0,0 +1,86 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package aiyh.utils.tool.org.apache.commons.jexl3.internal.introspection; + +import java.lang.reflect.Array; +import java.util.AbstractList; +import java.util.RandomAccess; + +/** + * A class that wraps an array within an AbstractList. + *

+ * It overrides some methods because introspection uses this class a a marker for wrapped arrays; the declared class + * for these method is thus ArrayListWrapper. + * The methods are get/set/size/contains and indexOf because it is used by contains. + *

+ */ +public class ArrayListWrapper extends AbstractList implements RandomAccess { + /** the array to wrap. */ + private final Object array; + + /** + * Create the wrapper. + * @param anArray {@link #array} + */ + public ArrayListWrapper(final Object anArray) { + if (!anArray.getClass().isArray()) { + throw new IllegalArgumentException(anArray.getClass() + " is not an array"); + } + this.array = anArray; + } + + @Override + public Object get(final int index) { + return Array.get(array, index); + } + + @Override + public Object set(final int index, final Object element) { + final Object old = Array.get(array, index); + Array.set(array, index, element); + return old; + } + + @Override + public int size() { + return Array.getLength(array); + } + + @Override + public int indexOf(final Object o) { + final int size = size(); + if (o == null) { + for (int i = 0; i < size; i++) { + if (get(i) == null) { + return i; + } + } + } else { + for (int i = 0; i < size; i++) { + if (o.equals(get(i))) { + return i; + } + } + } + return -1; + } + + @Override + public boolean contains(final Object o) { + return indexOf(o) != -1; + } +} diff --git a/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/internal/introspection/BooleanGetExecutor.java b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/internal/introspection/BooleanGetExecutor.java new file mode 100644 index 0000000..bb6feea --- /dev/null +++ b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/internal/introspection/BooleanGetExecutor.java @@ -0,0 +1,87 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package aiyh.utils.tool.org.apache.commons.jexl3.internal.introspection; +import java.lang.reflect.InvocationTargetException; + +import aiyh.utils.tool.org.apache.commons.jexl3.JexlException; + +/** + * Specialized executor to get a boolean property from an object. + * @since 2.0 + */ +public final class BooleanGetExecutor extends AbstractExecutor.Get { + /** The property. */ + private final String property; + + /** + * Discovers a BooleanGetExecutor. + *

The method to be found should be named "is{P,p}property and return a boolean.

+ * + * @param is the introspector + * @param clazz the class to find the get method from + * @param property the property name + * @return the executor if found, null otherwise + */ + public static BooleanGetExecutor discover(final Introspector is, final Class clazz, final String property) { + if (property != null && !property.isEmpty()) { + final java.lang.reflect.Method m = PropertyGetExecutor.discoverGet(is, "is", clazz, property); + if (m != null && (m.getReturnType() == Boolean.TYPE || m.getReturnType() == Boolean.class)) { + return new BooleanGetExecutor(clazz, m, property); + } + } + return null; + } + + /** + * Creates an instance by attempting discovery of the get method. + * @param clazz the class to introspect + * @param method the method held by this executor + * @param key the property to get + */ + private BooleanGetExecutor(final Class clazz, final java.lang.reflect.Method method, final String key) { + super(clazz, method); + property = key; + } + + @Override + public Object getTargetProperty() { + return property; + } + + @Override + public Object invoke(final Object obj) throws IllegalAccessException, InvocationTargetException { + return method == null ? null : method.invoke(obj, (Object[]) null); + } + + @Override + public Object tryInvoke(final Object obj, final Object key) { + if (obj != null && method != null + // ensure method name matches the property name + && property.equals(key) + && objectClass.equals(obj.getClass())) { + try { + return method.invoke(obj, (Object[]) null); + } catch (final IllegalAccessException xill) { + return TRY_FAILED;// fail + } catch (final InvocationTargetException xinvoke) { + throw JexlException.tryFailed(xinvoke); // throw + } + } + return TRY_FAILED; + } +} \ No newline at end of file diff --git a/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/internal/introspection/ClassMap.java b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/internal/introspection/ClassMap.java new file mode 100644 index 0000000..18fbad9 --- /dev/null +++ b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/internal/introspection/ClassMap.java @@ -0,0 +1,308 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package aiyh.utils.tool.org.apache.commons.jexl3.internal.introspection; + +import org.apache.commons.logging.Log; + +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; + +/** + * A cache of introspection information for a specific class instance. + * Keys objects by an aggregation of the method name and the classes + * that make up the parameters. + *

+ * Originally taken from the Velocity tree so we can be self-sufficient. + *

+ * + * @see MethodKey + * @since 1.0 + */ +final class ClassMap { + /** + * A method that returns itself used as a marker for cache miss, + * allows the underlying cache map to be strongly typed. + * + * @return itself as a method + */ + public static Method cacheMiss() { + try { + return ClassMap.class.getMethod("cacheMiss"); + } catch (final Exception xio) { + // this really can't make an error... + return null; + } + } + + /** + * The cache miss marker method. + */ + private static final Method CACHE_MISS = cacheMiss(); + /** + * This is the cache to store and look up the method information. + *

+ * It stores the association between: + * - a key made of a method name and an array of argument types. + * - a method. + *

+ *

+ * Since the invocation of the associated method is dynamic, there is no need (nor way) to differentiate between + * foo(int,int) and foo(Integer,Integer) since in practice only the latter form will be used through a call. + * This of course, applies to all 8 primitive types. + *

+ * Uses ConcurrentMap since 3.0, marginally faster than 2.1 under contention. + */ + private final ConcurrentMap byKey = new ConcurrentHashMap<>(); + /** + * Keep track of all methods with the same name; this is not modified after creation. + */ + private final Map byName = new HashMap<>(); + /** + * Cache of fields. + */ + private final Map fieldCache; + + /** + * Standard constructor. + * + * @param aClass the class to deconstruct. + * @param permissions the permissions to apply during introspection + * @param log the logger. + */ + @SuppressWarnings("LeakingThisInConstructor") + ClassMap(final Class aClass, final Permissions permissions, final Log log) { + // eagerly cache methods + create(this, permissions, aClass, log); + // eagerly cache public fields + final Field[] fields = aClass.getFields(); + if (fields.length > 0) { + final Map cache = new HashMap<>(); + for (final Field field : fields) { + if (permissions.allow(field)) { + cache.put(field.getName(), field); + } + } + fieldCache = cache; + } else { + fieldCache = Collections.emptyMap(); + } + } + + /** + * Find a Field using its name. + * + * @param fname the field name + * @return A Field object representing the field to invoke or null. + */ + Field getField(final String fname) { + return fieldCache.get(fname); + } + + /** + * Gets the field names cached by this map. + * + * @return the array of field names + */ + String[] getFieldNames() { + return fieldCache.keySet().toArray(new String[fieldCache.size()]); + } + + /** + * Gets the methods names cached by this map. + * + * @return the array of method names + */ + String[] getMethodNames() { + return byName.keySet().toArray(new String[byName.size()]); + } + + /** + * Gets all the methods with a given name from this map. + * + * @param methodName the seeked methods name + * @return the array of methods (null or non-empty) + */ + Method[] getMethods(final String methodName) { + final Method[] lm = byName.get(methodName); + if (lm != null && lm.length > 0) { + return lm.clone(); + } + return null; + } + + /** + * Find a Method using the method name and parameter objects. + *

+ * Look in the methodMap for an entry. If found, + * it'll either be a CACHE_MISS, in which case we + * simply give up, or it'll be a Method, in which + * case, we return it. + *

+ *

+ * If nothing is found, then we must actually go + * and introspect the method from the MethodMap. + *

+ * + * @param methodKey the method key + * @return A Method object representing the method to invoke or null. + * @throws MethodKey.AmbiguousException When more than one method is a match for the parameters. + */ + Method getMethod(final MethodKey methodKey) throws MethodKey.AmbiguousException { + // Look up by key + Method cacheEntry = byKey.get(methodKey); + // We looked this up before and failed. + if (cacheEntry == CACHE_MISS) { + return null; + } + if (cacheEntry == null) { + try { + // That one is expensive... + final Method[] methodList = byName.get(methodKey.getMethod()); + if (methodList != null) { + cacheEntry = methodKey.getMostSpecificMethod(methodList); + } + if (cacheEntry == null) { + byKey.put(methodKey, CACHE_MISS); + } else { + byKey.put(methodKey, cacheEntry); + } + } catch (final MethodKey.AmbiguousException ae) { + // that's a miss :-) + byKey.put(methodKey, CACHE_MISS); + throw ae; + } + } + + // Yes, this might just be null. + return cacheEntry; + } + + /** + * Populate the Map of direct hits. These are taken from all the public methods + * that our class, its parents and their implemented interfaces provide. + * + * @param cache the ClassMap instance we create + * @param permissions the permissions to apply during introspection + * @param classToReflect the class to cache + * @param log the Log + */ + private static void create(final ClassMap cache, final Permissions permissions, Class classToReflect, final Log log) { + // + // Build a list of all elements in the class hierarchy. This one is bottom-first (i.e. we start + // with the actual declaring class and its interfaces and then move up (superclass etc.) until we + // hit java.lang.Object. That is important because it will give us the methods of the declaring class + // which might in turn be abstract further up the tree. + // + // We also ignore all SecurityExceptions that might happen due to SecurityManager restrictions. + // + for (; classToReflect != null; classToReflect = classToReflect.getSuperclass()) { + if (Modifier.isPublic(classToReflect.getModifiers())) { + populateWithClass(cache, permissions, classToReflect, log); + } + final Class[] interfaces = classToReflect.getInterfaces(); + for (final Class anInterface : interfaces) { + populateWithInterface(cache, permissions, anInterface, log); + } + } + // now that we've got all methods keyed in, lets organize them by name + if (!cache.byKey.isEmpty()) { + final List lm = new ArrayList<>(cache.byKey.size()); + lm.addAll(cache.byKey.values()); + // sort all methods by name + lm.sort(Comparator.comparing(Method::getName)); + // put all lists of methods with same name in byName cache + int start = 0; + while (start < lm.size()) { + final String name = lm.get(start).getName(); + int end = start + 1; + while (end < lm.size()) { + final String walk = lm.get(end).getName(); + if (!walk.equals(name)) { + break; + } + end += 1; + } + final Method[] lmn = lm.subList(start, end).toArray(new Method[end - start]); + cache.byName.put(name, lmn); + start = end; + } + } + } + + /** + * Recurses up interface hierarchy to get all super interfaces. + * + * @param cache the cache to fill + * @param permissions the permissions to apply during introspection + * @param iface the interface to populate the cache from + * @param log the Log + */ + private static void populateWithInterface(final ClassMap cache, + final Permissions permissions, + final Class iface, + final Log log) { + if (Modifier.isPublic(iface.getModifiers())) { + populateWithClass(cache, permissions, iface, log); + final Class[] supers = iface.getInterfaces(); + for (final Class aSuper : supers) { + populateWithInterface(cache, permissions, aSuper, log); + } + } + } + + /** + * Recurses up class hierarchy to get all super classes. + * + * @param cache the cache to fill + * @param permissions the permissions to apply during introspection + * @param clazz the class to populate the cache from + * @param log the Log + */ + private static void populateWithClass(final ClassMap cache, + final Permissions permissions, + final Class clazz, + final Log log) { + try { + final Method[] methods = clazz.getDeclaredMethods(); + for (final Method mi : methods) { + // add method to byKey cache; do not override + final MethodKey key = new MethodKey(mi); + final Method pmi = cache.byKey.putIfAbsent(key, permissions.allow(mi) ? mi : CACHE_MISS); + if (pmi != null && pmi != CACHE_MISS && log.isDebugEnabled() && !key.equals(new MethodKey(pmi))) { + // foo(int) and foo(Integer) have the same signature for JEXL + log.debug("Method " + pmi + " is already registered, key: " + key.debugString()); + } + } + } catch (final SecurityException se) { + // Everybody feels better with... + if (log.isDebugEnabled()) { + log.debug("While accessing methods of " + clazz + ": ", se); + } + } + } +} diff --git a/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/internal/introspection/ConstructorMethod.java b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/internal/introspection/ConstructorMethod.java new file mode 100644 index 0000000..d046cd9 --- /dev/null +++ b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/internal/introspection/ConstructorMethod.java @@ -0,0 +1,121 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package aiyh.utils.tool.org.apache.commons.jexl3.internal.introspection; + +import java.beans.IntrospectionException; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; + +import aiyh.utils.tool.org.apache.commons.jexl3.JexlException; +import aiyh.utils.tool.org.apache.commons.jexl3.introspection.JexlMethod; + +/** + * A JexlMethod that wraps a constructor. + */ +public final class ConstructorMethod implements JexlMethod { + /** The wrapped constructor. */ + private final Constructor ctor; + + /** + * Discovers a class constructor and wrap it as a JexlMethod. + * @param is the introspector + * @param ctorHandle a class or class name + * @param args constructor arguments + * @return a {@link JexlMethod} + */ + public static ConstructorMethod discover(final Introspector is, final Object ctorHandle, final Object... args) { + String className; + Class clazz = null; + if (ctorHandle instanceof Class) { + clazz = (Class) ctorHandle; + className = clazz.getName(); + } else if (ctorHandle != null) { + className = ctorHandle.toString(); + } else { + return null; + } + final Constructor ctor = is.getConstructor(clazz, new MethodKey(className, args)); + if (ctor != null) { + return new ConstructorMethod(ctor); + } + return null; + } + /** + * Creates a constructor method. + * @param theCtor the constructor to wrap + */ + ConstructorMethod(final Constructor theCtor) { + this.ctor = theCtor; + } + + @Override + public Object invoke(final Object obj, final Object... params) throws Exception { + final Class ctorClass = ctor.getDeclaringClass(); + boolean invoke = true; + if (obj != null) { + if (obj instanceof Class) { + invoke = ctorClass.equals(obj); + } else { + invoke = ctorClass.getName().equals(obj.toString()); + } + } + if (invoke) { + return ctor.newInstance(params); + } + throw new IntrospectionException("constructor resolution error"); + } + + @Override + public Object tryInvoke(final String name, final Object obj, final Object... params) { + try { + final Class ctorClass = ctor.getDeclaringClass(); + boolean invoke = true; + if (obj != null) { + if (obj instanceof Class) { + invoke = ctorClass.equals(obj); + } else { + invoke = ctorClass.getName().equals(obj.toString()); + } + } + invoke &= name == null || ctorClass.getName().equals(name); + if (invoke) { + return ctor.newInstance(params); + } + } catch (InstantiationException | IllegalArgumentException | IllegalAccessException xinstance) { + return Uberspect.TRY_FAILED; + } catch (final InvocationTargetException xinvoke) { + throw JexlException.tryFailed(xinvoke); // throw + } + return Uberspect.TRY_FAILED; + } + + @Override + public boolean tryFailed(final Object rval) { + return rval == Uberspect.TRY_FAILED; + } + + @Override + public boolean isCacheable() { + return true; + } + + @Override + public Class getReturnType() { + return ctor.getDeclaringClass(); + } + +} diff --git a/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/internal/introspection/DuckGetExecutor.java b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/internal/introspection/DuckGetExecutor.java new file mode 100644 index 0000000..b59de15 --- /dev/null +++ b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/internal/introspection/DuckGetExecutor.java @@ -0,0 +1,91 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package aiyh.utils.tool.org.apache.commons.jexl3.internal.introspection; + +import java.lang.reflect.InvocationTargetException; + +import aiyh.utils.tool.org.apache.commons.jexl3.JexlException; + +/** + * Specialized executor to get a property from an object. + *

Duck as in duck-typing for an interface like: + * + * interface Get { + * Object get(Object key); + * } + * + *

+ * @since 2.0 + */ +public final class DuckGetExecutor extends AbstractExecutor.Get { + /** The property, may be null. */ + private final Object property; + + /** + * Attempts to discover a DuckGetExecutor. + * @param is the introspector + * @param clazz the class to find the get method from + * @param identifier the key to use as an argument to the get method + * @return the executor if found, null otherwise + */ + public static DuckGetExecutor discover(final Introspector is, final Class clazz, final Object identifier) { + final java.lang.reflect.Method method = is.getMethod(clazz, "get", makeArgs(identifier)); + return method == null? null : new DuckGetExecutor(clazz, method, identifier); + } + + /** + * Creates an instance. + * @param clazz he class the get method applies to + * @param method the method held by this executor + * @param identifier the property to get + */ + private DuckGetExecutor(final Class clazz, final java.lang.reflect.Method method, final Object identifier) { + super(clazz, method); + property = identifier; + } + + @Override + public Object getTargetProperty() { + return property; + } + + @Override + public Object invoke(final Object obj) throws IllegalAccessException, InvocationTargetException { + final Object[] args = {property}; + return method == null ? null : method.invoke(obj, args); + } + + @Override + public Object tryInvoke(final Object obj, final Object key) { + if (obj != null + && objectClass.equals(obj.getClass()) + // ensure method name matches the property name + && method != null + && ((property == null && key == null) + || (property != null && property.equals(key)))) { + try { + final Object[] args = {property}; + return method.invoke(obj, args); + } catch (IllegalAccessException | IllegalArgumentException xill) { + return TRY_FAILED;// fail + } catch (final InvocationTargetException xinvoke) { + throw JexlException.tryFailed(xinvoke); // throw + } + } + return TRY_FAILED; + } +} diff --git a/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/internal/introspection/DuckSetExecutor.java b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/internal/introspection/DuckSetExecutor.java new file mode 100644 index 0000000..1c28983 --- /dev/null +++ b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/internal/introspection/DuckSetExecutor.java @@ -0,0 +1,110 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package aiyh.utils.tool.org.apache.commons.jexl3.internal.introspection; + +import java.lang.reflect.InvocationTargetException; + +import aiyh.utils.tool.org.apache.commons.jexl3.JexlException; + +/** + * Specialized executor to set a property of an object. + *

Duck as in duck-typing for an interface like: + * + * interface Setable { + * Object set(Object property, Object value); + * } + * + * or + * + * interface Putable { + * Object put(Object property, Object value); + * } + * + *

+ * @since 2.0 + */ +public final class DuckSetExecutor extends AbstractExecutor.Set { + /** The property, may be null. */ + private final Object property; + /** The property value class. */ + private final Class valueClass; + + /** + * Discovers a DuckSetExecutor. + * + * @param is the introspector + * @param clazz the class to find the set method from + * @param key the key to use as 1st argument to the set method + * @param value the value to use as 2nd argument to the set method + * @return the executor if found, null otherwise + */ + public static DuckSetExecutor discover(final Introspector is, final Class clazz, final Object key, final Object value) { + java.lang.reflect.Method method = is.getMethod(clazz, "set", makeArgs(key, value)); + if (method == null) { + method = is.getMethod(clazz, "put", makeArgs(key, value)); + } + return method == null? null : new DuckSetExecutor(clazz, method, key, value); + } + + /** + * Creates an instance. + * @param clazz the class the set method applies to + * @param method the method called through this executor + * @param key the key to use as 1st argument to the set method + * @param value the value to use as 2nd argument to the set method + */ + private DuckSetExecutor(final Class clazz, final java.lang.reflect.Method method, final Object key, final Object value) { + super(clazz, method); + property = key; + valueClass = classOf(value); + } + + @Override + public Object getTargetProperty() { + return property; + } + + @Override + public Object invoke(final Object obj, final Object value) throws IllegalAccessException, InvocationTargetException { + final Object[] pargs = {property, value}; + if (method != null) { + method.invoke(obj, pargs); + } + return value; + } + + @Override + public Object tryInvoke(final Object obj, final Object key, final Object value) { + if (obj != null + && objectClass.equals(obj.getClass()) + && method != null + && ((property != null && property.equals(key)) + || (property == null && key == null)) + && valueClass.equals(classOf(value))) { + try { + final Object[] args = {property, value}; + method.invoke(obj, args); + return value; + } catch (IllegalAccessException | IllegalArgumentException xill) { + return TRY_FAILED;// fail + } catch (final InvocationTargetException xinvoke) { + throw JexlException.tryFailed(xinvoke); // throw + } + } + return TRY_FAILED; + } +} \ No newline at end of file diff --git a/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/internal/introspection/EnumerationIterator.java b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/internal/introspection/EnumerationIterator.java new file mode 100644 index 0000000..10e7405 --- /dev/null +++ b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/internal/introspection/EnumerationIterator.java @@ -0,0 +1,59 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package aiyh.utils.tool.org.apache.commons.jexl3.internal.introspection; + + +import java.util.Iterator; +import java.util.Enumeration; + +/** + * An Iterator wrapper for an Enumeration. + * @param the type of object this iterator returns + * @since 1.0 + */ +public class EnumerationIterator implements Iterator { + /** + * The enumeration to iterate over. + */ + private final Enumeration enumeration; + + /** + * Creates a new iteratorwrapper instance for the specified + * Enumeration. + * + * @param enumer The Enumeration to wrap. + */ + public EnumerationIterator(final Enumeration enumer) { + enumeration = enumer; + } + + @Override + public T next() { + return enumeration.nextElement(); + } + + @Override + public boolean hasNext() { + return enumeration.hasMoreElements(); + } + + @Override + public void remove() { + // not implemented + } +} \ No newline at end of file diff --git a/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/internal/introspection/FieldGetExecutor.java b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/internal/introspection/FieldGetExecutor.java new file mode 100644 index 0000000..e8a8605 --- /dev/null +++ b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/internal/introspection/FieldGetExecutor.java @@ -0,0 +1,85 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package aiyh.utils.tool.org.apache.commons.jexl3.internal.introspection; + +import java.lang.reflect.Field; + +import aiyh.utils.tool.org.apache.commons.jexl3.introspection.JexlPropertyGet; + +/** + * A JexlPropertyGet for public fields. + */ +public final class FieldGetExecutor implements JexlPropertyGet { + /** + * The public field. + */ + private final Field field; + + /** + * Attempts to discover a FieldGetExecutor. + * + * @param is the introspector + * @param clazz the class to find the get method from + * @param identifier the key to use as an argument to the get method + * @return the executor if found, null otherwise + */ + public static JexlPropertyGet discover(final Introspector is, final Class clazz, final String identifier) { + if (identifier != null) { + final Field field = is.getField(clazz, identifier); + if (field != null) { + return new FieldGetExecutor(field); + } + } + return null; + } + /** + * Creates a new instance of FieldPropertyGet. + * @param theField the class public field + */ + private FieldGetExecutor(final Field theField) { + field = theField; + } + + @Override + public Object invoke(final Object obj) throws Exception { + return field.get(obj); + } + + @Override + public Object tryInvoke(final Object obj, final Object key) { + if (obj.getClass().equals(field.getDeclaringClass()) && key.equals(field.getName())) { + try { + return field.get(obj); + } catch (final IllegalAccessException xill) { + return Uberspect.TRY_FAILED; + } + } + return Uberspect.TRY_FAILED; + } + + @Override + public boolean tryFailed(final Object rval) { + return rval == Uberspect.TRY_FAILED; + } + + @Override + public boolean isCacheable() { + return true; + } + +} diff --git a/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/internal/introspection/FieldSetExecutor.java b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/internal/introspection/FieldSetExecutor.java new file mode 100644 index 0000000..3a05410 --- /dev/null +++ b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/internal/introspection/FieldSetExecutor.java @@ -0,0 +1,96 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package aiyh.utils.tool.org.apache.commons.jexl3.internal.introspection; + +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; + +import aiyh.utils.tool.org.apache.commons.jexl3.introspection.JexlPropertySet; + +/** + * A JexlPropertySet for public fields. + */ +public final class FieldSetExecutor implements JexlPropertySet { + /** + * The public field. + */ + private final Field field; + + /** + * Attempts to discover a FieldSetExecutor. + * + * @param is the introspector + * @param clazz the class to find the get method from + * @param identifier the key to use as an argument to the get method + * @param value the value to set the field to + * @return the executor if found, null otherwise + */ + public static JexlPropertySet discover(final Introspector is, + final Class clazz, + final String identifier, + final Object value) { + if (identifier != null) { + final Field field = is.getField(clazz, identifier); + if (field != null + && !Modifier.isFinal(field.getModifiers()) + && (value == null || MethodKey.isInvocationConvertible(field.getType(), value.getClass(), false))) { + return new FieldSetExecutor(field); + } + } + return null; + } + + /** + * Creates a new instance of FieldPropertySet. + * @param theField the class public field + */ + private FieldSetExecutor(final Field theField) { + field = theField; + } + + @Override + public Object invoke(final Object obj, final Object arg) throws Exception { + field.set(obj, arg); + return arg; + } + + @Override + public Object tryInvoke(final Object obj, final Object key, final Object value) { + if (obj.getClass().equals(field.getDeclaringClass()) + && key.equals(field.getName()) + && (value == null || MethodKey.isInvocationConvertible(field.getType(), value.getClass(), false))) { + try { + field.set(obj, value); + return value; + } catch (final IllegalAccessException xill) { + return Uberspect.TRY_FAILED; + } + } + return Uberspect.TRY_FAILED; + } + + @Override + public boolean tryFailed(final Object rval) { + return rval == Uberspect.TRY_FAILED; + } + + @Override + public boolean isCacheable() { + return true; + } +} diff --git a/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/internal/introspection/IndexedType.java b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/internal/introspection/IndexedType.java new file mode 100644 index 0000000..70094aa --- /dev/null +++ b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/internal/introspection/IndexedType.java @@ -0,0 +1,239 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package aiyh.utils.tool.org.apache.commons.jexl3.internal.introspection; + + +import aiyh.utils.tool.org.apache.commons.jexl3.introspection.JexlPropertyGet; + +import java.lang.reflect.Method; +import java.beans.IntrospectionException; + +/** + * Abstract an indexed property container. + *

This allows getting properties from expressions like var.container.property. + * This stores the container name and class as well as the list of available getter and setter methods. + * It implements JexlPropertyGet since such a container can only be accessed from its owning instance (not set). + */ +public final class IndexedType implements JexlPropertyGet { + /** The container name. */ + private final String container; + /** The container class. */ + private final Class clazz; + /** The array of getter methods. */ + private final Method[] getters; + /** Last get method used. */ + private volatile Method get = null; + /** The array of setter methods. */ + private final Method[] setters; + /** Last set method used. */ + private volatile Method set = null; + + /** + * Attempts to find an indexed-property getter in an object. + * The code attempts to find the list of methods getXXX() and setXXX(). + * Note that this is not equivalent to the strict bean definition of indexed properties; the type of the key + * is not necessarily an int and the set/get arrays are not resolved. + * + * @param is the introspector + * @param object the object + * @param name the container name + * @return a JexlPropertyGet is successful, null otherwise + */ + public static JexlPropertyGet discover(final Introspector is, final Object object, final String name) { + if (object != null && name != null && !name.isEmpty()) { + final String base = name.substring(0, 1).toUpperCase() + name.substring(1); + final String container = name; + final Class clazz = object.getClass(); + final Method[] getters = is.getMethods(object.getClass(), "get" + base); + final Method[] setters = is.getMethods(object.getClass(), "set" + base); + if (getters != null) { + return new IndexedType(container, clazz, getters, setters); + } + } + return null; + } + + /** + * A generic indexed property container, exposes get(key) and set(key, value) + * and solves method call dynamically based on arguments. + *

Must remain public for introspection purpose.

+ */ + public static final class IndexedContainer { + /** The container instance. */ + private final Object container; + /** The container type instance. */ + private final IndexedType type; + + /** + * Creates a new duck container. + * @param theType the container type + * @param theContainer the container instance + */ + private IndexedContainer(final IndexedType theType, final Object theContainer) { + this.type = theType; + this.container = theContainer; + } + + /** + * Gets the property container name. + * @return the container name + */ + public String getContainerName() { + return type.container; + } + + /** + * Gets the property container class. + * @return the container class + */ + public Class getContainerClass() { + return type.clazz; + } + + /** + * Gets a property from this indexed container. + * @param key the property key + * @return the property value + * @throws Exception if inner invocation fails + */ + public Object get(final Object key) throws Exception { + return type.invokeGet(container, key); + } + + /** + * Sets a property in this indexed container. + * @param key the property key + * @param value the property value + * @return the invocation result (frequently null) + * @throws Exception if inner invocation fails + */ + public Object set(final Object key, final Object value) throws Exception { + return type.invokeSet(container, key, value); + } + } + + /** + * Creates a new indexed property container type. + * @param name the container name + * @param c the owning class + * @param gets the array of getter methods + * @param sets the array of setter methods + */ + private IndexedType(final String name, final Class c, final Method[] gets, final Method[] sets) { + this.container = name; + this.clazz = c; + this.getters = gets; + this.setters = sets; + } + + @Override + public Object invoke(final Object obj) throws Exception { + if (obj != null && clazz.equals(obj.getClass())) { + return new IndexedContainer(this, obj); + } + throw new IntrospectionException("property resolution error"); + } + + @Override + public Object tryInvoke(final Object obj, final Object key) { + if (obj != null && key != null + && clazz.equals(obj.getClass()) + && container.equals(key.toString())) { + return new IndexedContainer(this, obj); + } + return Uberspect.TRY_FAILED; + } + + @Override + public boolean tryFailed(final Object rval) { + return rval == Uberspect.TRY_FAILED; + } + + @Override + public boolean isCacheable() { + return true; + } + + /** + * Gets the value of a property from a container. + * @param object the container instance (not null) + * @param key the property key (not null) + * @return the property value + * @throws Exception if invocation failed; + * IntrospectionException if a property getter could not be found + */ + private Object invokeGet(final Object object, final Object key) throws Exception { + if (getters != null && getters.length > 0) { + Method jm = get; + if (jm != null) { + final Class[] ptypes = jm.getParameterTypes(); + if (ptypes[0].isAssignableFrom(key.getClass())) { + return jm.invoke(object, key); + } + } + final Object[] args = {key}; + final String mname = getters[0].getName(); + final MethodKey km = new MethodKey(mname, args); + jm = km.getMostSpecificMethod(getters); + if (jm != null) { + final Object invoked = jm.invoke(object, args); + get = jm; + return invoked; + } + } + throw new IntrospectionException("property get error: " + + object.getClass().toString() + + "@" + key.toString()); + } + + /** + * Sets the value of a property in a container. + * @param object the container instance (not null) + * @param key the property key (not null) + * @param value the property value (not null) + * @return the result of the method invocation (frequently null) + * @throws Exception if invocation failed; + * IntrospectionException if a property setter could not be found + */ + private Object invokeSet(final Object object, final Object key, final Object value) throws Exception { + if (setters != null && setters.length > 0) { + Method jm = set; + if (jm != null) { + final Class[] ptypes = jm.getParameterTypes(); + if (ptypes[0].isAssignableFrom(key.getClass()) + && (value == null + || ptypes[1].isAssignableFrom(value.getClass()))) { + return jm.invoke(object, key, value); + } + } + final Object[] args = {key, value}; + final String mname = setters[0].getName(); + final MethodKey km = new MethodKey(mname, args); + jm = km.getMostSpecificMethod(setters); + if (jm != null) { + final Object invoked = jm.invoke(object, args); + set = jm; + return invoked; + } + } + throw new IntrospectionException("property set error: " + + object.getClass().toString() + + "@" + key.toString()); + } + +} diff --git a/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/internal/introspection/Introspector.java b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/internal/introspection/Introspector.java new file mode 100644 index 0000000..04fc268 --- /dev/null +++ b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/internal/introspection/Introspector.java @@ -0,0 +1,392 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package aiyh.utils.tool.org.apache.commons.jexl3.internal.introspection; + +import org.apache.commons.logging.Log; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.Method; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import java.util.concurrent.locks.ReadWriteLock; +import java.util.concurrent.locks.ReentrantReadWriteLock; + +/** + * This basic function of this class is to return a Method object for a + * particular class given the name of a method and the parameters to the method + * in the form of an Object[]. + * + *

The first time the Introspector sees a class it creates a class method map + * for the class in question. + * Basically the class method map is a Hashtable where Method objects are keyed by the aggregation of + * the method name and the array of parameters classes. + * This mapping is performed for all the public methods of a class and stored.

+ * + * @since 1.0 + */ +public final class Introspector { + /** + * A Constructor get cache-miss. + */ + private static class CacheMiss { + /** The constructor used as cache-miss. */ + @SuppressWarnings("unused") + public CacheMiss() { + } + } + /** + * The cache-miss marker for the constructors map. + */ + private static final Constructor CTOR_MISS = CacheMiss.class.getConstructors()[0]; + /** + * the logger. + */ + protected final Log logger; + /** + * The class loader used to solve constructors if needed. + */ + private ClassLoader loader; + /** + * The permissions. + */ + private final Permissions permissions; + /** + * The read/write lock. + */ + private final ReadWriteLock lock = new ReentrantReadWriteLock(); + /** + * Holds the method maps for the classes we know about, keyed by Class. + */ + private final Map, ClassMap> classMethodMaps = new HashMap, ClassMap>(); + /** + * Holds the map of classes ctors we know about as well as unknown ones. + */ + private final Map> constructorsMap = new HashMap>(); + /** + * Holds the set of classes we have introspected. + */ + private final Map> constructibleClasses = new HashMap>(); + + /** + * Create the introspector. + * @param log the logger to use + * @param cloader the class loader + */ + public Introspector(final Log log, final ClassLoader cloader) { + this(log, cloader, null); + } + + /** + * Create the introspector. + * @param log the logger to use + * @param cloader the class loader + * @param perms the permissions + */ + public Introspector(final Log log, final ClassLoader cloader, final Permissions perms) { + this.logger = log; + this.loader = cloader; + this.permissions = perms != null? perms : Permissions.DEFAULT; + } + + /** + * Gets a class by name through this introspector class loader. + * @param className the class name + * @return the class instance or null if it could not be found + */ + public Class getClassByName(final String className) { + try { + return Class.forName(className, false, loader); + } catch (final ClassNotFoundException xignore) { + return null; + } + } + + /** + * Gets a method defined by a class, a name and a set of parameters. + * @param c the class + * @param name the method name + * @param params the method parameters + * @return the desired method object + * @throws MethodKey.AmbiguousException if no unambiguous method could be found through introspection + */ + public Method getMethod(final Class c, final String name, final Object[] params) { + return getMethod(c, new MethodKey(name, params)); + } + + /** + * Gets the method defined by the MethodKey for the class c. + * + * @param c Class in which the method search is taking place + * @param key Key of the method being searched for + * @return The desired method object + * @throws MethodKey.AmbiguousException if no unambiguous method could be found through introspection + */ + public Method getMethod(final Class c, final MethodKey key) { + try { + return getMap(c).getMethod(key); + } catch (final MethodKey.AmbiguousException xambiguous) { + // whoops. Ambiguous and not benign. Make a nice log message and return null... + if (logger != null && xambiguous.isSevere() && logger.isInfoEnabled()) { + logger.info("ambiguous method invocation: " + + c.getName() + "." + + key.debugString(), xambiguous); + } + return null; + } + } + + /** + * Gets the field named by key for the class c. + * + * @param c Class in which the field search is taking place + * @param key Name of the field being searched for + * @return the desired field or null if it does not exist or is not accessible + */ + public Field getField(final Class c, final String key) { + return getMap(c).getField(key); + } + + /** + * Gets the array of accessible field names known for a given class. + * @param c the class + * @return the class field names + */ + public String[] getFieldNames(final Class c) { + if (c == null) { + return new String[0]; + } + final ClassMap classMap = getMap(c); + return classMap.getFieldNames(); + } + + /** + * Gets the array of accessible methods names known for a given class. + * @param c the class + * @return the class method names + */ + public String[] getMethodNames(final Class c) { + if (c == null) { + return new String[0]; + } + final ClassMap classMap = getMap(c); + return classMap.getMethodNames(); + } + + /** + * Gets the array of accessible method known for a given class. + * @param c the class + * @param methodName the method name + * @return the array of methods (null or not empty) + */ + public Method[] getMethods(final Class c, final String methodName) { + if (c == null) { + return null; + } + final ClassMap classMap = getMap(c); + return classMap.getMethods(methodName); + } + + /** + * Gets the constructor defined by the MethodKey. + * + * @param key Key of the constructor being searched for + * @return The desired constructor object + * or null if no unambiguous constructor could be found through introspection. + */ + public Constructor getConstructor(final MethodKey key) { + return getConstructor(null, key); + } + + /** + * Gets the constructor defined by the MethodKey. + * @param c the class we want to instantiate + * @param key Key of the constructor being searched for + * @return The desired constructor object + * or null if no unambiguous constructor could be found through introspection. + */ + public Constructor getConstructor(final Class c, final MethodKey key) { + Constructor ctor; + lock.readLock().lock(); + try { + ctor = constructorsMap.get(key); + if (ctor != null) { + // miss or not? + return CTOR_MISS.equals(ctor) ? null : ctor; + } + } finally { + lock.readLock().unlock(); + } + // let's introspect... + lock.writeLock().lock(); + try { + // again for kicks + ctor = constructorsMap.get(key); + if (ctor != null) { + // miss or not? + return CTOR_MISS.equals(ctor) ? null : ctor; + } + final String cname = key.getMethod(); + // do we know about this class? + Class clazz = constructibleClasses.get(cname); + try { + // do find the most specific ctor + if (clazz == null) { + if (c != null && c.getName().equals(key.getMethod())) { + clazz = c; + } else { + clazz = loader.loadClass(cname); + } + // add it to list of known loaded classes + constructibleClasses.put(cname, clazz); + } + final List> l = new ArrayList<>(); + for (final Constructor ictor : clazz.getConstructors()) { + if (permissions.allow(ictor)) { + l.add(ictor); + } + } + // try to find one + ctor = key.getMostSpecificConstructor(l.toArray(new Constructor[l.size()])); + if (ctor != null) { + constructorsMap.put(key, ctor); + } else { + constructorsMap.put(key, CTOR_MISS); + } + } catch (final ClassNotFoundException xnotfound) { + if (logger != null && logger.isDebugEnabled()) { + logger.debug("unable to find class: " + + cname + "." + + key.debugString(), xnotfound); + } + ctor = null; + } catch (final MethodKey.AmbiguousException xambiguous) { + if (logger != null && xambiguous.isSevere() && logger.isInfoEnabled()) { + logger.info("ambiguous constructor invocation: " + + cname + "." + + key.debugString(), xambiguous); + } + ctor = null; + } + return ctor; + } finally { + lock.writeLock().unlock(); + } + } + + /** + * Gets the ClassMap for a given class. + * @param c the class + * @return the class map + */ + private ClassMap getMap(final Class c) { + ClassMap classMap; + lock.readLock().lock(); + try { + classMap = classMethodMaps.get(c); + } finally { + lock.readLock().unlock(); + } + if (classMap == null) { + lock.writeLock().lock(); + try { + // try again + classMap = classMethodMaps.get(c); + if (classMap == null) { + classMap = new ClassMap(c, permissions, logger); + classMethodMaps.put(c, classMap); + } + } finally { + lock.writeLock().unlock(); + } + + } + return classMap; + } + + /** + * Sets the class loader used to solve constructors. + *

Also cleans the constructors and methods caches.

+ * @param cloader the class loader; if null, use this instance class loader + */ + public void setLoader(ClassLoader cloader) { + final ClassLoader previous = loader; + if (cloader == null) { + cloader = getClass().getClassLoader(); + } + if (!cloader.equals(loader)) { + lock.writeLock().lock(); + try { + // clean up constructor and class maps + final Iterator>> mentries = constructorsMap.entrySet().iterator(); + while (mentries.hasNext()) { + final Map.Entry> entry = mentries.next(); + final Class clazz = entry.getValue().getDeclaringClass(); + if (isLoadedBy(previous, clazz)) { + mentries.remove(); + // the method name is the name of the class + constructibleClasses.remove(entry.getKey().getMethod()); + } + } + // clean up method maps + final Iterator, ClassMap>> centries = classMethodMaps.entrySet().iterator(); + while (centries.hasNext()) { + final Map.Entry, ClassMap> entry = centries.next(); + final Class clazz = entry.getKey(); + if (isLoadedBy(previous, clazz)) { + centries.remove(); + } + } + loader = cloader; + } finally { + lock.writeLock().unlock(); + } + } + } + + /** + * Gets the class loader used by this introspector. + * @return the class loader + */ + public ClassLoader getLoader() { + return loader; + } + + /** + * Checks whether a class is loaded through a given class loader or one of its ascendants. + * @param loader the class loader + * @param clazz the class to check + * @return true if clazz was loaded through the loader, false otherwise + */ + private static boolean isLoadedBy(final ClassLoader loader, final Class clazz) { + if (loader != null) { + ClassLoader cloader = clazz.getClassLoader(); + while (cloader != null) { + if (cloader.equals(loader)) { + return true; + } + cloader = cloader.getParent(); + } + } + return false; + } +} \ No newline at end of file diff --git a/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/internal/introspection/ListGetExecutor.java b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/internal/introspection/ListGetExecutor.java new file mode 100644 index 0000000..b16a7be --- /dev/null +++ b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/internal/introspection/ListGetExecutor.java @@ -0,0 +1,93 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package aiyh.utils.tool.org.apache.commons.jexl3.internal.introspection; + +import java.util.List; +import java.lang.reflect.Array; + +/** + * Specialized executor to get a property from a List or array. + * @since 2.0 + */ +public final class ListGetExecutor extends AbstractExecutor.Get { + /** The java.lang.reflect.Array.get method used as an active marker in ListGet. */ + private static final java.lang.reflect.Method ARRAY_GET = + initMarker(Array.class, "get", Object.class, Integer.TYPE); + /** The java.util.obj.get method used as an active marker in ListGet. */ + private static final java.lang.reflect.Method LIST_GET = + initMarker(List.class, "get", Integer.TYPE); + /** The property. */ + private final Integer property; + + /** + * Attempts to discover a ListGetExecutor. + * + * @param is the introspector + * @param clazz the class to find the get method from + * @param index the index to use as an argument to the get method + * @return the executor if found, null otherwise + */ + public static ListGetExecutor discover(final Introspector is, final Class clazz, final Integer index) { + if (index != null) { + if (clazz.isArray()) { + return new ListGetExecutor(clazz, ARRAY_GET, index); + } + if (List.class.isAssignableFrom(clazz)) { + return new ListGetExecutor(clazz, LIST_GET, index); + } + } + return null; + } + + /** + * Creates an instance. + * @param clazz he class the get method applies to + * @param method the method held by this executor + * @param index the index to use as an argument to the get method + */ + private ListGetExecutor(final Class clazz, final java.lang.reflect.Method method, final Integer index) { + super(clazz, method); + property = index; + } + + @Override + public Object getTargetProperty() { + return property; + } + + @Override + public Object invoke(final Object obj) { + if (method == ARRAY_GET) { + return Array.get(obj, property); + } + return ((List) obj).get(property); + } + + @Override + public Object tryInvoke(final Object obj, final Object identifier) { + final Integer index = castInteger(identifier); + if (obj != null && method != null + && objectClass.equals(obj.getClass()) + && index != null) { + if (method == ARRAY_GET) { + return Array.get(obj, index); + } + return ((List) obj).get(index); + } + return TRY_FAILED; + } +} diff --git a/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/internal/introspection/ListSetExecutor.java b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/internal/introspection/ListSetExecutor.java new file mode 100644 index 0000000..b75fc51 --- /dev/null +++ b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/internal/introspection/ListSetExecutor.java @@ -0,0 +1,113 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package aiyh.utils.tool.org.apache.commons.jexl3.internal.introspection; + +import java.util.List; +import java.lang.reflect.Array; + +/** + * Specialized executor to set a property in a List or array. + * @since 2.0 + */ +public final class ListSetExecutor extends AbstractExecutor.Set { + /** The java.lang.reflect.Array.get method used as an active marker in ListGet. */ + private static final java.lang.reflect.Method ARRAY_SET = + initMarker(Array.class, "set", Object.class, Integer.TYPE, Object.class); + /** The java.util.obj.set method used as an active marker in ListSet. */ + private static final java.lang.reflect.Method LIST_SET = + initMarker(List.class, "set", Integer.TYPE, Object.class); + /** The property. */ + private final Integer property; + + /** + * Attempts to discover a ListSetExecutor. + * + * @param is the introspector + * @param clazz the class to find the get method from + * @param identifier the key to use as an argument to the get method + * @param value the value to use as argument in list.put(key,value) + * @return the executor if found, null otherwise + */ + public static ListSetExecutor discover(final Introspector is, + final Class clazz, + final Object identifier, + final Object value) { + final Integer index = castInteger(identifier); + if (index != null) { + if (clazz.isArray()) { + // we could verify if the call can be performed but it does not change + // the fact we would fail... + // Class formal = clazz.getComponentType(); + // Class actual = value == null? Object.class : value.getClass(); + // if (IntrospectionUtils.isMethodInvocationConvertible(formal, actual, false)) { + return new ListSetExecutor(clazz, ARRAY_SET, index); + // } + } + if (List.class.isAssignableFrom(clazz)) { + return new ListSetExecutor(clazz, LIST_SET, index); + } + } + return null; + } + + /** + * Creates an instance. + * + * @param clazz the class the set method applies to + * @param method the method called through this executor + * @param key the key to use as 1st argument to the set method + */ + private ListSetExecutor(final Class clazz, final java.lang.reflect.Method method, final Integer key) { + super(clazz, method); + property = key; + } + + @Override + public Object getTargetProperty() { + return property; + } + + @Override + public Object invoke(final Object obj, final Object value) { + if (method == ARRAY_SET) { + Array.set(obj, property, value); + } else { + @SuppressWarnings("unchecked") // LSE should only be created for array or list types + final List list = (List) obj; + list.set(property, value); + } + return value; + } + + @Override + public Object tryInvoke(final Object obj, final Object key, final Object value) { + final Integer index = castInteger(key); + if (obj != null && method != null + && objectClass.equals(obj.getClass()) + && index != null) { + if (method == ARRAY_SET) { + Array.set(obj, index, value); + } else { + @SuppressWarnings("unchecked") // LSE should only be created for array or list types + final List list = (List) obj; + list.set(index, value); + } + return value; + } + return TRY_FAILED; + } +} diff --git a/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/internal/introspection/MapGetExecutor.java b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/internal/introspection/MapGetExecutor.java new file mode 100644 index 0000000..6517159 --- /dev/null +++ b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/internal/introspection/MapGetExecutor.java @@ -0,0 +1,84 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package aiyh.utils.tool.org.apache.commons.jexl3.internal.introspection; + +import java.util.Map; + +/** + * Specialized executor to get a property from a Map. + * @since 2.0 + */ +public final class MapGetExecutor extends AbstractExecutor.Get { + /** The java.util.map.get method used as an active marker in MapGet. */ + private static final java.lang.reflect.Method MAP_GET = + initMarker(Map.class, "get", Object.class); + /** The property. */ + private final Object property; + + /** + * Attempts to discover a MapGetExecutor. + * + * @param is the introspector + * @param clazz the class to find the get method from + * @param identifier the key to use as an argument to the get method + * @return the executor if found, null otherwise + */ + public static MapGetExecutor discover(final Introspector is, final Class clazz, final Object identifier) { + if (Map.class.isAssignableFrom(clazz)) { + return new MapGetExecutor(clazz, MAP_GET, identifier); + } + return null; + } + + /** + * Creates an instance. + * @param clazz he class the get method applies to + * @param method the method held by this executor + * @param key the property to get + */ + private MapGetExecutor(final Class clazz, final java.lang.reflect.Method method, final Object key) { + super(clazz, method); + property = key; + } + + @Override + public Object getTargetProperty() { + return property; + } + + @Override + public Object invoke(final Object obj) { + @SuppressWarnings("unchecked") // ctor only allows Map instances - see discover() method + final Map map = (Map) obj; + return map.get(property); + } + + @Override + public Object tryInvoke(final Object obj, final Object key) { + if (obj != null + && method != null + && objectClass.equals(obj.getClass()) + && ((property == null && key == null) + || (property != null && key != null && property.getClass().equals(key.getClass())))) { + @SuppressWarnings("unchecked") // ctor only allows Map instances - see discover() method + final Map map = (Map) obj; + return map.get(key); + } + return TRY_FAILED; + } +} diff --git a/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/internal/introspection/MapSetExecutor.java b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/internal/introspection/MapSetExecutor.java new file mode 100644 index 0000000..6707b21 --- /dev/null +++ b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/internal/introspection/MapSetExecutor.java @@ -0,0 +1,93 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package aiyh.utils.tool.org.apache.commons.jexl3.internal.introspection; +import java.util.Map; + +/** + * Specialized executor to set a property in a Map. + * @since 2.0 + */ +public final class MapSetExecutor extends AbstractExecutor.Set { + /** The java.util.map.put method used as an active marker in MapSet. */ + private static final java.lang.reflect.Method MAP_SET = initMarker(Map.class, "put", Object.class, Object.class); + /** The property. */ + private final Object property; + /** The property value class. */ + private final Class valueClass; + + /** + * Attempts to discover a MapSetExecutor. + * + * @param is the introspector + * @param clazz the class to find the set method from + * @param identifier the key to use as an argument to the get method + * @param value the value to use as argument in map.put(key,value) + * @return the executor if found, null otherwise + */ + public static MapSetExecutor discover(final Introspector is, + final Class clazz, + final Object identifier, + final Object value) { + if (Map.class.isAssignableFrom(clazz)) { + return new MapSetExecutor(clazz, MAP_SET, identifier, value); + } + return null; + } + + /** + * Creates an instance. + * @param clazz the class the set method applies to + * @param method the method called through this executor + * @param key the key to use as 1st argument to the set method + * @param value the value to use as 2nd argument to the set method + */ + private MapSetExecutor(final Class clazz, final java.lang.reflect.Method method, final Object key, final Object value) { + super(clazz, method); + property = key; + valueClass = classOf(value); + } + + @Override + public Object getTargetProperty() { + return property; + } + + @Override + public Object invoke(final Object obj, final Object value) { + @SuppressWarnings("unchecked") // ctor only allows Map instances - see discover() method + final Map map = ((Map) obj); + map.put(property, value); + return value; + } + + @Override + public Object tryInvoke(final Object obj, final Object key, final Object value) { + if (obj != null + && method != null + && objectClass.equals(obj.getClass()) + && ((property == null && key == null) + || (property != null && key != null && property.getClass().equals(key.getClass()))) + && valueClass.equals(classOf(value))) { + @SuppressWarnings("unchecked") // ctor only allows Map instances - see discover() method + final Map map = ((Map) obj); + map.put(key, value); + return value; + } + return TRY_FAILED; + } +} diff --git a/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/internal/introspection/MethodExecutor.java b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/internal/introspection/MethodExecutor.java new file mode 100644 index 0000000..863963d --- /dev/null +++ b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/internal/introspection/MethodExecutor.java @@ -0,0 +1,157 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package aiyh.utils.tool.org.apache.commons.jexl3.internal.introspection; + +import aiyh.utils.tool.org.apache.commons.jexl3.JexlEngine; +import aiyh.utils.tool.org.apache.commons.jexl3.JexlException; + +import java.lang.reflect.Array; +import java.lang.reflect.InvocationTargetException; + +/** + * Specialized executor to invoke a method on an object. + * @since 2.0 + */ +public final class MethodExecutor extends AbstractExecutor.Method { + /** If this method is a vararg method, vaStart is the last argument index. */ + private final int vaStart; + /** If this method is a vararg method, vaClass is the component type of the vararg array. */ + private final Class vaClass; + + /** + * Discovers a {@link MethodExecutor}. + *

+ * If the object is an array, an attempt will be made to find the + * method in a List (see {@link ArrayListWrapper}) + *

+ *

+ * If the object is a class, an attempt will be made to find the + * method as a static method of that class. + *

+ * @param is the introspector used to discover the method + * @param obj the object to introspect + * @param method the name of the method to find + * @param args the method arguments + * @return a filled up parameter (may contain a null method) + */ + public static MethodExecutor discover(final Introspector is, final Object obj, final String method, final Object[] args) { + final Class clazz = obj.getClass(); + final MethodKey key = new MethodKey(method, args); + java.lang.reflect.Method m = is.getMethod(clazz, key); + if (m == null && clazz.isArray()) { + // check for support via our array->list wrapper + m = is.getMethod(ArrayListWrapper.class, key); + } + if (m == null && obj instanceof Class) { + m = is.getMethod((Class) obj, key); + } + return m == null? null : new MethodExecutor(clazz, m, key); + } + + /** + * Creates a new instance. + * @param c the class this executor applies to + * @param m the method + * @param k the MethodKey + */ + private MethodExecutor(final Class c, final java.lang.reflect.Method m, final MethodKey k) { + super(c, m, k); + int vastart = -1; + Class vaclass = null; + if (MethodKey.isVarArgs(method)) { + // if the last parameter is an array, the method is considered as vararg + final Class[] formal = method.getParameterTypes(); + vastart = formal.length - 1; + vaclass = formal[vastart].getComponentType(); + } + vaStart = vastart; + vaClass = vaclass; + } + + @Override + public Object invoke(final Object o, Object... args) throws IllegalAccessException, InvocationTargetException { + if (vaClass != null && args != null) { + args = handleVarArg(args); + } + if (method.getDeclaringClass() == ArrayListWrapper.class && o.getClass().isArray()) { + return method.invoke(new ArrayListWrapper(o), args); + } + return method.invoke(o, args); + } + + @Override + public Object tryInvoke(final String name, final Object obj, final Object... args) { + final MethodKey tkey = new MethodKey(name, args); + // let's assume that invocation will fly if the declaring class is the + // same and arguments have the same type + if (objectClass.equals(obj.getClass()) && tkey.equals(key)) { + try { + return invoke(obj, args); + } catch (IllegalAccessException | IllegalArgumentException xill) { + return TRY_FAILED;// fail + } catch (final InvocationTargetException xinvoke) { + throw JexlException.tryFailed(xinvoke); // throw + } + } + return JexlEngine.TRY_FAILED; + } + + + /** + * Reassembles arguments if the method is a vararg method. + * @param actual The actual arguments being passed to this method + * @return The actual parameters adjusted for the varargs in order + * to fit the method declaration. + */ + @SuppressWarnings("SuspiciousSystemArraycopy") + private Object[] handleVarArg(Object[] actual) { + final Class vaclass = vaClass; + final int vastart = vaStart; + // variable arguments count + final int varargc = actual.length - vastart; + // if no values are being passed into the vararg, size == 0 + if (varargc == 1) { + // if one non-null value is being passed into the vararg, + // and that arg is not the sole argument and not an array of the expected type, + // make the last arg an array of the expected type + if (actual[vastart] != null) { + final Class aclazz = actual[vastart].getClass(); + if (!aclazz.isArray() || !vaclass.isAssignableFrom(aclazz.getComponentType())) { + // create a 1-length array to hold and replace the last argument + final Object lastActual = Array.newInstance(vaclass, 1); + Array.set(lastActual, 0, actual[vastart]); + actual[vastart] = lastActual; + } + } + // else, the vararg is null and used as is, considered as T[] + } else { + // if no or multiple values are being passed into the vararg, + // put them in an array of the expected type + final Object varargs = Array.newInstance(vaclass, varargc); + System.arraycopy(actual, vastart, varargs, 0, varargc); + // put all arguments into a new actual array of the appropriate size + final Object[] newActual = new Object[vastart + 1]; + System.arraycopy(actual, 0, newActual, 0, vastart); + newActual[vastart] = varargs; + // replace the old actual array + actual = newActual; + } + return actual; + } +} + + diff --git a/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/internal/introspection/MethodKey.java b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/internal/introspection/MethodKey.java new file mode 100644 index 0000000..00fcaaf --- /dev/null +++ b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/internal/introspection/MethodKey.java @@ -0,0 +1,840 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package aiyh.utils.tool.org.apache.commons.jexl3.internal.introspection; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Method; +import java.util.*; + +/** + * A method key usable by the introspector cache. + *

+ * This stores a method (or class) name and parameters. + *

+ *

+ * This replaces the original key scheme which used to build the key + * by concatenating the method name and parameters class names as one string + * with the exception that primitive types were converted to their object class equivalents. + *

+ *

+ * The key is still based on the same information, it is just wrapped in an object instead. + * Primitive type classes are converted to they object equivalent to make a key; + * int foo(int) and int foo(Integer) do generate the same key. + *

+ * A key can be constructed either from arguments (array of objects) or from parameters + * (array of class). + * Roughly 3x faster than string key to access the map and uses less memory. + */ +public final class MethodKey { + /** The initial size of the primitive conversion map. */ + private static final int PRIMITIVE_SIZE = 11; + /** The hash code. */ + private final int hashCode; + /** The method name. */ + private final String method; + /** The parameters. */ + private final Class[] params; + /** A marker for empty parameter list. */ + private static final Class[] NOARGS = new Class[0]; + /** The hash code constants. */ + private static final int HASH = 37; + + /** + * Creates a key from a method name and a set of arguments. + * + * @param aMethod the method to generate the key from + * @param args the intended method arguments + */ + public MethodKey(final String aMethod, final Object[] args) { + // !! keep this in sync with the other ctor (hash code) !! + this.method = aMethod; + int hash = this.method.hashCode(); + final int size; + // CSOFF: InnerAssignment + if (args != null && (size = args.length) > 0) { + this.params = new Class[size]; + for (int p = 0; p < size; ++p) { + final Object arg = args[p]; + // null arguments use void as Void.class as marker + final Class parm = arg == null ? Void.class : arg.getClass(); + hash = (HASH * hash) + parm.hashCode(); + this.params[p] = parm; + } + } else { + this.params = NOARGS; + } + this.hashCode = hash; + } + + /** + * Creates a key from a method. + * + * @param aMethod the method to generate the key from. + */ + MethodKey(final Method aMethod) { + this(aMethod.getName(), aMethod.getParameterTypes()); + } + + /** + * Creates a key from a constructor. + * + * @param aCtor the constructor to generate the key from. + */ + MethodKey(final Constructor aCtor) { + this(aCtor.getDeclaringClass().getName(), aCtor.getParameterTypes()); + } + + /** + * Creates a key from a method name and a set of parameters. + * + * @param aMethod the method to generate the key from + * @param args the intended method parameters + */ + MethodKey(final String aMethod, final Class[] args) { + // !! keep this in sync with the other ctor (hash code) !! + this.method = aMethod.intern(); + int hash = this.method.hashCode(); + final int size; + // CSOFF: InnerAssignment + if (args != null && (size = args.length) > 0) { + this.params = new Class[size]; + for (int p = 0; p < size; ++p) { + final Class parm = primitiveClass(args[p]); + hash = (HASH * hash) + parm.hashCode(); + this.params[p] = parm; + } + } else { + this.params = NOARGS; + } + this.hashCode = hash; + } + + /** + * Gets this key's method name. + * + * @return the method name + */ + String getMethod() { + return method; + } + + /** + * Gets this key's method parameter classes. + * + * @return the parameters + */ + Class[] getParameters() { + return params; + } + + @Override + public int hashCode() { + return hashCode; + } + + @Override + public boolean equals(final Object obj) { + if (obj instanceof MethodKey) { + final MethodKey key = (MethodKey) obj; + return method.equals(key.method) && Arrays.equals(params, key.params); + } + return false; + } + + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(method); + for (final Class c : params) { + builder.append(c == Void.class ? "null" : c.getName()); + } + return builder.toString(); + } + + /** + * Outputs a human readable debug representation of this key. + * + * @return method(p0, p1, ...) + */ + public String debugString() { + final StringBuilder builder = new StringBuilder(method); + builder.append('('); + for (int i = 0; i < params.length; i++) { + if (i > 0) { + builder.append(", "); + } + builder.append(Void.class == params[i] ? "null" : params[i].getName()); + } + builder.append(')'); + return builder.toString(); + } + + /** + * Checks whether a method accepts a variable number of arguments. + *

May be due to a subtle bug in some JVMs, if a varargs method is an override, depending on (may be) the + * class introspection order, the isVarargs flag on the method itself will be false. + * To circumvent the potential problem, fetch the method with the same signature from the super-classes, + * - which will be different if override -and get the varargs flag from it. + * + * @param method the method to check for varargs + * @return true if declared varargs, false otherwise + */ + public static boolean isVarArgs(final Method method) { + if (method == null) { + return false; + } + if (method.isVarArgs()) { + return true; + } + // before climbing up the hierarchy, verify that the last parameter is an array + final Class[] ptypes = method.getParameterTypes(); + if (ptypes.length == 0 || ptypes[ptypes.length - 1].getComponentType() == null) { + return false; + } + final String mname = method.getName(); + // if this is an override, was it actually declared as varargs? + Class clazz = method.getDeclaringClass(); + do { + try { + final Method m = clazz.getMethod(mname, ptypes); + if (m.isVarArgs()) { + return true; + } + } catch (final NoSuchMethodException xignore) { + // this should not happen... + } + clazz = clazz.getSuperclass(); + } while (clazz != null); + return false; + } + + /** + * Gets the most specific method that is applicable to the parameters of this key. + * + * @param methods a list of methods. + * @return the most specific method. + * @throws AmbiguousException if there is more than one. + */ + public Method getMostSpecificMethod(final Method[] methods) { + return METHODS.getMostSpecific(this, methods); + } + + /** + * Gets the most specific constructor that is applicable to the parameters of this key. + * + * @param methods a list of constructors. + * @return the most specific constructor. + * @throws AmbiguousException if there is more than one. + */ + public Constructor getMostSpecificConstructor(final Constructor[] methods) { + return CONSTRUCTORS.getMostSpecific(this, methods); + } + + /** + * Determines whether a type represented by a class object is + * convertible to another type represented by a class object using a + * method invocation conversion, treating object types of primitive + * types as if they were primitive types (that is, a Boolean actual + * parameter type matches boolean primitive formal type). This behavior + * is because this method is used to determine applicable methods for + * an actual parameter list, and primitive types are represented by + * their object duals in reflective method calls. + * + * @param formal the formal parameter type to which the actual + * parameter type should be convertible + * @param actual the actual parameter type. + * @param possibleVarArg whether or not we're dealing with the last parameter + * in the method declaration + * @return true if either formal type is assignable from actual type, + * or formal is a primitive type and actual is its corresponding object + * type or an object type of a primitive type that can be converted to + * the formal type. + */ + public static boolean isInvocationConvertible(final Class formal, + final Class actual, + final boolean possibleVarArg) { + return isInvocationConvertible(formal, actual, false, possibleVarArg); + } + + /** + * Determines whether a type represented by a class object is + * convertible to another type represented by a class object using a + * method invocation conversion, without matching object and primitive + * types. This method is used to determine the more specific type when + * comparing signatures of methods. + * + * @param formal the formal parameter type to which the actual + * parameter type should be convertible + * @param actual the actual parameter type. + * @param possibleVarArg whether or not we're dealing with the last parameter + * in the method declaration + * @return true if either formal type is assignable from actual type, + * or formal and actual are both primitive types and actual can be + * subject to widening conversion to formal. + */ + public static boolean isStrictInvocationConvertible(final Class formal, + final Class actual, + final boolean possibleVarArg) { + return isInvocationConvertible(formal, actual, true, possibleVarArg); + } + + /** + * Converts a primitive type to its corresponding class. + *

+ * If the argument type is primitive then we want to convert our + * primitive type signature to the corresponding Object type so + * introspection for methods with primitive types will work + * correctly. + *

+ * + * @param parm a may-be primitive type class + * @return the equivalent object class + */ + static Class primitiveClass(final Class parm) { + // it was marginally faster to get from the map than call isPrimitive... + // if (!parm.isPrimitive()) return parm; + final Class[] prim = CONVERTIBLES.get(parm); + return prim == null ? parm : prim[0]; + } + + /** + * Helper to build class arrays. + * + * @param args the classes + * @return the array + */ + private static Class[] asArray(final Class... args) { + return args; + } + + /** + * Maps from primitive types to invocation compatible classes. + *

Considering the key as a parameter type, the value is the list of argument classes that are invocation + * compatible with the parameter. Example is Long is invocation convertible to long. + */ + private static final Map, Class[]> CONVERTIBLES; + + static { + CONVERTIBLES = new HashMap, Class[]>(PRIMITIVE_SIZE); + CONVERTIBLES.put(Boolean.TYPE, + asArray(Boolean.class)); + CONVERTIBLES.put(Character.TYPE, + asArray(Character.class)); + CONVERTIBLES.put(Byte.TYPE, + asArray(Byte.class)); + CONVERTIBLES.put(Short.TYPE, + asArray(Short.class, Byte.class)); + CONVERTIBLES.put(Integer.TYPE, + asArray(Integer.class, Short.class, Byte.class)); + CONVERTIBLES.put(Long.TYPE, + asArray(Long.class, Integer.class, Short.class, Byte.class)); + CONVERTIBLES.put(Float.TYPE, + asArray(Float.class, Long.class, Integer.class, Short.class, Byte.class)); + CONVERTIBLES.put(Double.TYPE, + asArray(Double.class, Float.class, Long.class, Integer.class, Short.class, Byte.class)); + } + + /** + * Maps from primitive types to invocation compatible primitive types. + *

Considering the key as a parameter type, the value is the list of argument types that are invocation + * compatible with the parameter. Example is 'int' is invocation convertible to 'long'. + */ + private static final Map, Class[]> STRICT_CONVERTIBLES; + + static { + STRICT_CONVERTIBLES = new HashMap, Class[]>(PRIMITIVE_SIZE); + STRICT_CONVERTIBLES.put(Short.TYPE, + asArray(Byte.TYPE)); + STRICT_CONVERTIBLES.put(Integer.TYPE, + asArray(Short.TYPE, Byte.TYPE)); + STRICT_CONVERTIBLES.put(Long.TYPE, + asArray(Integer.TYPE, Short.TYPE, Byte.TYPE)); + STRICT_CONVERTIBLES.put(Float.TYPE, + asArray(Long.TYPE, Integer.TYPE, Short.TYPE, Byte.TYPE)); + STRICT_CONVERTIBLES.put(Double.TYPE, + asArray(Float.TYPE, Long.TYPE, Integer.TYPE, Short.TYPE, Byte.TYPE)); + } + + /** + * Determines parameter-argument invocation compatibility. + * + * @param formal the formal parameter type + * @param actual the argument type + * @param strict whether the check is strict or not + * @param possibleVarArg whether or not we're dealing with the last parameter in the method declaration + * @return true if compatible, false otherwise + */ + private static boolean isInvocationConvertible( + final Class formal, Class actual, final boolean strict, final boolean possibleVarArg) { + /* if it's a null, it means the arg was null */ + if (actual == null && !formal.isPrimitive()) { + return true; + } + /* system asssignable, both sides must be array or not */ + if (actual != null && formal.isAssignableFrom(actual) && actual.isArray() == formal.isArray()) { + return true; + } + /* catch all... */ + if (!strict && formal == Object.class) { + return true; + } + /* Primitive conversion check. */ + if (formal.isPrimitive()) { + final Class[] clist = strict ? STRICT_CONVERTIBLES.get(formal) : CONVERTIBLES.get(formal); + if (clist != null) { + for (final Class aClass : clist) { + if (actual == aClass) { + return true; + } + } + } + return false; + } + /* Check for vararg conversion. */ + if (possibleVarArg && formal.isArray()) { + if (actual != null && actual.isArray()) { + actual = actual.getComponentType(); + } + return isInvocationConvertible(formal.getComponentType(), actual, strict, false); + } + return false; + } + + /** + * whether a method/ctor is more specific than a previously compared one. + */ + private static final int MORE_SPECIFIC = 0; + /** + * whether a method/ctor is less specific than a previously compared one. + */ + private static final int LESS_SPECIFIC = 1; + /** + * A method/ctor doesn't match a previously compared one. + */ + private static final int INCOMPARABLE = 2; + + /** + * Simple distinguishable exception, used when + * we run across ambiguous overloading. Caught + * by the introspector. + */ + public static class AmbiguousException extends RuntimeException { + /** Version Id for serializable. */ + private static final long serialVersionUID = -201801091655L; + /** Whether this exception should be considered severe. */ + private final boolean severe; + + /** + * A severe or not ambiguous exception. + * + * @param flag logging flag + */ + AmbiguousException(final boolean flag) { + this.severe = flag; + } + + /** + * Whether this exception is considered severe or benign. + *

Note that this is meant in the context of an ambiguous exception; benign cases can only be triggered + * by null arguments often related to runtime problems (not simply on overload signatures). + * + * @return true if severe, false if benign. + */ + public boolean isSevere() { + return severe; + } + } + + /** + * Utility for parameters matching. + * + * @param Method or Constructor + */ + private abstract static class Parameters { + /** + * Extract the parameter types from its applicable argument. + * + * @param app a method or constructor + * @return the parameters + */ + protected abstract Class[] getParameterTypes(T app); + + /** + * Whether a constructor or method handles varargs. + * + * @param app the constructor or method + * @return true if varargs, false otherwise + */ + protected abstract boolean isVarArgs(T app); + + // CSOFF: RedundantThrows + + /** + * Gets the most specific method that is applicable to actual argument types.

+ * Attempts to find the most specific applicable method using the + * algorithm described in the JLS section 15.12.2 (with the exception that it can't + * distinguish a primitive type argument from an object type argument, since in reflection + * primitive type arguments are represented by their object counterparts, so for an argument of + * type (say) java.lang.Integer, it will not be able to decide between a method that takes int and a + * method that takes java.lang.Integer as a parameter. + *

+ *

+ * This turns out to be a relatively rare case where this is needed - however, functionality + * like this is needed. + *

+ * + * @param key a method key, esp its parameters + * @param methods a list of methods + * @return the most specific method. + * @throws AmbiguousException if there is more than one. + */ + private T getMostSpecific(final MethodKey key, final T[] methods) { + final Class[] args = key.params; + final LinkedList applicables = getApplicables(methods, args); + if (applicables.isEmpty()) { + return null; + } + + if (applicables.size() == 1) { + return applicables.getFirst(); + } + + /* + * This list will contain the maximally specific methods. Hopefully at + * the end of the below loop, the list will contain exactly one method, + * (the most specific method) otherwise we have ambiguity. + */ + final LinkedList maximals = new LinkedList(); + for (final T app : applicables) { + final Class[] parms = getParameterTypes(app); + boolean lessSpecific = false; + final Iterator maximal = maximals.iterator(); + while (!lessSpecific && maximal.hasNext()) { + final T max = maximal.next(); + switch (moreSpecific(args, parms, getParameterTypes(max))) { + case MORE_SPECIFIC: + /* + * This method is more specific than the previously + * known maximally specific, so remove the old maximum. + */ + maximal.remove(); + break; + + case LESS_SPECIFIC: + /* + * This method is less specific than some of the + * currently known maximally specific methods, so we + * won't add it into the set of maximally specific + * methods + */ + + lessSpecific = true; + break; + default: + // nothing do do + } + } + + if (!lessSpecific) { + maximals.addLast(app); + } + } + // if we have more than one maximally specific method, this call is ambiguous... + if (maximals.size() > 1) { + throw ambiguousException(args, applicables); + } + return maximals.getFirst(); + } // CSON: RedundantThrows + + /** + * Creates an ambiguous exception. + *

+ * This method computes the severity of the ambiguity. The only non-severe case is when there is + * at least one null argument and at most one applicable method or constructor has a corresponding 'Object' + * parameter. + * We thus consider that ambiguity is benign in presence of null arguments but in the case where + * the corresponding parameter is of type Object in more than one applicable overloads. + *

+ * Rephrasing: + *

    + *
  • If all arguments are valid instances - no null argument -, ambiguity is severe.
  • + *
  • If there is at least one null argument, the ambiguity is severe if more than one method has a + * corresponding parameter of class 'Object'.
  • + *
+ * + * @param classes the argument args + * @param applicables the list of applicable methods or constructors + * @return an ambiguous exception + */ + private AmbiguousException ambiguousException(final Class[] classes, final List applicables) { + boolean severe = false; + int instanceArgCount = 0; // count the number of valid instances, aka not null + for (int c = 0; c < classes.length; ++c) { + final Class argClazz = classes[c]; + if (Void.class.equals(argClazz)) { + // count the number of methods for which the current arg maps to an Object parameter + int objectParmCount = 0; + for (final T app : applicables) { + final Class[] parmClasses = getParameterTypes(app); + final Class parmClass = parmClasses[c]; + if (Object.class.equals(parmClass) && (objectParmCount++ == 2)) { + severe = true; + break; + } + } + } else { + instanceArgCount += 1; + } + } + return new AmbiguousException(severe || instanceArgCount == classes.length); + } + + /** + * Determines which method signature (represented by a class array) is more + * specific. This defines a partial ordering on the method signatures. + * + * @param a the arguments signature + * @param c1 first method signature to compare + * @param c2 second method signature to compare + * @return MORE_SPECIFIC if c1 is more specific than c2, LESS_SPECIFIC if + * c1 is less specific than c2, INCOMPARABLE if they are incomparable. + */ + private int moreSpecific(final Class[] a, final Class[] c1, final Class[] c2) { + // compare lengths to handle comparisons where the size of the arrays + // doesn't match, but the methods are both applicable due to the fact + // that one is a varargs method + if (c1.length > a.length) { + return LESS_SPECIFIC; + } + if (c2.length > a.length) { + return MORE_SPECIFIC; + } + if (c1.length > c2.length) { + return MORE_SPECIFIC; + } + if (c2.length > c1.length) { + return LESS_SPECIFIC; + } + // same length, keep ultimate param offset for vararg checks + final int length = c1.length; + final int ultimate = c1.length - 1; + + // ok, move on and compare those of equal lengths + for (int i = 0; i < length; ++i) { + if (c1[i] != c2[i]) { + final boolean last = (i == ultimate); + // argument is null, prefer an Object param + if (a[i] == Void.class) { + if (c1[i] == Object.class && c2[i] != Object.class) { + return MORE_SPECIFIC; + } + if (c1[i] != Object.class && c2[i] == Object.class) { + return LESS_SPECIFIC; + } + } + // prefer primitive on non null arg, non primitive otherwise + boolean c1s = isPrimitive(c1[i], last); + boolean c2s = isPrimitive(c2[i], last); + if (c1s != c2s) { + return (c1s == (a[i] != Void.class)) ? MORE_SPECIFIC : LESS_SPECIFIC; + } + // if c2 can be converted to c1 but not the opposite, + // c1 is more specific than c2 + c1s = isStrictConvertible(c2[i], c1[i], last); + c2s = isStrictConvertible(c1[i], c2[i], last); + if (c1s != c2s) { + return c1s ? MORE_SPECIFIC : LESS_SPECIFIC; + } + } + } + // Incomparable due to non-related arguments (i.e.foo(Runnable) vs. foo(Serializable)) + return INCOMPARABLE; + } + + /** + * Checks whether a parameter class is a primitive. + * + * @param c the parameter class + * @param possibleVarArg true if this is the last parameter which can be a primitive array (vararg call) + * @return true if primitive, false otherwise + */ + private boolean isPrimitive(final Class c, final boolean possibleVarArg) { + if (c != null) { + if (c.isPrimitive()) { + return true; + } + if (possibleVarArg) { + final Class t = c.getComponentType(); + return t != null && t.isPrimitive(); + } + } + return false; + } + + /** + * Returns all methods that are applicable to actual argument types. + * + * @param methods list of all candidate methods + * @param classes the actual types of the arguments + * @return a list that contains only applicable methods (number of + * formal and actual arguments matches, and argument types are assignable + * to formal types through a method invocation conversion). + */ + private LinkedList getApplicables(final T[] methods, final Class[] classes) { + final LinkedList list = new LinkedList(); + for (final T method : methods) { + if (isApplicable(method, classes)) { + list.add(method); + } + } + return list; + } + + /** + * Returns true if the supplied method is applicable to actual + * argument types. + * + * @param method method that will be called + * @param actuals arguments signature for method + * @return true if method is applicable to arguments + */ + private boolean isApplicable(final T method, final Class[] actuals) { + final Class[] formals = getParameterTypes(method); + // if same number or args or + // there's just one more methodArg than class arg + // and the last methodArg is an array, then treat it as a vararg + if (formals.length == actuals.length) { + // this will properly match when the last methodArg + // is an array/varargs and the last class is the type of array + // (e.g. String when the method is expecting String...) + for (int i = 0; i < actuals.length; ++i) { + if (!isConvertible(formals[i], actuals[i], false)) { + // if we're on the last arg and the method expects an array + if (i == actuals.length - 1 && formals[i].isArray()) { + // check to see if the last arg is convertible + // to the array's component type + return isConvertible(formals[i], actuals[i], true); + } + return false; + } + } + return true; + } + + // number of formal and actual differ, method must be vararg + if (!isVarArgs(method)) { + return false; + } + + // less arguments than method parameters: vararg is null + if (formals.length > actuals.length) { + // only one parameter, the last (ie vararg) can be missing + if (formals.length - actuals.length > 1) { + return false; + } + // check that all present args match up to the method parms + for (int i = 0; i < actuals.length; ++i) { + if (!isConvertible(formals[i], actuals[i], false)) { + return false; + } + } + return true; + } + + // more arguments given than the method accepts; check for varargs + if (formals.length > 0 && actuals.length > 0) { + // check that they all match up to the last method arg + for (int i = 0; i < formals.length - 1; ++i) { + if (!isConvertible(formals[i], actuals[i], false)) { + return false; + } + } + // check that all remaining arguments are convertible to the vararg type + // (last parm is an array since method is vararg) + final Class vararg = formals[formals.length - 1].getComponentType(); + for (int i = formals.length - 1; i < actuals.length; ++i) { + if (!isConvertible(vararg, actuals[i], false)) { + return false; + } + } + return true; + } + // no match + return false; + } + + /** + * @param formal the formal parameter type to which the actual + * parameter type should be convertible + * @param actual the actual parameter type. + * @param possibleVarArg whether or not we're dealing with the last parameter + * in the method declaration + * @return see isMethodInvocationConvertible. + * @see #isInvocationConvertible(Class, Class, boolean) + */ + private boolean isConvertible(final Class formal, final Class actual, final boolean possibleVarArg) { + // if we see Void.class, the argument was null + return isInvocationConvertible(formal, actual.equals(Void.class) ? null : actual, possibleVarArg); + } + + /** + * @param formal the formal parameter type to which the actual + * parameter type should be convertible + * @param actual the actual parameter type. + * @param possibleVarArg whether or not we're dealing with the last parameter + * in the method declaration + * @return see isStrictMethodInvocationConvertible. + * @see #isStrictInvocationConvertible(Class, Class, boolean) + */ + private boolean isStrictConvertible(final Class formal, final Class actual, + final boolean possibleVarArg) { + // if we see Void.class, the argument was null + return isStrictInvocationConvertible(formal, actual.equals(Void.class) ? null : actual, possibleVarArg); + } + } + + /** + * The parameter matching service for methods. + */ + private static final Parameters METHODS = new Parameters() { + @Override + protected Class[] getParameterTypes(final Method app) { + return app.getParameterTypes(); + } + + @Override + public boolean isVarArgs(final Method app) { + return MethodKey.isVarArgs(app); + } + + }; + + /** + * The parameter matching service for constructors. + */ + private static final Parameters> CONSTRUCTORS = new Parameters>() { + @Override + protected Class[] getParameterTypes(final Constructor app) { + return app.getParameterTypes(); + } + + @Override + public boolean isVarArgs(final Constructor app) { + return app.isVarArgs(); + } + + }; +} diff --git a/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/internal/introspection/Permissions.java b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/internal/introspection/Permissions.java new file mode 100644 index 0000000..587d54f --- /dev/null +++ b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/internal/introspection/Permissions.java @@ -0,0 +1,224 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package aiyh.utils.tool.org.apache.commons.jexl3.internal.introspection; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; + +import aiyh.utils.tool.org.apache.commons.jexl3.annotations.NoJexl; + +/** + * Checks whether an element (ctor, field or method) is visible by JEXL introspection. + * Default implementation does this by checking if element has been annotated with NoJexl. + */ +public class Permissions { + /** Allow inheritance. */ + protected Permissions() { + } + /** + * The default singleton. + */ + public static final Permissions DEFAULT = new Permissions(); + + /** + * Checks whether a package explicitly disallows JEXL introspection. + * @param pack the package + * @return true if JEXL is allowed to introspect, false otherwise + */ + public boolean allow(final Package pack) { + if (pack == null || pack.getAnnotation(NoJexl.class) != null) { + return false; + } + return true; + } + + /** + * Checks whether a class or one of its super-classes or implemented interfaces + * explicitly disallows JEXL introspection. + * @param clazz the class to check + * @return true if JEXL is allowed to introspect, false otherwise + */ + public boolean allow(final Class clazz) { + return clazz != null && allow(clazz.getPackage()) && allow(clazz, true); + } + + /** + * Checks whether a constructor explicitly disallows JEXL introspection. + * @param ctor the constructor to check + * @return true if JEXL is allowed to introspect, false otherwise + */ + public boolean allow(final Constructor ctor) { + if (ctor == null) { + return false; + } + if (!Modifier.isPublic(ctor.getModifiers())) { + return false; + } + final Class clazz = ctor.getDeclaringClass(); + if (!allow(clazz, false)) { + return false; + } + // is ctor annotated with nojexl ? + final NoJexl nojexl = ctor.getAnnotation(NoJexl.class); + if (nojexl != null) { + return false; + } + return true; + } + + /** + * Checks whether a field explicitly disallows JEXL introspection. + * @param field the field to check + * @return true if JEXL is allowed to introspect, false otherwise + */ + public boolean allow(final Field field) { + if (field == null) { + return false; + } + if (!Modifier.isPublic(field.getModifiers())) { + return false; + } + final Class clazz = field.getDeclaringClass(); + if (!allow(clazz, false)) { + return false; + } + // is field annotated with nojexl ? + final NoJexl nojexl = field.getAnnotation(NoJexl.class); + if (nojexl != null) { + return false; + } + return true; + } + + /** + * Checks whether a method explicitly disallows JEXL introspection. + *

Since methods can be overridden, this also checks that no superclass or interface + * explicitly disallows this methods.

+ * @param method the method to check + * @return true if JEXL is allowed to introspect, false otherwise + */ + public boolean allow(final Method method) { + if (method == null) { + return false; + } + if (!Modifier.isPublic(method.getModifiers())) { + return false; + } + // is method annotated with nojexl ? + NoJexl nojexl = method.getAnnotation(NoJexl.class); + if (nojexl != null) { + return false; + } + // is the class annotated with nojexl ? + Class clazz = method.getDeclaringClass(); + nojexl = clazz.getAnnotation(NoJexl.class); + if (nojexl != null) { + return false; + } + // lets walk all interfaces + for (final Class inter : clazz.getInterfaces()) { + if (!allow(inter, method)) { + return false; + } + } + // lets walk all super classes + clazz = clazz.getSuperclass(); + // walk all superclasses + while (clazz != null) { + if (!allow(clazz, method)) { + return false; + } + clazz = clazz.getSuperclass(); + } + return true; + } + + /** + * Checks whether a class or one of its superclasses or implemented interfaces + * explicitly disallows JEXL introspection. + * @param clazz the class to check + * @param interf whether interfaces should be checked as well + * @return true if JEXL is allowed to introspect, false otherwise + */ + protected boolean allow(Class clazz, final boolean interf) { + if (clazz == null) { + return false; + } + if (!Modifier.isPublic(clazz.getModifiers())) { + return false; + } + // lets walk all interfaces + if (interf) { + for (final Class inter : clazz.getInterfaces()) { + // is clazz annotated with nojexl ? + final NoJexl nojexl = inter.getAnnotation(NoJexl.class); + if (nojexl != null) { + return false; + } + } + } + // lets walk all super classes + clazz = clazz.getSuperclass(); + // walk all superclasses + while (clazz != null) { + // is clazz annotated with nojexl ? + final NoJexl nojexl = clazz.getAnnotation(NoJexl.class); + if (nojexl != null) { + return false; + } + clazz = clazz.getSuperclass(); + } + return true; + } + + /** + * Check whether a method is allowed to be JEXL introspected in all its + * superclasses and interfaces. + * @param clazz the class + * @param method the method + * @return true if JEXL is allowed to introspect, false otherwise + */ + protected boolean allow(final Class clazz, final Method method) { + if (clazz != null) { + try { + // check if method in that class is different from the method argument + final Method wmethod = clazz.getMethod(method.getName(), method.getParameterTypes()); + if (wmethod != null) { + NoJexl nojexl = clazz.getAnnotation(NoJexl.class); + if (nojexl != null) { + return false; + } + // check if parent declaring class said nojexl (transitivity) + nojexl = wmethod.getAnnotation(NoJexl.class); + if (nojexl != null) { + return false; + } + } + } catch (final NoSuchMethodException ex) { + // unexpected, return no + return true; + } catch (final SecurityException ex) { + // unexpected, can't do much + return false; + } + } + return true; + } +} diff --git a/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/internal/introspection/PropertyGetExecutor.java b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/internal/introspection/PropertyGetExecutor.java new file mode 100644 index 0000000..73862a8 --- /dev/null +++ b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/internal/introspection/PropertyGetExecutor.java @@ -0,0 +1,117 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package aiyh.utils.tool.org.apache.commons.jexl3.internal.introspection; +import java.lang.reflect.InvocationTargetException; + +import aiyh.utils.tool.org.apache.commons.jexl3.JexlException; + +/** + * Specialized executor to get a property from an object. + * @since 2.0 + */ +public final class PropertyGetExecutor extends AbstractExecutor.Get { + /** A static signature for method(). */ + private static final Object[] EMPTY_PARAMS = {}; + /** The property. */ + private final String property; + + /** + * Discovers a PropertyGetExecutor. + *

The method to be found should be named "get{P,p}property.

+ * + * @param is the introspector + * @param clazz the class to find the get method from + * @param property the property name to find + * @return the executor if found, null otherwise + */ + public static PropertyGetExecutor discover(final Introspector is, final Class clazz, final String property) { + final java.lang.reflect.Method method = discoverGet(is, "get", clazz, property); + return method == null? null : new PropertyGetExecutor(clazz, method, property); + } + + /** + * Creates an instance. + * @param clazz he class the get method applies to + * @param method the method held by this executor + * @param identifier the property to get + */ + private PropertyGetExecutor(final Class clazz, final java.lang.reflect.Method method, final String identifier) { + super(clazz, method); + property = identifier; + } + + @Override + public Object getTargetProperty() { + return property; + } + + @Override + public Object invoke(final Object o) throws IllegalAccessException, InvocationTargetException { + return method == null ? null : method.invoke(o, (Object[]) null); + } + + @Override + public Object tryInvoke(final Object o, final Object identifier) { + if (o != null && method != null + && property.equals(castString(identifier)) + && objectClass.equals(o.getClass())) { + try { + return method.invoke(o, (Object[]) null); + } catch (IllegalAccessException | IllegalArgumentException xill) { + return TRY_FAILED;// fail + } catch (final InvocationTargetException xinvoke) { + throw JexlException.tryFailed(xinvoke); // throw + } + } + return TRY_FAILED; + } + + /** + * Base method for boolean and object property get. + * @param is the introspector + * @param which "is" or "get" for boolean or object + * @param clazz The class being examined. + * @param property The property being addressed. + * @return The {get,is}{p,P}roperty method if one exists, null otherwise. + */ + static java.lang.reflect.Method discoverGet(final Introspector is, + final String which, + final Class clazz, + final String property) { + if (property == null || property.isEmpty()) { + return null; + } + // this is gross and linear, but it keeps it straightforward. + java.lang.reflect.Method method; + final int start = which.length(); // "get" or "is" so 3 or 2 for char case switch + // start with get + final StringBuilder sb = new StringBuilder(which); + sb.append(property); + // uppercase nth char + final char c = sb.charAt(start); + sb.setCharAt(start, Character.toUpperCase(c)); + method = is.getMethod(clazz, sb.toString(), EMPTY_PARAMS); + //lowercase nth char + if (method == null) { + sb.setCharAt(start, Character.toLowerCase(c)); + method = is.getMethod(clazz, sb.toString(), EMPTY_PARAMS); + } + return method; + } +} + diff --git a/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/internal/introspection/PropertySetExecutor.java b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/internal/introspection/PropertySetExecutor.java new file mode 100644 index 0000000..df2d0f9 --- /dev/null +++ b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/internal/introspection/PropertySetExecutor.java @@ -0,0 +1,192 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package aiyh.utils.tool.org.apache.commons.jexl3.internal.introspection; + +import java.lang.reflect.Array; +import java.lang.reflect.InvocationTargetException; + +import aiyh.utils.tool.org.apache.commons.jexl3.JexlException; +import aiyh.utils.tool.org.apache.commons.jexl3.introspection.JexlPropertySet; + +/** + * Specialized executor to set a property in an object. + * @since 2.0 + */ +public class PropertySetExecutor extends AbstractExecutor.Set { + /** Index of the first character of the set{p,P}roperty. */ + private static final int SET_START_INDEX = 3; + /** The property. */ + protected final String property; + /** The property value class. */ + protected final Class valueClass; + + /** + * Discovers a PropertySetExecutor. + *

The method to be found should be named "set{P,p}property.

+ * + * @param is the introspector + * @param clazz the class to find the get method from + * @param property the property name to find + * @param value the value to assign to the property + * @return the executor if found, null otherwise + */ + public static PropertySetExecutor discover(final Introspector is, + final Class clazz, + final String property, + final Object value) { + if (property == null || property.isEmpty()) { + return null; + } + final java.lang.reflect.Method method = discoverSet(is, clazz, property, value); + return method != null? new PropertySetExecutor(clazz, method, property, value) : null; + } + + /** + * Creates an instance. + * @param clazz the class the set method applies to + * @param method the method called through this executor + * @param key the key to use as 1st argument to the set method + * @param value the value + */ + protected PropertySetExecutor(final Class clazz, + final java.lang.reflect.Method method, + final String key, + final Object value) { + super(clazz, method); + property = key; + valueClass = classOf(value); + } + + @Override + public Object getTargetProperty() { + return property; + } + + @Override + public Object invoke(final Object o, Object arg) throws IllegalAccessException, InvocationTargetException { + if (method != null) { + // handle the empty array case + if (isEmptyArray(arg)) { + // if array is empty but its component type is different from the method first parameter component type, + // replace argument with a new empty array instance (of the method first parameter component type) + final Class componentType = method.getParameterTypes()[0].getComponentType(); + if (componentType != null && !componentType.equals(arg.getClass().getComponentType())) { + arg = Array.newInstance(componentType, 0); + } + } + method.invoke(o, arg); + } + return arg; + } + + @Override + public Object tryInvoke(final Object o, final Object identifier, final Object value) { + if (o != null && method != null + // ensure method name matches the property name + && property.equals(castString(identifier)) + // object class should be same as executor's method declaring class + && objectClass.equals(o.getClass()) + // argument class should be eq + && valueClass.equals(classOf(value))) { + try { + return invoke(o, value); + } catch (IllegalAccessException | IllegalArgumentException xill) { + return TRY_FAILED;// fail + } catch (final InvocationTargetException xinvoke) { + throw JexlException.tryFailed(xinvoke); // throw + } + } + return TRY_FAILED; + } + + /** + * Checks whether an argument is an empty array. + * @param arg the argument + * @return true if arg is an empty array + */ + private static boolean isEmptyArray(final Object arg) { + return (arg != null && arg.getClass().isArray() && Array.getLength(arg) == 0); + } + + /** + * Discovers the method for a {@link JexlPropertySet}. + *

The method to be found should be named "set{P,p}property. + * As a special case, any empty array will try to find a valid array-setting non-ambiguous method. + * + * @param is the introspector + * @param clazz the class to find the get method from + * @param property the name of the property to set + * @param arg the value to assign to the property + * @return the method if found, null otherwise + */ + private static java.lang.reflect.Method discoverSet(final Introspector is, + final Class clazz, + final String property, + final Object arg) { + // first, we introspect for the set setter method + final Object[] params = {arg}; + final StringBuilder sb = new StringBuilder("set"); + sb.append(property); + // uppercase nth char + final char c = sb.charAt(SET_START_INDEX); + sb.setCharAt(SET_START_INDEX, Character.toUpperCase(c)); + java.lang.reflect.Method method = is.getMethod(clazz, sb.toString(), params); + // lowercase nth char + if (method == null) { + sb.setCharAt(SET_START_INDEX, Character.toLowerCase(c)); + method = is.getMethod(clazz, sb.toString(), params); + // uppercase nth char, try array + if (method == null && isEmptyArray(arg)) { + sb.setCharAt(SET_START_INDEX, Character.toUpperCase(c)); + method = lookupSetEmptyArray(is, clazz, sb.toString()); + // lowercase nth char + if (method == null) { + sb.setCharAt(SET_START_INDEX, Character.toLowerCase(c)); + method = lookupSetEmptyArray(is, clazz, sb.toString()); + } + } + } + return method; + } + + /** + * Finds an empty array property setter method by methodName. + *

This checks only one method with that name accepts an array as sole parameter. + * @param is the introspector + * @param clazz the class to find the get method from + * @param mname the method name to find + * @return the sole method that accepts an array as parameter + */ + private static java.lang.reflect.Method lookupSetEmptyArray(final Introspector is, final Class clazz, final String mname) { + java.lang.reflect.Method candidate = null; + final java.lang.reflect.Method[] methods = is.getMethods(clazz, mname); + if (methods != null) { + for (final java.lang.reflect.Method method : methods) { + final Class[] paramTypes = method.getParameterTypes(); + if (paramTypes.length == 1 && paramTypes[0].isArray()) { + if (candidate != null) { + // because the setter method is overloaded for different parameter type, + // return null here to report the ambiguity. + return null; + } + candidate = method; + } + } + } + return candidate; + } +} diff --git a/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/internal/introspection/SandboxUberspect.java b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/internal/introspection/SandboxUberspect.java new file mode 100644 index 0000000..1a13f24 --- /dev/null +++ b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/internal/introspection/SandboxUberspect.java @@ -0,0 +1,167 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package aiyh.utils.tool.org.apache.commons.jexl3.internal.introspection; + +import aiyh.utils.tool.org.apache.commons.jexl3.JexlArithmetic; +import aiyh.utils.tool.org.apache.commons.jexl3.JexlOperator; +import aiyh.utils.tool.org.apache.commons.jexl3.introspection.JexlMethod; +import aiyh.utils.tool.org.apache.commons.jexl3.introspection.JexlPropertyGet; +import aiyh.utils.tool.org.apache.commons.jexl3.introspection.JexlPropertySet; +import aiyh.utils.tool.org.apache.commons.jexl3.introspection.JexlSandbox; +import aiyh.utils.tool.org.apache.commons.jexl3.introspection.JexlUberspect; + +import java.util.Iterator; +import java.util.List; + +/** + * An uberspect that controls usage of properties, methods and constructors through a sandbox. + * @since 3.0 + */ +public final class SandboxUberspect implements JexlUberspect { + /** The base uberspect. */ + private final JexlUberspect uberspect; + /** The sandbox. */ + private final JexlSandbox sandbox; + + /** + * A constructor for JexlSandbox uberspect. + * @param theUberspect the JexlUberspect to sandbox + * @param theSandbox the sandbox which is copied to avoid changes at runtime + */ + public SandboxUberspect(final JexlUberspect theUberspect, final JexlSandbox theSandbox) { + if (theSandbox == null) { + throw new NullPointerException("sandbox can not be null"); + } + if (theUberspect == null) { + throw new NullPointerException("uberspect can not be null"); + } + this.uberspect = theUberspect; + this.sandbox = theSandbox.copy(); + } + + @Override + public void setClassLoader(final ClassLoader loader) { + uberspect.setClassLoader(loader); + } + + @Override + public ClassLoader getClassLoader() { + return uberspect.getClassLoader(); + } + + @Override + public int getVersion() { + return uberspect.getVersion(); + } + + @Override + public JexlMethod getConstructor(final Object ctorHandle, final Object... args) { + final String className; + if (ctorHandle instanceof Class) { + className = sandbox.execute((Class) ctorHandle, ""); + } else if (ctorHandle != null) { + className = sandbox.execute(ctorHandle.toString(), ""); + } else { + className = null; + } + return className != null && className != JexlSandbox.NULL ? uberspect.getConstructor(className, args) : null; + } + + @Override + public JexlMethod getMethod(final Object obj, final String method, final Object... args) { + if (obj != null && method != null) { + final Class clazz = (obj instanceof Class) ? (Class) obj : obj.getClass(); + final String actual = sandbox.execute(clazz, method); + if (actual != null && actual != JexlSandbox.NULL) { + return uberspect.getMethod(obj, actual, args); + } + } + return null; + } + + @Override + public List getResolvers(final JexlOperator op, final Object obj) { + return uberspect.getResolvers(op, obj); + } + + @Override + public JexlPropertyGet getPropertyGet(final Object obj, final Object identifier) { + return getPropertyGet(null, obj, identifier); + } + + @Override + public JexlPropertyGet getPropertyGet(final List resolvers, + final Object obj, + final Object identifier) { + if (obj != null) { + if (identifier != null) { + final String property = identifier.toString(); + final String actual = sandbox.read(obj.getClass(), property); + if (actual != null) { + // no transformation, strict equality: use identifier before string conversion + final Object pty = actual == property ? identifier : actual; + return uberspect.getPropertyGet(resolvers, obj, pty); + } + } else { + final String actual = sandbox.read(obj.getClass(), null); + if (actual != JexlSandbox.NULL) { + return uberspect.getPropertyGet(resolvers, obj, null); + } + } + } + return null; + } + + @Override + public JexlPropertySet getPropertySet(final Object obj, final Object identifier, final Object arg) { + return getPropertySet(null, obj, identifier, arg); + } + + @Override + public JexlPropertySet getPropertySet(final List resolvers, + final Object obj, + final Object identifier, + final Object arg) { + if (obj != null) { + if (identifier != null) { + final String property = identifier.toString(); + final String actual = sandbox.write(obj.getClass(), property); + if (actual != null) { + // no transformation, strict equality: use identifier before string conversion + final Object pty = actual == property ? identifier : actual; + return uberspect.getPropertySet(resolvers, obj, pty, arg); + } + } else { + final String actual = sandbox.write(obj.getClass(), null); + if (actual != JexlSandbox.NULL) { + return uberspect.getPropertySet(resolvers, obj, null, arg); + } + } + } + return null; + } + + @Override + public Iterator getIterator(final Object obj) { + return uberspect.getIterator(obj); + } + + @Override + public JexlArithmetic.Uberspect getArithmetic(final JexlArithmetic arithmetic) { + return uberspect.getArithmetic(arithmetic); + } +} diff --git a/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/internal/introspection/Uberspect.java b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/internal/introspection/Uberspect.java new file mode 100644 index 0000000..f904ef1 --- /dev/null +++ b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/internal/introspection/Uberspect.java @@ -0,0 +1,479 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package aiyh.utils.tool.org.apache.commons.jexl3.internal.introspection; + +import aiyh.utils.tool.org.apache.commons.jexl3.JexlArithmetic; +import aiyh.utils.tool.org.apache.commons.jexl3.JexlEngine; +import aiyh.utils.tool.org.apache.commons.jexl3.JexlOperator; +import aiyh.utils.tool.org.apache.commons.jexl3.introspection.JexlMethod; +import aiyh.utils.tool.org.apache.commons.jexl3.introspection.JexlPropertyGet; +import aiyh.utils.tool.org.apache.commons.jexl3.introspection.JexlPropertySet; +import aiyh.utils.tool.org.apache.commons.jexl3.introspection.JexlUberspect; +import org.apache.commons.logging.Log; + +import java.lang.ref.Reference; +import java.lang.ref.SoftReference; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicInteger; + +/** + * Implementation of Uberspect to provide the default introspective + * functionality of JEXL. + *

+ * This is the class to derive to customize introspection.

+ * + * @since 1.0 + */ +public class Uberspect implements JexlUberspect { + /** Publicly exposed special failure object returned by tryInvoke. */ + public static final Object TRY_FAILED = JexlEngine.TRY_FAILED; + /** The logger to use for all warnings and errors. */ + protected final Log logger; + /** The resolver strategy. */ + private final ResolverStrategy strategy; + /** The permissions. */ + private final Permissions permissions; + /** The introspector version. */ + private final AtomicInteger version; + /** The soft reference to the introspector currently in use. */ + private volatile Reference ref; + /** The class loader reference; used to recreate the introspector when necessary. */ + private volatile Reference loader; + /** + * The map from arithmetic classes to overloaded operator sets. + *

+ * This keeps track of which operator methods are overloaded per JexlArithemtic classes + * allowing a fail fast test during interpretation by avoiding seeking a method when there is none. + */ + private final Map, Set> operatorMap; + + /** + * Creates a new Uberspect. + * + * @param runtimeLogger the logger used for all logging needs + * @param sty the resolver strategy + */ + public Uberspect(final Log runtimeLogger, final ResolverStrategy sty) { + this(runtimeLogger, sty, null); + } + + /** + * Creates a new Uberspect. + * + * @param runtimeLogger the logger used for all logging needs + * @param sty the resolver strategy + * @param perms the introspector permissions + */ + public Uberspect(final Log runtimeLogger, final ResolverStrategy sty, final Permissions perms) { + logger = runtimeLogger; + strategy = sty == null ? JexlUberspect.JEXL_STRATEGY : sty; + permissions = perms; + ref = new SoftReference(null); + loader = new SoftReference(getClass().getClassLoader()); + operatorMap = new ConcurrentHashMap, Set>(); + version = new AtomicInteger(0); + } + + /** + * Gets the current introspector base. + *

+ * If the reference has been collected, this method will recreate the underlying introspector.

+ * + * @return the introspector + */ + // CSOFF: DoubleCheckedLocking + protected final Introspector base() { + Introspector intro = ref.get(); + if (intro == null) { + // double checked locking is ok (fixed by Java 5 memory model). + synchronized (this) { + intro = ref.get(); + if (intro == null) { + intro = new Introspector(logger, loader.get(), permissions); + ref = new SoftReference(intro); + loader = new SoftReference(intro.getLoader()); + version.incrementAndGet(); + } + } + } + return intro; + } + // CSON: DoubleCheckedLocking + + @Override + public void setClassLoader(final ClassLoader nloader) { + synchronized (this) { + Introspector intro = ref.get(); + if (intro != null) { + intro.setLoader(nloader); + } else { + intro = new Introspector(logger, nloader, permissions); + ref = new SoftReference(intro); + } + loader = new SoftReference(intro.getLoader()); + operatorMap.clear(); + version.incrementAndGet(); + } + } + + @Override + public ClassLoader getClassLoader() { + return loader.get(); + } + + @Override + public int getVersion() { + return version.intValue(); + } + + /** + * Gets a class by name through this introspector class loader. + * + * @param className the class name + * @return the class instance or null if it could not be found + */ + public final Class getClassByName(final String className) { + return base().getClassByName(className); + } + + /** + * Gets the field named by + * key for the class + * c. + * + * @param c Class in which the field search is taking place + * @param key Name of the field being searched for + * @return a {@link Field} or null if it does not exist or is not accessible + */ + public final Field getField(final Class c, final String key) { + return base().getField(c, key); + } + + /** + * Gets the accessible field names known for a given class. + * + * @param c the class + * @return the class field names + */ + public final String[] getFieldNames(final Class c) { + return base().getFieldNames(c); + } + + /** + * Gets the method defined by + * name and + * params for the Class + * c. + * + * @param c Class in which the method search is taking place + * @param name Name of the method being searched for + * @param params An array of Objects (not Classes) that describe the + * the parameters + * @return a {@link Method} + * or null if no unambiguous method could be found through introspection. + */ + public final Method getMethod(final Class c, final String name, final Object[] params) { + return base().getMethod(c, new MethodKey(name, params)); + } + + /** + * Gets the method defined by + * key and for the Class + * c. + * + * @param c Class in which the method search is taking place + * @param key MethodKey of the method being searched for + * @return a {@link Method} + * or null if no unambiguous method could be found through introspection. + */ + public final Method getMethod(final Class c, final MethodKey key) { + return base().getMethod(c, key); + } + + /** + * Gets the accessible methods names known for a given class. + * + * @param c the class + * @return the class method names + */ + public final String[] getMethodNames(final Class c) { + return base().getMethodNames(c); + } + + /** + * Gets all the methods with a given name from this map. + * + * @param c the class + * @param methodName the seeked methods name + * @return the array of methods + */ + public final Method[] getMethods(final Class c, final String methodName) { + return base().getMethods(c, methodName); + } + + @Override + public JexlMethod getMethod(final Object obj, final String method, final Object... args) { + return MethodExecutor.discover(base(), obj, method, args); + } + + @Override + public List getResolvers(final JexlOperator op, final Object obj) { + return strategy.apply(op, obj); + } + + @Override + public JexlPropertyGet getPropertyGet(final Object obj, final Object identifier) { + return getPropertyGet(null, obj, identifier); + } + + @Override + public JexlPropertyGet getPropertyGet( + final List resolvers, final Object obj, final Object identifier + ) { + final Class claz = obj.getClass(); + final String property = AbstractExecutor.castString(identifier); + final Introspector is = base(); + final List r = resolvers == null ? strategy.apply(null, obj) : resolvers; + JexlPropertyGet executor = null; + for (final PropertyResolver resolver : r) { + if (resolver instanceof JexlResolver) { + switch ((JexlResolver) resolver) { + case PROPERTY: + // first try for a getFoo() type of property (also getfoo() ) + executor = PropertyGetExecutor.discover(is, claz, property); + if (executor == null) { + executor = BooleanGetExecutor.discover(is, claz, property); + } + break; + case MAP: + // let's see if we are a map... + executor = MapGetExecutor.discover(is, claz, identifier); + break; + case LIST: + // let's see if this is a list or array + final Integer index = AbstractExecutor.castInteger(identifier); + if (index != null) { + executor = ListGetExecutor.discover(is, claz, index); + } + break; + case DUCK: + // if that didn't work, look for get(foo) + executor = DuckGetExecutor.discover(is, claz, identifier); + if (executor == null && property != null && property != identifier) { + // look for get("foo") if we did not try yet (just above) + executor = DuckGetExecutor.discover(is, claz, property); + } + break; + case FIELD: + // a field may be? (can not be a number) + executor = FieldGetExecutor.discover(is, claz, property); + // static class fields (enums included) + if (obj instanceof Class) { + executor = FieldGetExecutor.discover(is, (Class) obj, property); + } + break; + case CONTAINER: + // or an indexed property? + executor = IndexedType.discover(is, obj, property); + break; + default: + continue; // in case we add new ones in enum + } + } else { + executor = resolver.getPropertyGet(this, obj, identifier); + } + if (executor != null) { + return executor; + } + } + return null; + } + + @Override + public JexlPropertySet getPropertySet(final Object obj, final Object identifier, final Object arg) { + return getPropertySet(null, obj, identifier, arg); + } + + @Override + public JexlPropertySet getPropertySet( + final List resolvers, final Object obj, final Object identifier, final Object arg + ) { + final Class claz = obj.getClass(); + final String property = AbstractExecutor.castString(identifier); + final Introspector is = base(); + final List actual = resolvers == null ? strategy.apply(null, obj) : resolvers; + JexlPropertySet executor = null; + for (final PropertyResolver resolver : actual) { + if (resolver instanceof JexlResolver) { + switch ((JexlResolver) resolver) { + case PROPERTY: + // first try for a setFoo() type of property (also setfoo() ) + executor = PropertySetExecutor.discover(is, claz, property, arg); + break; + case MAP: + // let's see if we are a map... + executor = MapSetExecutor.discover(is, claz, identifier, arg); + break; + case LIST: + // let's see if we can convert the identifier to an int, + // if obj is an array or a list, we can still do something + final Integer index = AbstractExecutor.castInteger(identifier); + if (index != null) { + executor = ListSetExecutor.discover(is, claz, identifier, arg); + } + break; + case DUCK: + // if that didn't work, look for set(foo) + executor = DuckSetExecutor.discover(is, claz, identifier, arg); + if (executor == null && property != null && property != identifier) { + executor = DuckSetExecutor.discover(is, claz, property, arg); + } + break; + case FIELD: + // a field may be? + executor = FieldSetExecutor.discover(is, claz, property, arg); + break; + case CONTAINER: + default: + continue; // in case we add new ones in enum + } + } else { + executor = resolver.getPropertySet(this, obj, identifier, arg); + } + if (executor != null) { + return executor; + } + } + return null; + } + + @Override + @SuppressWarnings("unchecked") + public Iterator getIterator(final Object obj) { + if (obj instanceof Iterator) { + return ((Iterator) obj); + } + if (obj.getClass().isArray()) { + return new ArrayIterator(obj); + } + if (obj instanceof Map) { + return ((Map) obj).values().iterator(); + } + if (obj instanceof Enumeration) { + return new EnumerationIterator((Enumeration) obj); + } + if (obj instanceof Iterable) { + return ((Iterable) obj).iterator(); + } + try { + // look for an iterator() method to support the JDK5 Iterable + // interface or any user tools/DTOs that want to work in + // foreach without implementing the Collection interface + final JexlMethod it = getMethod(obj, "iterator", (Object[]) null); + if (it != null && Iterator.class.isAssignableFrom(it.getReturnType())) { + return (Iterator) it.invoke(obj, (Object[]) null); + } + } catch (final Exception xany) { + if (logger != null && logger.isDebugEnabled()) { + logger.info("unable to solve iterator()", xany); + } + } + return null; + } + + @Override + public JexlMethod getConstructor(final Object ctorHandle, final Object... args) { + return ConstructorMethod.discover(base(), ctorHandle, args); + } + + /** + * The concrete uberspect Arithmetic class. + */ + protected class ArithmeticUberspect implements JexlArithmetic.Uberspect { + /** The arithmetic instance being analyzed. */ + private final JexlArithmetic arithmetic; + /** The set of overloaded operators. */ + private final Set overloads; + + /** + * Creates an instance. + * + * @param theArithmetic the arithmetic instance + * @param theOverloads the overloaded operators + */ + private ArithmeticUberspect(final JexlArithmetic theArithmetic, final Set theOverloads) { + this.arithmetic = theArithmetic; + this.overloads = theOverloads; + } + + @Override + public JexlMethod getOperator(final JexlOperator operator, final Object... args) { + return overloads.contains(operator) && args != null + ? getMethod(arithmetic, operator.getMethodName(), args) + : null; + } + + @Override + public boolean overloads(final JexlOperator operator) { + return overloads.contains(operator); + } + } + + @Override + public JexlArithmetic.Uberspect getArithmetic(final JexlArithmetic arithmetic) { + JexlArithmetic.Uberspect jau = null; + if (arithmetic != null) { + final Class aclass = arithmetic.getClass(); + Set ops = operatorMap.get(aclass); + if (ops == null) { + ops = EnumSet.noneOf(JexlOperator.class); + // deal only with derived classes + if (!JexlArithmetic.class.equals(aclass)) { + for (final JexlOperator op : JexlOperator.values()) { + final Method[] methods = getMethods(arithmetic.getClass(), op.getMethodName()); + if (methods != null) { + for (final Method method : methods) { + final Class[] parms = method.getParameterTypes(); + if (parms.length != op.getArity()) { + continue; + } + // filter method that is an actual overload: + // - not inherited (not declared by base class) + // - nor overridden (not present in base class) + if (!JexlArithmetic.class.equals(method.getDeclaringClass())) { + try { + JexlArithmetic.class.getMethod(method.getName(), method.getParameterTypes()); + } catch (final NoSuchMethodException xmethod) { + // method was not found in JexlArithmetic; this is an operator definition + ops.add(op); + } + } + } + } + } + } + // register this arithmetic class in the operator map + operatorMap.put(aclass, ops); + } + jau = new ArithmeticUberspect(arithmetic, ops); + } + return jau; + } +} diff --git a/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/internal/introspection/package.html b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/internal/introspection/package.html new file mode 100644 index 0000000..710ccf8 --- /dev/null +++ b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/internal/introspection/package.html @@ -0,0 +1,39 @@ + + + + Package Documentation for org.apache.commons.jexl3.introspection Package + + + Provides low-level introspective services. +

+ This internal package is not intended for public usage and there is no + guarantee that its public classes or methods will remain as is in subsequent + versions. +

+

+ The IntrospectorBase, ClassMap, MethodKey, MethodMap form the + base of the introspection service. They allow to describe classes and their + methods, keeping them in a cache (@see IntrospectorBase) to speed up property + getters/setters and method discovery used during expression evaluation. +

+

+ The cache materialized in Introspector creates one entry per class containing a map of all + accessible public methods keyed by name and signature. +

+ + diff --git a/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/internal/package.html b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/internal/package.html new file mode 100644 index 0000000..20d4916 --- /dev/null +++ b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/internal/package.html @@ -0,0 +1,37 @@ + + + + Package Documentation for org.apache.commons.jexl3 Package + + +

Provides utilities for introspection services.

+

+ This internal package is not intended for public usage and there is no + guarantee that its public classes or methods will remain as is in subsequent + versions. +

+

+ This set of classes implement the various forms of setters and getters + used by JEXL. These are specialized forms for 'pure' properties, discovering + methods of the {s,g}etProperty form, for Maps, Lists and Ducks - + attempting to discover a 'get' or 'set' method, making an object walk and + quack. +

+ + + diff --git a/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/introspection/JexlMethod.java b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/introspection/JexlMethod.java new file mode 100644 index 0000000..7c5582d --- /dev/null +++ b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/introspection/JexlMethod.java @@ -0,0 +1,80 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package aiyh.utils.tool.org.apache.commons.jexl3.introspection; +import aiyh.utils.tool.org.apache.commons.jexl3.JexlException; + +/** + * Interface used for regular method invocation. + * Ex. + * + * ${foo.bar()} + * + * + * @since 1.0 + */ +public interface JexlMethod { + /** + * Invocation method, called when the method invocation should be performed + * and a value returned. + + * @param obj the object + * @param params method parameters. + * @return the result + * @throws Exception on any error. + */ + Object invoke(Object obj, Object... params) throws Exception; + + /** + * Attempts to reuse this JexlMethod, checking that it is compatible with + * the actual set of arguments. + * Related to isCacheable since this method is often used with cached JexlMethod instances. + * + * @param name the method name + * @param obj the object to invoke the method upon + * @param params the method arguments + * @return the result of the method invocation that should be checked by tryFailed to determine if it succeeded + * or failed. + * @throws JexlException.TryFailed if the underlying method was invoked but threw an exception + * ({@link java.lang.reflect.InvocationTargetException}) + */ + Object tryInvoke(String name, Object obj, Object... params) throws JexlException.TryFailed; + + /** + * Checks whether a tryInvoke return value indicates a failure or not. + *

Usage is : Object r = tryInvoke(...); if (tryFailed(r) {...} else {...} + * + * @param rval the value returned by tryInvoke + * @return true if tryInvoke failed, false otherwise + */ + boolean tryFailed(Object rval); + + /** + * Specifies if this JexlMethod is cacheable and able to be reused for this + * class of object it was returned for. + * + * @return true if can be reused for this class, false if not + */ + boolean isCacheable(); + + /** + * returns the return type of the method invoked. + * + * @return return type + */ + Class getReturnType(); +} \ No newline at end of file diff --git a/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/introspection/JexlPropertyGet.java b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/introspection/JexlPropertyGet.java new file mode 100644 index 0000000..f77625a --- /dev/null +++ b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/introspection/JexlPropertyGet.java @@ -0,0 +1,69 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package aiyh.utils.tool.org.apache.commons.jexl3.introspection; + +import aiyh.utils.tool.org.apache.commons.jexl3.JexlException; + +/** + * Interface for getting values that appear to be properties. + * Ex. + * + * ${foo.bar} + * + * + * @since 1.0 + */ +public interface JexlPropertyGet { + /** + * Method used to get the property value of an object. + * + * @param obj the object to get the property value from. + * @return the property value. + * @throws Exception on any error. + */ + Object invoke(Object obj) throws Exception; + + /** + * Attempts to reuse this JexlPropertyGet, checking that it is compatible with + * the actual set of arguments. + * + * @param obj the object to invoke the property get upon + * @param key the property key to get + * @return the result of the method invocation that should be checked by tryFailed to determine if it succeeded + * or failed. + * @throws JexlException.TryFailed if the underlying method was invoked but threw an exception + * ({@link java.lang.reflect.InvocationTargetException}) + */ + Object tryInvoke(Object obj, Object key) throws JexlException.TryFailed; + + /** + * Checks whether a tryInvoke failed or not. + * + * @param rval the value returned by tryInvoke + * @return true if tryInvoke failed, false otherwise + */ + boolean tryFailed(Object rval); + + /** + * Specifies if this JexlPropertyGet is cacheable and able to be reused for + * this class of object it was returned for. + * + * @return true if can be reused for this class, false if not + */ + boolean isCacheable(); +} diff --git a/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/introspection/JexlPropertySet.java b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/introspection/JexlPropertySet.java new file mode 100644 index 0000000..4980c35 --- /dev/null +++ b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/introspection/JexlPropertySet.java @@ -0,0 +1,71 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package aiyh.utils.tool.org.apache.commons.jexl3.introspection; + +import aiyh.utils.tool.org.apache.commons.jexl3.JexlException; + +/** + * Interface used for setting values that appear to be properties. + * Ex. + * + * ${foo.bar = "hello"} + * + * + * @since 1.0 + */ +public interface JexlPropertySet { + /** + * Method used to set the property value of an object. + * + * @param obj Object on which the property setter will be called with the value + * @param arg value to be set + * @return the value returned from the set operation (impl specific) + * @throws Exception on any error. + */ + Object invoke(Object obj, Object arg) throws Exception; + + /** + * Attempts to reuse this JexlPropertySet, checking that it is compatible with + * the actual set of arguments. + * + * @param obj the object to invoke the get upon + * @param key the property key to get + * @param value the property value to set + * @return the result of the method invocation that should be checked by tryFailed to determine if it succeeded + * or failed. + * @throws JexlException.TryFailed if the underlying method was invoked but threw an exception + * ({@link java.lang.reflect.InvocationTargetException}) + */ + Object tryInvoke(Object obj, Object key, Object value) throws JexlException.TryFailed; + + /** + * Checks whether a tryInvoke failed or not. + * + * @param rval the value returned by tryInvoke + * @return true if tryInvoke failed, false otherwise + */ + boolean tryFailed(Object rval); + + /** + * Specifies if this JexlPropertySet is cacheable and able to be reused for + * this class of object it was returned for. + * + * @return true if can be reused for this class, false if not + */ + boolean isCacheable(); +} diff --git a/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/introspection/JexlSandbox.java b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/introspection/JexlSandbox.java new file mode 100644 index 0000000..ac0fe81 --- /dev/null +++ b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/introspection/JexlSandbox.java @@ -0,0 +1,681 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package aiyh.utils.tool.org.apache.commons.jexl3.introspection; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; + +/** + * A sandbox describes permissions on a class by explicitly allowing or forbidding + * access to methods and properties through "allowlists" and "blocklists". + * + *

A allowlist explicitly allows methods/properties for a class;

+ * + *
    + *
  • If a allowlist is empty and thus does not contain any names, + * all properties/methods are allowed for its class.
  • + *
  • If it is not empty, the only allowed properties/methods are the ones contained.
  • + *
+ * + *

A blocklist explicitly forbids methods/properties for a class;

+ * + *
    + *
  • If a blocklist is empty and thus does not contain any names, + * all properties/methods are forbidden for its class.
  • + *
  • If it is not empty, the only forbidden properties/methods are the ones contained.
  • + *
+ * + *

Permissions are composed of three lists, read, write, execute, each being + * "allow" or "block":

+ * + *
    + *
  • read controls readable properties
  • + *
  • write controls writable properties
  • + *
  • execute controls executable methods and constructor
  • + *
+ * + *

When specified, permissions - allow or block lists - can be created inheritable + * on interfaces or classes and thus applicable to their implementations or derived + * classes; the sandbox must be created with the 'inheritable' flag for this behavior + * to be triggered. Note that even in this configuration, it is still possible to + * add non-inheritable permissions. + * Adding inheritable lists to a non inheritable sandbox has no added effect; + * permissions only apply to their specified class.

+ * + *

Note that a JexlUberspect always uses a copy of the JexlSandbox + * used to built it preventing permission changes after its instantiation.

+ * + * @since 3.0 + */ +public final class JexlSandbox { + /** + * The marker string for explicitly disallowed null properties. + */ + public static final String NULL = "?"; + /** + * The map from class names to permissions. + */ + private final Map sandbox; + /** + * Whether permissions can be inherited (through implementation or extension). + */ + private final boolean inherit; + /** + * Default behavior, block or allow. + */ + private final boolean allow; + + /** + * Creates a new default sandbox. + *

In the absence of explicit permissions on a class, the + * sandbox is a allow-box, allow-listing that class for all permissions (read, write and execute). + */ + public JexlSandbox() { + this(true, false, null); + } + + /** + * Creates a new default sandbox. + *

A allow-box considers no permissions as "everything is allowed" when + * a block-box considers no permissions as "nothing is allowed". + * @param ab whether this sandbox is allow (true) or block (false) + * if no permission is explicitly defined for a class. + * @since 3.1 + */ + public JexlSandbox(final boolean ab) { + this(ab, false, null); + } + + /** + * Creates a sandbox. + * @param ab whether this sandbox is allow (true) or block (false) + * @param inh whether permissions on interfaces and classes are inherited (true) or not (false) + * @since 3.2 + */ + public JexlSandbox(final boolean ab, final boolean inh) { + this(ab, inh, null); + } + + /** + * Creates a sandbox based on an existing permissions map. + * @param map the permissions map + */ + @Deprecated + protected JexlSandbox(final Map map) { + this(true, false, map); + } + + /** + * Creates a sandbox based on an existing permissions map. + * @param ab whether this sandbox is allow (true) or block (false) + * @param map the permissions map + * @since 3.1 + */ + @Deprecated + protected JexlSandbox(final boolean ab, final Map map) { + this(ab, false, map); + } + + /** + * Creates a sandbox based on an existing permissions map. + * @param ab whether this sandbox is allow (true) or block (false) + * @param inh whether permissions are inherited, default false + * @param map the permissions map + * @since 3.2 + */ + protected JexlSandbox(final boolean ab, final boolean inh, final Map map) { + allow = ab; + inherit = inh; + sandbox = map != null? map : new HashMap<>(); + } + + /** + * @return a copy of this sandbox + */ + public JexlSandbox copy() { + // modified concurrently at runtime so... + final Map map = new ConcurrentHashMap<>(); + for (final Map.Entry entry : sandbox.entrySet()) { + map.put(entry.getKey(), entry.getValue().copy()); + } + return new JexlSandbox(allow, inherit, map); + } + + /** + * Gets a class by name, crude mechanism for backwards (<3.2 ) compatibility. + * @param cname the class name + * @return the class + */ + static Class forName(final String cname) { + try { + return Class.forName(cname); + } catch(final Exception xany) { + return null; + } + } + + /** + * Gets the read permission value for a given property of a class. + * + * @param clazz the class + * @param name the property name + * @return null (or NULL if name is null) if not allowed, the name of the property to use otherwise + */ + public String read(final Class clazz, final String name) { + return get(clazz).read().get(name); + } + + /** + * Gets the read permission value for a given property of a class. + * + * @param clazz the class name + * @param name the property name + * @return null if not allowed, the name of the property to use otherwise + */ + @Deprecated + public String read(final String clazz, final String name) { + return get(clazz).read().get(name); + } + + /** + * Gets the write permission value for a given property of a class. + * + * @param clazz the class + * @param name the property name + * @return null (or NULL if name is null) if not allowed, the name of the property to use otherwise + */ + public String write(final Class clazz, final String name) { + return get(clazz).write().get(name); + } + + /** + * Gets the write permission value for a given property of a class. + * + * @param clazz the class name + * @param name the property name + * @return null if not allowed, the name of the property to use otherwise + */ + @Deprecated + public String write(final String clazz, final String name) { + return get(clazz).write().get(name); + } + + /** + * Gets the execute permission value for a given method of a class. + * + * @param clazz the class + * @param name the method name + * @return null if not allowed, the name of the method to use otherwise + */ + public String execute(final Class clazz, final String name) { + final String m = get(clazz).execute().get(name); + return "".equals(name) && m != null? clazz.getName() : m; + } + + /** + * Gets the execute permission value for a given method of a class. + * + * @param clazz the class name + * @param name the method name + * @return null if not allowed, the name of the method to use otherwise + */ + @Deprecated + public String execute(final String clazz, final String name) { + final String m = get(clazz).execute().get(name); + return "".equals(name) && m != null? clazz : m; + } + + /** + * A base set of names. + */ + public abstract static class Names { + + /** + * Adds a name to this set. + * + * @param name the name to add + * @return true if the name was really added, false if not + */ + public abstract boolean add(String name); + + /** + * Adds an alias to a name to this set. + *

This only has an effect on allow lists.

+ * + * @param name the name to alias + * @param alias the alias + * @return true if the alias was added, false if it was already present + */ + public boolean alias(final String name, final String alias) { + return false; + } + + /** + * Whether a given name is allowed or not. + * + * @param name the method/property name to check + * @return null (or NULL if name is null) if not allowed, the actual name to use otherwise + */ + public String get(final String name) { + return name; + } + + /** + * @return a copy of these Names + */ + protected Names copy() { + return this; + } + } + + /** + * The pass-thru name set. + */ + private static final Names ALLOW_NAMES = new Names() { + @Override + public boolean add(final String name) { + return false; + } + + @Override + protected Names copy() { + return this; + } + }; + + /** + * The block-all name set. + */ + private static final Names BLOCK_NAMES = new Names() { + @Override + public boolean add(final String name) { + return false; + } + + @Override + protected Names copy() { + return this; + } + + @Override + public String get(final String name) { + return name == null? NULL : null; + } + }; + + /** + * A allow set of names. + */ + static class AllowSet extends Names { + /** The map of controlled names and aliases. */ + private Map names = null; + + @Override + protected Names copy() { + final AllowSet copy = new AllowSet(); + copy.names = names == null ? null : new HashMap<>(names); + return copy; + } + + @Override + public boolean add(final String name) { + if (names == null) { + names = new HashMap<>(); + } + return names.put(name, name) == null; + } + + @Override + public boolean alias(final String name, final String alias) { + if (names == null) { + names = new HashMap<>(); + } + return names.put(alias, name) == null; + } + + @Override + public String get(final String name) { + if (names == null) { + return name; + } + String actual = names.get(name); + // if null is not explicitly allowed, explicit null aka NULL + if (name == null && actual == null && !names.containsKey(null)) { + return JexlSandbox.NULL; + } + return actual; + } + } + + /** + * A block set of names. + */ + static class BlockSet extends Names { + /** The set of controlled names. */ + private Set names = null; + + @Override + protected Names copy() { + final BlockSet copy = new BlockSet(); + copy.names = names == null ? null : new HashSet<>(names); + return copy; + } + + @Override + public boolean add(final String name) { + if (names == null) { + names = new HashSet<>(); + } + return names.add(name); + } + + @Override + public String get(final String name) { + // if name is null and contained in set, explicit null aka NULL + return names != null && !names.contains(name) ? name : name != null? null : NULL; + } + } + + /** + * Unused. + */ + @Deprecated + public static final class WhiteSet extends AllowSet {} + + /** + * Unused. + */ + @Deprecated + public static final class BlackSet extends BlockSet {} + + /** + * Contains the allow or block lists for properties and methods for a given class. + */ + public static final class Permissions { + /** Whether these permissions are inheritable, ie can be used by derived classes. */ + private final boolean inheritable; + /** The controlled readable properties. */ + private final Names read; + /** The controlled writable properties. */ + private final Names write; + /** The controlled methods. */ + private final Names execute; + + /** + * Creates a new permissions instance. + * + * @param inherit whether these permissions are inheritable + * @param readFlag whether the read property list is allow or block + * @param writeFlag whether the write property list is allow or block + * @param executeFlag whether the method list is allow of block + */ + Permissions(final boolean inherit, final boolean readFlag, final boolean writeFlag, final boolean executeFlag) { + this(inherit, + readFlag ? new AllowSet() : new BlockSet(), + writeFlag ? new AllowSet() : new BlockSet(), + executeFlag ? new AllowSet() : new BlockSet()); + } + + /** + * Creates a new permissions instance. + * + * @param inherit whether these permissions are inheritable + * @param nread the read set + * @param nwrite the write set + * @param nexecute the method set + */ + Permissions(final boolean inherit, final Names nread, final Names nwrite, final Names nexecute) { + this.read = nread != null ? nread : ALLOW_NAMES; + this.write = nwrite != null ? nwrite : ALLOW_NAMES; + this.execute = nexecute != null ? nexecute : ALLOW_NAMES; + this.inheritable = inherit; + } + + /** + * @return a copy of these permissions + */ + Permissions copy() { + return new Permissions(inheritable, read.copy(), write.copy(), execute.copy()); + } + + /** + * @return whether these permissions applies to derived classes. + */ + public boolean isInheritable() { + return inheritable; + } + + /** + * Adds a list of readable property names to these permissions. + * + * @param pnames the property names + * @return this instance of permissions + */ + public Permissions read(final String... pnames) { + for (final String pname : pnames) { + read.add(pname); + } + return this; + } + + /** + * Adds a list of writable property names to these permissions. + * + * @param pnames the property names + * @return this instance of permissions + */ + public Permissions write(final String... pnames) { + for (final String pname : pnames) { + write.add(pname); + } + return this; + } + + /** + * Adds a list of executable methods names to these permissions. + *

The constructor is denoted as the empty-string, all other methods by their names.

+ * + * @param mnames the method names + * @return this instance of permissions + */ + public Permissions execute(final String... mnames) { + for (final String mname : mnames) { + execute.add(mname); + } + return this; + } + + /** + * Gets the set of readable property names in these permissions. + * + * @return the set of property names + */ + public Names read() { + return read; + } + + /** + * Gets the set of writable property names in these permissions. + * + * @return the set of property names + */ + public Names write() { + return write; + } + + /** + * Gets the set of method names in these permissions. + * + * @return the set of method names + */ + public Names execute() { + return execute; + } + } + + /** + * The pass-thru permissions. + */ + private static final Permissions ALLOW_ALL = new Permissions(false, ALLOW_NAMES, ALLOW_NAMES, ALLOW_NAMES); + /** + * The block-all permissions. + */ + private static final Permissions BLOCK_ALL = new Permissions(false, BLOCK_NAMES, BLOCK_NAMES, BLOCK_NAMES); + + /** + * Creates the set of permissions for a given class. + *

The sandbox inheritance property will apply to the permissions created by this method + * + * @param clazz the class for which these permissions apply + * @param readFlag whether the readable property list is allow - true - or block - false - + * @param writeFlag whether the writable property list is allow - true - or block - false - + * @param executeFlag whether the executable method list is allow allow - true - or block - false - + * @return the set of permissions + */ + public Permissions permissions(final String clazz, + final boolean readFlag, + final boolean writeFlag, + final boolean executeFlag) { + return permissions(clazz, inherit, readFlag, writeFlag, executeFlag); + } + + /** + * Creates the set of permissions for a given class. + * + * @param clazz the class for which these permissions apply + * @param inhf whether these permissions are inheritable + * @param readf whether the readable property list is allow - true - or block - false - + * @param writef whether the writable property list is allow - true - or block - false - + * @param execf whether the executable method list is allow allow - true - or block - false - + * @return the set of permissions + */ + public Permissions permissions(final String clazz, + final boolean inhf, + final boolean readf, + final boolean writef, + final boolean execf) { + final Permissions box = new Permissions(inhf, readf, writef, execf); + sandbox.put(clazz, box); + return box; + } + + /** + * Creates a new set of permissions based on allow lists for methods and properties for a given class. + *

The sandbox inheritance property will apply to the permissions created by this method + * + * @param clazz the allowed class name + * @return the permissions instance + */ + public Permissions allow(final String clazz) { + return permissions(clazz, true, true, true); + } + /** + * Use allow() instead. + * @param clazz the allowed class name + * @return the permissions instance + */ + @Deprecated + public Permissions white(final String clazz) { + return allow(clazz); + } + + /** + * Creates a new set of permissions based on block lists for methods and properties for a given class. + *

The sandbox inheritance property will apply to the permissions created by this method + * + * @param clazz the blocked class name + * @return the permissions instance + */ + public Permissions block(final String clazz) { + return permissions(clazz, false, false, false); + } + + /** + * Use block() instead. + * @param clazz the allowed class name + * @return the permissions instance + */ + @Deprecated + public Permissions black(final String clazz) { + return block(clazz); + } + + /** + * Gets the set of permissions associated to a class. + * + * @param clazz the class name + * @return the defined permissions or an all-allow permission instance if none were defined + */ + public Permissions get(final String clazz) { + if (inherit) { + return get(forName(clazz)); + } + final Permissions permissions = sandbox.get(clazz); + if (permissions == null) { + return allow ? ALLOW_ALL : BLOCK_ALL; + } + return permissions; + } + + /** + * Get the permissions associated to a class. + * @param clazz the class + * @return the permissions + */ + @SuppressWarnings("null") // clazz can not be null since permissions would be not null and block; + public Permissions get(final Class clazz) { + Permissions permissions = clazz == null ? BLOCK_ALL : sandbox.get(clazz.getName()); + if (permissions == null) { + if (inherit) { + // find first inherited interface that defines permissions + for (final Class inter : clazz.getInterfaces()) { + permissions = sandbox.get(inter.getName()); + if (permissions != null && permissions.isInheritable()) { + break; + } + } + // nothing defined yet, find first superclass that defines permissions + if (permissions == null) { + // lets walk all super classes + Class zuper = clazz.getSuperclass(); + // walk all superclasses + while (zuper != null) { + permissions = sandbox.get(zuper.getName()); + if (permissions != null && permissions.isInheritable()) { + break; + } + zuper = zuper.getSuperclass(); + } + } + // nothing was inheritable + if (permissions == null) { + permissions = allow ? ALLOW_ALL : BLOCK_ALL; + } + // store the info to avoid doing this costly look up + sandbox.put(clazz.getName(), permissions); + } else { + permissions = allow ? ALLOW_ALL : BLOCK_ALL; + } + } + return permissions; + } + +} diff --git a/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/introspection/JexlUberspect.java b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/introspection/JexlUberspect.java new file mode 100644 index 0000000..bbebaa2 --- /dev/null +++ b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/introspection/JexlUberspect.java @@ -0,0 +1,318 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package aiyh.utils.tool.org.apache.commons.jexl3.introspection; + +import aiyh.utils.tool.org.apache.commons.jexl3.JexlArithmetic; +import aiyh.utils.tool.org.apache.commons.jexl3.JexlBuilder; +import aiyh.utils.tool.org.apache.commons.jexl3.JexlOperator; + +import java.util.*; + +/** + * 'Federated' introspection/reflection interface to allow JEXL introspection + * behavior to be customized. + * + * @since 1.0 + */ +public interface JexlUberspect { + /** + * Abstracts getting property setter and getter. + *

+ * These are used through 'strategies' to solve properties; a strategy orders a list of resolver types, + * and each resolver type is tried in sequence; the first resolver that discovers a non null {s,g}etter + * stops the search. + * + * @see JexlResolver + * @see JexlUberspect#getPropertyGet + * @see JexlUberspect#getPropertySet + * @since 3.0 + */ + interface PropertyResolver { + + /** + * Gets a property getter. + * + * @param uber the uberspect + * @param obj the object + * @param identifier the property identifier + * @return the property getter or null + */ + JexlPropertyGet getPropertyGet(JexlUberspect uber, Object obj, Object identifier); + + /** + * Gets a property setter. + * + * @param uber the uberspect + * @param obj the object + * @param identifier the property identifier + * @param arg the property value + * @return the property setter or null + */ + JexlPropertySet getPropertySet(JexlUberspect uber, Object obj, Object identifier, Object arg); + } + + /** + * The various builtin property resolvers. + *

+ * Each resolver discovers how to set/get a property with different techniques; seeking + * method names or field names, etc. + * + * @since 3.0 + */ + enum JexlResolver implements PropertyResolver { + /** Seeks methods named get{P,p}property and is{P,p}property. */ + PROPERTY, + + /** Seeks map methods get/put. */ + MAP, + + /** Seeks list methods get/set. */ + LIST, + + /** Seeks any get/{set,put} method (quacking like a list or a map). */ + DUCK, + + /** Seeks public instance members. */ + FIELD, + + /** Seeks a getContainer(property) and setContainer(property, value) as in x.container.property. */ + CONTAINER; + + @Override + public final JexlPropertyGet getPropertyGet(final JexlUberspect uber, + final Object obj, + final Object identifier) { + return uber.getPropertyGet(Collections.singletonList(this), obj, identifier); + } + + @Override + public final JexlPropertySet getPropertySet(final JexlUberspect uber, + final Object obj, + final Object identifier, + final Object arg) { + return uber.getPropertySet(Collections.singletonList(this), obj, identifier, arg); + } + } + + /** + * A resolver types list tailored for POJOs, favors '.' over '[]'. + */ + List POJO = Collections.unmodifiableList(Arrays.asList( + JexlResolver.PROPERTY, + JexlResolver.MAP, + JexlResolver.LIST, + JexlResolver.DUCK, + JexlResolver.FIELD, + JexlResolver.CONTAINER + )); + + + /** + * A resolver types list tailored for Maps, favors '[]' over '.'. + */ + List MAP = Collections.unmodifiableList(Arrays.asList( + JexlResolver.MAP, + JexlResolver.LIST, + JexlResolver.DUCK, + JexlResolver.PROPERTY, + JexlResolver.FIELD, + JexlResolver.CONTAINER + )); + + /** + * Determines property resolution strategy. + * + *

To use a strategy instance, you have to set it at engine creation using + * {@link JexlBuilder#strategy(ResolverStrategy)} + * as in:

+ * + * JexlEngine jexl = new JexlBuilder().strategy(MY_STRATEGY).create(); + * + * @since 3.0 + */ + interface ResolverStrategy { + /** + * Applies this strategy to a list of resolver types. + * + * @param operator the property access operator, may be null + * @param obj the instance we seek to obtain a property setter/getter from, can not be null + * @return the ordered list of resolvers types, must not be null + */ + List apply(JexlOperator operator, Object obj); + } + + /** + * The default strategy. + *

+ * If the operator is '[]' or if the operator is null and the object is a map, use the MAP list of resolvers; + * Other cases use the POJO list of resolvers. + */ + ResolverStrategy JEXL_STRATEGY = (op, obj) -> { + if (op == JexlOperator.ARRAY_GET) { + return MAP; + } + if (op == JexlOperator.ARRAY_SET) { + return MAP; + } + if (op == null && obj instanceof Map) { + return MAP; + } + return POJO; + }; + + /** + * The map strategy. + * + *

If the operator is '[]' or if the object is a map, use the MAP list of resolvers. + * Otherwise, use the POJO list of resolvers.

+ */ + ResolverStrategy MAP_STRATEGY = (op, obj) -> { + if (op == JexlOperator.ARRAY_GET) { + return MAP; + } + if (op == JexlOperator.ARRAY_SET) { + return MAP; + } + if (obj instanceof Map) { + return MAP; + } + return POJO; + }; + + /** + * Applies this uberspect property resolver strategy. + * + * @param op the operator + * @param obj the object + * @return the applied strategy resolver list + */ + List getResolvers(JexlOperator op, Object obj); + + /** + * Sets the class loader to use. + * + *

This increments the version.

+ * + * @param loader the class loader + */ + void setClassLoader(ClassLoader loader); + + /** + * Gets the current class loader. + * + * @return the class loader + */ + ClassLoader getClassLoader(); + + /** + * Gets this uberspect version. + * + * @return the class loader modification count + */ + int getVersion(); + + /** + * Returns a class constructor. + * + * @param ctorHandle a class or class name + * @param args constructor arguments + * @return a {@link JexlMethod} + * @since 3.0 + */ + JexlMethod getConstructor(Object ctorHandle, Object... args); + + /** + * Returns a JexlMethod. + * + * @param obj the object + * @param method the method name + * @param args method arguments + * @return a {@link JexlMethod} + */ + JexlMethod getMethod(Object obj, String method, Object... args); + + /** + * Property getter. + * + *

returns a JelPropertySet apropos to an expression like bar.woogie.

+ * + * @param obj the object to get the property from + * @param identifier property name + * @return a {@link JexlPropertyGet} or null + */ + JexlPropertyGet getPropertyGet(Object obj, Object identifier); + + /** + * Property getter. + *

+ * Seeks a JexlPropertyGet apropos to an expression like bar.woogie.

+ * See {@link ResolverStrategy#apply(JexlOperator, Object) } + * + * @param resolvers the list of property resolvers to try + * @param obj the object to get the property from + * @param identifier property name + * @return a {@link JexlPropertyGet} or null + * @since 3.0 + */ + JexlPropertyGet getPropertyGet(List resolvers, Object obj, Object identifier); + + /** + * Property setter. + *

+ * Seeks a JelPropertySet apropos to an expression like foo.bar = "geir".

+ * + * @param obj the object to get the property from. + * @param identifier property name + * @param arg value to set + * @return a {@link JexlPropertySet} or null + */ + JexlPropertySet getPropertySet(Object obj, Object identifier, Object arg); + + /** + * Property setter. + *

+ * Seeks a JelPropertySet apropos to an expression like foo.bar = "geir".

+ * See {@link ResolverStrategy#apply(JexlOperator, Object) } + * + * @param resolvers the list of property resolvers to try, + * @param obj the object to get the property from + * @param identifier property name + * @param arg value to set + * @return a {@link JexlPropertySet} or null + * @since 3.0 + */ + JexlPropertySet getPropertySet(List resolvers, Object obj, Object identifier, Object arg); + + /** + * Gets an iterator from an object. + * + * @param obj to get the iterator from + * @return an iterator over obj or null + */ + Iterator getIterator(Object obj); + + /** + * Gets an arithmetic operator resolver for a given arithmetic instance. + * + * @param arithmetic the arithmetic instance + * @return the arithmetic uberspect or null if no operator method were overridden + * @since 3.0 + */ + JexlArithmetic.Uberspect getArithmetic(JexlArithmetic arithmetic); + +} diff --git a/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/introspection/package.html b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/introspection/package.html new file mode 100644 index 0000000..6df16da --- /dev/null +++ b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/introspection/package.html @@ -0,0 +1,34 @@ + + + + Package Documentation for org.apache.commons.jexl3.introspection Package + + +

Provides high-level introspective services.

+

+ The Uberspect, JexlMethod, JexlPropertyGet and JexlPropertySet interfaces + form the exposed face of introspective services. +

+

+ The Uberspectimpl is the concrete class implementing the Uberspect interface. + Deriving from this class is the preferred way of augmenting Jexl introspective + capabilities when special needs to be fulfilled or when default behaviors + need to be modified. +

+ + diff --git a/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/package.html b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/package.html new file mode 100644 index 0000000..c853492 --- /dev/null +++ b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/package.html @@ -0,0 +1,535 @@ + + + + Package Documentation for org.apache.commons.jexl3 Package + + + Provides a framework for evaluating JEXL expressions. + + +

Introduction

+

+ JEXL is a library intended to facilitate the implementation of dynamic and scripting features in applications + and frameworks. +

+ +

A Brief Example

+

+ In its simplest form, JEXL merges an + {@link org.apache.commons.jexl3.JexlExpression} + with a + {@link org.apache.commons.jexl3.JexlContext} when evaluating expressions. + An Expression is created using + {@link org.apache.commons.jexl3.JexlEngine#createExpression(java.lang.String)}, + passing a String containing valid JEXL syntax. A simple JexlContext can be created using + a {@link org.apache.commons.jexl3.MapContext} instance; + a map of variables that will be internally wrapped can be optionally provided through its constructor. + The following example, takes a variable named 'car', and + invokes the checkStatus() method on the property 'engine' +

+
+            // Create a JexlEngine (could reuse one instead)
+            JexlEngine jexl = new JexlBuilder().create();
+            // Create an expression object equivalent to 'car.getEngine().checkStatus()':
+            String jexlExp = "car.engine.checkStatus()";
+            Expression e = jexl.createExpression( jexlExp );
+            // The car we have to handle coming as an argument...
+            Car car = theCarThatWeHandle;
+            // Create a context and add data
+            JexlContext jc = new MapContext();
+            jc.set("car", car );
+            // Now evaluate the expression, getting the result
+            Object o = e.evaluate(jc);
+        
+ + +

Using JEXL

+ The API is composed of three levels addressing different functional needs: +
    +
  • Dynamic invocation of setters, getters, methods and constructors
  • +
  • Script expressions known as JEXL expressions
  • +
  • JSP/JSF like expression known as JXLT expressions
  • +
+ +

Important note

+ The public API classes reside in the 2 packages: +
    +
  • org.apache.commons.jexl3
  • +
  • org.apache.commons.jexl3.introspection
  • +
+

+ The following packages follow a "use at your own maintenance cost" policy; these are only intended to be used + for extending JEXL. + Their classes and methods are not guaranteed to remain compatible in subsequent versions. + If you think you need to use directly some of their features or methods, it might be a good idea to check with + the community through the mailing list first. +

+
    +
  • org.apache.commons.jexl3.parser
  • +
  • org.apache.commons.jexl3.scripting
  • +
  • org.apache.commons.jexl3.internal
  • +
  • org.apache.commons.jexl3.internal.introspection
  • +
+ +

Dynamic Invocation

+

+ These functionalities are close to the core level utilities found in + BeanUtils. + For basic dynamic property manipulations and method invocation, you can use the following + set of methods: +

+
    +
  • {@link org.apache.commons.jexl3.JexlEngine#newInstance}
  • +
  • {@link org.apache.commons.jexl3.JexlEngine#setProperty}
  • +
  • {@link org.apache.commons.jexl3.JexlEngine#getProperty}
  • +
  • {@link org.apache.commons.jexl3.JexlEngine#invokeMethod}
  • +
+ The following example illustrate their usage: +
+            // test outer class
+            public static class Froboz {
+                int value;
+                public Froboz(int v) { value = v; }
+                public void setValue(int v) { value = v; }
+                public int getValue() { return value; }
+            }
+            // test inner class
+            public static class Quux {
+                String str;
+                Froboz froboz;
+                public Quux(String str, int fro) {
+                    this.str = str;
+                    froboz = new Froboz(fro);
+                }
+                public Froboz getFroboz() { return froboz; }
+                public void setFroboz(Froboz froboz) { this.froboz = froboz; }
+                public String getStr() { return str; }
+                public void setStr(String str) { this.str = str; }
+            }
+            // test API
+            JexlEngine jexl = new JexlBuilder().create();
+            Quux quux = jexl.newInstance(Quux.class, "xuuq", 100);
+            jexl.setProperty(quux, "froboz.value", Integer.valueOf(100));
+            Object o = jexl.getProperty(quux, "froboz.value");
+            assertEquals("Result is not 100", new Integer(100), o);
+            jexl.setProperty(quux, "['froboz'].value", Integer.valueOf(1000));
+            o = jexl.getProperty(quux, "['froboz']['value']");
+            assertEquals("Result is not 1000", new Integer(1000), o);
+        
+ +

Expressions and Scripts

+

+ If your needs require simple expression evaluation capabilities, the core JEXL features + will most likely fit. + The main methods are: +

+
    +
  • {@link org.apache.commons.jexl3.JexlEngine#createScript}
  • +
  • {@link org.apache.commons.jexl3.JexlScript#execute}
  • +
  • {@link org.apache.commons.jexl3.JexlEngine#createExpression}
  • +
  • {@link org.apache.commons.jexl3.JexlExpression#evaluate}
  • +
+ The following example illustrates their usage: +
+            JexlEngine jexl = new JexlBuilder().create();
+
+            JexlContext jc = new MapContext();
+            jc.set("quuxClass", quux.class);
+
+            JexlExpression create = jexl.createExpression("quux = new(quuxClass, 'xuuq', 100)");
+            JelxExpression assign = jexl.createExpression("quux.froboz.value = 10");
+            JexlExpression check = jexl.createExpression("quux[\"froboz\"].value");
+            Quux quux = (Quux) create.evaluate(jc);
+            Object o = assign.evaluate(jc);
+            assertEquals("Result is not 10", new Integer(10), o);
+            o = check.evaluate(jc);
+            assertEquals("Result is not 10", new Integer(10), o);
+        
+ +

Unified Expressions and Templates

+

+ If you are looking for JSP-EL like and basic templating features, you can + use Expression from a JxltEngine. +

+ The main methods are: +
    +
  • {@link org.apache.commons.jexl3.JxltEngine#createExpression}
  • +
  • {@link aiyh.utils.tool.org.apache.commons.jexl3.JxltEngine.Expression#prepare}
  • +
  • {@link aiyh.utils.tool.org.apache.commons.jexl3.JxltEngine.Expression#evaluate}
  • +
  • {@link org.apache.commons.jexl3.JxltEngine#createTemplate}
  • +
  • {@link aiyh.utils.tool.org.apache.commons.jexl3.JxltEngine.Template#prepare}
  • +
  • {@link aiyh.utils.tool.org.apache.commons.jexl3.JxltEngine.Template#evaluate}
  • +
+ The following example illustrates their usage: +
+            JexlEngine jexl = new JexlBuilder().create();
+            JxltEngine jxlt = jexl.createJxltEngine();
+            JxltEngine.Expression expr = jxlt.createExpression("Hello ${user}");
+            String hello = expr.evaluate(context).toString();
+        
+ +

JexlExpression, JexlScript, Expression and Template: summary

+

JexlExpression

+

+ These are the most basic form of JexlEngine expressions and only allow for a single command + to be executed and its result returned. If you try to use multiple commands, it ignores + everything after the first semi-colon and just returns the result from + the first command. +

+

+ Also note that expressions are not statements (which is what scripts are made of) and do not allow + using the flow control (if, while, for), variables or lambdas syntactic elements. +

+

JexlScript

+

+ These allow you to use multiple statements and you can + use variable assignments, loops, calculations, etc. More or less what can be achieved in Shell or + JavaScript at its basic level. The result from the last command is returned from the script. +

+

JxltEngine.Expression

+

+ These are ideal to produce "one-liner" text, like a 'toString()' on steroids. + To get a calculation you use the EL-like syntax + as in ${someVariable}. The expression that goes between the brackets + behaves like a JexlScript, not an expression. You can use semi-colons to + execute multiple commands and the result from the last command is + returned from the script. You also have the ability to use a 2-pass evaluation using + the #{someScript} syntax. +

+

JxltEngine.Template

+

+ These produce text documents. Each line beginning with '$$' (as a default) is + considered JEXL code and all others considered as JxltEngine.Expression. + Think of those as simple Velocity templates. A rewritten MudStore initial Velocity sample looks like this: +

+

+        <html>
+            <body>
+                Hello ${customer.name}!
+                <table>
+        $$      for(var mud : mudsOnSpecial ) {
+        $$          if (customer.hasPurchased(mud) ) {
+                    <tr>
+                        <td>
+                            ${flogger.getPromo( mud )}
+                        </td>
+                    </tr>
+        $$          }
+        $$      }
+        </table>
+        </body>
+        </html>
+        
+ +

JEXL Configuration

+

+ The JexlEngine can be configured through a few parameters that will drive how it reacts + in case of errors. + These configuration methods are embedded through a {@link org.apache.commons.jexl3.JexlBuilder}. +

+

Static & Shared Configuration

+

+ Both JexlEngine and JxltEngine are thread-safe, most of their inner fields are final; the same instance can be shared between different + threads and proper synchronization is enforced in critical areas (introspection caches). +

+

+ Of particular importance is {@link org.apache.commons.jexl3.JexlBuilder#loader} which indicates to the JexlEngine + being built which class loader to use to solve a class name; this directly affects how JexlEngine.newInstance and the 'new' script method operates. +

+

+ This can also be very useful in cases where you rely on JEXL to dynamically load and call plugins for your application. + To avoid having to restart the server in case of a plugin implementation change, you can call + {@link org.apache.commons.jexl3.JexlEngine#setClassLoader} and all the scripts created through this engine instance + will automatically point to the newly loaded classes. +

+

+ You can state what can be manipulated through scripting by the {@link org.apache.commons.jexl3.annotations.NoJexl} + annotation that completely shield classes and methods from JEXL introspection. + The other configurable way to restrict JEXL is by using a + {@link org.apache.commons.jexl3.introspection.JexlSandbox} which allows finer control over what is exposed; the sandbox + can be set through {@link org.apache.commons.jexl3.JexlBuilder#sandbox}. +

+

+ {@link org.apache.commons.jexl3.JexlBuilder#namespaces} extends JEXL scripting by registering your own classes as + namespaces allowing your own functions to be exposed at will. +

+ This can be used as in: +

+            public static MyMath {
+                public double cos(double x) {
+                    return Math.cos(x);
+                }
+            }
+            Map<String, Object> funcs = new HashMap<String, Object>();
+            funcs.put("math", new MyMath());
+            JexlEngine jexl = new JexlBuilder().namespaces(funcs).create();
+
+            JexlContext jc = new MapContext();
+            jc.set("pi", Math.PI);
+
+            JexlExpression e = JEXL.createExpression("math:cos(pi)");
+            o = e.evaluate(jc);
+            assertEquals(Double.valueOf(-1),o);
+        
+

+ If the namespace is a Class and that class declares a constructor that takes a JexlContext (or + a class extending JexlContext), one namespace instance is created on first usage in an + expression; this instance lifetime is limited to the expression evaluation. +

+

+ JexlEngine and JxltEngine expression caches can be configured as well. If you intend to use JEXL + repeatedly in your application, these are worth configuring since expression parsing is quite heavy. + Note that all caches created by JEXL are held through SoftReference; under high memory pressure, the GC will be able + to reclaim those caches and JEXL will rebuild them if needed. By default, a JexlEngine does create a cache for "small" expressions + and a JxltEngine does create one for Expression . +

+

{@link org.apache.commons.jexl3.JexlBuilder#cache} will set how many expressions can be simultaneously cached by the + JEXL engine. JxltEngine allows to define the cache size through its constructor. +

+

+ {@link org.apache.commons.jexl3.JexlBuilder#debug} + makes stacktraces carried by JExlException more meaningful; in particular, these + traces will carry the exact caller location the Expression was created from. +

+ +

Dynamic Configuration

+

+ Those configuration options can be overridden during evaluation by implementing a + {@link org.apache.commons.jexl3.JexlContext} + that also implements {@link aiyh.utils.tool.org.apache.commons.jexl3.JexlEngine.Options} to carry evaluation + options. + An example of such a class exists in the test package. +

+

+ {@link org.apache.commons.jexl3.JexlBuilder#strict} or {@link + aiyh.utils.tool.org.apache.commons.jexl3.JexlEngine.Options#isStrict} + configures when JEXL considers 'null' as an error or not in various situations; + when facing an unreferenceable variable, using null as an argument to an arithmetic operator or failing to + call + a method or constructor. The lenient mode is close to JEXL-1.1 behavior. +

+

+ {@link org.apache.commons.jexl3.JexlBuilder#silent} or {@link + aiyh.utils.tool.org.apache.commons.jexl3.JexlEngine.Options#isSilent} + configures how JEXL reacts to errors; if silent, the engine will not throw exceptions + but will warn through loggers and return null in case of errors. Note that when non-silent, JEXL throws + JexlException which are unchecked exception. +

+

+ Implementing a {@link org.apache.commons.jexl3.JexlContext.NamespaceResolver} through a JexlContext - look at + JexlEvalContext + as an example - allows to override the namespace resolution and the default namespace map defined + through {@link org.apache.commons.jexl3.JexlBuilder#namespaces}. +

+ + +

JEXL Customization

+

+ The {@link org.apache.commons.jexl3.JexlContext}, {@link org.apache.commons.jexl3.JexlBuilder} and + {@link aiyh.utils.tool.org.apache.commons.jexl3.JexlEngine.Options} are + the most likely interfaces you'll want to implement for customization. Since they expose variables and + options, + they are the primary targets. Before you do so, have a look at JexlEvalContext in the test directory + and {@link org.apache.commons.jexl3.ObjectContext} which may already cover some of your needs. +

+

+ {@link org.apache.commons.jexl3.JexlArithmetic} + is the class to derive if you need to change how operators behave or add types upon which they + operate. + + There are 3 entry points that allow customizing the type of objects created: +

+
    +
  • array literals: {@link org.apache.commons.jexl3.JexlArithmetic#arrayBuilder}
  • +
  • map literals: {@link org.apache.commons.jexl3.JexlArithmetic#mapBuilder}
  • +
  • set literals: {@link org.apache.commons.jexl3.JexlArithmetic#setBuilder}
  • +
  • range objects: {@link org.apache.commons.jexl3.JexlArithmetic#createRange}
  • +
+

+ You can also overload operator methods; by convention, each operator has a method name associated to it. + If you overload some in your JexlArithmetic derived implementation, these methods will be called when the + arguments match your method signature. + For example, this would be the case if you wanted '+' to operate on arrays; you'd need to derive + JexlArithmetic and implement 'public Object add(Set<?;> x, Set<?;> y)' method. + Note however that you can not change the operator precedence. + The list of operator / method matches is the following: +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Operators
OperatorMethod NameExample
+addadd(x, y)
-subtractsubtract(x, y)
*multiplymultiply(x, y)
/dividedivide(x, y)
%modmod(x, y)
&bitwiseAndbitwiseAnd(x, y)
|bitwiseOrbitwiseOr(x, y)
^bitwiseXorbitwiseXor(x, y)
!logicalNotlogicalNot(x)
-bitwiseComplementbitiwiseComplement(x)
==equalsequals(x, y)
<lessThanlessThan(x, y)
<=lessThanOrEquallessThanOrEqual(x, y)
>greaterThangreaterThan(x, y)
>=greaterThanOrEqualgreaterThanOrEqual(x, y)
-negatenegate(x)
sizesizesize(x)
emptyemptyempty(x)
+

+ You can also add methods to overload property getters and setters operators behaviors. + Public methods of the JexlArithmetic instance named propertyGet/propertySet/arrayGet/arraySet are potential + overrides that will be called when appropriate. + The following table is an overview of the relation between a syntactic form and the method to call + where V is the property value class, O the object class and P the property identifier class (usually String or Integer). +

+ + + + + + + + + + + + + + + + + + + + + + +
Property Accessors
ExpressionMethod Template
foo.propertypublic V propertyGet(O obj, P property);
foo.property = valuepublic V propertySet(O obj, P property, V value);
foo[property]public V arrayGet(O obj, P property, V value);
foo[property] = valuepublic V arraySet(O obj, P property, V value);
+

+ You can also override the base operator methods, those whose arguments are Object which gives you total + control. +

+ +

Extending JEXL

+ If you need to make JEXL treat some objects in a specialized manner or tweak how it + reacts to some settings, you can derive most of its inner-workings. The classes and methods are rarely private or + final - only when the inner contract really requires it. However, using the protected methods + and internal package classes imply you might have to re-adapt your code when new JEXL versions are released. +

+ {@link org.apache.commons.jexl3.internal.Engine} can be + extended to let you capture your own configuration defaults wrt cache sizes and various flags. + Implementing your own cache - instead of the basic LinkedHashMap based one - would be + another possible extension. +

+

+ {@link org.apache.commons.jexl3.internal.Interpreter} + is the class to derive if you need to add more features to the evaluation + itself; for instance, you want pre- and post- resolvers for variables or nested scopes for + for variable contexts. +

+

+ {@link org.apache.commons.jexl3.internal.introspection.Uberspect} + is the class to derive if you need to add introspection or reflection capabilities for some objects, for + instance adding factory based support to the 'new' operator. + The code already reflects public fields as properties on top of Java-beans conventions. +

+ + diff --git a/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTAddNode.java b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTAddNode.java new file mode 100644 index 0000000..8188597 --- /dev/null +++ b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTAddNode.java @@ -0,0 +1,22 @@ +/* Generated by: JJTree: Do not edit this line. ASTAddNode.java Version 1.1 */ +/* ParserGeneratorCCOptions:MULTI=true,NODE_USES_PARSER=false,VISITOR=true,TRACK_TOKENS=true,NODE_PREFIX=AST,NODE_EXTENDS=,NODE_FACTORY=,SUPPORT_CLASS_VISIBILITY_PUBLIC=true */ +package aiyh.utils.tool.org.apache.commons.jexl3.parser; + +public +class ASTAddNode extends JexlNode { + public ASTAddNode(int id) { + super(id); + } + + public ASTAddNode(Parser p, int id) { + super(p, id); + } + + + /** Accept the visitor. **/ + public Object jjtAccept(ParserVisitor visitor, Object data) { + return + visitor.visit(this, data); + } +} +/* ParserGeneratorCC - OriginalChecksum=806bab8d64feb5b185229e22923f6ad8 (do not edit this line) */ diff --git a/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTAmbiguous.java b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTAmbiguous.java new file mode 100644 index 0000000..52b3097 --- /dev/null +++ b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTAmbiguous.java @@ -0,0 +1,34 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package aiyh.utils.tool.org.apache.commons.jexl3.parser; + +public final class ASTAmbiguous extends JexlNode { + + ASTAmbiguous(final int id) { + super(id); + } + + ASTAmbiguous(final Parser p, final int id) { + super(p, id); + } + + @Override + public Object jjtAccept(final ParserVisitor visitor, final Object data) { + return visitor.visit(this, data); + } +} + diff --git a/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTAndNode.java b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTAndNode.java new file mode 100644 index 0000000..2ecf895 --- /dev/null +++ b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTAndNode.java @@ -0,0 +1,22 @@ +/* Generated by: JJTree: Do not edit this line. ASTAndNode.java Version 1.1 */ +/* ParserGeneratorCCOptions:MULTI=true,NODE_USES_PARSER=false,VISITOR=true,TRACK_TOKENS=true,NODE_PREFIX=AST,NODE_EXTENDS=,NODE_FACTORY=,SUPPORT_CLASS_VISIBILITY_PUBLIC=true */ +package aiyh.utils.tool.org.apache.commons.jexl3.parser; + +public +class ASTAndNode extends JexlNode { + public ASTAndNode(int id) { + super(id); + } + + public ASTAndNode(Parser p, int id) { + super(p, id); + } + + + /** Accept the visitor. **/ + public Object jjtAccept(ParserVisitor visitor, Object data) { + return + visitor.visit(this, data); + } +} +/* ParserGeneratorCC - OriginalChecksum=40b6ccfc8a7653590cc725e7b1494630 (do not edit this line) */ diff --git a/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTAnnotatedStatement.java b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTAnnotatedStatement.java new file mode 100644 index 0000000..c5a976e --- /dev/null +++ b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTAnnotatedStatement.java @@ -0,0 +1,22 @@ +/* Generated by: JJTree: Do not edit this line. ASTAnnotatedStatement.java Version 1.1 */ +/* ParserGeneratorCCOptions:MULTI=true,NODE_USES_PARSER=false,VISITOR=true,TRACK_TOKENS=true,NODE_PREFIX=AST,NODE_EXTENDS=,NODE_FACTORY=,SUPPORT_CLASS_VISIBILITY_PUBLIC=true */ +package aiyh.utils.tool.org.apache.commons.jexl3.parser; + +public +class ASTAnnotatedStatement extends JexlNode { + public ASTAnnotatedStatement(int id) { + super(id); + } + + public ASTAnnotatedStatement(Parser p, int id) { + super(p, id); + } + + + /** Accept the visitor. **/ + public Object jjtAccept(ParserVisitor visitor, Object data) { + return + visitor.visit(this, data); + } +} +/* ParserGeneratorCC - OriginalChecksum=a4b6f99ce64d9977701193a577e07147 (do not edit this line) */ diff --git a/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTAnnotation.java b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTAnnotation.java new file mode 100644 index 0000000..7927062 --- /dev/null +++ b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTAnnotation.java @@ -0,0 +1,54 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package aiyh.utils.tool.org.apache.commons.jexl3.parser; + +/** + * Annotation. + */ +public class ASTAnnotation extends JexlNode { + private String name = null; + + ASTAnnotation(final int id) { + super(id); + } + + ASTAnnotation(final Parser p, final int id) { + super(p, id); + } + + @Override + public String toString() { + return name; + } + + void setName(final String identifier) { + if (identifier.charAt(0) == '@') { + name = identifier.substring(1); + } else { + name = identifier; + } + } + + public String getName() { + return name; + } + + @Override + public Object jjtAccept(final ParserVisitor visitor, final Object data) { + return visitor.visit(this, data); + } +} diff --git a/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTArguments.java b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTArguments.java new file mode 100644 index 0000000..20d82eb --- /dev/null +++ b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTArguments.java @@ -0,0 +1,22 @@ +/* Generated by: JJTree: Do not edit this line. ASTArguments.java Version 1.1 */ +/* ParserGeneratorCCOptions:MULTI=true,NODE_USES_PARSER=false,VISITOR=true,TRACK_TOKENS=true,NODE_PREFIX=AST,NODE_EXTENDS=,NODE_FACTORY=,SUPPORT_CLASS_VISIBILITY_PUBLIC=true */ +package aiyh.utils.tool.org.apache.commons.jexl3.parser; + +public +class ASTArguments extends JexlNode { + public ASTArguments(int id) { + super(id); + } + + public ASTArguments(Parser p, int id) { + super(p, id); + } + + + /** Accept the visitor. **/ + public Object jjtAccept(ParserVisitor visitor, Object data) { + return + visitor.visit(this, data); + } +} +/* ParserGeneratorCC - OriginalChecksum=f3e0f0702d6213b0deb816ec8c6c77e1 (do not edit this line) */ diff --git a/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTArrayAccess.java b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTArrayAccess.java new file mode 100644 index 0000000..33884c6 --- /dev/null +++ b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTArrayAccess.java @@ -0,0 +1,22 @@ +/* Generated by: JJTree: Do not edit this line. ASTArrayAccess.java Version 1.1 */ +/* ParserGeneratorCCOptions:MULTI=true,NODE_USES_PARSER=false,VISITOR=true,TRACK_TOKENS=true,NODE_PREFIX=AST,NODE_EXTENDS=,NODE_FACTORY=,SUPPORT_CLASS_VISIBILITY_PUBLIC=true */ +package aiyh.utils.tool.org.apache.commons.jexl3.parser; + +public +class ASTArrayAccess extends JexlNode { + public ASTArrayAccess(int id) { + super(id); + } + + public ASTArrayAccess(Parser p, int id) { + super(p, id); + } + + + /** Accept the visitor. **/ + public Object jjtAccept(ParserVisitor visitor, Object data) { + return + visitor.visit(this, data); + } +} +/* ParserGeneratorCC - OriginalChecksum=99079363d4bda2f6e895bd0d1ec02ad0 (do not edit this line) */ diff --git a/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTArrayLiteral.java b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTArrayLiteral.java new file mode 100644 index 0000000..c6149a3 --- /dev/null +++ b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTArrayLiteral.java @@ -0,0 +1,64 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package aiyh.utils.tool.org.apache.commons.jexl3.parser; + +import aiyh.utils.tool.org.apache.commons.jexl3.internal.Debugger; + +/** + * An array literal. + */ +public final class ASTArrayLiteral extends JexlNode { + /** Whether this array is constant or not. */ + private boolean constant = false; + + ASTArrayLiteral(final int id) { + super(id); + } + + ASTArrayLiteral(final Parser p, final int id) { + super(p, id); + } + + @Override + public String toString() { + final Debugger dbg = new Debugger(); + return dbg.data(this); + } + + @Override + protected boolean isConstant(final boolean literal) { + return constant; + } + + @Override + public void jjtClose() { + constant = true; + for (int c = 0; c < jjtGetNumChildren() && constant; ++c) { + final JexlNode child = jjtGetChild(c); + if (child instanceof ASTReference) { + constant = child.isConstant(true); + } else if (!child.isConstant()) { + constant = false; + } + } + } + + @Override + public Object jjtAccept(final ParserVisitor visitor, final Object data) { + return visitor.visit(this, data); + } +} diff --git a/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTAssignment.java b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTAssignment.java new file mode 100644 index 0000000..dd128e4 --- /dev/null +++ b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTAssignment.java @@ -0,0 +1,22 @@ +/* Generated by: JJTree: Do not edit this line. ASTAssignment.java Version 1.1 */ +/* ParserGeneratorCCOptions:MULTI=true,NODE_USES_PARSER=false,VISITOR=true,TRACK_TOKENS=true,NODE_PREFIX=AST,NODE_EXTENDS=,NODE_FACTORY=,SUPPORT_CLASS_VISIBILITY_PUBLIC=true */ +package aiyh.utils.tool.org.apache.commons.jexl3.parser; + +public +class ASTAssignment extends JexlNode { + public ASTAssignment(int id) { + super(id); + } + + public ASTAssignment(Parser p, int id) { + super(p, id); + } + + + /** Accept the visitor. **/ + public Object jjtAccept(ParserVisitor visitor, Object data) { + return + visitor.visit(this, data); + } +} +/* ParserGeneratorCC - OriginalChecksum=dfc31f1d65291e39f709e9d7b8af723e (do not edit this line) */ diff --git a/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTBitwiseAndNode.java b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTBitwiseAndNode.java new file mode 100644 index 0000000..9715689 --- /dev/null +++ b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTBitwiseAndNode.java @@ -0,0 +1,22 @@ +/* Generated by: JJTree: Do not edit this line. ASTBitwiseAndNode.java Version 1.1 */ +/* ParserGeneratorCCOptions:MULTI=true,NODE_USES_PARSER=false,VISITOR=true,TRACK_TOKENS=true,NODE_PREFIX=AST,NODE_EXTENDS=,NODE_FACTORY=,SUPPORT_CLASS_VISIBILITY_PUBLIC=true */ +package aiyh.utils.tool.org.apache.commons.jexl3.parser; + +public +class ASTBitwiseAndNode extends JexlNode { + public ASTBitwiseAndNode(int id) { + super(id); + } + + public ASTBitwiseAndNode(Parser p, int id) { + super(p, id); + } + + + /** Accept the visitor. **/ + public Object jjtAccept(ParserVisitor visitor, Object data) { + return + visitor.visit(this, data); + } +} +/* ParserGeneratorCC - OriginalChecksum=73cab80f3e9aa8c9d4756518457f1f62 (do not edit this line) */ diff --git a/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTBitwiseComplNode.java b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTBitwiseComplNode.java new file mode 100644 index 0000000..817006e --- /dev/null +++ b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTBitwiseComplNode.java @@ -0,0 +1,22 @@ +/* Generated by: JJTree: Do not edit this line. ASTBitwiseComplNode.java Version 1.1 */ +/* ParserGeneratorCCOptions:MULTI=true,NODE_USES_PARSER=false,VISITOR=true,TRACK_TOKENS=true,NODE_PREFIX=AST,NODE_EXTENDS=,NODE_FACTORY=,SUPPORT_CLASS_VISIBILITY_PUBLIC=true */ +package aiyh.utils.tool.org.apache.commons.jexl3.parser; + +public +class ASTBitwiseComplNode extends JexlNode { + public ASTBitwiseComplNode(int id) { + super(id); + } + + public ASTBitwiseComplNode(Parser p, int id) { + super(p, id); + } + + + /** Accept the visitor. **/ + public Object jjtAccept(ParserVisitor visitor, Object data) { + return + visitor.visit(this, data); + } +} +/* ParserGeneratorCC - OriginalChecksum=26eb7afeff3a890bd89a308bd3baaff4 (do not edit this line) */ diff --git a/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTBitwiseOrNode.java b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTBitwiseOrNode.java new file mode 100644 index 0000000..175c413 --- /dev/null +++ b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTBitwiseOrNode.java @@ -0,0 +1,22 @@ +/* Generated by: JJTree: Do not edit this line. ASTBitwiseOrNode.java Version 1.1 */ +/* ParserGeneratorCCOptions:MULTI=true,NODE_USES_PARSER=false,VISITOR=true,TRACK_TOKENS=true,NODE_PREFIX=AST,NODE_EXTENDS=,NODE_FACTORY=,SUPPORT_CLASS_VISIBILITY_PUBLIC=true */ +package aiyh.utils.tool.org.apache.commons.jexl3.parser; + +public +class ASTBitwiseOrNode extends JexlNode { + public ASTBitwiseOrNode(int id) { + super(id); + } + + public ASTBitwiseOrNode(Parser p, int id) { + super(p, id); + } + + + /** Accept the visitor. **/ + public Object jjtAccept(ParserVisitor visitor, Object data) { + return + visitor.visit(this, data); + } +} +/* ParserGeneratorCC - OriginalChecksum=29bc374c952cfb8d5f1300388b826894 (do not edit this line) */ diff --git a/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTBitwiseXorNode.java b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTBitwiseXorNode.java new file mode 100644 index 0000000..9d116f5 --- /dev/null +++ b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTBitwiseXorNode.java @@ -0,0 +1,22 @@ +/* Generated by: JJTree: Do not edit this line. ASTBitwiseXorNode.java Version 1.1 */ +/* ParserGeneratorCCOptions:MULTI=true,NODE_USES_PARSER=false,VISITOR=true,TRACK_TOKENS=true,NODE_PREFIX=AST,NODE_EXTENDS=,NODE_FACTORY=,SUPPORT_CLASS_VISIBILITY_PUBLIC=true */ +package aiyh.utils.tool.org.apache.commons.jexl3.parser; + +public +class ASTBitwiseXorNode extends JexlNode { + public ASTBitwiseXorNode(int id) { + super(id); + } + + public ASTBitwiseXorNode(Parser p, int id) { + super(p, id); + } + + + /** Accept the visitor. **/ + public Object jjtAccept(ParserVisitor visitor, Object data) { + return + visitor.visit(this, data); + } +} +/* ParserGeneratorCC - OriginalChecksum=82b605fc2163491bb0de4206592a9dcb (do not edit this line) */ diff --git a/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTBlock.java b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTBlock.java new file mode 100644 index 0000000..68e6099 --- /dev/null +++ b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTBlock.java @@ -0,0 +1,36 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package aiyh.utils.tool.org.apache.commons.jexl3.parser; + +/** + * Declares a block. + */ +public class ASTBlock extends JexlLexicalNode { + + public ASTBlock(final int id) { + super(id); + } + + public ASTBlock(final Parser p, final int id) { + super(p, id); + } + + @Override + public Object jjtAccept(final ParserVisitor visitor, final Object data) { + return visitor.visit(this, data); + } +} diff --git a/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTBreak.java b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTBreak.java new file mode 100644 index 0000000..0189f7b --- /dev/null +++ b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTBreak.java @@ -0,0 +1,22 @@ +/* Generated by: JJTree: Do not edit this line. ASTBreak.java Version 1.1 */ +/* ParserGeneratorCCOptions:MULTI=true,NODE_USES_PARSER=false,VISITOR=true,TRACK_TOKENS=true,NODE_PREFIX=AST,NODE_EXTENDS=,NODE_FACTORY=,SUPPORT_CLASS_VISIBILITY_PUBLIC=true */ +package aiyh.utils.tool.org.apache.commons.jexl3.parser; + +public +class ASTBreak extends JexlNode { + public ASTBreak(int id) { + super(id); + } + + public ASTBreak(Parser p, int id) { + super(p, id); + } + + + /** Accept the visitor. **/ + public Object jjtAccept(ParserVisitor visitor, Object data) { + return + visitor.visit(this, data); + } +} +/* ParserGeneratorCC - OriginalChecksum=436506a6afa286186b15cc92716f6ace (do not edit this line) */ diff --git a/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTConstructorNode.java b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTConstructorNode.java new file mode 100644 index 0000000..646e972 --- /dev/null +++ b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTConstructorNode.java @@ -0,0 +1,22 @@ +/* Generated by: JJTree: Do not edit this line. ASTConstructorNode.java Version 1.1 */ +/* ParserGeneratorCCOptions:MULTI=true,NODE_USES_PARSER=false,VISITOR=true,TRACK_TOKENS=true,NODE_PREFIX=AST,NODE_EXTENDS=,NODE_FACTORY=,SUPPORT_CLASS_VISIBILITY_PUBLIC=true */ +package aiyh.utils.tool.org.apache.commons.jexl3.parser; + +public +class ASTConstructorNode extends JexlNode { + public ASTConstructorNode(int id) { + super(id); + } + + public ASTConstructorNode(Parser p, int id) { + super(p, id); + } + + + /** Accept the visitor. **/ + public Object jjtAccept(ParserVisitor visitor, Object data) { + return + visitor.visit(this, data); + } +} +/* ParserGeneratorCC - OriginalChecksum=70bc27721984d7019eed5bf429966764 (do not edit this line) */ diff --git a/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTContinue.java b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTContinue.java new file mode 100644 index 0000000..f7c528d --- /dev/null +++ b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTContinue.java @@ -0,0 +1,22 @@ +/* Generated by: JJTree: Do not edit this line. ASTContinue.java Version 1.1 */ +/* ParserGeneratorCCOptions:MULTI=true,NODE_USES_PARSER=false,VISITOR=true,TRACK_TOKENS=true,NODE_PREFIX=AST,NODE_EXTENDS=,NODE_FACTORY=,SUPPORT_CLASS_VISIBILITY_PUBLIC=true */ +package aiyh.utils.tool.org.apache.commons.jexl3.parser; + +public +class ASTContinue extends JexlNode { + public ASTContinue(int id) { + super(id); + } + + public ASTContinue(Parser p, int id) { + super(p, id); + } + + + /** Accept the visitor. **/ + public Object jjtAccept(ParserVisitor visitor, Object data) { + return + visitor.visit(this, data); + } +} +/* ParserGeneratorCC - OriginalChecksum=fef9263bbdb7e5c0628ea4eb289ea023 (do not edit this line) */ diff --git a/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTDivNode.java b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTDivNode.java new file mode 100644 index 0000000..9b136a7 --- /dev/null +++ b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTDivNode.java @@ -0,0 +1,22 @@ +/* Generated by: JJTree: Do not edit this line. ASTDivNode.java Version 1.1 */ +/* ParserGeneratorCCOptions:MULTI=true,NODE_USES_PARSER=false,VISITOR=true,TRACK_TOKENS=true,NODE_PREFIX=AST,NODE_EXTENDS=,NODE_FACTORY=,SUPPORT_CLASS_VISIBILITY_PUBLIC=true */ +package aiyh.utils.tool.org.apache.commons.jexl3.parser; + +public +class ASTDivNode extends JexlNode { + public ASTDivNode(int id) { + super(id); + } + + public ASTDivNode(Parser p, int id) { + super(p, id); + } + + + /** Accept the visitor. **/ + public Object jjtAccept(ParserVisitor visitor, Object data) { + return + visitor.visit(this, data); + } +} +/* ParserGeneratorCC - OriginalChecksum=11e33898e07b9cc8bb68e2d83b4733c4 (do not edit this line) */ diff --git a/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTDoWhileStatement.java b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTDoWhileStatement.java new file mode 100644 index 0000000..fe8900d --- /dev/null +++ b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTDoWhileStatement.java @@ -0,0 +1,22 @@ +/* Generated by: JJTree: Do not edit this line. ASTDoWhileStatement.java Version 1.1 */ +/* ParserGeneratorCCOptions:MULTI=true,NODE_USES_PARSER=false,VISITOR=true,TRACK_TOKENS=true,NODE_PREFIX=AST,NODE_EXTENDS=,NODE_FACTORY=,SUPPORT_CLASS_VISIBILITY_PUBLIC=true */ +package aiyh.utils.tool.org.apache.commons.jexl3.parser; + +public +class ASTDoWhileStatement extends JexlNode { + public ASTDoWhileStatement(int id) { + super(id); + } + + public ASTDoWhileStatement(Parser p, int id) { + super(p, id); + } + + + /** Accept the visitor. **/ + public Object jjtAccept(ParserVisitor visitor, Object data) { + return + visitor.visit(this, data); + } +} +/* ParserGeneratorCC - OriginalChecksum=a5f9770b80d8e9585817452b8b4e3f07 (do not edit this line) */ diff --git a/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTEQNode.java b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTEQNode.java new file mode 100644 index 0000000..f605165 --- /dev/null +++ b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTEQNode.java @@ -0,0 +1,22 @@ +/* Generated by: JJTree: Do not edit this line. ASTEQNode.java Version 1.1 */ +/* ParserGeneratorCCOptions:MULTI=true,NODE_USES_PARSER=false,VISITOR=true,TRACK_TOKENS=true,NODE_PREFIX=AST,NODE_EXTENDS=,NODE_FACTORY=,SUPPORT_CLASS_VISIBILITY_PUBLIC=true */ +package aiyh.utils.tool.org.apache.commons.jexl3.parser; + +public +class ASTEQNode extends JexlNode { + public ASTEQNode(int id) { + super(id); + } + + public ASTEQNode(Parser p, int id) { + super(p, id); + } + + + /** Accept the visitor. **/ + public Object jjtAccept(ParserVisitor visitor, Object data) { + return + visitor.visit(this, data); + } +} +/* ParserGeneratorCC - OriginalChecksum=b834385ba52e4b0493b9b29c08260092 (do not edit this line) */ diff --git a/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTERNode.java b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTERNode.java new file mode 100644 index 0000000..94e6db3 --- /dev/null +++ b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTERNode.java @@ -0,0 +1,22 @@ +/* Generated by: JJTree: Do not edit this line. ASTERNode.java Version 1.1 */ +/* ParserGeneratorCCOptions:MULTI=true,NODE_USES_PARSER=false,VISITOR=true,TRACK_TOKENS=true,NODE_PREFIX=AST,NODE_EXTENDS=,NODE_FACTORY=,SUPPORT_CLASS_VISIBILITY_PUBLIC=true */ +package aiyh.utils.tool.org.apache.commons.jexl3.parser; + +public +class ASTERNode extends JexlNode { + public ASTERNode(int id) { + super(id); + } + + public ASTERNode(Parser p, int id) { + super(p, id); + } + + + /** Accept the visitor. **/ + public Object jjtAccept(ParserVisitor visitor, Object data) { + return + visitor.visit(this, data); + } +} +/* ParserGeneratorCC - OriginalChecksum=d1d28a19686e08a7e2d769e1b48e4ac8 (do not edit this line) */ diff --git a/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTEWNode.java b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTEWNode.java new file mode 100644 index 0000000..a65ff58 --- /dev/null +++ b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTEWNode.java @@ -0,0 +1,22 @@ +/* Generated by: JJTree: Do not edit this line. ASTEWNode.java Version 1.1 */ +/* ParserGeneratorCCOptions:MULTI=true,NODE_USES_PARSER=false,VISITOR=true,TRACK_TOKENS=true,NODE_PREFIX=AST,NODE_EXTENDS=,NODE_FACTORY=,SUPPORT_CLASS_VISIBILITY_PUBLIC=true */ +package aiyh.utils.tool.org.apache.commons.jexl3.parser; + +public +class ASTEWNode extends JexlNode { + public ASTEWNode(int id) { + super(id); + } + + public ASTEWNode(Parser p, int id) { + super(p, id); + } + + + /** Accept the visitor. **/ + public Object jjtAccept(ParserVisitor visitor, Object data) { + return + visitor.visit(this, data); + } +} +/* ParserGeneratorCC - OriginalChecksum=6b784152590211436433bd8dd544f8a0 (do not edit this line) */ diff --git a/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTEmptyFunction.java b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTEmptyFunction.java new file mode 100644 index 0000000..7812660 --- /dev/null +++ b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTEmptyFunction.java @@ -0,0 +1,22 @@ +/* Generated by: JJTree: Do not edit this line. ASTEmptyFunction.java Version 1.1 */ +/* ParserGeneratorCCOptions:MULTI=true,NODE_USES_PARSER=false,VISITOR=true,TRACK_TOKENS=true,NODE_PREFIX=AST,NODE_EXTENDS=,NODE_FACTORY=,SUPPORT_CLASS_VISIBILITY_PUBLIC=true */ +package aiyh.utils.tool.org.apache.commons.jexl3.parser; + +public +class ASTEmptyFunction extends JexlNode { + public ASTEmptyFunction(int id) { + super(id); + } + + public ASTEmptyFunction(Parser p, int id) { + super(p, id); + } + + + /** Accept the visitor. **/ + public Object jjtAccept(ParserVisitor visitor, Object data) { + return + visitor.visit(this, data); + } +} +/* ParserGeneratorCC - OriginalChecksum=1638f1cd2222b1635675576d3784d87a (do not edit this line) */ diff --git a/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTExtendedLiteral.java b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTExtendedLiteral.java new file mode 100644 index 0000000..d3fd88d --- /dev/null +++ b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTExtendedLiteral.java @@ -0,0 +1,22 @@ +/* Generated by: JJTree: Do not edit this line. ASTExtendedLiteral.java Version 1.1 */ +/* ParserGeneratorCCOptions:MULTI=true,NODE_USES_PARSER=false,VISITOR=true,TRACK_TOKENS=true,NODE_PREFIX=AST,NODE_EXTENDS=,NODE_FACTORY=,SUPPORT_CLASS_VISIBILITY_PUBLIC=true */ +package aiyh.utils.tool.org.apache.commons.jexl3.parser; + +public +class ASTExtendedLiteral extends JexlNode { + public ASTExtendedLiteral(int id) { + super(id); + } + + public ASTExtendedLiteral(Parser p, int id) { + super(p, id); + } + + + /** Accept the visitor. **/ + public Object jjtAccept(ParserVisitor visitor, Object data) { + return + visitor.visit(this, data); + } +} +/* ParserGeneratorCC - OriginalChecksum=32ae9984ba69f72b0cf1b4122ec9946f (do not edit this line) */ diff --git a/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTFalseNode.java b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTFalseNode.java new file mode 100644 index 0000000..ccc5e6e --- /dev/null +++ b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTFalseNode.java @@ -0,0 +1,22 @@ +/* Generated by: JJTree: Do not edit this line. ASTFalseNode.java Version 1.1 */ +/* ParserGeneratorCCOptions:MULTI=true,NODE_USES_PARSER=false,VISITOR=true,TRACK_TOKENS=true,NODE_PREFIX=AST,NODE_EXTENDS=,NODE_FACTORY=,SUPPORT_CLASS_VISIBILITY_PUBLIC=true */ +package aiyh.utils.tool.org.apache.commons.jexl3.parser; + +public +class ASTFalseNode extends JexlNode { + public ASTFalseNode(int id) { + super(id); + } + + public ASTFalseNode(Parser p, int id) { + super(p, id); + } + + + /** Accept the visitor. **/ + public Object jjtAccept(ParserVisitor visitor, Object data) { + return + visitor.visit(this, data); + } +} +/* ParserGeneratorCC - OriginalChecksum=6826c5e1f1f05de07085361c398ab664 (do not edit this line) */ diff --git a/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTForeachStatement.java b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTForeachStatement.java new file mode 100644 index 0000000..a71465e --- /dev/null +++ b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTForeachStatement.java @@ -0,0 +1,37 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package aiyh.utils.tool.org.apache.commons.jexl3.parser; + +/** + * Declares a for each loop. + */ +public class ASTForeachStatement extends JexlLexicalNode { + + public ASTForeachStatement(final int id) { + super(id); + } + + public ASTForeachStatement(final Parser p, final int id) { + super(p, id); + } + + @Override + public Object jjtAccept(final ParserVisitor visitor, final Object data) { + return visitor.visit(this, data); + } + +} \ No newline at end of file diff --git a/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTFunctionNode.java b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTFunctionNode.java new file mode 100644 index 0000000..ac6c7c3 --- /dev/null +++ b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTFunctionNode.java @@ -0,0 +1,22 @@ +/* Generated by: JJTree: Do not edit this line. ASTFunctionNode.java Version 1.1 */ +/* ParserGeneratorCCOptions:MULTI=true,NODE_USES_PARSER=false,VISITOR=true,TRACK_TOKENS=true,NODE_PREFIX=AST,NODE_EXTENDS=,NODE_FACTORY=,SUPPORT_CLASS_VISIBILITY_PUBLIC=true */ +package aiyh.utils.tool.org.apache.commons.jexl3.parser; + +public +class ASTFunctionNode extends JexlNode { + public ASTFunctionNode(int id) { + super(id); + } + + public ASTFunctionNode(Parser p, int id) { + super(p, id); + } + + + /** Accept the visitor. **/ + public Object jjtAccept(ParserVisitor visitor, Object data) { + return + visitor.visit(this, data); + } +} +/* ParserGeneratorCC - OriginalChecksum=c045697b9bdb20ca4cac761e749e164e (do not edit this line) */ diff --git a/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTGENode.java b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTGENode.java new file mode 100644 index 0000000..2749392 --- /dev/null +++ b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTGENode.java @@ -0,0 +1,22 @@ +/* Generated by: JJTree: Do not edit this line. ASTGENode.java Version 1.1 */ +/* ParserGeneratorCCOptions:MULTI=true,NODE_USES_PARSER=false,VISITOR=true,TRACK_TOKENS=true,NODE_PREFIX=AST,NODE_EXTENDS=,NODE_FACTORY=,SUPPORT_CLASS_VISIBILITY_PUBLIC=true */ +package aiyh.utils.tool.org.apache.commons.jexl3.parser; + +public +class ASTGENode extends JexlNode { + public ASTGENode(int id) { + super(id); + } + + public ASTGENode(Parser p, int id) { + super(p, id); + } + + + /** Accept the visitor. **/ + public Object jjtAccept(ParserVisitor visitor, Object data) { + return + visitor.visit(this, data); + } +} +/* ParserGeneratorCC - OriginalChecksum=dd856035ccad3970bc25804016f791dd (do not edit this line) */ diff --git a/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTGTNode.java b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTGTNode.java new file mode 100644 index 0000000..0f9aca6 --- /dev/null +++ b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTGTNode.java @@ -0,0 +1,22 @@ +/* Generated by: JJTree: Do not edit this line. ASTGTNode.java Version 1.1 */ +/* ParserGeneratorCCOptions:MULTI=true,NODE_USES_PARSER=false,VISITOR=true,TRACK_TOKENS=true,NODE_PREFIX=AST,NODE_EXTENDS=,NODE_FACTORY=,SUPPORT_CLASS_VISIBILITY_PUBLIC=true */ +package aiyh.utils.tool.org.apache.commons.jexl3.parser; + +public +class ASTGTNode extends JexlNode { + public ASTGTNode(int id) { + super(id); + } + + public ASTGTNode(Parser p, int id) { + super(p, id); + } + + + /** Accept the visitor. **/ + public Object jjtAccept(ParserVisitor visitor, Object data) { + return + visitor.visit(this, data); + } +} +/* ParserGeneratorCC - OriginalChecksum=fd623ed5a60d86628257ad557622e633 (do not edit this line) */ diff --git a/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTIdentifier.java b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTIdentifier.java new file mode 100644 index 0000000..c60b12f --- /dev/null +++ b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTIdentifier.java @@ -0,0 +1,120 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package aiyh.utils.tool.org.apache.commons.jexl3.parser; + +/** + * Identifiers, variables, ie symbols. + */ +public class ASTIdentifier extends JexlNode { + protected String name = null; + protected int symbol = -1; + protected int flags = 0; + + /** The redefined variable flag. */ + private static final int REDEFINED = 0; + /** The shaded variable flag. */ + private static final int SHADED = 1; + /** The captured variable flag. */ + private static final int CAPTURED = 2; + + ASTIdentifier(final int id) { + super(id); + } + + ASTIdentifier(final Parser p, final int id) { + super(p, id); + } + + @Override + public String toString() { + return name; + } + + void setSymbol(final String identifier) { + if (identifier.charAt(0) == '#') { + symbol = Integer.parseInt(identifier.substring(1)); + } + name = identifier; + } + + void setSymbol(final int r, final String identifier) { + symbol = r; + name = identifier; + } + + public int getSymbol() { + return symbol; + } + + /** + * Sets the value of a flag in a mask. + * @param ordinal the flag ordinal + * @param mask the flags mask + * @param value true or false + * @return the new flags mask value + */ + private static int set(final int ordinal, final int mask, final boolean value) { + return value? mask | (1 << ordinal) : mask & ~(1 << ordinal); + } + + /** + * Checks the value of a flag in the mask. + * @param ordinal the flag ordinal + * @param mask the flags mask + * @return the mask value with this flag or-ed in + */ + private static boolean isSet(final int ordinal, final int mask) { + return (mask & 1 << ordinal) != 0; + } + + public void setRedefined(final boolean f) { + flags = set(REDEFINED, flags, f); + } + + public boolean isRedefined() { + return isSet(REDEFINED, flags); + } + + public void setShaded(final boolean f) { + flags = set(SHADED, flags, f); + } + + public boolean isShaded() { + return isSet(SHADED, flags); + } + + public void setCaptured(final boolean f) { + flags = set(CAPTURED, flags, f); + } + + public boolean isCaptured() { + return isSet(CAPTURED, flags); + } + + public String getName() { + return name; + } + + public String getNamespace() { + return null; + } + + @Override + public Object jjtAccept(final ParserVisitor visitor, final Object data) { + return visitor.visit(this, data); + } +} diff --git a/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTIdentifierAccess.java b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTIdentifierAccess.java new file mode 100644 index 0000000..7bddad6 --- /dev/null +++ b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTIdentifierAccess.java @@ -0,0 +1,111 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package aiyh.utils.tool.org.apache.commons.jexl3.parser; + + +/** + * Identifiers, variables and registers. + */ +public class ASTIdentifierAccess extends JexlNode { + private String name = null; + private Integer identifier = null; + + ASTIdentifierAccess(final int id) { + super(id); + } + + ASTIdentifierAccess(final Parser p, final int id) { + super(p, id); + } + + void setIdentifier(final String id) { + name = id; + identifier = parseIdentifier(id); + } + + @Override + public boolean isGlobalVar() { + return !isSafe() && !isExpression(); + } + + /** + * Whether this is a dot or a question-mark-dot aka safe-navigation access. + * @return true is ?., false if . + */ + public boolean isSafe() { + return false; + } + + /** + * Whether this is a Jxlt based identifier. + * @return true if `..${...}...`, false otherwise + */ + public boolean isExpression() { + return false; + } + + /** + * Parse an identifier which must be of the form: + * 0|([1-9][0-9]*) + * @param id the identifier + * @return an integer or null + */ + public static Integer parseIdentifier(final String id) { + // hand coded because the was no way to fail on leading '0's using NumberFormat + if (id != null) { + final int length = id.length(); + int val = 0; + for (int i = 0; i < length; ++i) { + final char c = id.charAt(i); + // leading 0s but no just 0, NaN + if (c == '0') { + if (length == 1) { + return 0; + } + if (val == 0) { + return null; + } + } // any non numeric, NaN + else if (c < '0' || c > '9') { + return null; + } + val *= 10; + val += (c - '0'); + } + return val; + } + return null; + } + + public Object getIdentifier() { + return identifier != null? identifier : name; + } + + public String getName() { + return name; + } + + @Override + public Object jjtAccept(final ParserVisitor visitor, final Object data) { + return visitor.visit(this, data); + } + + @Override + public String toString() { + return name; + } +} diff --git a/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTIdentifierAccessJxlt.java b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTIdentifierAccessJxlt.java new file mode 100644 index 0000000..f31a5f5 --- /dev/null +++ b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTIdentifierAccessJxlt.java @@ -0,0 +1,49 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package aiyh.utils.tool.org.apache.commons.jexl3.parser; + +import aiyh.utils.tool.org.apache.commons.jexl3.JxltEngine; + +/** + * x.`expr`. + */ +public class ASTIdentifierAccessJxlt extends ASTIdentifierAccess { + protected JxltEngine.Expression jxltExpr; + + ASTIdentifierAccessJxlt(final int id) { + super(id); + } + + ASTIdentifierAccessJxlt(final Parser p, final int id) { + super(p, id); + } + + @Override + public boolean isExpression() { + return true; + } + + public void setExpression(final JxltEngine.Expression tp) { + jxltExpr = tp; + } + + public JxltEngine.Expression getExpression() { + return jxltExpr; + } + +} diff --git a/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTIdentifierAccessSafe.java b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTIdentifierAccessSafe.java new file mode 100644 index 0000000..8d79ae4 --- /dev/null +++ b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTIdentifierAccessSafe.java @@ -0,0 +1,37 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package aiyh.utils.tool.org.apache.commons.jexl3.parser; + +/** + * x?.identifier . + */ +public class ASTIdentifierAccessSafe extends ASTIdentifierAccess { + ASTIdentifierAccessSafe(final int id) { + super(id); + } + + ASTIdentifierAccessSafe(final Parser p, final int id) { + super(p, id); + } + + @Override + public boolean isSafe() { + return true; + } + +} diff --git a/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTIdentifierAccessSafeJxlt.java b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTIdentifierAccessSafeJxlt.java new file mode 100644 index 0000000..c0dca64 --- /dev/null +++ b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTIdentifierAccessSafeJxlt.java @@ -0,0 +1,37 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package aiyh.utils.tool.org.apache.commons.jexl3.parser; + +/** + * x?.`expr` . + */ +public class ASTIdentifierAccessSafeJxlt extends ASTIdentifierAccessJxlt { + ASTIdentifierAccessSafeJxlt(final int id) { + super(id); + } + + ASTIdentifierAccessSafeJxlt(final Parser p, final int id) { + super(p, id); + } + + @Override + public boolean isSafe() { + return true; + } + +} diff --git a/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTIfStatement.java b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTIfStatement.java new file mode 100644 index 0000000..d8a076b --- /dev/null +++ b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTIfStatement.java @@ -0,0 +1,22 @@ +/* Generated by: JJTree: Do not edit this line. ASTIfStatement.java Version 1.1 */ +/* ParserGeneratorCCOptions:MULTI=true,NODE_USES_PARSER=false,VISITOR=true,TRACK_TOKENS=true,NODE_PREFIX=AST,NODE_EXTENDS=,NODE_FACTORY=,SUPPORT_CLASS_VISIBILITY_PUBLIC=true */ +package aiyh.utils.tool.org.apache.commons.jexl3.parser; + +public +class ASTIfStatement extends JexlNode { + public ASTIfStatement(int id) { + super(id); + } + + public ASTIfStatement(Parser p, int id) { + super(p, id); + } + + + /** Accept the visitor. **/ + public Object jjtAccept(ParserVisitor visitor, Object data) { + return + visitor.visit(this, data); + } +} +/* ParserGeneratorCC - OriginalChecksum=a514bbeeb5097f0f9c332b309d1f25c3 (do not edit this line) */ diff --git a/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTJexlLambda.java b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTJexlLambda.java new file mode 100644 index 0000000..5481ddf --- /dev/null +++ b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTJexlLambda.java @@ -0,0 +1,37 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package aiyh.utils.tool.org.apache.commons.jexl3.parser; + +/** + * Lambda (function). + */ +public final class ASTJexlLambda extends ASTJexlScript { + ASTJexlLambda(final int id) { + super(id); + } + + ASTJexlLambda(final Parser p, final int id) { + super(p, id); + } + + /** + * @return true if outermost script. + */ + public boolean isTopLevel() { + return jjtGetParent() == null; + } +} diff --git a/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTJexlScript.java b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTJexlScript.java new file mode 100644 index 0000000..ee4a5d5 --- /dev/null +++ b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTJexlScript.java @@ -0,0 +1,171 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package aiyh.utils.tool.org.apache.commons.jexl3.parser; + +import aiyh.utils.tool.org.apache.commons.jexl3.JexlFeatures; +import aiyh.utils.tool.org.apache.commons.jexl3.internal.Frame; +import aiyh.utils.tool.org.apache.commons.jexl3.internal.Scope; + +import java.util.Map; + +/** + * Enhanced script to allow parameters declaration. + */ +public class ASTJexlScript extends JexlLexicalNode { + /** The pragmas. */ + private Map pragmas = null; + /** Features. */ + private JexlFeatures features = null; + /** The script scope. */ + private Scope scope = null; + + public ASTJexlScript(final int id) { + super(id); + } + + public ASTJexlScript(final Parser p, final int id) { + super(p, id); + } + + /** + * Consider script with no parameters that return lambda as parametric-scripts. + * @return the script + */ + public ASTJexlScript script() { + if (scope == null && jjtGetNumChildren() == 1 && jjtGetChild(0) instanceof ASTJexlLambda) { + final ASTJexlLambda lambda = (ASTJexlLambda) jjtGetChild(0); + lambda.jjtSetParent(null); + return lambda; + } + return this; + } + + @Override + public Object jjtAccept(final ParserVisitor visitor, final Object data) { + return visitor.visit(this, data); + } + + /** + * Sets this script pragmas. + * @param thePragmas the pragmas + */ + public void setPragmas(final Map thePragmas) { + this.pragmas = thePragmas; + } + + /** + * @return this script pragmas. + */ + public Map getPragmas() { + return pragmas; + } + + /** + * Sets this script features. + * @param theFeatures the features + */ + public void setFeatures(final JexlFeatures theFeatures) { + this.features = theFeatures; + } + + /** + * @return this script scope + */ + public JexlFeatures getFeatures() { + return features; + } + + /** + * Sets this script scope. + * @param theScope the scope + */ + public void setScope(final Scope theScope) { + this.scope = theScope; + if (theScope != null) { + for(int a = 0; a < theScope.getArgCount(); ++a) { + this.declareSymbol(a); + } + } + } + + /** + * @return this script scope + */ + public Scope getScope() { + return scope; + } + + /** + * Creates an array of arguments by copying values up to the number of parameters. + * @param caller the calling frame + * @param values the argument values + * @return the arguments array + */ + public Frame createFrame(final Frame caller, final Object... values) { + return scope != null? scope.createFrame(caller, values) : null; + } + + /** + * Creates an array of arguments by copying values up to the number of parameters. + * @param values the argument values + * @return the arguments array + */ + public Frame createFrame(final Object... values) { + return createFrame(null, values); + } + + /** + * Gets the (maximum) number of arguments this script expects. + * @return the number of parameters + */ + public int getArgCount() { + return scope != null? scope.getArgCount() : 0; + } + + /** + * Gets this script symbols, i.e. parameters and local variables. + * @return the symbol names + */ + public String[] getSymbols() { + return scope != null? scope.getSymbols() : null; + } + + /** + * Gets this script parameters, i.e. symbols assigned before creating local variables. + * @return the parameter names + */ + public String[] getParameters() { + return scope != null? scope.getParameters() : null; + } + + /** + * Gets this script local variable, i.e. symbols assigned to local variables. + * @return the local variable names + */ + public String[] getLocalVariables() { + return scope != null? scope.getLocalVariables() : null; + } + + /** + * Checks whether a given symbol is captured. + * @param symbol the symbol number + * @return true if captured, false otherwise + */ + public boolean isCapturedSymbol(final int symbol) { + return scope != null && scope.isCapturedSymbol(symbol); + } +} diff --git a/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTJxltLiteral.java b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTJxltLiteral.java new file mode 100644 index 0000000..b96605d --- /dev/null +++ b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTJxltLiteral.java @@ -0,0 +1,52 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package aiyh.utils.tool.org.apache.commons.jexl3.parser; + +public final class ASTJxltLiteral extends JexlNode { + /** The actual literal value; the inherited 'value' member may host a cached template expression. */ + private String literal = null; + + ASTJxltLiteral(final int id) { + super(id); + } + + ASTJxltLiteral(final Parser p, final int id) { + super(p, id); + } + + void setLiteral(final String literal) { + this.literal = literal; + } + + /** + * Gets the literal value. + * @return the string literal + */ + public String getLiteral() { + return this.literal; + } + + @Override + public String toString() { + return this.literal; + } + + @Override + public Object jjtAccept(final ParserVisitor visitor, final Object data) { + return visitor.visit(this, data); + } +} diff --git a/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTLENode.java b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTLENode.java new file mode 100644 index 0000000..087064e --- /dev/null +++ b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTLENode.java @@ -0,0 +1,22 @@ +/* Generated by: JJTree: Do not edit this line. ASTLENode.java Version 1.1 */ +/* ParserGeneratorCCOptions:MULTI=true,NODE_USES_PARSER=false,VISITOR=true,TRACK_TOKENS=true,NODE_PREFIX=AST,NODE_EXTENDS=,NODE_FACTORY=,SUPPORT_CLASS_VISIBILITY_PUBLIC=true */ +package aiyh.utils.tool.org.apache.commons.jexl3.parser; + +public +class ASTLENode extends JexlNode { + public ASTLENode(int id) { + super(id); + } + + public ASTLENode(Parser p, int id) { + super(p, id); + } + + + /** Accept the visitor. **/ + public Object jjtAccept(ParserVisitor visitor, Object data) { + return + visitor.visit(this, data); + } +} +/* ParserGeneratorCC - OriginalChecksum=b15c6b8613b14d915d281c5ab136a54e (do not edit this line) */ diff --git a/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTLTNode.java b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTLTNode.java new file mode 100644 index 0000000..4c97b26 --- /dev/null +++ b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTLTNode.java @@ -0,0 +1,22 @@ +/* Generated by: JJTree: Do not edit this line. ASTLTNode.java Version 1.1 */ +/* ParserGeneratorCCOptions:MULTI=true,NODE_USES_PARSER=false,VISITOR=true,TRACK_TOKENS=true,NODE_PREFIX=AST,NODE_EXTENDS=,NODE_FACTORY=,SUPPORT_CLASS_VISIBILITY_PUBLIC=true */ +package aiyh.utils.tool.org.apache.commons.jexl3.parser; + +public +class ASTLTNode extends JexlNode { + public ASTLTNode(int id) { + super(id); + } + + public ASTLTNode(Parser p, int id) { + super(p, id); + } + + + /** Accept the visitor. **/ + public Object jjtAccept(ParserVisitor visitor, Object data) { + return + visitor.visit(this, data); + } +} +/* ParserGeneratorCC - OriginalChecksum=2ed2a6d7cabd19b7b9f1009d0d808b97 (do not edit this line) */ diff --git a/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTMapEntry.java b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTMapEntry.java new file mode 100644 index 0000000..ba99cbc --- /dev/null +++ b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTMapEntry.java @@ -0,0 +1,22 @@ +/* Generated by: JJTree: Do not edit this line. ASTMapEntry.java Version 1.1 */ +/* ParserGeneratorCCOptions:MULTI=true,NODE_USES_PARSER=false,VISITOR=true,TRACK_TOKENS=true,NODE_PREFIX=AST,NODE_EXTENDS=,NODE_FACTORY=,SUPPORT_CLASS_VISIBILITY_PUBLIC=true */ +package aiyh.utils.tool.org.apache.commons.jexl3.parser; + +public +class ASTMapEntry extends JexlNode { + public ASTMapEntry(int id) { + super(id); + } + + public ASTMapEntry(Parser p, int id) { + super(p, id); + } + + + /** Accept the visitor. **/ + public Object jjtAccept(ParserVisitor visitor, Object data) { + return + visitor.visit(this, data); + } +} +/* ParserGeneratorCC - OriginalChecksum=0f8df401558825987100ff250ff2729c (do not edit this line) */ diff --git a/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTMapLiteral.java b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTMapLiteral.java new file mode 100644 index 0000000..b370a4f --- /dev/null +++ b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTMapLiteral.java @@ -0,0 +1,62 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package aiyh.utils.tool.org.apache.commons.jexl3.parser; + +import aiyh.utils.tool.org.apache.commons.jexl3.internal.Debugger; + +public final class ASTMapLiteral extends JexlNode { + /** Whether this array is constant or not. */ + private boolean constant = false; + + ASTMapLiteral(final int id) { + super(id); + } + + ASTMapLiteral(final Parser p, final int id) { + super(p, id); + } + + @Override + public String toString() { + final Debugger dbg = new Debugger(); + return dbg.data(this); + } + + @Override + protected boolean isConstant(final boolean literal) { + return constant; + } + + @Override + public void jjtClose() { + constant = true; + for (int c = 0; c < jjtGetNumChildren() && constant; ++c) { + final JexlNode child = jjtGetChild(c); + if (child instanceof ASTMapEntry) { + constant = child.isConstant(true); + } else if (!child.isConstant()) { + constant = false; + } + } + } + + @Override + public Object jjtAccept(final ParserVisitor visitor, final Object data) { + return visitor.visit(this, data); + } + +} diff --git a/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTMethodNode.java b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTMethodNode.java new file mode 100644 index 0000000..a9235b7 --- /dev/null +++ b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTMethodNode.java @@ -0,0 +1,22 @@ +/* Generated by: JJTree: Do not edit this line. ASTMethodNode.java Version 1.1 */ +/* ParserGeneratorCCOptions:MULTI=true,NODE_USES_PARSER=false,VISITOR=true,TRACK_TOKENS=true,NODE_PREFIX=AST,NODE_EXTENDS=,NODE_FACTORY=,SUPPORT_CLASS_VISIBILITY_PUBLIC=true */ +package aiyh.utils.tool.org.apache.commons.jexl3.parser; + +public +class ASTMethodNode extends JexlNode { + public ASTMethodNode(int id) { + super(id); + } + + public ASTMethodNode(Parser p, int id) { + super(p, id); + } + + + /** Accept the visitor. **/ + public Object jjtAccept(ParserVisitor visitor, Object data) { + return + visitor.visit(this, data); + } +} +/* ParserGeneratorCC - OriginalChecksum=6ac4d73db02109669152b29719e7576e (do not edit this line) */ diff --git a/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTModNode.java b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTModNode.java new file mode 100644 index 0000000..e1448ef --- /dev/null +++ b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTModNode.java @@ -0,0 +1,22 @@ +/* Generated by: JJTree: Do not edit this line. ASTModNode.java Version 1.1 */ +/* ParserGeneratorCCOptions:MULTI=true,NODE_USES_PARSER=false,VISITOR=true,TRACK_TOKENS=true,NODE_PREFIX=AST,NODE_EXTENDS=,NODE_FACTORY=,SUPPORT_CLASS_VISIBILITY_PUBLIC=true */ +package aiyh.utils.tool.org.apache.commons.jexl3.parser; + +public +class ASTModNode extends JexlNode { + public ASTModNode(int id) { + super(id); + } + + public ASTModNode(Parser p, int id) { + super(p, id); + } + + + /** Accept the visitor. **/ + public Object jjtAccept(ParserVisitor visitor, Object data) { + return + visitor.visit(this, data); + } +} +/* ParserGeneratorCC - OriginalChecksum=59f73b886acd13b070fe00328b6574da (do not edit this line) */ diff --git a/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTMulNode.java b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTMulNode.java new file mode 100644 index 0000000..bec21d3 --- /dev/null +++ b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTMulNode.java @@ -0,0 +1,22 @@ +/* Generated by: JJTree: Do not edit this line. ASTMulNode.java Version 1.1 */ +/* ParserGeneratorCCOptions:MULTI=true,NODE_USES_PARSER=false,VISITOR=true,TRACK_TOKENS=true,NODE_PREFIX=AST,NODE_EXTENDS=,NODE_FACTORY=,SUPPORT_CLASS_VISIBILITY_PUBLIC=true */ +package aiyh.utils.tool.org.apache.commons.jexl3.parser; + +public +class ASTMulNode extends JexlNode { + public ASTMulNode(int id) { + super(id); + } + + public ASTMulNode(Parser p, int id) { + super(p, id); + } + + + /** Accept the visitor. **/ + public Object jjtAccept(ParserVisitor visitor, Object data) { + return + visitor.visit(this, data); + } +} +/* ParserGeneratorCC - OriginalChecksum=4af5ac4d6a4b4a04d16f900d916a6f02 (do not edit this line) */ diff --git a/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTNENode.java b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTNENode.java new file mode 100644 index 0000000..51da3b3 --- /dev/null +++ b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTNENode.java @@ -0,0 +1,22 @@ +/* Generated by: JJTree: Do not edit this line. ASTNENode.java Version 1.1 */ +/* ParserGeneratorCCOptions:MULTI=true,NODE_USES_PARSER=false,VISITOR=true,TRACK_TOKENS=true,NODE_PREFIX=AST,NODE_EXTENDS=,NODE_FACTORY=,SUPPORT_CLASS_VISIBILITY_PUBLIC=true */ +package aiyh.utils.tool.org.apache.commons.jexl3.parser; + +public +class ASTNENode extends JexlNode { + public ASTNENode(int id) { + super(id); + } + + public ASTNENode(Parser p, int id) { + super(p, id); + } + + + /** Accept the visitor. **/ + public Object jjtAccept(ParserVisitor visitor, Object data) { + return + visitor.visit(this, data); + } +} +/* ParserGeneratorCC - OriginalChecksum=ef7834f4c03fd33a761c1e77775fbbc3 (do not edit this line) */ diff --git a/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTNEWNode.java b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTNEWNode.java new file mode 100644 index 0000000..3a4454b --- /dev/null +++ b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTNEWNode.java @@ -0,0 +1,22 @@ +/* Generated by: JJTree: Do not edit this line. ASTNEWNode.java Version 1.1 */ +/* ParserGeneratorCCOptions:MULTI=true,NODE_USES_PARSER=false,VISITOR=true,TRACK_TOKENS=true,NODE_PREFIX=AST,NODE_EXTENDS=,NODE_FACTORY=,SUPPORT_CLASS_VISIBILITY_PUBLIC=true */ +package aiyh.utils.tool.org.apache.commons.jexl3.parser; + +public +class ASTNEWNode extends JexlNode { + public ASTNEWNode(int id) { + super(id); + } + + public ASTNEWNode(Parser p, int id) { + super(p, id); + } + + + /** Accept the visitor. **/ + public Object jjtAccept(ParserVisitor visitor, Object data) { + return + visitor.visit(this, data); + } +} +/* ParserGeneratorCC - OriginalChecksum=66caac16910d7f3460c83bda40a1002b (do not edit this line) */ diff --git a/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTNRNode.java b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTNRNode.java new file mode 100644 index 0000000..5a3b9ee --- /dev/null +++ b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTNRNode.java @@ -0,0 +1,22 @@ +/* Generated by: JJTree: Do not edit this line. ASTNRNode.java Version 1.1 */ +/* ParserGeneratorCCOptions:MULTI=true,NODE_USES_PARSER=false,VISITOR=true,TRACK_TOKENS=true,NODE_PREFIX=AST,NODE_EXTENDS=,NODE_FACTORY=,SUPPORT_CLASS_VISIBILITY_PUBLIC=true */ +package aiyh.utils.tool.org.apache.commons.jexl3.parser; + +public +class ASTNRNode extends JexlNode { + public ASTNRNode(int id) { + super(id); + } + + public ASTNRNode(Parser p, int id) { + super(p, id); + } + + + /** Accept the visitor. **/ + public Object jjtAccept(ParserVisitor visitor, Object data) { + return + visitor.visit(this, data); + } +} +/* ParserGeneratorCC - OriginalChecksum=f437e3aa6512451823ad292d07b4c35d (do not edit this line) */ diff --git a/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTNSWNode.java b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTNSWNode.java new file mode 100644 index 0000000..53c29eb --- /dev/null +++ b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTNSWNode.java @@ -0,0 +1,22 @@ +/* Generated by: JJTree: Do not edit this line. ASTNSWNode.java Version 1.1 */ +/* ParserGeneratorCCOptions:MULTI=true,NODE_USES_PARSER=false,VISITOR=true,TRACK_TOKENS=true,NODE_PREFIX=AST,NODE_EXTENDS=,NODE_FACTORY=,SUPPORT_CLASS_VISIBILITY_PUBLIC=true */ +package aiyh.utils.tool.org.apache.commons.jexl3.parser; + +public +class ASTNSWNode extends JexlNode { + public ASTNSWNode(int id) { + super(id); + } + + public ASTNSWNode(Parser p, int id) { + super(p, id); + } + + + /** Accept the visitor. **/ + public Object jjtAccept(ParserVisitor visitor, Object data) { + return + visitor.visit(this, data); + } +} +/* ParserGeneratorCC - OriginalChecksum=becd29726e25ed57a18195c2d37e5cb5 (do not edit this line) */ diff --git a/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTNamespaceIdentifier.java b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTNamespaceIdentifier.java new file mode 100644 index 0000000..628f89f --- /dev/null +++ b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTNamespaceIdentifier.java @@ -0,0 +1,44 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package aiyh.utils.tool.org.apache.commons.jexl3.parser; + +/** + * Namespace : identifier. + */ +public class ASTNamespaceIdentifier extends ASTIdentifier { + private String namespace; + + public ASTNamespaceIdentifier(final int id) { + super(id); + } + + @Override + public String getNamespace() { + return namespace; + } + + /** + * Sets the namespace:identifier. + * + * @param ns the namespace + * @param id the names + */ + public void setNamespace(final String ns, final String id) { + this.namespace = ns; + this.name = id; + } +} diff --git a/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTNotNode.java b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTNotNode.java new file mode 100644 index 0000000..ef94692 --- /dev/null +++ b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTNotNode.java @@ -0,0 +1,22 @@ +/* Generated by: JJTree: Do not edit this line. ASTNotNode.java Version 1.1 */ +/* ParserGeneratorCCOptions:MULTI=true,NODE_USES_PARSER=false,VISITOR=true,TRACK_TOKENS=true,NODE_PREFIX=AST,NODE_EXTENDS=,NODE_FACTORY=,SUPPORT_CLASS_VISIBILITY_PUBLIC=true */ +package aiyh.utils.tool.org.apache.commons.jexl3.parser; + +public +class ASTNotNode extends JexlNode { + public ASTNotNode(int id) { + super(id); + } + + public ASTNotNode(Parser p, int id) { + super(p, id); + } + + + /** Accept the visitor. **/ + public Object jjtAccept(ParserVisitor visitor, Object data) { + return + visitor.visit(this, data); + } +} +/* ParserGeneratorCC - OriginalChecksum=914adf75a0b896595405a5c0f8c3f528 (do not edit this line) */ diff --git a/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTNullLiteral.java b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTNullLiteral.java new file mode 100644 index 0000000..42218db --- /dev/null +++ b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTNullLiteral.java @@ -0,0 +1,22 @@ +/* Generated by: JJTree: Do not edit this line. ASTNullLiteral.java Version 1.1 */ +/* ParserGeneratorCCOptions:MULTI=true,NODE_USES_PARSER=false,VISITOR=true,TRACK_TOKENS=true,NODE_PREFIX=AST,NODE_EXTENDS=,NODE_FACTORY=,SUPPORT_CLASS_VISIBILITY_PUBLIC=true */ +package aiyh.utils.tool.org.apache.commons.jexl3.parser; + +public +class ASTNullLiteral extends JexlNode { + public ASTNullLiteral(int id) { + super(id); + } + + public ASTNullLiteral(Parser p, int id) { + super(p, id); + } + + + /** Accept the visitor. **/ + public Object jjtAccept(ParserVisitor visitor, Object data) { + return + visitor.visit(this, data); + } +} +/* ParserGeneratorCC - OriginalChecksum=4cb9bae85cc75e887dba9ad09eb8534a (do not edit this line) */ diff --git a/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTNullpNode.java b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTNullpNode.java new file mode 100644 index 0000000..e6d23c0 --- /dev/null +++ b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTNullpNode.java @@ -0,0 +1,22 @@ +/* Generated by: JJTree: Do not edit this line. ASTNullpNode.java Version 1.1 */ +/* ParserGeneratorCCOptions:MULTI=true,NODE_USES_PARSER=false,VISITOR=true,TRACK_TOKENS=true,NODE_PREFIX=AST,NODE_EXTENDS=,NODE_FACTORY=,SUPPORT_CLASS_VISIBILITY_PUBLIC=true */ +package aiyh.utils.tool.org.apache.commons.jexl3.parser; + +public +class ASTNullpNode extends JexlNode { + public ASTNullpNode(int id) { + super(id); + } + + public ASTNullpNode(Parser p, int id) { + super(p, id); + } + + + /** Accept the visitor. **/ + public Object jjtAccept(ParserVisitor visitor, Object data) { + return + visitor.visit(this, data); + } +} +/* ParserGeneratorCC - OriginalChecksum=ba2622ffbf5db4315aefd9152dee4a15 (do not edit this line) */ diff --git a/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTNumberLiteral.java b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTNumberLiteral.java new file mode 100644 index 0000000..b0b182f --- /dev/null +++ b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTNumberLiteral.java @@ -0,0 +1,77 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package aiyh.utils.tool.org.apache.commons.jexl3.parser; + +public final class ASTNumberLiteral extends JexlNode implements JexlNode.Constant { + private final NumberParser nlp; + + ASTNumberLiteral(final int id) { + super(id); + nlp = new NumberParser(); + } + + ASTNumberLiteral(final Parser p, final int id) { + super(p, id); + nlp = new NumberParser(); + } + + @Override + public String toString() { + return nlp.toString(); + } + + @Override + public Number getLiteral() { + return nlp.getLiteralValue(); + } + + @Override + protected boolean isConstant(final boolean literal) { + return true; + } + + public Class getLiteralClass() { + return nlp.getLiteralClass(); + } + + public boolean isInteger() { + return nlp.isInteger(); + } + + /** + * Sets this node as a natural literal. + * Originally from OGNL. + * @param s the natural as string + */ + void setNatural(final String s) { + nlp.setNatural(s); + } + + /** + * Sets this node as a real literal. + * Originally from OGNL. + * @param s the real as string + */ + void setReal(final String s) { + nlp.setReal(s); + } + + @Override + public Object jjtAccept(final ParserVisitor visitor, final Object data) { + return visitor.visit(this, data); + } +} diff --git a/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTOrNode.java b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTOrNode.java new file mode 100644 index 0000000..2cd7f35 --- /dev/null +++ b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTOrNode.java @@ -0,0 +1,22 @@ +/* Generated by: JJTree: Do not edit this line. ASTOrNode.java Version 1.1 */ +/* ParserGeneratorCCOptions:MULTI=true,NODE_USES_PARSER=false,VISITOR=true,TRACK_TOKENS=true,NODE_PREFIX=AST,NODE_EXTENDS=,NODE_FACTORY=,SUPPORT_CLASS_VISIBILITY_PUBLIC=true */ +package aiyh.utils.tool.org.apache.commons.jexl3.parser; + +public +class ASTOrNode extends JexlNode { + public ASTOrNode(int id) { + super(id); + } + + public ASTOrNode(Parser p, int id) { + super(p, id); + } + + + /** Accept the visitor. **/ + public Object jjtAccept(ParserVisitor visitor, Object data) { + return + visitor.visit(this, data); + } +} +/* ParserGeneratorCC - OriginalChecksum=b81c5866e4dc218da22e6659400dd301 (do not edit this line) */ diff --git a/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTRangeNode.java b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTRangeNode.java new file mode 100644 index 0000000..c1d48bf --- /dev/null +++ b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTRangeNode.java @@ -0,0 +1,22 @@ +/* Generated by: JJTree: Do not edit this line. ASTRangeNode.java Version 1.1 */ +/* ParserGeneratorCCOptions:MULTI=true,NODE_USES_PARSER=false,VISITOR=true,TRACK_TOKENS=true,NODE_PREFIX=AST,NODE_EXTENDS=,NODE_FACTORY=,SUPPORT_CLASS_VISIBILITY_PUBLIC=true */ +package aiyh.utils.tool.org.apache.commons.jexl3.parser; + +public +class ASTRangeNode extends JexlNode { + public ASTRangeNode(int id) { + super(id); + } + + public ASTRangeNode(Parser p, int id) { + super(p, id); + } + + + /** Accept the visitor. **/ + public Object jjtAccept(ParserVisitor visitor, Object data) { + return + visitor.visit(this, data); + } +} +/* ParserGeneratorCC - OriginalChecksum=9c509b4868f183ba78e6594a5db9aaa2 (do not edit this line) */ diff --git a/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTReference.java b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTReference.java new file mode 100644 index 0000000..a0df5f5 --- /dev/null +++ b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTReference.java @@ -0,0 +1,22 @@ +/* Generated by: JJTree: Do not edit this line. ASTReference.java Version 1.1 */ +/* ParserGeneratorCCOptions:MULTI=true,NODE_USES_PARSER=false,VISITOR=true,TRACK_TOKENS=true,NODE_PREFIX=AST,NODE_EXTENDS=,NODE_FACTORY=,SUPPORT_CLASS_VISIBILITY_PUBLIC=true */ +package aiyh.utils.tool.org.apache.commons.jexl3.parser; + +public +class ASTReference extends JexlNode { + public ASTReference(int id) { + super(id); + } + + public ASTReference(Parser p, int id) { + super(p, id); + } + + + /** Accept the visitor. **/ + public Object jjtAccept(ParserVisitor visitor, Object data) { + return + visitor.visit(this, data); + } +} +/* ParserGeneratorCC - OriginalChecksum=b26c674a7c2167f5b89df8bf61428ebe (do not edit this line) */ diff --git a/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTReferenceExpression.java b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTReferenceExpression.java new file mode 100644 index 0000000..3e59b6e --- /dev/null +++ b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTReferenceExpression.java @@ -0,0 +1,33 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package aiyh.utils.tool.org.apache.commons.jexl3.parser; + +public final class ASTReferenceExpression extends JexlNode { + ASTReferenceExpression(final int id) { + super(id); + } + + ASTReferenceExpression(final Parser p, final int id) { + super(p, id); + } + + /** Accept the visitor. **/ + @Override + public Object jjtAccept(final ParserVisitor visitor, final Object data) { + return visitor.visit(this, data); + } +} diff --git a/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTRegexLiteral.java b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTRegexLiteral.java new file mode 100644 index 0000000..2074440 --- /dev/null +++ b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTRegexLiteral.java @@ -0,0 +1,62 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package aiyh.utils.tool.org.apache.commons.jexl3.parser; + +import java.util.regex.Pattern; + +public final class ASTRegexLiteral extends JexlNode implements JexlNode.Constant { + + /** The actual literal value; the inherited 'value' member may host a cached getter. */ + + private Pattern literal = null; + + ASTRegexLiteral(final int id) { + super(id); + } + + ASTRegexLiteral(final Parser p, final int id) { + super(p, id); + } + + @Override + public String toString() { + return literal != null ? literal.toString() : ""; + } + + /** + * Gets the literal value. + * @return the Pattern literal + */ + @Override + public Pattern getLiteral() { + return this.literal; + } + + @Override + protected boolean isConstant(final boolean literal) { + return true; + } + + void setLiteral(final String literal) { + this.literal = Pattern.compile(literal); + } + + @Override + public Object jjtAccept(final ParserVisitor visitor, final Object data) { + return visitor.visit(this, data); + } +} diff --git a/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTReturnStatement.java b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTReturnStatement.java new file mode 100644 index 0000000..b62091e --- /dev/null +++ b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTReturnStatement.java @@ -0,0 +1,22 @@ +/* Generated by: JJTree: Do not edit this line. ASTReturnStatement.java Version 1.1 */ +/* ParserGeneratorCCOptions:MULTI=true,NODE_USES_PARSER=false,VISITOR=true,TRACK_TOKENS=true,NODE_PREFIX=AST,NODE_EXTENDS=,NODE_FACTORY=,SUPPORT_CLASS_VISIBILITY_PUBLIC=true */ +package aiyh.utils.tool.org.apache.commons.jexl3.parser; + +public +class ASTReturnStatement extends JexlNode { + public ASTReturnStatement(int id) { + super(id); + } + + public ASTReturnStatement(Parser p, int id) { + super(p, id); + } + + + /** Accept the visitor. **/ + public Object jjtAccept(ParserVisitor visitor, Object data) { + return + visitor.visit(this, data); + } +} +/* ParserGeneratorCC - OriginalChecksum=2627c8aceab3023092ee73054f0a7493 (do not edit this line) */ diff --git a/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTSWNode.java b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTSWNode.java new file mode 100644 index 0000000..8f177e9 --- /dev/null +++ b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTSWNode.java @@ -0,0 +1,22 @@ +/* Generated by: JJTree: Do not edit this line. ASTSWNode.java Version 1.1 */ +/* ParserGeneratorCCOptions:MULTI=true,NODE_USES_PARSER=false,VISITOR=true,TRACK_TOKENS=true,NODE_PREFIX=AST,NODE_EXTENDS=,NODE_FACTORY=,SUPPORT_CLASS_VISIBILITY_PUBLIC=true */ +package aiyh.utils.tool.org.apache.commons.jexl3.parser; + +public +class ASTSWNode extends JexlNode { + public ASTSWNode(int id) { + super(id); + } + + public ASTSWNode(Parser p, int id) { + super(p, id); + } + + + /** Accept the visitor. **/ + public Object jjtAccept(ParserVisitor visitor, Object data) { + return + visitor.visit(this, data); + } +} +/* ParserGeneratorCC - OriginalChecksum=bb57a74b10bf9d43993ca905d64111a8 (do not edit this line) */ diff --git a/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTSetAddNode.java b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTSetAddNode.java new file mode 100644 index 0000000..1ab69ff --- /dev/null +++ b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTSetAddNode.java @@ -0,0 +1,22 @@ +/* Generated by: JJTree: Do not edit this line. ASTSetAddNode.java Version 1.1 */ +/* ParserGeneratorCCOptions:MULTI=true,NODE_USES_PARSER=false,VISITOR=true,TRACK_TOKENS=true,NODE_PREFIX=AST,NODE_EXTENDS=,NODE_FACTORY=,SUPPORT_CLASS_VISIBILITY_PUBLIC=true */ +package aiyh.utils.tool.org.apache.commons.jexl3.parser; + +public +class ASTSetAddNode extends JexlNode { + public ASTSetAddNode(int id) { + super(id); + } + + public ASTSetAddNode(Parser p, int id) { + super(p, id); + } + + + /** Accept the visitor. **/ + public Object jjtAccept(ParserVisitor visitor, Object data) { + return + visitor.visit(this, data); + } +} +/* ParserGeneratorCC - OriginalChecksum=375f8c82e39c6cb7d97e73f9cd737f2f (do not edit this line) */ diff --git a/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTSetAndNode.java b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTSetAndNode.java new file mode 100644 index 0000000..d399ab2 --- /dev/null +++ b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTSetAndNode.java @@ -0,0 +1,22 @@ +/* Generated by: JJTree: Do not edit this line. ASTSetAndNode.java Version 1.1 */ +/* ParserGeneratorCCOptions:MULTI=true,NODE_USES_PARSER=false,VISITOR=true,TRACK_TOKENS=true,NODE_PREFIX=AST,NODE_EXTENDS=,NODE_FACTORY=,SUPPORT_CLASS_VISIBILITY_PUBLIC=true */ +package aiyh.utils.tool.org.apache.commons.jexl3.parser; + +public +class ASTSetAndNode extends JexlNode { + public ASTSetAndNode(int id) { + super(id); + } + + public ASTSetAndNode(Parser p, int id) { + super(p, id); + } + + + /** Accept the visitor. **/ + public Object jjtAccept(ParserVisitor visitor, Object data) { + return + visitor.visit(this, data); + } +} +/* ParserGeneratorCC - OriginalChecksum=5151d8ebf60c6df7f2e04b7c15f281e8 (do not edit this line) */ diff --git a/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTSetDivNode.java b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTSetDivNode.java new file mode 100644 index 0000000..7f90799 --- /dev/null +++ b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTSetDivNode.java @@ -0,0 +1,22 @@ +/* Generated by: JJTree: Do not edit this line. ASTSetDivNode.java Version 1.1 */ +/* ParserGeneratorCCOptions:MULTI=true,NODE_USES_PARSER=false,VISITOR=true,TRACK_TOKENS=true,NODE_PREFIX=AST,NODE_EXTENDS=,NODE_FACTORY=,SUPPORT_CLASS_VISIBILITY_PUBLIC=true */ +package aiyh.utils.tool.org.apache.commons.jexl3.parser; + +public +class ASTSetDivNode extends JexlNode { + public ASTSetDivNode(int id) { + super(id); + } + + public ASTSetDivNode(Parser p, int id) { + super(p, id); + } + + + /** Accept the visitor. **/ + public Object jjtAccept(ParserVisitor visitor, Object data) { + return + visitor.visit(this, data); + } +} +/* ParserGeneratorCC - OriginalChecksum=841dc846cdb3b8726778ad7a65085133 (do not edit this line) */ diff --git a/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTSetLiteral.java b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTSetLiteral.java new file mode 100644 index 0000000..d0bd470 --- /dev/null +++ b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTSetLiteral.java @@ -0,0 +1,60 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package aiyh.utils.tool.org.apache.commons.jexl3.parser; + +import aiyh.utils.tool.org.apache.commons.jexl3.internal.Debugger; + +public final class ASTSetLiteral extends JexlNode { + /** Whether this set is constant or not. */ + private boolean constant = false; + + ASTSetLiteral(final int id) { + super(id); + } + + ASTSetLiteral(final Parser p, final int id) { + super(p, id); + } + + @Override + public String toString() { + final Debugger dbg = new Debugger(); + return dbg.data(this); + } + + @Override + protected boolean isConstant(final boolean literal) { + return constant; + } + + @Override + public void jjtClose() { + constant = true; + for (int c = 0; c < jjtGetNumChildren() && constant; ++c) { + final JexlNode child = jjtGetChild(c); + if (!child.isConstant()) { + constant = false; + } + } + } + + @Override + public Object jjtAccept(final ParserVisitor visitor, final Object data) { + return visitor.visit(this, data); + } + +} diff --git a/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTSetModNode.java b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTSetModNode.java new file mode 100644 index 0000000..fb70975 --- /dev/null +++ b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTSetModNode.java @@ -0,0 +1,22 @@ +/* Generated by: JJTree: Do not edit this line. ASTSetModNode.java Version 1.1 */ +/* ParserGeneratorCCOptions:MULTI=true,NODE_USES_PARSER=false,VISITOR=true,TRACK_TOKENS=true,NODE_PREFIX=AST,NODE_EXTENDS=,NODE_FACTORY=,SUPPORT_CLASS_VISIBILITY_PUBLIC=true */ +package aiyh.utils.tool.org.apache.commons.jexl3.parser; + +public +class ASTSetModNode extends JexlNode { + public ASTSetModNode(int id) { + super(id); + } + + public ASTSetModNode(Parser p, int id) { + super(p, id); + } + + + /** Accept the visitor. **/ + public Object jjtAccept(ParserVisitor visitor, Object data) { + return + visitor.visit(this, data); + } +} +/* ParserGeneratorCC - OriginalChecksum=ea623863a2110be1226059e297cb0cc4 (do not edit this line) */ diff --git a/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTSetMultNode.java b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTSetMultNode.java new file mode 100644 index 0000000..a2cfd36 --- /dev/null +++ b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTSetMultNode.java @@ -0,0 +1,22 @@ +/* Generated by: JJTree: Do not edit this line. ASTSetMultNode.java Version 1.1 */ +/* ParserGeneratorCCOptions:MULTI=true,NODE_USES_PARSER=false,VISITOR=true,TRACK_TOKENS=true,NODE_PREFIX=AST,NODE_EXTENDS=,NODE_FACTORY=,SUPPORT_CLASS_VISIBILITY_PUBLIC=true */ +package aiyh.utils.tool.org.apache.commons.jexl3.parser; + +public +class ASTSetMultNode extends JexlNode { + public ASTSetMultNode(int id) { + super(id); + } + + public ASTSetMultNode(Parser p, int id) { + super(p, id); + } + + + /** Accept the visitor. **/ + public Object jjtAccept(ParserVisitor visitor, Object data) { + return + visitor.visit(this, data); + } +} +/* ParserGeneratorCC - OriginalChecksum=b237cc925708eae813ed18639a503e16 (do not edit this line) */ diff --git a/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTSetOrNode.java b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTSetOrNode.java new file mode 100644 index 0000000..6dfa006 --- /dev/null +++ b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTSetOrNode.java @@ -0,0 +1,22 @@ +/* Generated by: JJTree: Do not edit this line. ASTSetOrNode.java Version 1.1 */ +/* ParserGeneratorCCOptions:MULTI=true,NODE_USES_PARSER=false,VISITOR=true,TRACK_TOKENS=true,NODE_PREFIX=AST,NODE_EXTENDS=,NODE_FACTORY=,SUPPORT_CLASS_VISIBILITY_PUBLIC=true */ +package aiyh.utils.tool.org.apache.commons.jexl3.parser; + +public +class ASTSetOrNode extends JexlNode { + public ASTSetOrNode(int id) { + super(id); + } + + public ASTSetOrNode(Parser p, int id) { + super(p, id); + } + + + /** Accept the visitor. **/ + public Object jjtAccept(ParserVisitor visitor, Object data) { + return + visitor.visit(this, data); + } +} +/* ParserGeneratorCC - OriginalChecksum=12ae29a350bae4671636675f1b075b73 (do not edit this line) */ diff --git a/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTSetSubNode.java b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTSetSubNode.java new file mode 100644 index 0000000..7f2a689 --- /dev/null +++ b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTSetSubNode.java @@ -0,0 +1,22 @@ +/* Generated by: JJTree: Do not edit this line. ASTSetSubNode.java Version 1.1 */ +/* ParserGeneratorCCOptions:MULTI=true,NODE_USES_PARSER=false,VISITOR=true,TRACK_TOKENS=true,NODE_PREFIX=AST,NODE_EXTENDS=,NODE_FACTORY=,SUPPORT_CLASS_VISIBILITY_PUBLIC=true */ +package aiyh.utils.tool.org.apache.commons.jexl3.parser; + +public +class ASTSetSubNode extends JexlNode { + public ASTSetSubNode(int id) { + super(id); + } + + public ASTSetSubNode(Parser p, int id) { + super(p, id); + } + + + /** Accept the visitor. **/ + public Object jjtAccept(ParserVisitor visitor, Object data) { + return + visitor.visit(this, data); + } +} +/* ParserGeneratorCC - OriginalChecksum=8d98520ac03f6a7d8bb7b2bd4b1d4af9 (do not edit this line) */ diff --git a/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTSetXorNode.java b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTSetXorNode.java new file mode 100644 index 0000000..15f4dd4 --- /dev/null +++ b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTSetXorNode.java @@ -0,0 +1,22 @@ +/* Generated by: JJTree: Do not edit this line. ASTSetXorNode.java Version 1.1 */ +/* ParserGeneratorCCOptions:MULTI=true,NODE_USES_PARSER=false,VISITOR=true,TRACK_TOKENS=true,NODE_PREFIX=AST,NODE_EXTENDS=,NODE_FACTORY=,SUPPORT_CLASS_VISIBILITY_PUBLIC=true */ +package aiyh.utils.tool.org.apache.commons.jexl3.parser; + +public +class ASTSetXorNode extends JexlNode { + public ASTSetXorNode(int id) { + super(id); + } + + public ASTSetXorNode(Parser p, int id) { + super(p, id); + } + + + /** Accept the visitor. **/ + public Object jjtAccept(ParserVisitor visitor, Object data) { + return + visitor.visit(this, data); + } +} +/* ParserGeneratorCC - OriginalChecksum=47ea70225f460a3b1d44dc7daa5a7302 (do not edit this line) */ diff --git a/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTSizeFunction.java b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTSizeFunction.java new file mode 100644 index 0000000..aa501e5 --- /dev/null +++ b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTSizeFunction.java @@ -0,0 +1,22 @@ +/* Generated by: JJTree: Do not edit this line. ASTSizeFunction.java Version 1.1 */ +/* ParserGeneratorCCOptions:MULTI=true,NODE_USES_PARSER=false,VISITOR=true,TRACK_TOKENS=true,NODE_PREFIX=AST,NODE_EXTENDS=,NODE_FACTORY=,SUPPORT_CLASS_VISIBILITY_PUBLIC=true */ +package aiyh.utils.tool.org.apache.commons.jexl3.parser; + +public +class ASTSizeFunction extends JexlNode { + public ASTSizeFunction(int id) { + super(id); + } + + public ASTSizeFunction(Parser p, int id) { + super(p, id); + } + + + /** Accept the visitor. **/ + public Object jjtAccept(ParserVisitor visitor, Object data) { + return + visitor.visit(this, data); + } +} +/* ParserGeneratorCC - OriginalChecksum=71dce91d00315b9caa79cb730ee88f10 (do not edit this line) */ diff --git a/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTStringLiteral.java b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTStringLiteral.java new file mode 100644 index 0000000..40de60d --- /dev/null +++ b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTStringLiteral.java @@ -0,0 +1,58 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package aiyh.utils.tool.org.apache.commons.jexl3.parser; + +public final class ASTStringLiteral extends JexlNode implements JexlNode.Constant { + /** The actual literal value; the inherited 'value' member may host a cached getter. */ + private String literal = null; + + ASTStringLiteral(final int id) { + super(id); + } + + ASTStringLiteral(final Parser p, final int id) { + super(p, id); + } + + @Override + public String toString() { + return this.literal; + } + + /** + * Gets the literal value. + * @return the string literal + */ + @Override + public String getLiteral() { + return this.literal; + } + + @Override + protected boolean isConstant(final boolean literal) { + return true; + } + + void setLiteral(final String literal) { + this.literal = literal; + } + + @Override + public Object jjtAccept(final ParserVisitor visitor, final Object data) { + return visitor.visit(this, data); + } +} diff --git a/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTSubNode.java b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTSubNode.java new file mode 100644 index 0000000..1f0e77b --- /dev/null +++ b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTSubNode.java @@ -0,0 +1,22 @@ +/* Generated by: JJTree: Do not edit this line. ASTSubNode.java Version 1.1 */ +/* ParserGeneratorCCOptions:MULTI=true,NODE_USES_PARSER=false,VISITOR=true,TRACK_TOKENS=true,NODE_PREFIX=AST,NODE_EXTENDS=,NODE_FACTORY=,SUPPORT_CLASS_VISIBILITY_PUBLIC=true */ +package aiyh.utils.tool.org.apache.commons.jexl3.parser; + +public +class ASTSubNode extends JexlNode { + public ASTSubNode(int id) { + super(id); + } + + public ASTSubNode(Parser p, int id) { + super(p, id); + } + + + /** Accept the visitor. **/ + public Object jjtAccept(ParserVisitor visitor, Object data) { + return + visitor.visit(this, data); + } +} +/* ParserGeneratorCC - OriginalChecksum=a384db28ac0bbbfa532cfd20725d63a1 (do not edit this line) */ diff --git a/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTTernaryNode.java b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTTernaryNode.java new file mode 100644 index 0000000..78852aa --- /dev/null +++ b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTTernaryNode.java @@ -0,0 +1,22 @@ +/* Generated by: JJTree: Do not edit this line. ASTTernaryNode.java Version 1.1 */ +/* ParserGeneratorCCOptions:MULTI=true,NODE_USES_PARSER=false,VISITOR=true,TRACK_TOKENS=true,NODE_PREFIX=AST,NODE_EXTENDS=,NODE_FACTORY=,SUPPORT_CLASS_VISIBILITY_PUBLIC=true */ +package aiyh.utils.tool.org.apache.commons.jexl3.parser; + +public +class ASTTernaryNode extends JexlNode { + public ASTTernaryNode(int id) { + super(id); + } + + public ASTTernaryNode(Parser p, int id) { + super(p, id); + } + + + /** Accept the visitor. **/ + public Object jjtAccept(ParserVisitor visitor, Object data) { + return + visitor.visit(this, data); + } +} +/* ParserGeneratorCC - OriginalChecksum=4d6eb0f8fa6d34d91f481299825efe1b (do not edit this line) */ diff --git a/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTTrueNode.java b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTTrueNode.java new file mode 100644 index 0000000..3f0a9a2 --- /dev/null +++ b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTTrueNode.java @@ -0,0 +1,22 @@ +/* Generated by: JJTree: Do not edit this line. ASTTrueNode.java Version 1.1 */ +/* ParserGeneratorCCOptions:MULTI=true,NODE_USES_PARSER=false,VISITOR=true,TRACK_TOKENS=true,NODE_PREFIX=AST,NODE_EXTENDS=,NODE_FACTORY=,SUPPORT_CLASS_VISIBILITY_PUBLIC=true */ +package aiyh.utils.tool.org.apache.commons.jexl3.parser; + +public +class ASTTrueNode extends JexlNode { + public ASTTrueNode(int id) { + super(id); + } + + public ASTTrueNode(Parser p, int id) { + super(p, id); + } + + + /** Accept the visitor. **/ + public Object jjtAccept(ParserVisitor visitor, Object data) { + return + visitor.visit(this, data); + } +} +/* ParserGeneratorCC - OriginalChecksum=c69e2b917c6c0ea24f139663a76f136f (do not edit this line) */ diff --git a/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTUnaryMinusNode.java b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTUnaryMinusNode.java new file mode 100644 index 0000000..12f6c05 --- /dev/null +++ b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTUnaryMinusNode.java @@ -0,0 +1,22 @@ +/* Generated by: JJTree: Do not edit this line. ASTUnaryMinusNode.java Version 1.1 */ +/* ParserGeneratorCCOptions:MULTI=true,NODE_USES_PARSER=false,VISITOR=true,TRACK_TOKENS=true,NODE_PREFIX=AST,NODE_EXTENDS=,NODE_FACTORY=,SUPPORT_CLASS_VISIBILITY_PUBLIC=true */ +package aiyh.utils.tool.org.apache.commons.jexl3.parser; + +public +class ASTUnaryMinusNode extends JexlNode { + public ASTUnaryMinusNode(int id) { + super(id); + } + + public ASTUnaryMinusNode(Parser p, int id) { + super(p, id); + } + + + /** Accept the visitor. **/ + public Object jjtAccept(ParserVisitor visitor, Object data) { + return + visitor.visit(this, data); + } +} +/* ParserGeneratorCC - OriginalChecksum=62166af7df7c07e0763c70d234905401 (do not edit this line) */ diff --git a/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTUnaryPlusNode.java b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTUnaryPlusNode.java new file mode 100644 index 0000000..5001a44 --- /dev/null +++ b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTUnaryPlusNode.java @@ -0,0 +1,22 @@ +/* Generated by: JJTree: Do not edit this line. ASTUnaryPlusNode.java Version 1.1 */ +/* ParserGeneratorCCOptions:MULTI=true,NODE_USES_PARSER=false,VISITOR=true,TRACK_TOKENS=true,NODE_PREFIX=AST,NODE_EXTENDS=,NODE_FACTORY=,SUPPORT_CLASS_VISIBILITY_PUBLIC=true */ +package aiyh.utils.tool.org.apache.commons.jexl3.parser; + +public +class ASTUnaryPlusNode extends JexlNode { + public ASTUnaryPlusNode(int id) { + super(id); + } + + public ASTUnaryPlusNode(Parser p, int id) { + super(p, id); + } + + + /** Accept the visitor. **/ + public Object jjtAccept(ParserVisitor visitor, Object data) { + return + visitor.visit(this, data); + } +} +/* ParserGeneratorCC - OriginalChecksum=26a257c4805637c57d349bc8393ac45a (do not edit this line) */ diff --git a/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTVar.java b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTVar.java new file mode 100644 index 0000000..ec78cda --- /dev/null +++ b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTVar.java @@ -0,0 +1,35 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package aiyh.utils.tool.org.apache.commons.jexl3.parser; + +/** + * Declares a local variable. + */ +public class ASTVar extends ASTIdentifier { + public ASTVar(final int id) { + super(id); + } + + public ASTVar(final Parser p, final int id) { + super(p, id); + } + + @Override + public Object jjtAccept(final ParserVisitor visitor, final Object data) { + return visitor.visit(this, data); + } +} diff --git a/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTWhileStatement.java b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTWhileStatement.java new file mode 100644 index 0000000..b6720fe --- /dev/null +++ b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ASTWhileStatement.java @@ -0,0 +1,22 @@ +/* Generated by: JJTree: Do not edit this line. ASTWhileStatement.java Version 1.1 */ +/* ParserGeneratorCCOptions:MULTI=true,NODE_USES_PARSER=false,VISITOR=true,TRACK_TOKENS=true,NODE_PREFIX=AST,NODE_EXTENDS=,NODE_FACTORY=,SUPPORT_CLASS_VISIBILITY_PUBLIC=true */ +package aiyh.utils.tool.org.apache.commons.jexl3.parser; + +public +class ASTWhileStatement extends JexlNode { + public ASTWhileStatement(int id) { + super(id); + } + + public ASTWhileStatement(Parser p, int id) { + super(p, id); + } + + + /** Accept the visitor. **/ + public Object jjtAccept(ParserVisitor visitor, Object data) { + return + visitor.visit(this, data); + } +} +/* ParserGeneratorCC - OriginalChecksum=035b97fbe77695dbb1b383d60d4fb158 (do not edit this line) */ diff --git a/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/AbstractCharStream.java b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/AbstractCharStream.java new file mode 100644 index 0000000..29bc47e --- /dev/null +++ b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/AbstractCharStream.java @@ -0,0 +1,547 @@ +/* Generated by: ParserGeneratorCC: Do not edit this line. AbstractCharStream.java Version 1.1 */ +/* ParserGeneratorCCOptions:SUPPORT_CLASS_VISIBILITY_PUBLIC=true */ +package aiyh.utils.tool.org.apache.commons.jexl3.parser; + +/** + * An implementation of interface CharStream, where the stream is assumed to + * contain only ASCII characters (without unicode processing). + */ + +public +abstract class AbstractCharStream +implements CharStream +{ + /** Default buffer size if nothing is specified */ + public static final int DEFAULT_BUF_SIZE = 4096; + + static final int hexval (final char c) throws java.io.IOException + { + switch (c) + { + case '0': + return 0; + case '1': + return 1; + case '2': + return 2; + case '3': + return 3; + case '4': + return 4; + case '5': + return 5; + case '6': + return 6; + case '7': + return 7; + case '8': + return 8; + case '9': + return 9; + case 'a': + case 'A': + return 10; + case 'b': + case 'B': + return 11; + case 'c': + case 'C': + return 12; + case 'd': + case 'D': + return 13; + case 'e': + case 'E': + return 14; + case 'f': + case 'F': + return 15; + default: + throw new java.io.IOException ("Invalid hex char '" + c + "' (=" + (int) c + ") provided!"); + } + } + + /** Tab size for formatting. Usually in the range 1 to 8. */ + private int m_nTabSize = 1; + + /** Internal circular buffer */ + protected char[] buffer; + + /** Overall buffer size - same as buffer.length */ + protected int bufsize; + + /** Current read position in buffer. */ + protected int bufpos; + + /** The number of unoccupied buffer array positions */ + protected int available; + + /** The first array index (of `buffer`) that the current token starts */ + protected int tokenBegin; + + /** Characters in the backup/pushBack buffer */ + protected int inBuf; + protected int maxNextCharInd; + + private int[] m_aBufLine; + private int[] m_aBufColumn; + + // Current line number + private int m_nLineNo; + // Current column number + private int m_nColumnNo; + + // Was the previous character a "\r" char? + private boolean m_bPrevCharIsCR; + // Was the previous character a "\n" char? + private boolean m_bPrevCharIsLF; + + // Is line/column tracking enabled? + private boolean m_bTrackLineColumn = true; + + + /** Constructor. */ + public AbstractCharStream(final int nStartLine, + final int nStartColumn, + final int nBufferSize) + { + reInit (nStartLine, nStartColumn, nBufferSize); + } + + /** Reinitialise. */ + public final void reInit(final int nStartLine, + final int nStartColumn, + final int nBufferSize) + { + m_nLineNo = nStartLine; + m_nColumnNo = nStartColumn - 1; + m_bPrevCharIsCR = false; + m_bPrevCharIsLF = false; + if (buffer == null || nBufferSize != buffer.length) + { + bufsize = nBufferSize; + available = nBufferSize; + buffer = new char[nBufferSize]; + m_aBufLine = new int[nBufferSize]; + m_aBufColumn = new int[nBufferSize]; + } + maxNextCharInd = 0; + inBuf = 0; + tokenBegin = 0; + bufpos = -1; + } + + /** + * Read from the underlying stream. + * @param aBuf the buffer to be filled + * @param nOfs The offset into the buffer. 0-based + * @param nLen Number of chars to read. + * @return Number of effective chars read, or -1 on error. + */ + protected abstract int streamRead (char[] aBuf, int nOfs, int nLen) throws java.io.IOException; + + /** + * Close the underlying stream. + * @throws java.io.IOException If closing fails. + */ + protected abstract void streamClose () throws java.io.IOException; + + // Override this method if you need more aggressive buffer size expansion + protected int getBufSizeAfterExpansion () + { + // Double the size by default + return bufsize * 2; + } + + protected void expandBuff (final boolean bWrapAround) + { + // Get the new buffer size + final int nNewBufSize = getBufSizeAfterExpansion (); + + final char[] newbuffer = new char[nNewBufSize]; + final int[] newbufline = new int[nNewBufSize]; + final int[] newbufcolumn = new int[nNewBufSize]; + + // Number of chars to be preserved + final int nPreservedChars = bufsize - tokenBegin; + + if (bWrapAround) + { + // Move from offset "tokenBegin" to offset 0 + // arraycopy(src, srcPos, dest, destPos, length) + + // copy the "tail end" to the "start" (index 0) of the new buffer array + System.arraycopy(buffer, tokenBegin, newbuffer, 0, nPreservedChars); + + // copy the remaining "wrap around" content of the buffer from the start of the original buffer (starting at srcPos index 0) + System.arraycopy(buffer, 0, newbuffer, nPreservedChars, bufpos); + + // swap the new buffer in place of the old buffer + buffer = newbuffer; + + System.arraycopy(m_aBufLine, tokenBegin, newbufline, 0, nPreservedChars); + System.arraycopy(m_aBufLine, 0, newbufline, nPreservedChars, bufpos); + m_aBufLine = newbufline; + + System.arraycopy(m_aBufColumn, tokenBegin, newbufcolumn, 0, nPreservedChars); + System.arraycopy(m_aBufColumn, 0, newbufcolumn, nPreservedChars, bufpos); + m_aBufColumn = newbufcolumn; + + bufpos += nPreservedChars; + maxNextCharInd = bufpos; + } + else + { + // Move from offset "tokenBegin" to offset 0 + + System.arraycopy(buffer, tokenBegin, newbuffer, 0, nPreservedChars); + buffer = newbuffer; + + System.arraycopy(m_aBufLine, tokenBegin, newbufline, 0, nPreservedChars); + m_aBufLine = newbufline; + + System.arraycopy(m_aBufColumn, tokenBegin, newbufcolumn, 0, nPreservedChars); + m_aBufColumn = newbufcolumn; + + bufpos -= tokenBegin; + maxNextCharInd = bufpos; + } + + // Increase buffer size + bufsize = nNewBufSize; + available = nNewBufSize; + tokenBegin = 0; + } + + protected final void internalAdjustBuffSize() + { + final int nHalfBufferSize = bufsize / 2; + if (available == bufsize) + { + if (tokenBegin < 0) + { + // If this method is called from "beginToken()" + // Just refill the buffer from the start + bufpos = 0; + maxNextCharInd = 0; + } + else + if (tokenBegin > nHalfBufferSize) + { + // The token started in the second half - fill the front part + bufpos = 0; + maxNextCharInd = 0; + + // Available bytes are > 50% + available = tokenBegin; + } + else + { + // Token starts in the first half + // just append to existing buffer + expandBuff (false); + } + } + else + { + // A token was read across array boundaries + if (available > tokenBegin) + { + available = bufsize; + } + else + if ((tokenBegin - available) < nHalfBufferSize) + { + expandBuff (true); + } + else + { + available = tokenBegin; + } + } + } + + protected void fillBuff() throws java.io.IOException + { + if (maxNextCharInd == available) + internalAdjustBuffSize(); + + try + { + // Read from underlying stream + final int nCharsRead = streamRead (buffer, maxNextCharInd, available - maxNextCharInd); + if (nCharsRead == -1) + { + // We reached the end of the file + streamClose (); + + // Caught down below and re-thrown + throw new java.io.IOException("PGCC end of stream"); + } + maxNextCharInd += nCharsRead; + } + catch (final java.io.IOException ex) + { + --bufpos; + // ?What is the reason of this? Backup of 0 does nothing + backup (0); + if (tokenBegin == -1) + { + // Error occurred in "beginToken()" + tokenBegin = bufpos; + } + throw ex; + } + } + + protected final void internalSetBufLineColumn (final int nLine, final int nColumn) + { + m_aBufLine[bufpos] = nLine; + m_aBufColumn[bufpos] = nColumn; + } + + protected final void internalUpdateLineColumn(final char c) + { + m_nColumnNo++; + + if (m_bPrevCharIsLF) + { + // It's a "\r\n" or "\n" + // Start of a new line + m_bPrevCharIsLF = false; + m_nColumnNo = 1; + m_nLineNo++; + } + else + if (m_bPrevCharIsCR) + { + m_bPrevCharIsCR = false; + if (c == '\n') + { + // It's a "\r\n" + m_bPrevCharIsLF = true; + } + else + { + // It's only a "\r" + m_nColumnNo = 1; + m_nLineNo++; + } + } + + switch (c) + { + case '\r': + m_bPrevCharIsCR = true; + break; + case '\n': + m_bPrevCharIsLF = true; + break; + case '\t': + m_nColumnNo--; + m_nColumnNo += (m_nTabSize - (m_nColumnNo % m_nTabSize)); + break; + } + + internalSetBufLineColumn (m_nLineNo, m_nColumnNo); + } + + public char readChar() throws java.io.IOException + { + if (inBuf > 0) + { + // Something is left from last backup + --inBuf; + + ++bufpos; + if (bufpos == bufsize) + { + // Buffer overflow + bufpos = 0; + } + + return buffer[bufpos]; + } + + ++bufpos; + if (bufpos >= maxNextCharInd) + fillBuff(); + + final char c = buffer[bufpos]; + + if (m_bTrackLineColumn) + internalUpdateLineColumn(c); + return c; + } + + public char beginToken() throws java.io.IOException + { + tokenBegin = -1; + final char c = readChar(); + tokenBegin = bufpos; + return c; + } + + public int getBeginColumn () + { + return m_aBufColumn[tokenBegin]; + } + + public int getBeginLine () + { + return m_aBufLine[tokenBegin]; + } + + public int getEndColumn () + { + return m_aBufColumn[bufpos]; + } + + public int getEndLine () + { + return m_aBufLine[bufpos]; + } + + public void backup (final int nAmount) + { + if (nAmount > bufsize) + throw new IllegalStateException ("Cannot back " + nAmount + " chars which is larger than the internal buffer size (" + bufsize + ")"); + + inBuf += nAmount; + bufpos -= nAmount; + if (bufpos < 0) + { + // Buffer underflow (modulo) + bufpos += bufsize; + } + } + + public String getImage() + { + if (bufpos >= tokenBegin) + { + // from tokenBegin to bufpos + return new String (buffer, tokenBegin, bufpos - tokenBegin + 1); + } + + // from tokenBegin to bufsize, and from 0 to bufpos + return new String (buffer, tokenBegin, bufsize - tokenBegin) + + new String (buffer, 0, bufpos + 1); + } + + public char[] getSuffix (final int len) + { + char[] ret = new char[len]; + if ((bufpos + 1) >= len) + { + // one piece + System.arraycopy(buffer, bufpos - len + 1, ret, 0, len); + } + else + { + // Wrap around + final int nPart1 = len - bufpos - 1; + System.arraycopy(buffer, bufsize - nPart1, ret, 0, nPart1); + System.arraycopy(buffer, 0, ret, nPart1, bufpos + 1); + } + return ret; + } + + public void done() + { + buffer = null; + m_aBufLine = null; + m_aBufColumn = null; + } + + public final int getTabSize() + { + return m_nTabSize; + } + + public final void setTabSize (final int nTabSize) + { + m_nTabSize = nTabSize; + } + + /** + * Method to adjust line and column numbers for the start of a token. + * This is used internally to + */ + public final void adjustBeginLineColumn(final int nNewLine, final int newCol) + { + int start = tokenBegin; + int newLine = nNewLine; + + int len; + if (bufpos >= tokenBegin) + { + len = bufpos - tokenBegin + inBuf + 1; + } + else + { + len = bufsize - tokenBegin + bufpos + 1 + inBuf; + } + + int i = 0; + int j = 0; + int k = 0; + int nextColDiff = 0; + int columnDiff = 0; + + // TODO disassemble meaning and split up + while (i < len && m_aBufLine[j = start % bufsize] == m_aBufLine[k = ++start % bufsize]) + { + m_aBufLine[j] = newLine; + nextColDiff = columnDiff + m_aBufColumn[k] - m_aBufColumn[j]; + m_aBufColumn[j] = newCol + columnDiff; + columnDiff = nextColDiff; + i++; + } + + if (i < len) + { + m_aBufLine[j] = newLine++; + m_aBufColumn[j] = newCol + columnDiff; + + while (i++ < len) + { + // TODO disassemble meaning and split up + if (m_aBufLine[j = start % bufsize] != m_aBufLine[++start % bufsize]) + m_aBufLine[j] = newLine++; + else + m_aBufLine[j] = newLine; + } + } + + m_nLineNo = m_aBufLine[j]; + m_nColumnNo = m_aBufColumn[j]; + } + + /** + * @return the current line number. 0-based. + */ + protected final int getLine () + { + return m_nLineNo; + } + + /** + * @return the current column number. 0-based. + */ + protected final int getColumn () + { + return m_nColumnNo; + } + + public final boolean isTrackLineColumn () + { + return m_bTrackLineColumn; + } + + public final void setTrackLineColumn (final boolean bTrackLineColumn) + { + m_bTrackLineColumn = bTrackLineColumn; + } +} +/* ParserGeneratorCC - OriginalChecksum=283eac7ebcfba4d68a92326195156b9a (do not edit this line) */ diff --git a/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/CharStream.java b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/CharStream.java new file mode 100644 index 0000000..7efc340 --- /dev/null +++ b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/CharStream.java @@ -0,0 +1,125 @@ +/* Generated by: ParserGeneratorCC: Do not edit this line. CharStream.java Version 1.1 */ +/* ParserGeneratorCCOptions:SUPPORT_CLASS_VISIBILITY_PUBLIC=true */ +package aiyh.utils.tool.org.apache.commons.jexl3.parser; + +/** + * This interface describes a character stream that maintains line and + * column number positions of the characters. It also has the capability + * to backup the stream to some extent. An implementation of this + * interface is used in the TokenManager implementation generated by + * JavaCCParser. + * + * All the methods except backup can be implemented in any fashion. backup + * needs to be implemented correctly for the correct operation of the lexer. + * Rest of the methods are all used to get information like line number, + * column number and the String that constitutes a token and are not used + * by the lexer. Hence their implementation won't affect the generated lexer's + * operation. + */ + +public +interface CharStream { + /** + * Get the next character from the selected input. The method + * of selecting the input is the responsibility of the class + * implementing this interface. + * @return the next character from the selected input + * @throws java.io.IOException on IO error + */ + char readChar() throws java.io.IOException; + + /** + * @return the column number of the first character for current token (being + * matched after the last call to beginToken). + */ + int getBeginColumn(); + + /** + * @return the line number of the first character for current token (being + * matched after the last call to BeginToken). + */ + int getBeginLine(); + + /** + * @return the column number of the last character for current token (being + * matched after the last call to BeginToken). + */ + int getEndColumn(); + + /** + * @return the line number of the last character for current token (being + * matched after the last call to BeginToken). + */ + int getEndLine(); + + /** + * Backs up the input stream by amount steps. Lexer calls this method if it + * had already read some characters, but could not use them to match a + * (longer) token. So, they will be used again as the prefix of the next + * token and it is the implemetation's responsibility to do this right. + * @param amount Number of chars to back up. + */ + void backup(int amount); + + /** + * @return the next character that marks the beginning of the next token. + * All characters must remain in the buffer between two successive calls + * to this method to implement backup correctly. + */ + char beginToken() throws java.io.IOException; + + /** + * @return a string made up of characters from the marked token beginning + * to the current buffer position. Implementations have the choice of returning + * anything that they want to. For example, for efficiency, one might decide + * to just return null, which is a valid implementation. + */ + String getImage(); + + /** + * @return an array of characters that make up the suffix of length 'len' for + * the currently matched token. This is used to build up the matched string + * for use in actions in the case of MORE. A simple and inefficient + * implementation of this is as follows: + *
+   *   {
+   *      String t = getImage();
+   *      return t.substring(t.length() - len, t.length()).toCharArray();
+   *   }
+   * 
+ */ + char[] getSuffix(int len); + + /** + * The lexer calls this function to indicate that it is done with the stream + * and hence implementations can free any resources held by this class. + * Again, the body of this function can be just empty and it will not + * affect the lexer's operation. + */ + void done(); + + // Getters and setters + + /** + * @return Current tab size. + */ + int getTabSize(); + + /** + * Set the tab size to use. + * @param i spaces per tab + */ + void setTabSize(int i); + + /** + * @return true if line number and column numbers should be tracked. + */ + boolean isTrackLineColumn(); + + /** + * Enable or disable line number and column number tracking. + * @param trackLineColumn true to track it, false to not do it. + */ + void setTrackLineColumn(boolean trackLineColumn); +} +/* ParserGeneratorCC - OriginalChecksum=339f3e12a400872cfc11d078eb9ad32e (do not edit this line) */ diff --git a/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/FeatureController.java b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/FeatureController.java new file mode 100644 index 0000000..27c40f8 --- /dev/null +++ b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/FeatureController.java @@ -0,0 +1,238 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package aiyh.utils.tool.org.apache.commons.jexl3.parser; + +import aiyh.utils.tool.org.apache.commons.jexl3.JexlException; +import aiyh.utils.tool.org.apache.commons.jexl3.JexlFeatures; +import aiyh.utils.tool.org.apache.commons.jexl3.JexlInfo; +import aiyh.utils.tool.org.apache.commons.jexl3.internal.ScriptVisitor; + +/** + * Controls that a script only uses enabled features. + */ +public class FeatureController extends ScriptVisitor { + /** The set of features. */ + private JexlFeatures features = null; + + /** + * Creates a features controller . + */ + public FeatureController(final JexlFeatures features) { + this.features = features; + } + + /** + * Sets the features to controlNode. + * @param fdesc the features + */ + public void setFeatures(final JexlFeatures fdesc) { + this.features = fdesc; + } + + /** + * @return the controlled features + */ + public JexlFeatures getFeatures() { + return features; + } + + /** + * Perform the control on a node. + *

Note that controlNode() does *not* visit node children in this class. + * @param node the node to controlNode + * @throws JexlException.Feature if required feature is disabled + */ + public void controlNode(final JexlNode node) { + node.jjtAccept(this, null); + } + + @Override + protected Object visitNode(final JexlNode node, final Object data) { + // no need to visit them since we close them one by one + return data; + } + + /** + * Throws a feature exception. + * @param feature the feature code + * @param node the node that caused it + */ + public void throwFeatureException(final int feature, final JexlNode node) { + final JexlInfo dbgInfo = node.jexlInfo(); + throw new JexlException.Feature(dbgInfo, feature, ""); + } + + /** + * Checks whether a node is a string or an integer. + * @param child the child node + * @return true if string / integer, false otherwise + */ + private boolean isArrayReferenceLiteral(final JexlNode child) { + if (child instanceof ASTStringLiteral) { + return true; + } + if (child instanceof ASTNumberLiteral && ((ASTNumberLiteral) child).isInteger()) { + return true; + } + return false; + } + + @Override + protected Object visit(final ASTArrayAccess node, final Object data) { + if (!features.supportsArrayReferenceExpr()) { + for (int i = 0; i < node.jjtGetNumChildren(); ++i) { + final JexlNode child = node.jjtGetChild(i); + if (!isArrayReferenceLiteral(child)) { + throwFeatureException(JexlFeatures.ARRAY_REF_EXPR, child); + } + } + } + return data; + } + + @Override + protected Object visit(final ASTWhileStatement node, final Object data) { + if (!features.supportsLoops()) { + throwFeatureException(JexlFeatures.LOOP, node); + } + return data; + } + + @Override + protected Object visit(final ASTDoWhileStatement node, final Object data) { + if (!features.supportsLoops()) { + throwFeatureException(JexlFeatures.LOOP, node); + } + return data; + } + + @Override + protected Object visit(final ASTForeachStatement node, final Object data) { + if (!features.supportsLoops()) { + throwFeatureException(JexlFeatures.LOOP, node); + } + return data; + } + + @Override + protected Object visit(final ASTConstructorNode node, final Object data) { + if (!features.supportsNewInstance()) { + throwFeatureException(JexlFeatures.NEW_INSTANCE, node); + } + return data; + } + + @Override + protected Object visit(final ASTMethodNode node, final Object data) { + if (!features.supportsMethodCall()) { + throwFeatureException(JexlFeatures.METHOD_CALL, node); + } + return data; + } + + @Override + protected Object visit(final ASTAnnotation node, final Object data) { + if (!features.supportsAnnotation()) { + throwFeatureException(JexlFeatures.ANNOTATION, node); + } + return data; + } + + @Override + protected Object visit(final ASTArrayLiteral node, final Object data) { + if (!features.supportsStructuredLiteral()) { + throwFeatureException(JexlFeatures.STRUCTURED_LITERAL, node); + } + return data; + } + + @Override + protected Object visit(final ASTMapLiteral node, final Object data) { + if (!features.supportsStructuredLiteral()) { + throwFeatureException(JexlFeatures.STRUCTURED_LITERAL, node); + } + return data; + } + + @Override + protected Object visit(final ASTSetLiteral node, final Object data) { + if (!features.supportsStructuredLiteral()) { + throwFeatureException(JexlFeatures.STRUCTURED_LITERAL, node); + } + return data; + } + + @Override + protected Object visit(final ASTRangeNode node, final Object data) { + if (!features.supportsStructuredLiteral()) { + throwFeatureException(JexlFeatures.STRUCTURED_LITERAL, node); + } + return data; + } + + private Object controlSideEffect(final JexlNode node, final Object data) { + final JexlNode lv = node.jjtGetChild(0); + if (!features.supportsSideEffectGlobal() && lv.isGlobalVar()) { + throwFeatureException(JexlFeatures.SIDE_EFFECT_GLOBAL, lv); + } + if (!features.supportsSideEffect()) { + throwFeatureException(JexlFeatures.SIDE_EFFECT, lv); + } + return data; + } + + @Override + protected Object visit(final ASTAssignment node, final Object data) { + return controlSideEffect(node, data); + } + + @Override + protected Object visit(final ASTSetAddNode node, final Object data) { + return controlSideEffect(node, data); + } + + @Override + protected Object visit(final ASTSetMultNode node, final Object data) { + return controlSideEffect(node, data); + } + + @Override + protected Object visit(final ASTSetDivNode node, final Object data) { + return controlSideEffect(node, data); + } + + @Override + protected Object visit(final ASTSetAndNode node, final Object data) { + return controlSideEffect(node, data); + } + + @Override + protected Object visit(final ASTSetOrNode node, final Object data) { + return controlSideEffect(node, data); + } + + @Override + protected Object visit(final ASTSetXorNode node, final Object data) { + return controlSideEffect(node, data); + } + + @Override + protected Object visit(final ASTSetSubNode node, final Object data) { + return controlSideEffect(node, data); + } + +} diff --git a/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/JJTParserState.java b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/JJTParserState.java new file mode 100644 index 0000000..600e4d8 --- /dev/null +++ b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/JJTParserState.java @@ -0,0 +1,125 @@ +/* Generated by: ParserGeneratorCC: Do not edit this line. JJTParserState.java Version 1.1.3 */ +package aiyh.utils.tool.org.apache.commons.jexl3.parser; + +public class JJTParserState implements java.io.Serializable { + private java.util.List nodes; + private java.util.List marks; + + /* number of nodes on stack */ + private int sp; + /* current mark */ + private int mk; + private boolean node_created; + + public JJTParserState() { + nodes = new java.util.ArrayList(); + marks = new java.util.ArrayList(); + sp = 0; + mk = 0; + } + + /* Determines whether the current node was actually closed and + pushed. This should only be called in the final user action of a + node scope. */ + public boolean nodeCreated() { + return node_created; + } + + /* Call this to reinitialize the node stack. It is called + automatically by the parser's ReInit() method. */ + public void reset() { + nodes.clear(); + marks.clear(); + sp = 0; + mk = 0; + } + + /* Returns the root node of the AST. It only makes sense to call + this after a successful parse. */ + public Node rootNode() { + return nodes.get(0); + } + + /* Pushes a node on to the stack. */ + public void pushNode(Node n) { + nodes.add(n); + ++sp; + } + + /* Returns the node on the top of the stack, and remove it from the + stack. */ + public Node popNode() { + --sp; + if (sp < mk) { + mk = marks.remove(marks.size()-1).intValue(); + } + return nodes.remove(nodes.size()-1); + } + + /* Returns the node currently on the top of the stack. */ + public Node peekNode() { + return nodes.get(nodes.size()-1); + } + + /* Returns the number of children on the stack in the current node + scope. */ + public int nodeArity() { + return sp - mk; + } + + /* Parameter is currently unused. */ + public void clearNodeScope(@SuppressWarnings("unused") final Node n) { + while (sp > mk) { + popNode(); + } + mk = marks.remove(marks.size()-1).intValue(); + } + + public void openNodeScope(final Node n) { + marks.add(Integer.valueOf(mk)); + mk = sp; + n.jjtOpen(); + } + + /* A definite node is constructed from a specified number of + children. That number of nodes are popped from the stack and + made the children of the definite node. Then the definite node + is pushed on to the stack. */ + public void closeNodeScope(final Node n, final int numIn) { + mk = marks.remove(marks.size()-1).intValue(); + int num = numIn; + while (num-- > 0) { + Node c = popNode(); + c.jjtSetParent(n); + n.jjtAddChild(c, num); + } + n.jjtClose(); + pushNode(n); + node_created = true; + } + + + /* A conditional node is constructed if its condition is true. All + the nodes that have been pushed since the node was opened are + made children of the conditional node, which is then pushed + on to the stack. If the condition is false the node is not + constructed and they are left on the stack. */ + public void closeNodeScope(final Node n, final boolean condition) { + if (condition) { + int a = nodeArity(); + mk = marks.remove(marks.size()-1).intValue(); + while (a-- > 0) { + final Node c = popNode(); + c.jjtSetParent(n); + n.jjtAddChild(c, a); + } + n.jjtClose(); + pushNode(n); + node_created = true; + } else { + mk = marks.remove(marks.size()-1).intValue(); + node_created = false; + } + } +} +/* ParserGeneratorCC - OriginalChecksum=fe5f01fafc40cbb10fa11619380dcb20 (do not edit this line) */ diff --git a/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/JavaccError.java b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/JavaccError.java new file mode 100644 index 0000000..8cd5648 --- /dev/null +++ b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/JavaccError.java @@ -0,0 +1,41 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package aiyh.utils.tool.org.apache.commons.jexl3.parser; + +/** + * The common info provided by Javacc errors. + */ +public interface JavaccError { + + /** + * Gets the line number. + * @return line number. + */ + int getLine(); + + /** + * Gets the column number. + * @return the column. + */ + int getColumn(); + + /** + * Gets the last correct input. + * @return the string after which the error occurred + */ + String getAfter(); +} diff --git a/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/JexlLexicalNode.java b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/JexlLexicalNode.java new file mode 100644 index 0000000..c55432e --- /dev/null +++ b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/JexlLexicalNode.java @@ -0,0 +1,58 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package aiyh.utils.tool.org.apache.commons.jexl3.parser; + +import aiyh.utils.tool.org.apache.commons.jexl3.internal.LexicalScope; + +/** + * Base class for AST nodes behaving as lexical units. + * @since 3.2 + */ +public class JexlLexicalNode extends JexlNode implements JexlParser.LexicalUnit { + private LexicalScope locals = null; + + public JexlLexicalNode(final int id) { + super(id); + } + + public JexlLexicalNode(final Parser p, final int id) { + super(p, id); + } + + @Override + public boolean declareSymbol(final int symbol) { + if (locals == null) { + locals = new LexicalScope(); + } + return locals.addSymbol(symbol); + } + + @Override + public int getSymbolCount() { + return locals == null? 0 : locals.getSymbolCount(); + } + + @Override + public boolean hasSymbol(final int symbol) { + return locals != null && locals.hasSymbol(symbol); + } + + @Override + public LexicalScope getLexicalScope() { + return locals; + } +} diff --git a/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/JexlNode.java b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/JexlNode.java new file mode 100644 index 0000000..3c80ad6 --- /dev/null +++ b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/JexlNode.java @@ -0,0 +1,354 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package aiyh.utils.tool.org.apache.commons.jexl3.parser; + +import aiyh.utils.tool.org.apache.commons.jexl3.JexlInfo; +import aiyh.utils.tool.org.apache.commons.jexl3.introspection.JexlMethod; +import aiyh.utils.tool.org.apache.commons.jexl3.introspection.JexlPropertyGet; +import aiyh.utils.tool.org.apache.commons.jexl3.introspection.JexlPropertySet; + +/** + * Base class for parser nodes - holds an 'image' of the token for later use. + * + * @since 2.0 + */ +public abstract class JexlNode extends SimpleNode { + // line + column encoded: up to 4096 columns (ie 20 bits for line + 12 bits for column) + private int lc = -1; + + /** + * A marker interface for constants. + * + * @param the literal type + */ + public interface Constant { + T getLiteral(); + } + + public JexlNode(final int id) { + super(id); + } + + public JexlNode(final Parser p, final int id) { + super(p, id); + } + + public void jjtSetFirstToken(final Token t) { + // 0xc = 12, 12 bits -> 4096 + // 0xfff, 12 bits mask + this.lc = (t.beginLine << 0xc) | (0xfff & t.beginColumn); + } + + public void jjtSetLastToken(final Token t) { + // nothing + } + + public int getLine() { + return this.lc >>> 0xc; + } + + public int getColumn() { + return this.lc & 0xfff; + } + + /** + * Gets the associated JexlInfo instance. + * + * @return the info + */ + public JexlInfo jexlInfo() { + JexlInfo info = null; + JexlNode node = this; + while (node != null) { + if (node.jjtGetValue() instanceof JexlInfo) { + info = (JexlInfo) node.jjtGetValue(); + break; + } + node = node.jjtGetParent(); + } + if (lc >= 0) { + final int c = lc & 0xfff; + final int l = lc >> 0xc; + // at least an info with line/column number + return info != null ? info.at(info.getLine() + l - 1, c) : new JexlInfo(null, l, c); + } + // weird though; no jjSetFirstToken(...) ever called? + return info; + } + + /** + * Marker interface for cachable function calls. + */ + public interface Funcall { + } + + /** + * Clears any cached value of type JexlProperty{G,S}et or JexlMethod. + *

+ * This is called when the engine detects the evaluation of a script occurs with a class loader + * different that the one that created it.

+ */ + public void clearCache() { + final Object value = jjtGetValue(); + if (value instanceof JexlPropertyGet + || value instanceof JexlPropertySet + || value instanceof JexlMethod + || value instanceof Funcall) { + jjtSetValue(null); + } + for (int n = 0; n < jjtGetNumChildren(); ++n) { + jjtGetChild(n).clearCache(); + } + } + + /** + * Checks whether this node is an operator. + * + * @return true if node is an operator node, false otherwise + */ + public boolean isOperator() { + return OperatorController.INSTANCE.control(this, Boolean.TRUE); + } + + /** + * Checks whether this node is an operator that accepts a null argument + * even when arithmetic is in strict mode. + * The sole cases are equals and not equals. + * + * @return true if node accepts null arguments, false otherwise + */ + public boolean isStrictOperator() { + return OperatorController.INSTANCE.control(this, Boolean.FALSE); + } + + /** + * Whether this node is a constant node Its value can not change after the first evaluation and can be cached + * indefinitely. + * + * @return true if constant, false otherwise + */ + public boolean isConstant() { + return isConstant(this instanceof Constant); + } + + protected boolean isConstant(final boolean literal) { + if (literal) { + for (int n = 0; n < jjtGetNumChildren(); ++n) { + final JexlNode child = jjtGetChild(n); + if (child instanceof ASTReference) { + final boolean is = child.isConstant(true); + if (!is) { + return false; + } + } else if (child instanceof ASTMapEntry) { + final boolean is = child.isConstant(true); + if (!is) { + return false; + } + } else if (!child.isConstant()) { + return false; + } + } + return true; + } + return false; + } + + /** + * Whether this node is a left value. + * + * @return true if node is assignable, false otherwise + */ + public boolean isLeftValue() { + JexlNode walk = this; + do { + if (walk instanceof ASTIdentifier + || walk instanceof ASTIdentifierAccess + || walk instanceof ASTArrayAccess) { + return true; + } + final int nc = walk.jjtGetNumChildren() - 1; + if (nc < 0) { + return walk.jjtGetParent() instanceof ASTReference; + } + walk = walk.jjtGetChild(nc); + } while (walk != null); + return false; + } + + /** + * @return true if this node looks like a global var + */ + public boolean isGlobalVar() { + if (this instanceof ASTVar) { + return false; + } + if (this instanceof ASTIdentifier) { + return ((ASTIdentifier) this).getSymbol() < 0; + } + final int nc = this.jjtGetNumChildren() - 1; + if (nc >= 0) { + final JexlNode first = this.jjtGetChild(0); + return first.isGlobalVar(); + } + return jjtGetParent() instanceof ASTReference; + } + + /** + * Whether this node is a local variable. + * + * @return true if local, false otherwise + */ + public boolean isLocalVar() { + return this instanceof ASTIdentifier && ((ASTIdentifier) this).getSymbol() >= 0; + } + + /** + * Whether this node is the left-hand side of a safe access identifier as in. + * For instance, in 'x?.y' , 'x' is safe. + * + * @param safe whether the engine is in safe-navigation mode + * @return true if safe lhs, false otherwise + */ + public boolean isSafeLhs(final boolean safe) { + if (this instanceof ASTReference) { + return jjtGetChild(0).isSafeLhs(safe); + } + if (this instanceof ASTMethodNode) { + if (this.jjtGetNumChildren() > 1 + && this.jjtGetChild(0) instanceof ASTIdentifierAccess + && (((ASTIdentifierAccess) this.jjtGetChild(0)).isSafe() || safe)) { + return true; + } + } + final JexlNode parent = this.jjtGetParent(); + if (parent == null) { + return false; + } + // find this node in its parent + final int nsiblings = parent.jjtGetNumChildren(); + int rhs = -1; + for (int s = 0; s < nsiblings; ++s) { + final JexlNode sibling = parent.jjtGetChild(s); + if (sibling == this) { + // the next chid offset of this nodes parent + rhs = s + 1; + break; + } + } + // seek next child in parent + if (rhs >= 0 && rhs < nsiblings) { + JexlNode rsibling = parent.jjtGetChild(rhs); + if (rsibling instanceof ASTMethodNode || rsibling instanceof ASTFunctionNode) { + rsibling = rsibling.jjtGetChild(0); + } + if (rsibling instanceof ASTIdentifierAccess + && (((ASTIdentifierAccess) rsibling).isSafe() || safe)) { + return true; + } + if (rsibling instanceof ASTArrayAccess) { + return safe; + } + } + return false; + } + + /** + * Check if a null evaluated expression is protected by a ternary expression. + *

+ * The rationale is that the ternary / elvis expressions are meant for the user to explicitly take control + * over the error generation; ie, ternaries can return null even if the engine in strict mode + * would normally throw an exception. + *

+ * + * @return true if nullable variable, false otherwise + */ + public boolean isTernaryProtected() { + JexlNode node = this; + for (JexlNode walk = node.jjtGetParent(); walk != null; walk = walk.jjtGetParent()) { + // protect only the condition part of the ternary + if (walk instanceof ASTTernaryNode + || walk instanceof ASTNullpNode) { + return node == walk.jjtGetChild(0); + } + if (!(walk instanceof ASTReference || walk instanceof ASTArrayAccess)) { + break; + } + node = walk; + } + return false; + } + + /** + * An info bound to its node. + *

Used to parse expressions for templates. + */ + public static class Info extends JexlInfo { + JexlNode node = null; + + /** + * Default ctor. + * + * @param jnode the node + */ + public Info(final JexlNode jnode) { + this(jnode, jnode.jexlInfo()); + } + + /** + * Copy ctor. + * + * @param jnode the node + * @param info the + */ + public Info(final JexlNode jnode, final JexlInfo info) { + this(jnode, info.getName(), info.getLine(), info.getColumn()); + } + + /** + * Full detail ctor. + * + * @param jnode the node + * @param name the file name + * @param l the line + * @param c the column + */ + private Info(final JexlNode jnode, final String name, final int l, final int c) { + super(name, l, c); + node = jnode; + } + + /** + * @return the node this info is bound to + */ + public JexlNode getNode() { + return node; + } + + @Override + public JexlInfo at(final int l, final int c) { + return new Info(node, getName(), l, c); + } + + @Override + public JexlInfo detach() { + node = null; + return this; + } + } + +} diff --git a/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/JexlParser.java b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/JexlParser.java new file mode 100644 index 0000000..2b963f8 --- /dev/null +++ b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/JexlParser.java @@ -0,0 +1,669 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package aiyh.utils.tool.org.apache.commons.jexl3.parser; + +import aiyh.utils.tool.org.apache.commons.jexl3.JexlEngine; +import aiyh.utils.tool.org.apache.commons.jexl3.JexlException; +import aiyh.utils.tool.org.apache.commons.jexl3.JexlFeatures; +import aiyh.utils.tool.org.apache.commons.jexl3.JexlInfo; +import aiyh.utils.tool.org.apache.commons.jexl3.internal.LexicalScope; +import aiyh.utils.tool.org.apache.commons.jexl3.internal.Scope; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.StringReader; +import java.lang.reflect.Constructor; +import java.util.*; +import java.util.function.Predicate; + + +/** + * The base class for parsing, manages the parameter/local variable frame. + */ +public abstract class JexlParser extends StringParser { + /** + * The associated controller. + */ + protected final FeatureController featureController = new FeatureController(JexlEngine.DEFAULT_FEATURES); + /** + * The basic source info. + */ + protected JexlInfo info = null; + /** + * The source being processed. + */ + protected String source = null; + /** + * The map of named registers aka script parameters. + *

Each parameter is associated to a register and is materialized + * as an offset in the registers array used during evaluation.

+ */ + protected Scope frame = null; + /** + * When parsing inner functions/lambda, need to stack the scope (sic). + */ + protected final Deque frames = new ArrayDeque<>(); + /** + * The list of pragma declarations. + */ + protected Map pragmas = null; + /** + * The known namespaces. + */ + protected Set namespaces = null; + /** + * The number of imbricated loops. + */ + protected int loopCount = 0; + /** + * Stack of parsing loop counts. + */ + protected final Deque loopCounts = new ArrayDeque<>(); + /** + * The current lexical block. + */ + protected LexicalUnit block = null; + /** + * Stack of lexical blocks. + */ + protected final Deque blocks = new ArrayDeque<>(); + + /** + * A lexical unit is the container defining local symbols and their + * visibility boundaries. + */ + public interface LexicalUnit { + /** + * Declares a local symbol. + * + * @param symbol the symbol index in the scope + * @return true if declaration was successful, false if symbol was already declared + */ + boolean declareSymbol(int symbol); + + /** + * Checks whether a symbol is declared in this lexical unit. + * + * @param symbol the symbol + * @return true if declared, false otherwise + */ + boolean hasSymbol(int symbol); + + /** + * @return the number of local variables declared in this unit + */ + int getSymbolCount(); + + LexicalScope getLexicalScope(); + } + + /** + * Cleanup. + * + * @param features the feature set to restore if any + */ + protected void cleanup(final JexlFeatures features) { + info = null; + source = null; + frame = null; + frames.clear(); + pragmas = null; + namespaces = null; + loopCounts.clear(); + loopCount = 0; + blocks.clear(); + block = null; + this.setFeatures(features); + } + + /** + * Utility function to create '.' separated string from a list of string. + * + * @param lstr the list of strings + * @return the dotted version + */ + protected static String stringify(final Iterable lstr) { + final StringBuilder strb = new StringBuilder(); + boolean dot = false; + for (final String str : lstr) { + if (!dot) { + dot = true; + } else { + strb.append('.'); + } + strb.append(str); + } + return strb.toString(); + } + + /** + * Read a given source line. + * + * @param src the source + * @param lineno the line number + * @return the line + */ + protected static String readSourceLine(final String src, final int lineno) { + String msg = ""; + if (src != null && lineno >= 0) { + try { + final BufferedReader reader = new BufferedReader(new StringReader(src)); + for (int l = 0; l < lineno; ++l) { + msg = reader.readLine(); + } + } catch (final IOException xio) { + // ignore, very unlikely but then again... + } + } + return msg; + } + + /** + * Internal, for debug purpose only. + * + * @param registers whether register syntax is recognized by this parser + */ + public void allowRegisters(final boolean registers) { + featureController.setFeatures(new JexlFeatures(featureController.getFeatures()).register(registers)); + } + + /** + * Sets a new set of options. + * + * @param features the parser features + */ + protected void setFeatures(final JexlFeatures features) { + this.featureController.setFeatures(features); + } + + /** + * @return the current set of features active during parsing + */ + protected JexlFeatures getFeatures() { + return featureController.getFeatures(); + } + + /** + * Gets the frame used by this parser. + *

Since local variables create new symbols, it is important to + * regain access after parsing to known which / how-many registers are needed.

+ * + * @return the named register map + */ + protected Scope getFrame() { + return frame; + } + + /** + * Create a new local variable frame and push it as current scope. + */ + protected void pushFrame() { + if (frame != null) { + frames.push(frame); + } + frame = new Scope(frame, (String[]) null); + loopCounts.push(loopCount); + loopCount = 0; + } + + /** + * Pops back to previous local variable frame. + */ + protected void popFrame() { + if (!frames.isEmpty()) { + frame = frames.pop(); + } else { + frame = null; + } + if (!loopCounts.isEmpty()) { + loopCount = loopCounts.pop(); + } + } + + /** + * Gets the lexical unit currently used by this parser. + * + * @return the named register map + */ + protected LexicalUnit getUnit() { + return block; + } + + /** + * Pushes a new lexical unit. + * + * @param unit the new lexical unit + */ + protected void pushUnit(final LexicalUnit unit) { + if (block != null) { + blocks.push(block); + } + block = unit; + } + + /** + * Restores the previous lexical unit. + * + * @param unit restores the previous lexical scope + */ + protected void popUnit(final LexicalUnit unit) { + if (block == unit) { + if (!blocks.isEmpty()) { + block = blocks.pop(); + } else { + block = null; + } + } + } + + /** + * Checks if a symbol is defined in lexical scopes. + *

This works with parsed scripts in template resolution only. + * + * @param info an info linked to a node + * @param symbol the symbol number + * @return true if symbol accessible in lexical scope + */ + private boolean isSymbolDeclared(final JexlNode.Info info, final int symbol) { + JexlNode walk = info.getNode(); + while (walk != null) { + if (walk instanceof LexicalUnit) { + final LexicalScope scope = ((LexicalUnit) walk).getLexicalScope(); + if (scope != null && scope.hasSymbol(symbol)) { + return true; + } + // stop at first new scope reset, aka lambda + if (walk instanceof ASTJexlLambda) { + break; + } + } + walk = walk.jjtGetParent(); + } + return false; + } + + /** + * Checks whether an identifier is a local variable or argument. + * + * @param name the variable name + * @return true if a variable with that name was declared + */ + protected boolean isVariable(String name) { + return frame != null && frame.getSymbol(name) != null; + } + + /** + * Checks whether an identifier is a local variable or argument, ie a symbol, stored in a register. + * + * @param identifier the identifier + * @param name the identifier name + * @return the image + */ + protected String checkVariable(final ASTIdentifier identifier, final String name) { + if (frame != null) { + final Integer symbol = frame.getSymbol(name); + if (symbol != null) { + boolean declared = true; + if (frame.isCapturedSymbol(symbol)) { + // captured are declared in all cases + identifier.setCaptured(true); + } else { + declared = block.hasSymbol(symbol); + // one of the lexical blocks above should declare it + if (!declared) { + for (final LexicalUnit u : blocks) { + if (u.hasSymbol(symbol)) { + declared = true; + break; + } + } + } + if (!declared && info instanceof JexlNode.Info) { + declared = isSymbolDeclared((JexlNode.Info) info, symbol); + } + } + identifier.setSymbol(symbol, name); + if (!declared) { + identifier.setShaded(true); + if (getFeatures().isLexicalShade()) { + // can not reuse a local as a global + throw new JexlException(identifier, name + ": variable is not defined"); + } + } + } + } + return name; + } + + /** + * Whether a given variable name is allowed. + * + * @param image the name + * @return true if allowed, false if reserved + */ + protected boolean allowVariable(final String image) { + final JexlFeatures features = getFeatures(); + if (!features.supportsLocalVar()) { + return false; + } + return !features.isReservedName(image); + } + + /** + * Declares a symbol. + * + * @param symbol the symbol index + * @return true if symbol can be declared in lexical scope, false (error) + * if it is already declared + */ + private boolean declareSymbol(final int symbol) { + for (final LexicalUnit lu : blocks) { + if (lu.hasSymbol(symbol)) { + return false; + } + // stop at first new scope reset, aka lambda + if (lu instanceof ASTJexlLambda) { + break; + } + } + return block == null || block.declareSymbol(symbol); + } + + /** + * Declares a local variable. + *

This method creates an new entry in the symbol map.

+ * + * @param variable the identifier used to declare + * @param token the variable name toekn + */ + protected void declareVariable(final ASTVar variable, final Token token) { + final String name = token.image; + if (!allowVariable(name)) { + throwFeatureException(JexlFeatures.LOCAL_VAR, token); + } + if (frame == null) { + frame = new Scope(null, (String[]) null); + } + final int symbol = frame.declareVariable(name); + variable.setSymbol(symbol, name); + if (frame.isCapturedSymbol(symbol)) { + variable.setCaptured(true); + } + // lexical feature error + if (!declareSymbol(symbol)) { + if (getFeatures().isLexical()) { + throw new JexlException(variable, name + ": variable is already declared"); + } + variable.setRedefined(true); + } + } + + /** + * The prefix of a namespace pragma. + */ + protected static final String PRAGMA_JEXLNS = "jexl.namespace."; + + /** + * Adds a pragma declaration. + * + * @param key the pragma key + * @param value the pragma value + */ + protected void declarePragma(final String key, final Object value) { + if (!getFeatures().supportsPragma()) { + throwFeatureException(JexlFeatures.PRAGMA, getToken(0)); + } + if (pragmas == null) { + pragmas = new TreeMap<>(); + } + // declaring a namespace + Predicate ns = getFeatures().namespaceTest(); + if (ns != null && key.startsWith(PRAGMA_JEXLNS)) { + // jexl.namespace.*** + final String nsname = key.substring(PRAGMA_JEXLNS.length()); + if (nsname != null && !nsname.isEmpty()) { + if (namespaces == null) { + namespaces = new HashSet<>(); + } + namespaces.add(nsname); + } + } + pragmas.put(key, value); + } + + /** + * Checks whether a name identifies a declared namespace. + * + * @param token the namespace token + * @return true if the name qualifies a namespace + */ + protected boolean isDeclaredNamespace(final Token token, final Token colon) { + // syntactic hint, the namespace sticks to the colon + if (colon != null && ":".equals(colon.image) && colon.beginColumn - 1 == token.endColumn) { + return true; + } + // if name is shared with a variable name, use syntactic hint + String name = token.image; + if (!isVariable(name)) { + final Set ns = namespaces; + // declared through local pragma ? + if (ns != null && ns.contains(name)) { + return true; + } + // declared through engine features ? + return getFeatures().namespaceTest().test(name); + } + return false; + } + + /** + * Declares a local parameter. + *

This method creates an new entry in the symbol map.

+ * + * @param token the parameter name toekn + */ + protected void declareParameter(final Token token) { + final String identifier = token.image; + if (!allowVariable(identifier)) { + throwFeatureException(JexlFeatures.LOCAL_VAR, token); + } + if (frame == null) { + frame = new Scope(null, (String[]) null); + } + final int symbol = frame.declareParameter(identifier); + // not sure how declaring a parameter could fail... + // lexical feature error + if (!block.declareSymbol(symbol) && getFeatures().isLexical()) { + final JexlInfo xinfo = info.at(token.beginLine, token.beginColumn); + throw new JexlException(xinfo, identifier + ": variable is already declared", null); + } + } + + /** + * Default implementation does nothing but is overridden by generated code. + * + * @param top whether the identifier is beginning an l/r value + * @throws ParseException subclasses may throw this + */ + protected void Identifier(final boolean top) throws ParseException { + // Overridden by generated code + } + + /** + * Overridden in actual parser to access tokens stack. + * + * @param index 0 to get current token + * @return the token on the stack + */ + protected abstract Token getToken(int index); + + /** + * Overridden in actual parser to access tokens stack. + * + * @return the next token on the stack + */ + protected abstract Token getNextToken(); + + /** + * The set of assignment operators as classes. + */ + private static final Set> ASSIGN_NODES = new HashSet<>( + Arrays.asList( + ASTAssignment.class, + ASTSetAddNode.class, + ASTSetMultNode.class, + ASTSetDivNode.class, + ASTSetAndNode.class, + ASTSetOrNode.class, + ASTSetXorNode.class, + ASTSetSubNode.class + ) + ); + + /** + * Called by parser at beginning of node construction. + * + * @param node the node + */ + protected void jjtreeOpenNodeScope(final JexlNode node) { + // nothing + } + + /** + * Called by parser at end of node construction. + *

+ * Detects "Ambiguous statement" and 'non-left value assignment'.

+ * + * @param node the node + * @throws JexlException.Parsing when parsing fails + */ + protected void jjtreeCloseNodeScope(final JexlNode node) { + if (node instanceof ASTAmbiguous) { + throwAmbiguousException(node); + } + if (node instanceof ASTJexlScript) { + if (node instanceof ASTJexlLambda && !getFeatures().supportsLambda()) { + throwFeatureException(JexlFeatures.LAMBDA, node.jexlInfo()); + } + final ASTJexlScript script = (ASTJexlScript) node; + // reaccess in case local variables have been declared + if (script.getScope() != frame) { + script.setScope(frame); + } + popFrame(); + } else if (ASSIGN_NODES.contains(node.getClass())) { + final JexlNode lv = node.jjtGetChild(0); + if (!lv.isLeftValue()) { + throwParsingException(JexlException.Assignment.class, null); + } + } + // heavy check + featureController.controlNode(node); + } + + /** + * Throws Ambiguous exception. + *

Seeks the end of the ambiguous statement to recover. + * + * @param node the first token in ambiguous expression + * @throws JexlException.Ambiguous in all cases + */ + protected void throwAmbiguousException(final JexlNode node) { + final JexlInfo begin = node.jexlInfo(); + final Token t = getToken(0); + final JexlInfo end = info.at(t.beginLine, t.endColumn); + final String msg = readSourceLine(source, end.getLine()); + throw new JexlException.Ambiguous(begin, end, msg); + } + + /** + * Throws a feature exception. + * + * @param feature the feature code + * @param info the exception surroundings + * @throws JexlException.Feature in all cases + */ + protected void throwFeatureException(final int feature, final JexlInfo info) { + final String msg = info != null ? readSourceLine(source, info.getLine()) : null; + throw new JexlException.Feature(info, feature, msg); + } + + /** + * Throws a feature exception. + * + * @param feature the feature code + * @param token the token that triggered it + * @throws JexlException.Parsing if actual error token can not be found + * @throws JexlException.Feature in all other cases + */ + protected void throwFeatureException(final int feature, Token token) { + if (token == null) { + token = this.getToken(0); + if (token == null) { + throw new JexlException.Parsing(null, JexlFeatures.stringify(feature)); + } + } + final JexlInfo xinfo = info.at(token.beginLine, token.beginColumn); + throwFeatureException(feature, xinfo); + } + + /** + * Creates a parsing exception. + * + * @param xclazz the class of exception + * @param tok the token to report + * @param the parsing exception subclass + * @throws JexlException.Parsing in all cases + */ + protected void throwParsingException(final Class xclazz, Token tok) { + JexlInfo xinfo = null; + String msg = "unrecoverable state"; + JexlException.Parsing xparse = null; + if (tok == null) { + tok = this.getToken(0); + } + if (tok != null) { + xinfo = info.at(tok.beginLine, tok.beginColumn); + msg = tok.image; + if (xclazz != null) { + try { + final Constructor ctor = xclazz.getConstructor(JexlInfo.class, String.class); + xparse = ctor.newInstance(xinfo, msg); + } catch (final Exception xany) { + // ignore, very unlikely but then again.. + } + } + } + // unlikely but safe + throw xparse != null ? xparse : new JexlException.Parsing(xinfo, msg); + } + + /** + * Pick the most significant token for error reporting. + * + * @param tokens the tokens to choose from + * @return the token + */ + protected static Token errorToken(final Token... tokens) { + for (final Token token : tokens) { + if (token != null && token.image != null && !token.image.isEmpty()) { + return token; + } + } + return null; + } +} diff --git a/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/Node.java b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/Node.java new file mode 100644 index 0000000..d0e7f39 --- /dev/null +++ b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/Node.java @@ -0,0 +1,55 @@ +/* Generated by: JJTree: Do not edit this line. Node.java Version 1.1 */ +/* ParserGeneratorCCOptions:MULTI=true,NODE_USES_PARSER=false,VISITOR=true,TRACK_TOKENS=true,NODE_PREFIX=AST,NODE_EXTENDS=,NODE_FACTORY=,SUPPORT_CLASS_VISIBILITY_PUBLIC=true */ +package aiyh.utils.tool.org.apache.commons.jexl3.parser; + +/* All AST nodes must implement this interface. It provides basic + machinery for constructing the parent and child relationships + between nodes. */ + +public +interface Node extends java.io.Serializable { + /** This method is called after the node has been made the current + node. It indicates that child nodes can now be added to it. */ + void jjtOpen(); + + /** This method is called after all the child nodes have been + added. */ + void jjtClose(); + + /** + * Set the parent node of this node + * @param n parent node to set + */ + void jjtSetParent(Node n); + + /** + * @return parent node + */ + Node jjtGetParent(); + + /** + * This method tells the node to add its argument to the node's + * list of children. + * @param n node to add as a child + * @param i zero-based index where to add the child + */ + void jjtAddChild(Node n, int i); + + /** + * This method returns a child node. The children are numbered + * from zero, left to right. + * @param i zero-baeed child index + */ + Node jjtGetChild(int i); + + /** + * @return the number of children the node has. Always ≥ 0. + */ + int jjtGetNumChildren(); + + int getId(); + + /** Accept the visitor. **/ + Object jjtAccept(ParserVisitor visitor, Object data); +} +/* ParserGeneratorCC - OriginalChecksum=80b3018e6ea6855ddbfa6c84f985a29e (do not edit this line) */ diff --git a/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/NumberParser.java b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/NumberParser.java new file mode 100644 index 0000000..14e80b4 --- /dev/null +++ b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/NumberParser.java @@ -0,0 +1,178 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package aiyh.utils.tool.org.apache.commons.jexl3.parser; + +import java.math.BigDecimal; +import java.math.BigInteger; +import java.text.DecimalFormat; +import java.text.DecimalFormatSymbols; +import java.util.Locale; + +public final class NumberParser { + /** The type literal value. */ + private Number literal = null; + /** The expected class. */ + private Class clazz = null; + /** JEXL locale-neutral big decimal format. */ + static final DecimalFormat BIGDF = new DecimalFormat("0.0b", new DecimalFormatSymbols(Locale.ENGLISH)); + + @Override + public String toString() { + if (literal == null || clazz == null || Double.isNaN(literal.doubleValue())) { + return "NaN"; + } + if (BigDecimal.class.equals(clazz)) { + synchronized (BIGDF) { + return BIGDF.format(literal); + } + } + final StringBuilder strb = new StringBuilder(literal.toString()); + if (Float.class.equals(clazz)) { + strb.append('f'); + } else if (Double.class.equals(clazz)) { + strb.append('d'); + } else if (BigInteger.class.equals(clazz)) { + strb.append('h'); + } else if (Long.class.equals(clazz)) { + strb.append('l'); + } + return strb.toString(); + } + + + Class getLiteralClass() { + return clazz; + } + + boolean isInteger() { + return Integer.class.equals(clazz); + } + + Number getLiteralValue() { + return literal; + } + + static Number parseInteger(final String s) { + final NumberParser np = new NumberParser(); + np.setNatural(s); + return np.getLiteralValue(); + } + + static Number parseDouble(final String s) { + final NumberParser np = new NumberParser(); + np.setReal(s); + return np.getLiteralValue(); + } + + /** + * Sets this node as a natural literal. + * Originally from OGNL. + * @param s the natural as string + */ + void setNatural(String s) { + Number result; + Class rclass; + // determine the base + final int base; + if (s.charAt(0) == '0') { + if ((s.length() > 1 && (s.charAt(1) == 'x' || s.charAt(1) == 'X'))) { + base = 16; + s = s.substring(2); // Trim the 0x off the front + } else { + base = 8; + } + } else { + base = 10; + } + final int last = s.length() - 1; + switch (s.charAt(last)) { + case 'l': + case 'L': { + rclass = Long.class; + result = Long.valueOf(s.substring(0, last), base); + break; + } + case 'h': + case 'H': { + rclass = BigInteger.class; + result = new BigInteger(s.substring(0, last), base); + break; + } + default: { + rclass = Integer.class; + try { + result = Integer.valueOf(s, base); + } catch (final NumberFormatException take2) { + try { + result = Long.valueOf(s, base); + } catch (final NumberFormatException take3) { + result = new BigInteger(s, base); + } + } + } + } + literal = result; + clazz = rclass; + } + + /** + * Sets this node as a real literal. + * Originally from OGNL. + * @param s the real as string + */ + void setReal(final String s) { + Number result; + Class rclass; + if ("#NaN".equals(s) || "NaN".equals(s)) { + result = Double.NaN; + rclass = Double.class; + } else { + final int last = s.length() - 1; + switch (s.charAt(last)) { + case 'b': + case 'B': { + rclass = BigDecimal.class; + result = new BigDecimal(s.substring(0, last)); + break; + } + case 'f': + case 'F': { + rclass = Float.class; + result = Float.valueOf(s.substring(0, last)); + break; + } + case 'd': + case 'D': + rclass = Double.class; + result = Double.valueOf(s.substring(0, last)); + break; + default: { + rclass = Double.class; + try { + result = Double.valueOf(s); + } catch (final NumberFormatException take3) { + result = new BigDecimal(s); + } + break; + } + } + } + literal = result; + clazz = rclass; + } + +} diff --git a/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/OperatorController.java b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/OperatorController.java new file mode 100644 index 0000000..88ff5b1 --- /dev/null +++ b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/OperatorController.java @@ -0,0 +1,190 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package aiyh.utils.tool.org.apache.commons.jexl3.parser; + +import aiyh.utils.tool.org.apache.commons.jexl3.internal.ScriptVisitor; + +/** + * Checks if node is an operator node. + **/ +class OperatorController extends ScriptVisitor { + static final OperatorController INSTANCE = new OperatorController(); + /** + * Controls the operator. + * @param node the node + * @param safe whether we are checking for any or only null-unsafe operators + * @return true if node is (null-unsafe) operator + */ + boolean control(final JexlNode node, Boolean safe) { + return Boolean.TRUE.equals(node.jjtAccept(this, safe)); + } + + @Override + protected Object visitNode(final JexlNode node, final Object data) { + return false; + } + + @Override + protected Object visit(final ASTNotNode node, final Object data) { + return true; + } + + @Override + protected Object visit(final ASTAddNode node, final Object data) { + return true; + } + + @Override + protected Object visit(final ASTSetAddNode node, final Object data) { + return true; + } + + @Override + protected Object visit(final ASTMulNode node, final Object data) { + return true; + } + + @Override + protected Object visit(final ASTSetMultNode node, final Object data) { + return true; + } + + @Override + protected Object visit(final ASTModNode node, final Object data) { + return true; + } + + @Override + protected Object visit(final ASTSetModNode node, final Object data) { + return true; + } + + @Override + protected Object visit(final ASTDivNode node, final Object data) { + return true; + } + + @Override + protected Object visit(final ASTSetDivNode node, final Object data) { + return true; + } + + @Override + protected Object visit(final ASTBitwiseAndNode node, final Object data) { + return true; + } + + @Override + protected Object visit(final ASTSetAndNode node, final Object data) { + return true; + } + + @Override + protected Object visit(final ASTBitwiseOrNode node, final Object data) { + return true; + } + + @Override + protected Object visit(final ASTSetOrNode node, final Object data) { + return true; + } + + @Override + protected Object visit(final ASTBitwiseXorNode node, final Object data) { + return true; + } + + @Override + protected Object visit(final ASTSetXorNode node, final Object data) { + return true; + } + + @Override + protected Object visit(final ASTBitwiseComplNode node, final Object data) { + return true; + } + + @Override + protected Object visit(final ASTSubNode node, final Object data) { + return true; + } + + @Override + protected Object visit(final ASTSetSubNode node, final Object data) { + return true; + } + + @Override + protected Object visit(final ASTEQNode node, final Object data) { + return data; + } + + @Override + protected Object visit(final ASTNENode node, final Object data) { + return data; + } + + @Override + protected Object visit(final ASTGTNode node, final Object data) { + return true; + } + + @Override + protected Object visit(final ASTGENode node, final Object data) { + return true; + } + + @Override + protected Object visit(final ASTLTNode node, final Object data) { + return true; + } + + @Override + protected Object visit(final ASTLENode node, final Object data) { + return true; + } + + @Override + protected Object visit(final ASTSWNode node, final Object data) { + return true; + } + + @Override + protected Object visit(final ASTNSWNode node, final Object data) { + return true; + } + + @Override + protected Object visit(final ASTEWNode node, final Object data) { + return true; + } + + @Override + protected Object visit(final ASTNEWNode node, final Object data) { + return true; + } + + @Override + protected Object visit(final ASTERNode node, final Object data) { + return true; + } + + @Override + protected Object visit(final ASTNRNode node, final Object data) { + return true; + } +} diff --git a/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ParseException.java b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ParseException.java new file mode 100644 index 0000000..b8d3530 --- /dev/null +++ b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ParseException.java @@ -0,0 +1,89 @@ + +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package aiyh.utils.tool.org.apache.commons.jexl3.parser; + +/** + * This exception is thrown when parse errors are encountered. + */ +public class ParseException extends Exception implements JavaccError { + /** + * The version identifier. + */ + private static final long serialVersionUID = 1L; + /** + * Last correct input before error occurs. + */ + private String after = ""; + /** + * Error line. + */ + private int line = -1; + /** + * Error column. + */ + private int column = -1; + + /** + * This constructor is used by the method "generateParseException" + * in the generated parser. Calling this constructor generates + * a new object of this type with the fields "currentToken", + * "expectedTokenSequences", and "tokenImage" set. + * @param currentToken This is the last token that has been consumed successfully. If + * this object has been created due to a parse error, the token + * followng this token will (therefore) be the first error token. + * @param expectedTokenSequences Each entry in this array is an array of integers. Each array + * of integers represents a sequence of tokens (by their ordinal + * values) that is expected at this point of the parse. + * @param tokenImage This is a reference to the "tokenImage" array of the generated + * parser within which the parse error occurred. This array is + * defined in the generated ...Constants interface. + */ + public ParseException(final Token currentToken, final int[][] expectedTokenSequences, final String[] tokenImage) { + super("parse error"); + final Token tok = currentToken.next; + after = tok.image; + line = tok.beginLine; + column = tok.beginColumn; + } + + /** + * Default ctor. + */ + public ParseException() { + } + + /** Constructor with message. */ + public ParseException(final String message) { + super(message); + } + + @Override + public int getLine() { + return line; + } + + @Override + public int getColumn() { + return column; + } + + @Override + public String getAfter() { + return after; + } +} diff --git a/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/Parser.java b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/Parser.java new file mode 100644 index 0000000..cf06ade --- /dev/null +++ b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/Parser.java @@ -0,0 +1,7038 @@ +/* Parser.java */ +/* Generated by: JJTree&ParserGeneratorCC: Do not edit this line. Parser.java */ +package aiyh.utils.tool.org.apache.commons.jexl3.parser; + +import java.util.Collections; +import java.util.LinkedList; + +import aiyh.utils.tool.org.apache.commons.jexl3.JexlInfo; +import aiyh.utils.tool.org.apache.commons.jexl3.JexlFeatures; +import aiyh.utils.tool.org.apache.commons.jexl3.JexlException; +import aiyh.utils.tool.org.apache.commons.jexl3.internal.Scope; + +public final class Parser extends JexlParser/*@bgen(jjtree)*/implements ParserTreeConstants, ParserConstants {/*@bgen(jjtree)*/ + protected JJTParserState jjtree = new JJTParserState();public ASTJexlScript parse(JexlInfo jexlInfo, JexlFeatures jexlFeatures, String jexlSrc, Scope scope) { + JexlFeatures previous = getFeatures(); + try { + setFeatures(jexlFeatures); + // If registers are allowed, the default parser state has to be REGISTERS. + if (jexlFeatures.supportsRegister()) { + token_source.defaultLexState = REGISTERS; + } + // lets do the 'Unique Init' in here to be safe - it's a pain to remember + info = jexlInfo != null? jexlInfo : new JexlInfo(); + source = jexlSrc; + pragmas = null; + frame = scope; + ReInit(jexlSrc); + ASTJexlScript script = jexlFeatures.supportsScript()? JexlScript(scope) : JexlExpression(scope); + script.jjtSetValue(info.detach()); + script.setFeatures(jexlFeatures); + script.setPragmas(pragmas != null + ? Collections.unmodifiableMap(pragmas) + : Collections.emptyMap()); + return script; + } catch (TokenMgrException xtme) { + throw new JexlException.Tokenization(info, xtme).clean(); + } catch (ParseException xparse) { + Token errortok = errorToken(jj_lastpos, jj_scanpos, token.next, token); + throw new JexlException.Parsing(info.at(errortok.beginLine, errortok.beginColumn), errortok.image).clean(); + } finally { + token_source.defaultLexState = DEFAULT; + cleanup(previous); + jjtree.reset(); + } + } + +/*************************************** + * Statements + ***************************************/ + final public +ASTJexlScript JexlScript(Scope frame) throws ParseException {/*@bgen(jjtree) JexlScript */ + ASTJexlScript jjtn000 = new ASTJexlScript(JJTJEXLSCRIPT); + boolean jjtc000 = true; + jjtree.openNodeScope(jjtn000); + jjtreeOpenNodeScope(jjtn000); + jjtn000.jjtSetFirstToken(getToken(1));jjtn000.setScope(frame); + try { +pushUnit(jjtn000); + label_1: + while (true) { + if (jj_2_1(1)) { + } else { + break label_1; + } + Statement(); + } + jj_consume_token(0); +jjtree.closeNodeScope(jjtn000, true); + jjtc000 = false; + if (jjtree.nodeCreated()) { + jjtreeCloseNodeScope(jjtn000); + } + jjtn000.jjtSetLastToken(getToken(0)); +popUnit(jjtn000); + {if ("" != null) return jjtn000.script();} + } catch (Throwable jjte000) { +if (jjtc000) { + jjtree.clearNodeScope(jjtn000); + jjtc000 = false; + } else { + jjtree.popNode(); + } + if (jjte000 instanceof ParseException) { + throw (ParseException)jjte000; + } + if (jjte000 instanceof RuntimeException) { + throw (RuntimeException)jjte000; + } + throw (Error)jjte000; + } finally { +if (jjtc000) { + jjtree.closeNodeScope(jjtn000, true); + if (jjtree.nodeCreated()) { + jjtreeCloseNodeScope(jjtn000); + } + jjtn000.jjtSetLastToken(getToken(0)); + } + } + throw new IllegalStateException ("Missing return statement in function"); +} + + final public ASTJexlScript JexlExpression(Scope frame) throws ParseException {/*@bgen(jjtree) JexlScript */ + ASTJexlScript jjtn000 = new ASTJexlScript(JJTJEXLSCRIPT); + boolean jjtc000 = true; + jjtree.openNodeScope(jjtn000); + jjtreeOpenNodeScope(jjtn000); + jjtn000.jjtSetFirstToken(getToken(1));jjtn000.setScope(frame); + try { +pushUnit(jjtn000); + if (jj_2_2(1)) { + Expression(); + } else { + ; + } + jj_consume_token(0); +jjtree.closeNodeScope(jjtn000, true); + jjtc000 = false; + if (jjtree.nodeCreated()) { + jjtreeCloseNodeScope(jjtn000); + } + jjtn000.jjtSetLastToken(getToken(0)); +popUnit(jjtn000); + {if ("" != null) return jjtn000.script();} + } catch (Throwable jjte000) { +if (jjtc000) { + jjtree.clearNodeScope(jjtn000); + jjtc000 = false; + } else { + jjtree.popNode(); + } + if (jjte000 instanceof ParseException) { + throw (ParseException)jjte000; + } + if (jjte000 instanceof RuntimeException) { + throw (RuntimeException)jjte000; + } + throw (Error)jjte000; + } finally { +if (jjtc000) { + jjtree.closeNodeScope(jjtn000, true); + if (jjtree.nodeCreated()) { + jjtreeCloseNodeScope(jjtn000); + } + jjtn000.jjtSetLastToken(getToken(0)); + } + } + throw new IllegalStateException ("Missing return statement in function"); +} + + final public void Annotation() throws ParseException {/*@bgen(jjtree) Annotation */ + ASTAnnotation jjtn000 = new ASTAnnotation(JJTANNOTATION); + boolean jjtc000 = true; + jjtree.openNodeScope(jjtn000); + jjtreeOpenNodeScope(jjtn000); + jjtn000.jjtSetFirstToken(getToken(1));Token t; + try { + t = jj_consume_token(ANNOTATION); + if (jj_2_3(2147483647)) { + Arguments(); + } else { + ; + } +jjtree.closeNodeScope(jjtn000, true); + jjtc000 = false; + if (jjtree.nodeCreated()) { + jjtreeCloseNodeScope(jjtn000); + } + jjtn000.jjtSetLastToken(getToken(0)); +jjtn000.setName(t.image); + } catch (Throwable jjte000) { +if (jjtc000) { + jjtree.clearNodeScope(jjtn000); + jjtc000 = false; + } else { + jjtree.popNode(); + } + if (jjte000 instanceof ParseException) { + throw (ParseException)jjte000; + } + if (jjte000 instanceof RuntimeException) { + throw (RuntimeException)jjte000; + } + throw (Error)jjte000; + } finally { +if (jjtc000) { + jjtree.closeNodeScope(jjtn000, true); + if (jjtree.nodeCreated()) { + jjtreeCloseNodeScope(jjtn000); + } + jjtn000.jjtSetLastToken(getToken(0)); + } + } +} + + final public void AnnotatedStatement() throws ParseException {/*@bgen(jjtree) AnnotatedStatement */ + ASTAnnotatedStatement jjtn000 = new ASTAnnotatedStatement(JJTANNOTATEDSTATEMENT); + boolean jjtc000 = true; + jjtree.openNodeScope(jjtn000); + jjtreeOpenNodeScope(jjtn000); + jjtn000.jjtSetFirstToken(getToken(1)); + try { + label_2: + while (true) { + Annotation(); + if (jj_2_4(2147483647)) { + } else { + break label_2; + } + } + switch (jj_nt.kind) { + case LCURLY:{ + Block(); + break; + } + default: + if (jj_2_5(1)) { + Statement(); + } else { + jj_consume_token(-1); + throw new ParseException(); + } + } + } catch (Throwable jjte000) { +if (jjtc000) { + jjtree.clearNodeScope(jjtn000); + jjtc000 = false; + } else { + jjtree.popNode(); + } + if (jjte000 instanceof ParseException) { + throw (ParseException)jjte000; + } + if (jjte000 instanceof RuntimeException) { + throw (RuntimeException)jjte000; + } + throw (Error)jjte000; + } finally { +if (jjtc000) { + jjtree.closeNodeScope(jjtn000, true); + if (jjtree.nodeCreated()) { + jjtreeCloseNodeScope(jjtn000); + } + jjtn000.jjtSetLastToken(getToken(0)); + } + } +} + + final public void Statement() throws ParseException { + switch (jj_nt.kind) { + case SEMICOL:{ + jj_consume_token(SEMICOL); + break; + } + default: + if (jj_2_6(2147483647)) { + AnnotatedStatement(); + } else if (jj_2_7(2147483647)) { + ExpressionStatement(); + } else { + switch (jj_nt.kind) { + case LCURLY:{ + Block(); + break; + } + case IF:{ + IfStatement(); + break; + } + case FOR:{ + ForeachStatement(); + break; + } + case WHILE:{ + WhileStatement(); + break; + } + case DO:{ + DoWhileStatement(); + break; + } + case RETURN:{ + ReturnStatement(); + break; + } + case CONTINUE:{ + Continue(); + break; + } + case BREAK:{ + Break(); + break; + } + case VAR:{ + Var(); + break; + } + case PRAGMA:{ + Pragma(); + break; + } + default: + jj_consume_token(-1); + throw new ParseException(); + } + } + } +} + + final public void Block() throws ParseException {/*@bgen(jjtree) Block */ + ASTBlock jjtn000 = new ASTBlock(JJTBLOCK); + boolean jjtc000 = true; + jjtree.openNodeScope(jjtn000); + jjtreeOpenNodeScope(jjtn000); + jjtn000.jjtSetFirstToken(getToken(1)); + try { + jj_consume_token(LCURLY); +pushUnit(jjtn000); + label_3: + while (true) { + if (jj_2_8(1)) { + } else { + break label_3; + } + Statement(); + } +popUnit(jjtn000); + jj_consume_token(RCURLY); + } catch (Throwable jjte000) { +if (jjtc000) { + jjtree.clearNodeScope(jjtn000); + jjtc000 = false; + } else { + jjtree.popNode(); + } + if (jjte000 instanceof ParseException) { + throw (ParseException)jjte000; + } + if (jjte000 instanceof RuntimeException) { + throw (RuntimeException)jjte000; + } + throw (Error)jjte000; + } finally { +if (jjtc000) { + jjtree.closeNodeScope(jjtn000, true); + if (jjtree.nodeCreated()) { + jjtreeCloseNodeScope(jjtn000); + } + jjtn000.jjtSetLastToken(getToken(0)); + } + } +} + + final public void ExpressionStatement() throws ParseException { + Expression(); + label_4: + while (true) { + if (jj_2_9(1)) { + } else { + break label_4; + } +ASTAmbiguous jjtn001 = new ASTAmbiguous(JJTAMBIGUOUS); + boolean jjtc001 = true; + jjtree.openNodeScope(jjtn001); + jjtreeOpenNodeScope(jjtn001); + jjtn001.jjtSetFirstToken(getToken(1)); + try { + Expression(); + } catch (Throwable jjte001) { +if (jjtc001) { + jjtree.clearNodeScope(jjtn001); + jjtc001 = false; + } else { + jjtree.popNode(); + } + if (jjte001 instanceof ParseException) { + throw (ParseException)jjte001; + } + if (jjte001 instanceof RuntimeException) { + throw (RuntimeException)jjte001; + } + throw (Error)jjte001; + } finally { +if (jjtc001) { + jjtree.closeNodeScope(jjtn001, 1); + if (jjtree.nodeCreated()) { + jjtreeCloseNodeScope(jjtn001); + } + jjtn001.jjtSetLastToken(getToken(0)); + } + } + } + switch (jj_nt.kind) { + case SEMICOL:{ + jj_consume_token(SEMICOL); + break; + } + default: + ; + } +} + + final public void IfStatement() throws ParseException {/*@bgen(jjtree) IfStatement */ + ASTIfStatement jjtn000 = new ASTIfStatement(JJTIFSTATEMENT); + boolean jjtc000 = true; + jjtree.openNodeScope(jjtn000); + jjtreeOpenNodeScope(jjtn000); + jjtn000.jjtSetFirstToken(getToken(1)); + try { + jj_consume_token(IF); + jj_consume_token(LPAREN); + Expression(); + jj_consume_token(RPAREN); + switch (jj_nt.kind) { + case LCURLY:{ + Block(); + break; + } + default: + if (jj_2_10(1)) { + Statement(); + } else { + jj_consume_token(-1); + throw new ParseException(); + } + } + label_5: + while (true) { + if (jj_2_11(2)) { + } else { + break label_5; + } + jj_consume_token(ELSE); + jj_consume_token(IF); + jj_consume_token(LPAREN); + Expression(); + jj_consume_token(RPAREN); + switch (jj_nt.kind) { + case LCURLY:{ + Block(); + break; + } + default: + if (jj_2_12(1)) { + Statement(); + } else { + jj_consume_token(-1); + throw new ParseException(); + } + } + } + switch (jj_nt.kind) { + case ELSE:{ + jj_consume_token(ELSE); + switch (jj_nt.kind) { + case LCURLY:{ + Block(); + break; + } + default: + if (jj_2_13(1)) { + Statement(); + } else { + jj_consume_token(-1); + throw new ParseException(); + } + } + break; + } + default: + ; + } + } catch (Throwable jjte000) { +if (jjtc000) { + jjtree.clearNodeScope(jjtn000); + jjtc000 = false; + } else { + jjtree.popNode(); + } + if (jjte000 instanceof ParseException) { + throw (ParseException)jjte000; + } + if (jjte000 instanceof RuntimeException) { + throw (RuntimeException)jjte000; + } + throw (Error)jjte000; + } finally { +if (jjtc000) { + jjtree.closeNodeScope(jjtn000, true); + if (jjtree.nodeCreated()) { + jjtreeCloseNodeScope(jjtn000); + } + jjtn000.jjtSetLastToken(getToken(0)); + } + } +} + + final public void WhileStatement() throws ParseException {/*@bgen(jjtree) WhileStatement */ + ASTWhileStatement jjtn000 = new ASTWhileStatement(JJTWHILESTATEMENT); + boolean jjtc000 = true; + jjtree.openNodeScope(jjtn000); + jjtreeOpenNodeScope(jjtn000); + jjtn000.jjtSetFirstToken(getToken(1)); + try { + jj_consume_token(WHILE); + jj_consume_token(LPAREN); + Expression(); + jj_consume_token(RPAREN); +loopCount += 1; + switch (jj_nt.kind) { + case LCURLY:{ + Block(); + break; + } + default: + if (jj_2_14(1)) { + Statement(); + } else { + jj_consume_token(-1); + throw new ParseException(); + } + } +jjtree.closeNodeScope(jjtn000, true); + jjtc000 = false; + if (jjtree.nodeCreated()) { + jjtreeCloseNodeScope(jjtn000); + } + jjtn000.jjtSetLastToken(getToken(0)); +loopCount -= 1; + } catch (Throwable jjte000) { +if (jjtc000) { + jjtree.clearNodeScope(jjtn000); + jjtc000 = false; + } else { + jjtree.popNode(); + } + if (jjte000 instanceof ParseException) { + throw (ParseException)jjte000; + } + if (jjte000 instanceof RuntimeException) { + throw (RuntimeException)jjte000; + } + throw (Error)jjte000; + } finally { +if (jjtc000) { + jjtree.closeNodeScope(jjtn000, true); + if (jjtree.nodeCreated()) { + jjtreeCloseNodeScope(jjtn000); + } + jjtn000.jjtSetLastToken(getToken(0)); + } + } +} + + final public void DoWhileStatement() throws ParseException {/*@bgen(jjtree) DoWhileStatement */ + ASTDoWhileStatement jjtn000 = new ASTDoWhileStatement(JJTDOWHILESTATEMENT); + boolean jjtc000 = true; + jjtree.openNodeScope(jjtn000); + jjtreeOpenNodeScope(jjtn000); + jjtn000.jjtSetFirstToken(getToken(1)); + try { + jj_consume_token(DO); +loopCount += 1; + switch (jj_nt.kind) { + case LCURLY:{ + Block(); + break; + } + default: + if (jj_2_15(1)) { + Statement(); + } else { + jj_consume_token(-1); + throw new ParseException(); + } + } + jj_consume_token(WHILE); + jj_consume_token(LPAREN); + Expression(); + jj_consume_token(RPAREN); +jjtree.closeNodeScope(jjtn000, true); + jjtc000 = false; + if (jjtree.nodeCreated()) { + jjtreeCloseNodeScope(jjtn000); + } + jjtn000.jjtSetLastToken(getToken(0)); +loopCount -= 1; + } catch (Throwable jjte000) { +if (jjtc000) { + jjtree.clearNodeScope(jjtn000); + jjtc000 = false; + } else { + jjtree.popNode(); + } + if (jjte000 instanceof ParseException) { + throw (ParseException)jjte000; + } + if (jjte000 instanceof RuntimeException) { + throw (RuntimeException)jjte000; + } + throw (Error)jjte000; + } finally { +if (jjtc000) { + jjtree.closeNodeScope(jjtn000, true); + if (jjtree.nodeCreated()) { + jjtreeCloseNodeScope(jjtn000); + } + jjtn000.jjtSetLastToken(getToken(0)); + } + } +} + + final public void ReturnStatement() throws ParseException {/*@bgen(jjtree) ReturnStatement */ + ASTReturnStatement jjtn000 = new ASTReturnStatement(JJTRETURNSTATEMENT); + boolean jjtc000 = true; + jjtree.openNodeScope(jjtn000); + jjtreeOpenNodeScope(jjtn000); + jjtn000.jjtSetFirstToken(getToken(1)); + try { + jj_consume_token(RETURN); + ExpressionStatement(); + } catch (Throwable jjte000) { +if (jjtc000) { + jjtree.clearNodeScope(jjtn000); + jjtc000 = false; + } else { + jjtree.popNode(); + } + if (jjte000 instanceof ParseException) { + throw (ParseException)jjte000; + } + if (jjte000 instanceof RuntimeException) { + throw (RuntimeException)jjte000; + } + throw (Error)jjte000; + } finally { +if (jjtc000) { + jjtree.closeNodeScope(jjtn000, true); + if (jjtree.nodeCreated()) { + jjtreeCloseNodeScope(jjtn000); + } + jjtn000.jjtSetLastToken(getToken(0)); + } + } +} + + final public void Continue() throws ParseException {/*@bgen(jjtree) Continue */ + ASTContinue jjtn000 = new ASTContinue(JJTCONTINUE); + boolean jjtc000 = true; + jjtree.openNodeScope(jjtn000); + jjtreeOpenNodeScope(jjtn000); + jjtn000.jjtSetFirstToken(getToken(1));Token t; + try { + t = jj_consume_token(CONTINUE); +jjtree.closeNodeScope(jjtn000, true); + jjtc000 = false; + if (jjtree.nodeCreated()) { + jjtreeCloseNodeScope(jjtn000); + } + jjtn000.jjtSetLastToken(getToken(0)); +if (loopCount == 0) { throwParsingException(null, t); } + } finally { +if (jjtc000) { + jjtree.closeNodeScope(jjtn000, true); + if (jjtree.nodeCreated()) { + jjtreeCloseNodeScope(jjtn000); + } + jjtn000.jjtSetLastToken(getToken(0)); + } + } +} + + final public void Break() throws ParseException {/*@bgen(jjtree) Break */ + ASTBreak jjtn000 = new ASTBreak(JJTBREAK); + boolean jjtc000 = true; + jjtree.openNodeScope(jjtn000); + jjtreeOpenNodeScope(jjtn000); + jjtn000.jjtSetFirstToken(getToken(1));Token t; + try { + t = jj_consume_token(BREAK); +jjtree.closeNodeScope(jjtn000, true); + jjtc000 = false; + if (jjtree.nodeCreated()) { + jjtreeCloseNodeScope(jjtn000); + } + jjtn000.jjtSetLastToken(getToken(0)); +if (loopCount == 0) { throwParsingException(null, t); } + } finally { +if (jjtc000) { + jjtree.closeNodeScope(jjtn000, true); + if (jjtree.nodeCreated()) { + jjtreeCloseNodeScope(jjtn000); + } + jjtn000.jjtSetLastToken(getToken(0)); + } + } +} + + final public void ForeachStatement() throws ParseException {/*@bgen(jjtree) ForeachStatement */ + ASTForeachStatement jjtn000 = new ASTForeachStatement(JJTFOREACHSTATEMENT); + boolean jjtc000 = true; + jjtree.openNodeScope(jjtn000); + jjtreeOpenNodeScope(jjtn000); + jjtn000.jjtSetFirstToken(getToken(1)); + try { +pushUnit(jjtn000); + jj_consume_token(FOR); + jj_consume_token(LPAREN); + ForEachVar(); + jj_consume_token(COLON); + Expression(); + jj_consume_token(RPAREN); +loopCount += 1; + switch (jj_nt.kind) { + case LCURLY:{ + Block(); + break; + } + default: + if (jj_2_16(1)) { + Statement(); + } else { + jj_consume_token(-1); + throw new ParseException(); + } + } +jjtree.closeNodeScope(jjtn000, true); + jjtc000 = false; + if (jjtree.nodeCreated()) { + jjtreeCloseNodeScope(jjtn000); + } + jjtn000.jjtSetLastToken(getToken(0)); +loopCount -= 1; popUnit(jjtn000); + } catch (Throwable jjte000) { +if (jjtc000) { + jjtree.clearNodeScope(jjtn000); + jjtc000 = false; + } else { + jjtree.popNode(); + } + if (jjte000 instanceof ParseException) { + throw (ParseException)jjte000; + } + if (jjte000 instanceof RuntimeException) { + throw (RuntimeException)jjte000; + } + throw (Error)jjte000; + } finally { +if (jjtc000) { + jjtree.closeNodeScope(jjtn000, true); + if (jjtree.nodeCreated()) { + jjtreeCloseNodeScope(jjtn000); + } + jjtn000.jjtSetLastToken(getToken(0)); + } + } +} + + final public void ForEachVar() throws ParseException {/*@bgen(jjtree) Reference */ + ASTReference jjtn000 = new ASTReference(JJTREFERENCE); + boolean jjtc000 = true; + jjtree.openNodeScope(jjtn000); + jjtreeOpenNodeScope(jjtn000); + jjtn000.jjtSetFirstToken(getToken(1)); + try { + switch (jj_nt.kind) { + case VAR:{ + jj_consume_token(VAR); + DeclareVar(); + break; + } + case IDENTIFIER: + case REGISTER:{ + Identifier(true); + break; + } + default: + jj_consume_token(-1); + throw new ParseException(); + } + } catch (Throwable jjte000) { +if (jjtc000) { + jjtree.clearNodeScope(jjtn000); + jjtc000 = false; + } else { + jjtree.popNode(); + } + if (jjte000 instanceof ParseException) { + throw (ParseException)jjte000; + } + if (jjte000 instanceof RuntimeException) { + throw (RuntimeException)jjte000; + } + throw (Error)jjte000; + } finally { +if (jjtc000) { + jjtree.closeNodeScope(jjtn000, true); + if (jjtree.nodeCreated()) { + jjtreeCloseNodeScope(jjtn000); + } + jjtn000.jjtSetLastToken(getToken(0)); + } + } +} + + final public void Var() throws ParseException { + jj_consume_token(VAR); + DeclareVar(); + switch (jj_nt.kind) { + case assign:{ + jj_consume_token(assign); +ASTAssignment jjtn001 = new ASTAssignment(JJTASSIGNMENT); + boolean jjtc001 = true; + jjtree.openNodeScope(jjtn001); + jjtreeOpenNodeScope(jjtn001); + jjtn001.jjtSetFirstToken(getToken(1)); + try { + Expression(); + } catch (Throwable jjte001) { +if (jjtc001) { + jjtree.clearNodeScope(jjtn001); + jjtc001 = false; + } else { + jjtree.popNode(); + } + if (jjte001 instanceof ParseException) { + throw (ParseException)jjte001; + } + if (jjte001 instanceof RuntimeException) { + throw (RuntimeException)jjte001; + } + throw (Error)jjte001; + } finally { +if (jjtc001) { + jjtree.closeNodeScope(jjtn001, 2); + if (jjtree.nodeCreated()) { + jjtreeCloseNodeScope(jjtn001); + } + jjtn001.jjtSetLastToken(getToken(0)); + } + } + break; + } + default: + ; + } +} + + final public void DeclareVar() throws ParseException {/*@bgen(jjtree) Var */ + ASTVar jjtn000 = new ASTVar(JJTVAR); + boolean jjtc000 = true; + jjtree.openNodeScope(jjtn000); + jjtreeOpenNodeScope(jjtn000); + jjtn000.jjtSetFirstToken(getToken(1));Token t; + try { + t = jj_consume_token(IDENTIFIER); +jjtree.closeNodeScope(jjtn000, true); + jjtc000 = false; + if (jjtree.nodeCreated()) { + jjtreeCloseNodeScope(jjtn000); + } + jjtn000.jjtSetLastToken(getToken(0)); +declareVariable(jjtn000, t); + } finally { +if (jjtc000) { + jjtree.closeNodeScope(jjtn000, true); + if (jjtree.nodeCreated()) { + jjtreeCloseNodeScope(jjtn000); + } + jjtn000.jjtSetLastToken(getToken(0)); + } + } +} + + final public void Pragma() throws ParseException {LinkedList lstr = new LinkedList(); + Object value; + jj_consume_token(PRAGMA); + pragmaKey(lstr); + value = pragmaValue(); +declarePragma(stringify(lstr), value); +} + + final public void pragmaKey(LinkedList lstr) throws ParseException {Token t; + switch (jj_nt.kind) { + case IDENTIFIER:{ + t = jj_consume_token(IDENTIFIER); +lstr.add(t.image); + label_6: + while (true) { + if (jj_2_17(2147483647)) { + } else { + break label_6; + } + pragmaKey(lstr); + } + break; + } + case DOT:{ + jj_consume_token(DOT); + t = jj_consume_token(DOT_IDENTIFIER); +lstr.add(t.image); + break; + } + default: + jj_consume_token(-1); + throw new ParseException(); + } +} + + final public Object pragmaValue() throws ParseException {Token v; +LinkedList lstr = new LinkedList(); +Object result; + switch (jj_nt.kind) { + case INTEGER_LITERAL:{ + v = jj_consume_token(INTEGER_LITERAL); +result = NumberParser.parseInteger(v.image); + break; + } + case FLOAT_LITERAL:{ + v = jj_consume_token(FLOAT_LITERAL); +result = NumberParser.parseDouble(v.image); + break; + } + case STRING_LITERAL:{ + v = jj_consume_token(STRING_LITERAL); +result = buildString(v.image, true); + break; + } + case DOT: + case IDENTIFIER:{ + pragmaKey(lstr); +result = stringify(lstr); + break; + } + case TRUE:{ + jj_consume_token(TRUE); +result = true; + break; + } + case FALSE:{ + jj_consume_token(FALSE); +result = false; + break; + } + case NULL:{ + jj_consume_token(NULL); +result = null; + break; + } + case NAN_LITERAL:{ + jj_consume_token(NAN_LITERAL); +result = Double.NaN; + break; + } + default: + jj_consume_token(-1); + throw new ParseException(); + } +{if ("" != null) return result;} + throw new IllegalStateException ("Missing return statement in function"); +} + +/*************************************** + * Expression syntax + ***************************************/ + final public +void Expression() throws ParseException { + AssignmentExpression(); +} + + final public void AssignmentExpression() throws ParseException { + ConditionalExpression(); + label_7: + while (true) { + if (jj_2_18(2)) { + } else { + break label_7; + } + switch (jj_nt.kind) { + case plus_assign:{ + jj_consume_token(plus_assign); +ASTSetAddNode jjtn001 = new ASTSetAddNode(JJTSETADDNODE); + boolean jjtc001 = true; + jjtree.openNodeScope(jjtn001); + jjtreeOpenNodeScope(jjtn001); + jjtn001.jjtSetFirstToken(getToken(1)); + try { + Expression(); + } catch (Throwable jjte001) { +if (jjtc001) { + jjtree.clearNodeScope(jjtn001); + jjtc001 = false; + } else { + jjtree.popNode(); + } + if (jjte001 instanceof ParseException) { + throw (ParseException)jjte001; + } + if (jjte001 instanceof RuntimeException) { + throw (RuntimeException)jjte001; + } + throw (Error)jjte001; + } finally { +if (jjtc001) { + jjtree.closeNodeScope(jjtn001, 2); + if (jjtree.nodeCreated()) { + jjtreeCloseNodeScope(jjtn001); + } + jjtn001.jjtSetLastToken(getToken(0)); + } + } + break; + } + case mult_assign:{ + jj_consume_token(mult_assign); +ASTSetMultNode jjtn002 = new ASTSetMultNode(JJTSETMULTNODE); + boolean jjtc002 = true; + jjtree.openNodeScope(jjtn002); + jjtreeOpenNodeScope(jjtn002); + jjtn002.jjtSetFirstToken(getToken(1)); + try { + Expression(); + } catch (Throwable jjte002) { +if (jjtc002) { + jjtree.clearNodeScope(jjtn002); + jjtc002 = false; + } else { + jjtree.popNode(); + } + if (jjte002 instanceof ParseException) { + throw (ParseException)jjte002; + } + if (jjte002 instanceof RuntimeException) { + throw (RuntimeException)jjte002; + } + throw (Error)jjte002; + } finally { +if (jjtc002) { + jjtree.closeNodeScope(jjtn002, 2); + if (jjtree.nodeCreated()) { + jjtreeCloseNodeScope(jjtn002); + } + jjtn002.jjtSetLastToken(getToken(0)); + } + } + break; + } + case div_assign:{ + jj_consume_token(div_assign); +ASTSetDivNode jjtn003 = new ASTSetDivNode(JJTSETDIVNODE); + boolean jjtc003 = true; + jjtree.openNodeScope(jjtn003); + jjtreeOpenNodeScope(jjtn003); + jjtn003.jjtSetFirstToken(getToken(1)); + try { + Expression(); + } catch (Throwable jjte003) { +if (jjtc003) { + jjtree.clearNodeScope(jjtn003); + jjtc003 = false; + } else { + jjtree.popNode(); + } + if (jjte003 instanceof ParseException) { + throw (ParseException)jjte003; + } + if (jjte003 instanceof RuntimeException) { + throw (RuntimeException)jjte003; + } + throw (Error)jjte003; + } finally { +if (jjtc003) { + jjtree.closeNodeScope(jjtn003, 2); + if (jjtree.nodeCreated()) { + jjtreeCloseNodeScope(jjtn003); + } + jjtn003.jjtSetLastToken(getToken(0)); + } + } + break; + } + case mod_assign:{ + jj_consume_token(mod_assign); +ASTSetModNode jjtn004 = new ASTSetModNode(JJTSETMODNODE); + boolean jjtc004 = true; + jjtree.openNodeScope(jjtn004); + jjtreeOpenNodeScope(jjtn004); + jjtn004.jjtSetFirstToken(getToken(1)); + try { + Expression(); + } catch (Throwable jjte004) { +if (jjtc004) { + jjtree.clearNodeScope(jjtn004); + jjtc004 = false; + } else { + jjtree.popNode(); + } + if (jjte004 instanceof ParseException) { + throw (ParseException)jjte004; + } + if (jjte004 instanceof RuntimeException) { + throw (RuntimeException)jjte004; + } + throw (Error)jjte004; + } finally { +if (jjtc004) { + jjtree.closeNodeScope(jjtn004, 2); + if (jjtree.nodeCreated()) { + jjtreeCloseNodeScope(jjtn004); + } + jjtn004.jjtSetLastToken(getToken(0)); + } + } + break; + } + case and_assign:{ + jj_consume_token(and_assign); +ASTSetAndNode jjtn005 = new ASTSetAndNode(JJTSETANDNODE); + boolean jjtc005 = true; + jjtree.openNodeScope(jjtn005); + jjtreeOpenNodeScope(jjtn005); + jjtn005.jjtSetFirstToken(getToken(1)); + try { + Expression(); + } catch (Throwable jjte005) { +if (jjtc005) { + jjtree.clearNodeScope(jjtn005); + jjtc005 = false; + } else { + jjtree.popNode(); + } + if (jjte005 instanceof ParseException) { + throw (ParseException)jjte005; + } + if (jjte005 instanceof RuntimeException) { + throw (RuntimeException)jjte005; + } + throw (Error)jjte005; + } finally { +if (jjtc005) { + jjtree.closeNodeScope(jjtn005, 2); + if (jjtree.nodeCreated()) { + jjtreeCloseNodeScope(jjtn005); + } + jjtn005.jjtSetLastToken(getToken(0)); + } + } + break; + } + case or_assign:{ + jj_consume_token(or_assign); +ASTSetOrNode jjtn006 = new ASTSetOrNode(JJTSETORNODE); + boolean jjtc006 = true; + jjtree.openNodeScope(jjtn006); + jjtreeOpenNodeScope(jjtn006); + jjtn006.jjtSetFirstToken(getToken(1)); + try { + Expression(); + } catch (Throwable jjte006) { +if (jjtc006) { + jjtree.clearNodeScope(jjtn006); + jjtc006 = false; + } else { + jjtree.popNode(); + } + if (jjte006 instanceof ParseException) { + throw (ParseException)jjte006; + } + if (jjte006 instanceof RuntimeException) { + throw (RuntimeException)jjte006; + } + throw (Error)jjte006; + } finally { +if (jjtc006) { + jjtree.closeNodeScope(jjtn006, 2); + if (jjtree.nodeCreated()) { + jjtreeCloseNodeScope(jjtn006); + } + jjtn006.jjtSetLastToken(getToken(0)); + } + } + break; + } + case xor_assign:{ + jj_consume_token(xor_assign); +ASTSetXorNode jjtn007 = new ASTSetXorNode(JJTSETXORNODE); + boolean jjtc007 = true; + jjtree.openNodeScope(jjtn007); + jjtreeOpenNodeScope(jjtn007); + jjtn007.jjtSetFirstToken(getToken(1)); + try { + Expression(); + } catch (Throwable jjte007) { +if (jjtc007) { + jjtree.clearNodeScope(jjtn007); + jjtc007 = false; + } else { + jjtree.popNode(); + } + if (jjte007 instanceof ParseException) { + throw (ParseException)jjte007; + } + if (jjte007 instanceof RuntimeException) { + throw (RuntimeException)jjte007; + } + throw (Error)jjte007; + } finally { +if (jjtc007) { + jjtree.closeNodeScope(jjtn007, 2); + if (jjtree.nodeCreated()) { + jjtreeCloseNodeScope(jjtn007); + } + jjtn007.jjtSetLastToken(getToken(0)); + } + } + break; + } + case minus_assign:{ + jj_consume_token(minus_assign); +ASTSetSubNode jjtn008 = new ASTSetSubNode(JJTSETSUBNODE); + boolean jjtc008 = true; + jjtree.openNodeScope(jjtn008); + jjtreeOpenNodeScope(jjtn008); + jjtn008.jjtSetFirstToken(getToken(1)); + try { + Expression(); + } catch (Throwable jjte008) { +if (jjtc008) { + jjtree.clearNodeScope(jjtn008); + jjtc008 = false; + } else { + jjtree.popNode(); + } + if (jjte008 instanceof ParseException) { + throw (ParseException)jjte008; + } + if (jjte008 instanceof RuntimeException) { + throw (RuntimeException)jjte008; + } + throw (Error)jjte008; + } finally { +if (jjtc008) { + jjtree.closeNodeScope(jjtn008, 2); + if (jjtree.nodeCreated()) { + jjtreeCloseNodeScope(jjtn008); + } + jjtn008.jjtSetLastToken(getToken(0)); + } + } + break; + } + case assign:{ + jj_consume_token(assign); +ASTAssignment jjtn009 = new ASTAssignment(JJTASSIGNMENT); + boolean jjtc009 = true; + jjtree.openNodeScope(jjtn009); + jjtreeOpenNodeScope(jjtn009); + jjtn009.jjtSetFirstToken(getToken(1)); + try { + Expression(); + } catch (Throwable jjte009) { +if (jjtc009) { + jjtree.clearNodeScope(jjtn009); + jjtc009 = false; + } else { + jjtree.popNode(); + } + if (jjte009 instanceof ParseException) { + throw (ParseException)jjte009; + } + if (jjte009 instanceof RuntimeException) { + throw (RuntimeException)jjte009; + } + throw (Error)jjte009; + } finally { +if (jjtc009) { + jjtree.closeNodeScope(jjtn009, 2); + if (jjtree.nodeCreated()) { + jjtreeCloseNodeScope(jjtn009); + } + jjtn009.jjtSetLastToken(getToken(0)); + } + } + break; + } + default: + jj_consume_token(-1); + throw new ParseException(); + } + } +} + +/*************************************** + * Conditional & relational + ***************************************/ + final public +void ConditionalExpression() throws ParseException { + ConditionalOrExpression(); + switch (jj_nt.kind) { + case QMARK: + case ELVIS: + case NULLP:{ + switch (jj_nt.kind) { + case QMARK:{ + jj_consume_token(QMARK); + Expression(); + jj_consume_token(COLON); +ASTTernaryNode jjtn001 = new ASTTernaryNode(JJTTERNARYNODE); + boolean jjtc001 = true; + jjtree.openNodeScope(jjtn001); + jjtreeOpenNodeScope(jjtn001); + jjtn001.jjtSetFirstToken(getToken(1)); + try { + Expression(); + } catch (Throwable jjte001) { +if (jjtc001) { + jjtree.clearNodeScope(jjtn001); + jjtc001 = false; + } else { + jjtree.popNode(); + } + if (jjte001 instanceof ParseException) { + throw (ParseException)jjte001; + } + if (jjte001 instanceof RuntimeException) { + throw (RuntimeException)jjte001; + } + throw (Error)jjte001; + } finally { +if (jjtc001) { + jjtree.closeNodeScope(jjtn001, 3); + if (jjtree.nodeCreated()) { + jjtreeCloseNodeScope(jjtn001); + } + jjtn001.jjtSetLastToken(getToken(0)); + } + } + break; + } + case ELVIS:{ + jj_consume_token(ELVIS); +ASTTernaryNode jjtn002 = new ASTTernaryNode(JJTTERNARYNODE); + boolean jjtc002 = true; + jjtree.openNodeScope(jjtn002); + jjtreeOpenNodeScope(jjtn002); + jjtn002.jjtSetFirstToken(getToken(1)); + try { + Expression(); + } catch (Throwable jjte002) { +if (jjtc002) { + jjtree.clearNodeScope(jjtn002); + jjtc002 = false; + } else { + jjtree.popNode(); + } + if (jjte002 instanceof ParseException) { + throw (ParseException)jjte002; + } + if (jjte002 instanceof RuntimeException) { + throw (RuntimeException)jjte002; + } + throw (Error)jjte002; + } finally { +if (jjtc002) { + jjtree.closeNodeScope(jjtn002, 2); + if (jjtree.nodeCreated()) { + jjtreeCloseNodeScope(jjtn002); + } + jjtn002.jjtSetLastToken(getToken(0)); + } + } + break; + } + case NULLP:{ + jj_consume_token(NULLP); +ASTNullpNode jjtn003 = new ASTNullpNode(JJTNULLPNODE); + boolean jjtc003 = true; + jjtree.openNodeScope(jjtn003); + jjtreeOpenNodeScope(jjtn003); + jjtn003.jjtSetFirstToken(getToken(1)); + try { + Expression(); + } catch (Throwable jjte003) { +if (jjtc003) { + jjtree.clearNodeScope(jjtn003); + jjtc003 = false; + } else { + jjtree.popNode(); + } + if (jjte003 instanceof ParseException) { + throw (ParseException)jjte003; + } + if (jjte003 instanceof RuntimeException) { + throw (RuntimeException)jjte003; + } + throw (Error)jjte003; + } finally { +if (jjtc003) { + jjtree.closeNodeScope(jjtn003, 2); + if (jjtree.nodeCreated()) { + jjtreeCloseNodeScope(jjtn003); + } + jjtn003.jjtSetLastToken(getToken(0)); + } + } + break; + } + default: + jj_consume_token(-1); + throw new ParseException(); + } + break; + } + default: + ; + } +} + + final public void ConditionalOrExpression() throws ParseException { + ConditionalAndExpression(); + label_8: + while (true) { + switch (jj_nt.kind) { + case OR: + case _OR:{ + break; + } + default: + break label_8; + } + switch (jj_nt.kind) { + case OR:{ + jj_consume_token(OR); + break; + } + case _OR:{ + jj_consume_token(_OR); + break; + } + default: + jj_consume_token(-1); + throw new ParseException(); + } +ASTOrNode jjtn001 = new ASTOrNode(JJTORNODE); + boolean jjtc001 = true; + jjtree.openNodeScope(jjtn001); + jjtreeOpenNodeScope(jjtn001); + jjtn001.jjtSetFirstToken(getToken(1)); + try { + ConditionalAndExpression(); + } catch (Throwable jjte001) { +if (jjtc001) { + jjtree.clearNodeScope(jjtn001); + jjtc001 = false; + } else { + jjtree.popNode(); + } + if (jjte001 instanceof ParseException) { + throw (ParseException)jjte001; + } + if (jjte001 instanceof RuntimeException) { + throw (RuntimeException)jjte001; + } + throw (Error)jjte001; + } finally { +if (jjtc001) { + jjtree.closeNodeScope(jjtn001, 2); + if (jjtree.nodeCreated()) { + jjtreeCloseNodeScope(jjtn001); + } + jjtn001.jjtSetLastToken(getToken(0)); + } + } + } +} + + final public void ConditionalAndExpression() throws ParseException { + InclusiveOrExpression(); + label_9: + while (true) { + switch (jj_nt.kind) { + case AND: + case _AND:{ + break; + } + default: + break label_9; + } + switch (jj_nt.kind) { + case AND:{ + jj_consume_token(AND); + break; + } + case _AND:{ + jj_consume_token(_AND); + break; + } + default: + jj_consume_token(-1); + throw new ParseException(); + } +ASTAndNode jjtn001 = new ASTAndNode(JJTANDNODE); + boolean jjtc001 = true; + jjtree.openNodeScope(jjtn001); + jjtreeOpenNodeScope(jjtn001); + jjtn001.jjtSetFirstToken(getToken(1)); + try { + InclusiveOrExpression(); + } catch (Throwable jjte001) { +if (jjtc001) { + jjtree.clearNodeScope(jjtn001); + jjtc001 = false; + } else { + jjtree.popNode(); + } + if (jjte001 instanceof ParseException) { + throw (ParseException)jjte001; + } + if (jjte001 instanceof RuntimeException) { + throw (RuntimeException)jjte001; + } + throw (Error)jjte001; + } finally { +if (jjtc001) { + jjtree.closeNodeScope(jjtn001, 2); + if (jjtree.nodeCreated()) { + jjtreeCloseNodeScope(jjtn001); + } + jjtn001.jjtSetLastToken(getToken(0)); + } + } + } +} + + final public void InclusiveOrExpression() throws ParseException { + ExclusiveOrExpression(); + label_10: + while (true) { + switch (jj_nt.kind) { + case or:{ + break; + } + default: + break label_10; + } + jj_consume_token(or); +ASTBitwiseOrNode jjtn001 = new ASTBitwiseOrNode(JJTBITWISEORNODE); + boolean jjtc001 = true; + jjtree.openNodeScope(jjtn001); + jjtreeOpenNodeScope(jjtn001); + jjtn001.jjtSetFirstToken(getToken(1)); + try { + ExclusiveOrExpression(); + } catch (Throwable jjte001) { +if (jjtc001) { + jjtree.clearNodeScope(jjtn001); + jjtc001 = false; + } else { + jjtree.popNode(); + } + if (jjte001 instanceof ParseException) { + throw (ParseException)jjte001; + } + if (jjte001 instanceof RuntimeException) { + throw (RuntimeException)jjte001; + } + throw (Error)jjte001; + } finally { +if (jjtc001) { + jjtree.closeNodeScope(jjtn001, 2); + if (jjtree.nodeCreated()) { + jjtreeCloseNodeScope(jjtn001); + } + jjtn001.jjtSetLastToken(getToken(0)); + } + } + } +} + + final public void ExclusiveOrExpression() throws ParseException { + AndExpression(); + label_11: + while (true) { + switch (jj_nt.kind) { + case xor:{ + break; + } + default: + break label_11; + } + jj_consume_token(xor); +ASTBitwiseXorNode jjtn001 = new ASTBitwiseXorNode(JJTBITWISEXORNODE); + boolean jjtc001 = true; + jjtree.openNodeScope(jjtn001); + jjtreeOpenNodeScope(jjtn001); + jjtn001.jjtSetFirstToken(getToken(1)); + try { + AndExpression(); + } catch (Throwable jjte001) { +if (jjtc001) { + jjtree.clearNodeScope(jjtn001); + jjtc001 = false; + } else { + jjtree.popNode(); + } + if (jjte001 instanceof ParseException) { + throw (ParseException)jjte001; + } + if (jjte001 instanceof RuntimeException) { + throw (RuntimeException)jjte001; + } + throw (Error)jjte001; + } finally { +if (jjtc001) { + jjtree.closeNodeScope(jjtn001, 2); + if (jjtree.nodeCreated()) { + jjtreeCloseNodeScope(jjtn001); + } + jjtn001.jjtSetLastToken(getToken(0)); + } + } + } +} + + final public void AndExpression() throws ParseException { + EqualityExpression(); + label_12: + while (true) { + switch (jj_nt.kind) { + case and:{ + break; + } + default: + break label_12; + } + jj_consume_token(and); +ASTBitwiseAndNode jjtn001 = new ASTBitwiseAndNode(JJTBITWISEANDNODE); + boolean jjtc001 = true; + jjtree.openNodeScope(jjtn001); + jjtreeOpenNodeScope(jjtn001); + jjtn001.jjtSetFirstToken(getToken(1)); + try { + EqualityExpression(); + } catch (Throwable jjte001) { +if (jjtc001) { + jjtree.clearNodeScope(jjtn001); + jjtc001 = false; + } else { + jjtree.popNode(); + } + if (jjte001 instanceof ParseException) { + throw (ParseException)jjte001; + } + if (jjte001 instanceof RuntimeException) { + throw (RuntimeException)jjte001; + } + throw (Error)jjte001; + } finally { +if (jjtc001) { + jjtree.closeNodeScope(jjtn001, 2); + if (jjtree.nodeCreated()) { + jjtreeCloseNodeScope(jjtn001); + } + jjtn001.jjtSetLastToken(getToken(0)); + } + } + } +} + + final public void EqualityExpression() throws ParseException { + RelationalExpression(); + switch (jj_nt.kind) { + case eq: + case EQ: + case ne: + case NE: + case range:{ + switch (jj_nt.kind) { + case eq: + case EQ:{ + switch (jj_nt.kind) { + case eq:{ + jj_consume_token(eq); + break; + } + case EQ:{ + jj_consume_token(EQ); + break; + } + default: + jj_consume_token(-1); + throw new ParseException(); + } +ASTEQNode jjtn001 = new ASTEQNode(JJTEQNODE); + boolean jjtc001 = true; + jjtree.openNodeScope(jjtn001); + jjtreeOpenNodeScope(jjtn001); + jjtn001.jjtSetFirstToken(getToken(1)); + try { + RelationalExpression(); + } catch (Throwable jjte001) { +if (jjtc001) { + jjtree.clearNodeScope(jjtn001); + jjtc001 = false; + } else { + jjtree.popNode(); + } + if (jjte001 instanceof ParseException) { + throw (ParseException)jjte001; + } + if (jjte001 instanceof RuntimeException) { + throw (RuntimeException)jjte001; + } + throw (Error)jjte001; + } finally { +if (jjtc001) { + jjtree.closeNodeScope(jjtn001, 2); + if (jjtree.nodeCreated()) { + jjtreeCloseNodeScope(jjtn001); + } + jjtn001.jjtSetLastToken(getToken(0)); + } + } + break; + } + case ne: + case NE:{ + switch (jj_nt.kind) { + case ne:{ + jj_consume_token(ne); + break; + } + case NE:{ + jj_consume_token(NE); + break; + } + default: + jj_consume_token(-1); + throw new ParseException(); + } +ASTNENode jjtn002 = new ASTNENode(JJTNENODE); + boolean jjtc002 = true; + jjtree.openNodeScope(jjtn002); + jjtreeOpenNodeScope(jjtn002); + jjtn002.jjtSetFirstToken(getToken(1)); + try { + RelationalExpression(); + } catch (Throwable jjte002) { +if (jjtc002) { + jjtree.clearNodeScope(jjtn002); + jjtc002 = false; + } else { + jjtree.popNode(); + } + if (jjte002 instanceof ParseException) { + throw (ParseException)jjte002; + } + if (jjte002 instanceof RuntimeException) { + throw (RuntimeException)jjte002; + } + throw (Error)jjte002; + } finally { +if (jjtc002) { + jjtree.closeNodeScope(jjtn002, 2); + if (jjtree.nodeCreated()) { + jjtreeCloseNodeScope(jjtn002); + } + jjtn002.jjtSetLastToken(getToken(0)); + } + } + break; + } + case range:{ + jj_consume_token(range); +ASTRangeNode jjtn003 = new ASTRangeNode(JJTRANGENODE); + boolean jjtc003 = true; + jjtree.openNodeScope(jjtn003); + jjtreeOpenNodeScope(jjtn003); + jjtn003.jjtSetFirstToken(getToken(1)); + try { + RelationalExpression(); + } catch (Throwable jjte003) { +if (jjtc003) { + jjtree.clearNodeScope(jjtn003); + jjtc003 = false; + } else { + jjtree.popNode(); + } + if (jjte003 instanceof ParseException) { + throw (ParseException)jjte003; + } + if (jjte003 instanceof RuntimeException) { + throw (RuntimeException)jjte003; + } + throw (Error)jjte003; + } finally { +if (jjtc003) { + jjtree.closeNodeScope(jjtn003, 2); + if (jjtree.nodeCreated()) { + jjtreeCloseNodeScope(jjtn003); + } + jjtn003.jjtSetLastToken(getToken(0)); + } + } + break; + } + default: + jj_consume_token(-1); + throw new ParseException(); + } + break; + } + default: + ; + } +} + + final public void RelationalExpression() throws ParseException { + AdditiveExpression(); + switch (jj_nt.kind) { + case gt: + case GT: + case ge: + case GE: + case lt: + case LT: + case le: + case LE: + case req: + case rne: + case seq: + case eeq: + case sne: + case ene:{ + switch (jj_nt.kind) { + case lt: + case LT:{ + switch (jj_nt.kind) { + case lt:{ + jj_consume_token(lt); + break; + } + case LT:{ + jj_consume_token(LT); + break; + } + default: + jj_consume_token(-1); + throw new ParseException(); + } +ASTLTNode jjtn001 = new ASTLTNode(JJTLTNODE); + boolean jjtc001 = true; + jjtree.openNodeScope(jjtn001); + jjtreeOpenNodeScope(jjtn001); + jjtn001.jjtSetFirstToken(getToken(1)); + try { + AdditiveExpression(); + } catch (Throwable jjte001) { +if (jjtc001) { + jjtree.clearNodeScope(jjtn001); + jjtc001 = false; + } else { + jjtree.popNode(); + } + if (jjte001 instanceof ParseException) { + throw (ParseException)jjte001; + } + if (jjte001 instanceof RuntimeException) { + throw (RuntimeException)jjte001; + } + throw (Error)jjte001; + } finally { +if (jjtc001) { + jjtree.closeNodeScope(jjtn001, 2); + if (jjtree.nodeCreated()) { + jjtreeCloseNodeScope(jjtn001); + } + jjtn001.jjtSetLastToken(getToken(0)); + } + } + break; + } + case gt: + case GT:{ + switch (jj_nt.kind) { + case gt:{ + jj_consume_token(gt); + break; + } + case GT:{ + jj_consume_token(GT); + break; + } + default: + jj_consume_token(-1); + throw new ParseException(); + } +ASTGTNode jjtn002 = new ASTGTNode(JJTGTNODE); + boolean jjtc002 = true; + jjtree.openNodeScope(jjtn002); + jjtreeOpenNodeScope(jjtn002); + jjtn002.jjtSetFirstToken(getToken(1)); + try { + AdditiveExpression(); + } catch (Throwable jjte002) { +if (jjtc002) { + jjtree.clearNodeScope(jjtn002); + jjtc002 = false; + } else { + jjtree.popNode(); + } + if (jjte002 instanceof ParseException) { + throw (ParseException)jjte002; + } + if (jjte002 instanceof RuntimeException) { + throw (RuntimeException)jjte002; + } + throw (Error)jjte002; + } finally { +if (jjtc002) { + jjtree.closeNodeScope(jjtn002, 2); + if (jjtree.nodeCreated()) { + jjtreeCloseNodeScope(jjtn002); + } + jjtn002.jjtSetLastToken(getToken(0)); + } + } + break; + } + case le: + case LE:{ + switch (jj_nt.kind) { + case le:{ + jj_consume_token(le); + break; + } + case LE:{ + jj_consume_token(LE); + break; + } + default: + jj_consume_token(-1); + throw new ParseException(); + } +ASTLENode jjtn003 = new ASTLENode(JJTLENODE); + boolean jjtc003 = true; + jjtree.openNodeScope(jjtn003); + jjtreeOpenNodeScope(jjtn003); + jjtn003.jjtSetFirstToken(getToken(1)); + try { + AdditiveExpression(); + } catch (Throwable jjte003) { +if (jjtc003) { + jjtree.clearNodeScope(jjtn003); + jjtc003 = false; + } else { + jjtree.popNode(); + } + if (jjte003 instanceof ParseException) { + throw (ParseException)jjte003; + } + if (jjte003 instanceof RuntimeException) { + throw (RuntimeException)jjte003; + } + throw (Error)jjte003; + } finally { +if (jjtc003) { + jjtree.closeNodeScope(jjtn003, 2); + if (jjtree.nodeCreated()) { + jjtreeCloseNodeScope(jjtn003); + } + jjtn003.jjtSetLastToken(getToken(0)); + } + } + break; + } + case ge: + case GE:{ + switch (jj_nt.kind) { + case ge:{ + jj_consume_token(ge); + break; + } + case GE:{ + jj_consume_token(GE); + break; + } + default: + jj_consume_token(-1); + throw new ParseException(); + } +ASTGENode jjtn004 = new ASTGENode(JJTGENODE); + boolean jjtc004 = true; + jjtree.openNodeScope(jjtn004); + jjtreeOpenNodeScope(jjtn004); + jjtn004.jjtSetFirstToken(getToken(1)); + try { + AdditiveExpression(); + } catch (Throwable jjte004) { +if (jjtc004) { + jjtree.clearNodeScope(jjtn004); + jjtc004 = false; + } else { + jjtree.popNode(); + } + if (jjte004 instanceof ParseException) { + throw (ParseException)jjte004; + } + if (jjte004 instanceof RuntimeException) { + throw (RuntimeException)jjte004; + } + throw (Error)jjte004; + } finally { +if (jjtc004) { + jjtree.closeNodeScope(jjtn004, 2); + if (jjtree.nodeCreated()) { + jjtreeCloseNodeScope(jjtn004); + } + jjtn004.jjtSetLastToken(getToken(0)); + } + } + break; + } + case req:{ + jj_consume_token(req); +ASTERNode jjtn005 = new ASTERNode(JJTERNODE); + boolean jjtc005 = true; + jjtree.openNodeScope(jjtn005); + jjtreeOpenNodeScope(jjtn005); + jjtn005.jjtSetFirstToken(getToken(1)); + try { + AdditiveExpression(); + } catch (Throwable jjte005) { +if (jjtc005) { + jjtree.clearNodeScope(jjtn005); + jjtc005 = false; + } else { + jjtree.popNode(); + } + if (jjte005 instanceof ParseException) { + throw (ParseException)jjte005; + } + if (jjte005 instanceof RuntimeException) { + throw (RuntimeException)jjte005; + } + throw (Error)jjte005; + } finally { +if (jjtc005) { + jjtree.closeNodeScope(jjtn005, 2); + if (jjtree.nodeCreated()) { + jjtreeCloseNodeScope(jjtn005); + } + jjtn005.jjtSetLastToken(getToken(0)); + } + } + break; + } + case rne:{ + jj_consume_token(rne); +ASTNRNode jjtn006 = new ASTNRNode(JJTNRNODE); + boolean jjtc006 = true; + jjtree.openNodeScope(jjtn006); + jjtreeOpenNodeScope(jjtn006); + jjtn006.jjtSetFirstToken(getToken(1)); + try { + AdditiveExpression(); + } catch (Throwable jjte006) { +if (jjtc006) { + jjtree.clearNodeScope(jjtn006); + jjtc006 = false; + } else { + jjtree.popNode(); + } + if (jjte006 instanceof ParseException) { + throw (ParseException)jjte006; + } + if (jjte006 instanceof RuntimeException) { + throw (RuntimeException)jjte006; + } + throw (Error)jjte006; + } finally { +if (jjtc006) { + jjtree.closeNodeScope(jjtn006, 2); + if (jjtree.nodeCreated()) { + jjtreeCloseNodeScope(jjtn006); + } + jjtn006.jjtSetLastToken(getToken(0)); + } + } + break; + } + case seq:{ + jj_consume_token(seq); +ASTSWNode jjtn007 = new ASTSWNode(JJTSWNODE); + boolean jjtc007 = true; + jjtree.openNodeScope(jjtn007); + jjtreeOpenNodeScope(jjtn007); + jjtn007.jjtSetFirstToken(getToken(1)); + try { + AdditiveExpression(); + } catch (Throwable jjte007) { +if (jjtc007) { + jjtree.clearNodeScope(jjtn007); + jjtc007 = false; + } else { + jjtree.popNode(); + } + if (jjte007 instanceof ParseException) { + throw (ParseException)jjte007; + } + if (jjte007 instanceof RuntimeException) { + throw (RuntimeException)jjte007; + } + throw (Error)jjte007; + } finally { +if (jjtc007) { + jjtree.closeNodeScope(jjtn007, 2); + if (jjtree.nodeCreated()) { + jjtreeCloseNodeScope(jjtn007); + } + jjtn007.jjtSetLastToken(getToken(0)); + } + } + break; + } + case sne:{ + jj_consume_token(sne); +ASTNSWNode jjtn008 = new ASTNSWNode(JJTNSWNODE); + boolean jjtc008 = true; + jjtree.openNodeScope(jjtn008); + jjtreeOpenNodeScope(jjtn008); + jjtn008.jjtSetFirstToken(getToken(1)); + try { + AdditiveExpression(); + } catch (Throwable jjte008) { +if (jjtc008) { + jjtree.clearNodeScope(jjtn008); + jjtc008 = false; + } else { + jjtree.popNode(); + } + if (jjte008 instanceof ParseException) { + throw (ParseException)jjte008; + } + if (jjte008 instanceof RuntimeException) { + throw (RuntimeException)jjte008; + } + throw (Error)jjte008; + } finally { +if (jjtc008) { + jjtree.closeNodeScope(jjtn008, 2); + if (jjtree.nodeCreated()) { + jjtreeCloseNodeScope(jjtn008); + } + jjtn008.jjtSetLastToken(getToken(0)); + } + } + break; + } + case eeq:{ + jj_consume_token(eeq); +ASTEWNode jjtn009 = new ASTEWNode(JJTEWNODE); + boolean jjtc009 = true; + jjtree.openNodeScope(jjtn009); + jjtreeOpenNodeScope(jjtn009); + jjtn009.jjtSetFirstToken(getToken(1)); + try { + AdditiveExpression(); + } catch (Throwable jjte009) { +if (jjtc009) { + jjtree.clearNodeScope(jjtn009); + jjtc009 = false; + } else { + jjtree.popNode(); + } + if (jjte009 instanceof ParseException) { + throw (ParseException)jjte009; + } + if (jjte009 instanceof RuntimeException) { + throw (RuntimeException)jjte009; + } + throw (Error)jjte009; + } finally { +if (jjtc009) { + jjtree.closeNodeScope(jjtn009, 2); + if (jjtree.nodeCreated()) { + jjtreeCloseNodeScope(jjtn009); + } + jjtn009.jjtSetLastToken(getToken(0)); + } + } + break; + } + case ene:{ + jj_consume_token(ene); +ASTNEWNode jjtn010 = new ASTNEWNode(JJTNEWNODE); + boolean jjtc010 = true; + jjtree.openNodeScope(jjtn010); + jjtreeOpenNodeScope(jjtn010); + jjtn010.jjtSetFirstToken(getToken(1)); + try { + AdditiveExpression(); + } catch (Throwable jjte010) { +if (jjtc010) { + jjtree.clearNodeScope(jjtn010); + jjtc010 = false; + } else { + jjtree.popNode(); + } + if (jjte010 instanceof ParseException) { + throw (ParseException)jjte010; + } + if (jjte010 instanceof RuntimeException) { + throw (RuntimeException)jjte010; + } + throw (Error)jjte010; + } finally { +if (jjtc010) { + jjtree.closeNodeScope(jjtn010, 2); + if (jjtree.nodeCreated()) { + jjtreeCloseNodeScope(jjtn010); + } + jjtn010.jjtSetLastToken(getToken(0)); + } + } + break; + } + default: + jj_consume_token(-1); + throw new ParseException(); + } + break; + } + default: + ; + } +} + +/*************************************** + * Arithmetic + ***************************************/ + final public +void AdditiveExpression() throws ParseException { + MultiplicativeExpression(); + label_13: + while (true) { + if (jj_2_19(2)) { + } else { + break label_13; + } + switch (jj_nt.kind) { + case plus:{ + jj_consume_token(plus); +ASTAddNode jjtn001 = new ASTAddNode(JJTADDNODE); + boolean jjtc001 = true; + jjtree.openNodeScope(jjtn001); + jjtreeOpenNodeScope(jjtn001); + jjtn001.jjtSetFirstToken(getToken(1)); + try { + MultiplicativeExpression(); + } catch (Throwable jjte001) { +if (jjtc001) { + jjtree.clearNodeScope(jjtn001); + jjtc001 = false; + } else { + jjtree.popNode(); + } + if (jjte001 instanceof ParseException) { + throw (ParseException)jjte001; + } + if (jjte001 instanceof RuntimeException) { + throw (RuntimeException)jjte001; + } + throw (Error)jjte001; + } finally { +if (jjtc001) { + jjtree.closeNodeScope(jjtn001, 2); + if (jjtree.nodeCreated()) { + jjtreeCloseNodeScope(jjtn001); + } + jjtn001.jjtSetLastToken(getToken(0)); + } + } + break; + } + case minus:{ + jj_consume_token(minus); +ASTSubNode jjtn002 = new ASTSubNode(JJTSUBNODE); + boolean jjtc002 = true; + jjtree.openNodeScope(jjtn002); + jjtreeOpenNodeScope(jjtn002); + jjtn002.jjtSetFirstToken(getToken(1)); + try { + MultiplicativeExpression(); + } catch (Throwable jjte002) { +if (jjtc002) { + jjtree.clearNodeScope(jjtn002); + jjtc002 = false; + } else { + jjtree.popNode(); + } + if (jjte002 instanceof ParseException) { + throw (ParseException)jjte002; + } + if (jjte002 instanceof RuntimeException) { + throw (RuntimeException)jjte002; + } + throw (Error)jjte002; + } finally { +if (jjtc002) { + jjtree.closeNodeScope(jjtn002, 2); + if (jjtree.nodeCreated()) { + jjtreeCloseNodeScope(jjtn002); + } + jjtn002.jjtSetLastToken(getToken(0)); + } + } + break; + } + default: + jj_consume_token(-1); + throw new ParseException(); + } + } +} + + final public void MultiplicativeExpression() throws ParseException { + UnaryExpression(); + label_14: + while (true) { + switch (jj_nt.kind) { + case mult: + case div: + case DIV: + case mod: + case MOD:{ + break; + } + default: + break label_14; + } + switch (jj_nt.kind) { + case mult:{ + jj_consume_token(mult); +ASTMulNode jjtn001 = new ASTMulNode(JJTMULNODE); + boolean jjtc001 = true; + jjtree.openNodeScope(jjtn001); + jjtreeOpenNodeScope(jjtn001); + jjtn001.jjtSetFirstToken(getToken(1)); + try { + UnaryExpression(); + } catch (Throwable jjte001) { +if (jjtc001) { + jjtree.clearNodeScope(jjtn001); + jjtc001 = false; + } else { + jjtree.popNode(); + } + if (jjte001 instanceof ParseException) { + throw (ParseException)jjte001; + } + if (jjte001 instanceof RuntimeException) { + throw (RuntimeException)jjte001; + } + throw (Error)jjte001; + } finally { +if (jjtc001) { + jjtree.closeNodeScope(jjtn001, 2); + if (jjtree.nodeCreated()) { + jjtreeCloseNodeScope(jjtn001); + } + jjtn001.jjtSetLastToken(getToken(0)); + } + } + break; + } + case div: + case DIV:{ + switch (jj_nt.kind) { + case div:{ + jj_consume_token(div); + break; + } + case DIV:{ + jj_consume_token(DIV); + break; + } + default: + jj_consume_token(-1); + throw new ParseException(); + } +ASTDivNode jjtn002 = new ASTDivNode(JJTDIVNODE); + boolean jjtc002 = true; + jjtree.openNodeScope(jjtn002); + jjtreeOpenNodeScope(jjtn002); + jjtn002.jjtSetFirstToken(getToken(1)); + try { + UnaryExpression(); + } catch (Throwable jjte002) { +if (jjtc002) { + jjtree.clearNodeScope(jjtn002); + jjtc002 = false; + } else { + jjtree.popNode(); + } + if (jjte002 instanceof ParseException) { + throw (ParseException)jjte002; + } + if (jjte002 instanceof RuntimeException) { + throw (RuntimeException)jjte002; + } + throw (Error)jjte002; + } finally { +if (jjtc002) { + jjtree.closeNodeScope(jjtn002, 2); + if (jjtree.nodeCreated()) { + jjtreeCloseNodeScope(jjtn002); + } + jjtn002.jjtSetLastToken(getToken(0)); + } + } + break; + } + case mod: + case MOD:{ + switch (jj_nt.kind) { + case mod:{ + jj_consume_token(mod); + break; + } + case MOD:{ + jj_consume_token(MOD); + break; + } + default: + jj_consume_token(-1); + throw new ParseException(); + } +ASTModNode jjtn003 = new ASTModNode(JJTMODNODE); + boolean jjtc003 = true; + jjtree.openNodeScope(jjtn003); + jjtreeOpenNodeScope(jjtn003); + jjtn003.jjtSetFirstToken(getToken(1)); + try { + UnaryExpression(); + } catch (Throwable jjte003) { +if (jjtc003) { + jjtree.clearNodeScope(jjtn003); + jjtc003 = false; + } else { + jjtree.popNode(); + } + if (jjte003 instanceof ParseException) { + throw (ParseException)jjte003; + } + if (jjte003 instanceof RuntimeException) { + throw (RuntimeException)jjte003; + } + throw (Error)jjte003; + } finally { +if (jjtc003) { + jjtree.closeNodeScope(jjtn003, 2); + if (jjtree.nodeCreated()) { + jjtreeCloseNodeScope(jjtn003); + } + jjtn003.jjtSetLastToken(getToken(0)); + } + } + break; + } + default: + jj_consume_token(-1); + throw new ParseException(); + } + } +} + + final public void UnaryExpression() throws ParseException { + switch (jj_nt.kind) { + case minus:{ + jj_consume_token(minus); +ASTUnaryMinusNode jjtn001 = new ASTUnaryMinusNode(JJTUNARYMINUSNODE); + boolean jjtc001 = true; + jjtree.openNodeScope(jjtn001); + jjtreeOpenNodeScope(jjtn001); + jjtn001.jjtSetFirstToken(getToken(1)); + try { + UnaryExpression(); + } catch (Throwable jjte001) { +if (jjtc001) { + jjtree.clearNodeScope(jjtn001); + jjtc001 = false; + } else { + jjtree.popNode(); + } + if (jjte001 instanceof ParseException) { + throw (ParseException)jjte001; + } + if (jjte001 instanceof RuntimeException) { + throw (RuntimeException)jjte001; + } + throw (Error)jjte001; + } finally { +if (jjtc001) { + jjtree.closeNodeScope(jjtn001, 1); + if (jjtree.nodeCreated()) { + jjtreeCloseNodeScope(jjtn001); + } + jjtn001.jjtSetLastToken(getToken(0)); + } + } + break; + } + case plus:{ + jj_consume_token(plus); +ASTUnaryPlusNode jjtn002 = new ASTUnaryPlusNode(JJTUNARYPLUSNODE); + boolean jjtc002 = true; + jjtree.openNodeScope(jjtn002); + jjtreeOpenNodeScope(jjtn002); + jjtn002.jjtSetFirstToken(getToken(1)); + try { + UnaryExpression(); + } catch (Throwable jjte002) { +if (jjtc002) { + jjtree.clearNodeScope(jjtn002); + jjtc002 = false; + } else { + jjtree.popNode(); + } + if (jjte002 instanceof ParseException) { + throw (ParseException)jjte002; + } + if (jjte002 instanceof RuntimeException) { + throw (RuntimeException)jjte002; + } + throw (Error)jjte002; + } finally { +if (jjtc002) { + jjtree.closeNodeScope(jjtn002, 1); + if (jjtree.nodeCreated()) { + jjtreeCloseNodeScope(jjtn002); + } + jjtn002.jjtSetLastToken(getToken(0)); + } + } + break; + } + case tilda:{ + jj_consume_token(tilda); +ASTBitwiseComplNode jjtn003 = new ASTBitwiseComplNode(JJTBITWISECOMPLNODE); + boolean jjtc003 = true; + jjtree.openNodeScope(jjtn003); + jjtreeOpenNodeScope(jjtn003); + jjtn003.jjtSetFirstToken(getToken(1)); + try { + UnaryExpression(); + } catch (Throwable jjte003) { +if (jjtc003) { + jjtree.clearNodeScope(jjtn003); + jjtc003 = false; + } else { + jjtree.popNode(); + } + if (jjte003 instanceof ParseException) { + throw (ParseException)jjte003; + } + if (jjte003 instanceof RuntimeException) { + throw (RuntimeException)jjte003; + } + throw (Error)jjte003; + } finally { +if (jjtc003) { + jjtree.closeNodeScope(jjtn003, 1); + if (jjtree.nodeCreated()) { + jjtreeCloseNodeScope(jjtn003); + } + jjtn003.jjtSetLastToken(getToken(0)); + } + } + break; + } + case not: + case NOT:{ + switch (jj_nt.kind) { + case not:{ + jj_consume_token(not); + break; + } + case NOT:{ + jj_consume_token(NOT); + break; + } + default: + jj_consume_token(-1); + throw new ParseException(); + } +ASTNotNode jjtn004 = new ASTNotNode(JJTNOTNODE); + boolean jjtc004 = true; + jjtree.openNodeScope(jjtn004); + jjtreeOpenNodeScope(jjtn004); + jjtn004.jjtSetFirstToken(getToken(1)); + try { + UnaryExpression(); + } catch (Throwable jjte004) { +if (jjtc004) { + jjtree.clearNodeScope(jjtn004); + jjtc004 = false; + } else { + jjtree.popNode(); + } + if (jjte004 instanceof ParseException) { + throw (ParseException)jjte004; + } + if (jjte004 instanceof RuntimeException) { + throw (RuntimeException)jjte004; + } + throw (Error)jjte004; + } finally { +if (jjtc004) { + jjtree.closeNodeScope(jjtn004, 1); + if (jjtree.nodeCreated()) { + jjtreeCloseNodeScope(jjtn004); + } + jjtn004.jjtSetLastToken(getToken(0)); + } + } + break; + } + case EMPTY:{ + jj_consume_token(EMPTY); +ASTEmptyFunction jjtn005 = new ASTEmptyFunction(JJTEMPTYFUNCTION); + boolean jjtc005 = true; + jjtree.openNodeScope(jjtn005); + jjtreeOpenNodeScope(jjtn005); + jjtn005.jjtSetFirstToken(getToken(1)); + try { + UnaryExpression(); + } catch (Throwable jjte005) { +if (jjtc005) { + jjtree.clearNodeScope(jjtn005); + jjtc005 = false; + } else { + jjtree.popNode(); + } + if (jjte005 instanceof ParseException) { + throw (ParseException)jjte005; + } + if (jjte005 instanceof RuntimeException) { + throw (RuntimeException)jjte005; + } + throw (Error)jjte005; + } finally { +if (jjtc005) { + jjtree.closeNodeScope(jjtn005, 1); + if (jjtree.nodeCreated()) { + jjtreeCloseNodeScope(jjtn005); + } + jjtn005.jjtSetLastToken(getToken(0)); + } + } + break; + } + case SIZE:{ + jj_consume_token(SIZE); +ASTSizeFunction jjtn006 = new ASTSizeFunction(JJTSIZEFUNCTION); + boolean jjtc006 = true; + jjtree.openNodeScope(jjtn006); + jjtreeOpenNodeScope(jjtn006); + jjtn006.jjtSetFirstToken(getToken(1)); + try { + UnaryExpression(); + } catch (Throwable jjte006) { +if (jjtc006) { + jjtree.clearNodeScope(jjtn006); + jjtc006 = false; + } else { + jjtree.popNode(); + } + if (jjte006 instanceof ParseException) { + throw (ParseException)jjte006; + } + if (jjte006 instanceof RuntimeException) { + throw (RuntimeException)jjte006; + } + throw (Error)jjte006; + } finally { +if (jjtc006) { + jjtree.closeNodeScope(jjtn006, 1); + if (jjtree.nodeCreated()) { + jjtreeCloseNodeScope(jjtn006); + } + jjtn006.jjtSetLastToken(getToken(0)); + } + } + break; + } + default: + if (jj_2_20(1)) { + ValueExpression(); + } else { + jj_consume_token(-1); + throw new ParseException(); + } + } +} + +/*************************************** + * Identifier & Literals + ***************************************/ + final public +void Identifier(boolean top) throws ParseException {/*@bgen(jjtree) Identifier */ + ASTIdentifier jjtn000 = new ASTIdentifier(JJTIDENTIFIER); + boolean jjtc000 = true; + jjtree.openNodeScope(jjtn000); + jjtreeOpenNodeScope(jjtn000); + jjtn000.jjtSetFirstToken(getToken(1));Token t; + try { + switch (jj_nt.kind) { + case IDENTIFIER:{ + t = jj_consume_token(IDENTIFIER); +jjtree.closeNodeScope(jjtn000, true); + jjtc000 = false; + if (jjtree.nodeCreated()) { + jjtreeCloseNodeScope(jjtn000); + } + jjtn000.jjtSetLastToken(getToken(0)); +jjtn000.setSymbol(top? checkVariable(jjtn000, t.image) : t.image); + break; + } + case REGISTER:{ + t = jj_consume_token(REGISTER); +jjtree.closeNodeScope(jjtn000, true); + jjtc000 = false; + if (jjtree.nodeCreated()) { + jjtreeCloseNodeScope(jjtn000); + } + jjtn000.jjtSetLastToken(getToken(0)); +jjtn000.setSymbol(t.image); + break; + } + default: + jj_consume_token(-1); + throw new ParseException(); + } + } finally { +if (jjtc000) { + jjtree.closeNodeScope(jjtn000, true); + if (jjtree.nodeCreated()) { + jjtreeCloseNodeScope(jjtn000); + } + jjtn000.jjtSetLastToken(getToken(0)); + } + } +} + + final public void NamespaceIdentifier() throws ParseException {/*@bgen(jjtree) NamespaceIdentifier */ + ASTNamespaceIdentifier jjtn000 = new ASTNamespaceIdentifier(JJTNAMESPACEIDENTIFIER); + boolean jjtc000 = true; + jjtree.openNodeScope(jjtn000); + jjtreeOpenNodeScope(jjtn000); + jjtn000.jjtSetFirstToken(getToken(1));Token ns; + Token id; + try { + ns = jj_consume_token(IDENTIFIER); + jj_consume_token(COLON); + id = jj_consume_token(IDENTIFIER); +jjtree.closeNodeScope(jjtn000, true); + jjtc000 = false; + if (jjtree.nodeCreated()) { + jjtreeCloseNodeScope(jjtn000); + } + jjtn000.jjtSetLastToken(getToken(0)); +jjtn000.setNamespace(ns.image, id.image); + } finally { +if (jjtc000) { + jjtree.closeNodeScope(jjtn000, true); + if (jjtree.nodeCreated()) { + jjtreeCloseNodeScope(jjtn000); + } + jjtn000.jjtSetLastToken(getToken(0)); + } + } +} + + final public void Literal() throws ParseException {Token t; + switch (jj_nt.kind) { + case INTEGER_LITERAL:{ + IntegerLiteral(); + break; + } + case FLOAT_LITERAL:{ + FloatLiteral(); + break; + } + case TRUE: + case FALSE:{ + BooleanLiteral(); + break; + } + case JXLT_LITERAL:{ + JxltLiteral(); + break; + } + case STRING_LITERAL:{ + StringLiteral(); + break; + } + case REGEX_LITERAL:{ + RegexLiteral(); + break; + } + case NULL:{ + NullLiteral(); + break; + } + case NAN_LITERAL:{ + NaNLiteral(); + break; + } + default: + jj_consume_token(-1); + throw new ParseException(); + } +} + + final public void NaNLiteral() throws ParseException {/*@bgen(jjtree) NumberLiteral */ + ASTNumberLiteral jjtn000 = new ASTNumberLiteral(JJTNUMBERLITERAL); + boolean jjtc000 = true; + jjtree.openNodeScope(jjtn000); + jjtreeOpenNodeScope(jjtn000); + jjtn000.jjtSetFirstToken(getToken(1)); + try { + jj_consume_token(NAN_LITERAL); +jjtree.closeNodeScope(jjtn000, true); + jjtc000 = false; + if (jjtree.nodeCreated()) { + jjtreeCloseNodeScope(jjtn000); + } + jjtn000.jjtSetLastToken(getToken(0)); +jjtn000.setReal("NaN"); + } finally { +if (jjtc000) { + jjtree.closeNodeScope(jjtn000, true); + if (jjtree.nodeCreated()) { + jjtreeCloseNodeScope(jjtn000); + } + jjtn000.jjtSetLastToken(getToken(0)); + } + } +} + + final public void NullLiteral() throws ParseException {/*@bgen(jjtree) NullLiteral */ + ASTNullLiteral jjtn000 = new ASTNullLiteral(JJTNULLLITERAL); + boolean jjtc000 = true; + jjtree.openNodeScope(jjtn000); + jjtreeOpenNodeScope(jjtn000); + jjtn000.jjtSetFirstToken(getToken(1)); + try { + jj_consume_token(NULL); + } finally { +if (jjtc000) { + jjtree.closeNodeScope(jjtn000, true); + if (jjtree.nodeCreated()) { + jjtreeCloseNodeScope(jjtn000); + } + jjtn000.jjtSetLastToken(getToken(0)); + } + } +} + + final public void BooleanLiteral() throws ParseException { + switch (jj_nt.kind) { + case TRUE:{ +ASTTrueNode jjtn001 = new ASTTrueNode(JJTTRUENODE); + boolean jjtc001 = true; + jjtree.openNodeScope(jjtn001); + jjtreeOpenNodeScope(jjtn001); + jjtn001.jjtSetFirstToken(getToken(1)); + try { + jj_consume_token(TRUE); + } finally { +if (jjtc001) { + jjtree.closeNodeScope(jjtn001, true); + if (jjtree.nodeCreated()) { + jjtreeCloseNodeScope(jjtn001); + } + jjtn001.jjtSetLastToken(getToken(0)); + } + } + break; + } + case FALSE:{ +ASTFalseNode jjtn002 = new ASTFalseNode(JJTFALSENODE); + boolean jjtc002 = true; + jjtree.openNodeScope(jjtn002); + jjtreeOpenNodeScope(jjtn002); + jjtn002.jjtSetFirstToken(getToken(1)); + try { + jj_consume_token(FALSE); + } finally { +if (jjtc002) { + jjtree.closeNodeScope(jjtn002, true); + if (jjtree.nodeCreated()) { + jjtreeCloseNodeScope(jjtn002); + } + jjtn002.jjtSetLastToken(getToken(0)); + } + } + break; + } + default: + jj_consume_token(-1); + throw new ParseException(); + } +} + + final public void IntegerLiteral() throws ParseException {/*@bgen(jjtree) NumberLiteral */ + ASTNumberLiteral jjtn000 = new ASTNumberLiteral(JJTNUMBERLITERAL); + boolean jjtc000 = true; + jjtree.openNodeScope(jjtn000); + jjtreeOpenNodeScope(jjtn000); + jjtn000.jjtSetFirstToken(getToken(1));Token t; + try { + t = jj_consume_token(INTEGER_LITERAL); +jjtree.closeNodeScope(jjtn000, true); + jjtc000 = false; + if (jjtree.nodeCreated()) { + jjtreeCloseNodeScope(jjtn000); + } + jjtn000.jjtSetLastToken(getToken(0)); +jjtn000.setNatural(t.image); + } finally { +if (jjtc000) { + jjtree.closeNodeScope(jjtn000, true); + if (jjtree.nodeCreated()) { + jjtreeCloseNodeScope(jjtn000); + } + jjtn000.jjtSetLastToken(getToken(0)); + } + } +} + + final public void FloatLiteral() throws ParseException {/*@bgen(jjtree) NumberLiteral */ + ASTNumberLiteral jjtn000 = new ASTNumberLiteral(JJTNUMBERLITERAL); + boolean jjtc000 = true; + jjtree.openNodeScope(jjtn000); + jjtreeOpenNodeScope(jjtn000); + jjtn000.jjtSetFirstToken(getToken(1));Token t; + try { + t = jj_consume_token(FLOAT_LITERAL); +jjtree.closeNodeScope(jjtn000, true); + jjtc000 = false; + if (jjtree.nodeCreated()) { + jjtreeCloseNodeScope(jjtn000); + } + jjtn000.jjtSetLastToken(getToken(0)); +jjtn000.setReal(t.image); + } finally { +if (jjtc000) { + jjtree.closeNodeScope(jjtn000, true); + if (jjtree.nodeCreated()) { + jjtreeCloseNodeScope(jjtn000); + } + jjtn000.jjtSetLastToken(getToken(0)); + } + } +} + + final public void StringLiteral() throws ParseException {/*@bgen(jjtree) StringLiteral */ + ASTStringLiteral jjtn000 = new ASTStringLiteral(JJTSTRINGLITERAL); + boolean jjtc000 = true; + jjtree.openNodeScope(jjtn000); + jjtreeOpenNodeScope(jjtn000); + jjtn000.jjtSetFirstToken(getToken(1));Token t; + try { + t = jj_consume_token(STRING_LITERAL); +jjtree.closeNodeScope(jjtn000, true); + jjtc000 = false; + if (jjtree.nodeCreated()) { + jjtreeCloseNodeScope(jjtn000); + } + jjtn000.jjtSetLastToken(getToken(0)); +jjtn000.setLiteral(buildString(t.image, true)); + } finally { +if (jjtc000) { + jjtree.closeNodeScope(jjtn000, true); + if (jjtree.nodeCreated()) { + jjtreeCloseNodeScope(jjtn000); + } + jjtn000.jjtSetLastToken(getToken(0)); + } + } +} + + final public void JxltLiteral() throws ParseException {/*@bgen(jjtree) JxltLiteral */ + ASTJxltLiteral jjtn000 = new ASTJxltLiteral(JJTJXLTLITERAL); + boolean jjtc000 = true; + jjtree.openNodeScope(jjtn000); + jjtreeOpenNodeScope(jjtn000); + jjtn000.jjtSetFirstToken(getToken(1));Token t; + try { + t = jj_consume_token(JXLT_LITERAL); +jjtree.closeNodeScope(jjtn000, true); + jjtc000 = false; + if (jjtree.nodeCreated()) { + jjtreeCloseNodeScope(jjtn000); + } + jjtn000.jjtSetLastToken(getToken(0)); +jjtn000.setLiteral(buildString(t.image, true)); + } finally { +if (jjtc000) { + jjtree.closeNodeScope(jjtn000, true); + if (jjtree.nodeCreated()) { + jjtreeCloseNodeScope(jjtn000); + } + jjtn000.jjtSetLastToken(getToken(0)); + } + } +} + + final public void RegexLiteral() throws ParseException {/*@bgen(jjtree) RegexLiteral */ + ASTRegexLiteral jjtn000 = new ASTRegexLiteral(JJTREGEXLITERAL); + boolean jjtc000 = true; + jjtree.openNodeScope(jjtn000); + jjtreeOpenNodeScope(jjtn000); + jjtn000.jjtSetFirstToken(getToken(1));Token t; + try { + t = jj_consume_token(REGEX_LITERAL); +jjtree.closeNodeScope(jjtn000, true); + jjtc000 = false; + if (jjtree.nodeCreated()) { + jjtreeCloseNodeScope(jjtn000); + } + jjtn000.jjtSetLastToken(getToken(0)); +jjtn000.setLiteral(buildRegex(t.image)); + } finally { +if (jjtc000) { + jjtree.closeNodeScope(jjtn000, true); + if (jjtree.nodeCreated()) { + jjtreeCloseNodeScope(jjtn000); + } + jjtn000.jjtSetLastToken(getToken(0)); + } + } +} + + final public void ExtendedLiteral() throws ParseException {/*@bgen(jjtree) ExtendedLiteral */ + ASTExtendedLiteral jjtn000 = new ASTExtendedLiteral(JJTEXTENDEDLITERAL); + boolean jjtc000 = true; + jjtree.openNodeScope(jjtn000); + jjtreeOpenNodeScope(jjtn000); + jjtn000.jjtSetFirstToken(getToken(1)); + try { + jj_consume_token(ELIPSIS); + } finally { +if (jjtc000) { + jjtree.closeNodeScope(jjtn000, true); + if (jjtree.nodeCreated()) { + jjtreeCloseNodeScope(jjtn000); + } + jjtn000.jjtSetLastToken(getToken(0)); + } + } +} + + final public void ArrayLiteral() throws ParseException {/*@bgen(jjtree) ArrayLiteral */ + ASTArrayLiteral jjtn000 = new ASTArrayLiteral(JJTARRAYLITERAL); + boolean jjtc000 = true; + jjtree.openNodeScope(jjtn000); + jjtreeOpenNodeScope(jjtn000); + jjtn000.jjtSetFirstToken(getToken(1)); + try { + jj_consume_token(LBRACKET); + switch (jj_nt.kind) { + case ELIPSIS:{ + ExtendedLiteral(); + break; + } + default: + if (jj_2_22(1)) { + Expression(); + label_15: + while (true) { + if (jj_2_21(2)) { + } else { + break label_15; + } + jj_consume_token(COMMA); + Expression(); + } + } else { + ; + } + switch (jj_nt.kind) { + case COMMA:{ + jj_consume_token(COMMA); + ExtendedLiteral(); + break; + } + default: + ; + } + } + jj_consume_token(RBRACKET); + } catch (Throwable jjte000) { +if (jjtc000) { + jjtree.clearNodeScope(jjtn000); + jjtc000 = false; + } else { + jjtree.popNode(); + } + if (jjte000 instanceof ParseException) { + throw (ParseException)jjte000; + } + if (jjte000 instanceof RuntimeException) { + throw (RuntimeException)jjte000; + } + throw (Error)jjte000; + } finally { +if (jjtc000) { + jjtree.closeNodeScope(jjtn000, true); + if (jjtree.nodeCreated()) { + jjtreeCloseNodeScope(jjtn000); + } + jjtn000.jjtSetLastToken(getToken(0)); + } + } +} + + final public void MapLiteral() throws ParseException {/*@bgen(jjtree) MapLiteral */ + ASTMapLiteral jjtn000 = new ASTMapLiteral(JJTMAPLITERAL); + boolean jjtc000 = true; + jjtree.openNodeScope(jjtn000); + jjtreeOpenNodeScope(jjtn000); + jjtn000.jjtSetFirstToken(getToken(1)); + try { + jj_consume_token(LCURLY); + if (jj_2_23(1)) { + MapEntry(); + label_16: + while (true) { + switch (jj_nt.kind) { + case COMMA:{ + break; + } + default: + break label_16; + } + jj_consume_token(COMMA); + MapEntry(); + } + } else { + switch (jj_nt.kind) { + case COLON:{ + jj_consume_token(COLON); + break; + } + default: + jj_consume_token(-1); + throw new ParseException(); + } + } + jj_consume_token(RCURLY); + } catch (Throwable jjte000) { +if (jjtc000) { + jjtree.clearNodeScope(jjtn000); + jjtc000 = false; + } else { + jjtree.popNode(); + } + if (jjte000 instanceof ParseException) { + throw (ParseException)jjte000; + } + if (jjte000 instanceof RuntimeException) { + throw (RuntimeException)jjte000; + } + throw (Error)jjte000; + } finally { +if (jjtc000) { + jjtree.closeNodeScope(jjtn000, true); + if (jjtree.nodeCreated()) { + jjtreeCloseNodeScope(jjtn000); + } + jjtn000.jjtSetLastToken(getToken(0)); + } + } +} + + final public void MapEntry() throws ParseException {/*@bgen(jjtree) MapEntry */ + ASTMapEntry jjtn000 = new ASTMapEntry(JJTMAPENTRY); + boolean jjtc000 = true; + jjtree.openNodeScope(jjtn000); + jjtreeOpenNodeScope(jjtn000); + jjtn000.jjtSetFirstToken(getToken(1)); + try { + Expression(); + jj_consume_token(COLON); + Expression(); + } catch (Throwable jjte000) { +if (jjtc000) { + jjtree.clearNodeScope(jjtn000); + jjtc000 = false; + } else { + jjtree.popNode(); + } + if (jjte000 instanceof ParseException) { + throw (ParseException)jjte000; + } + if (jjte000 instanceof RuntimeException) { + throw (RuntimeException)jjte000; + } + throw (Error)jjte000; + } finally { +if (jjtc000) { + jjtree.closeNodeScope(jjtn000, true); + if (jjtree.nodeCreated()) { + jjtreeCloseNodeScope(jjtn000); + } + jjtn000.jjtSetLastToken(getToken(0)); + } + } +} + + final public void SetLiteral() throws ParseException {/*@bgen(jjtree) SetLiteral */ + ASTSetLiteral jjtn000 = new ASTSetLiteral(JJTSETLITERAL); + boolean jjtc000 = true; + jjtree.openNodeScope(jjtn000); + jjtreeOpenNodeScope(jjtn000); + jjtn000.jjtSetFirstToken(getToken(1)); + try { + jj_consume_token(LCURLY); + if (jj_2_24(1)) { + Expression(); + label_17: + while (true) { + switch (jj_nt.kind) { + case COMMA:{ + break; + } + default: + break label_17; + } + jj_consume_token(COMMA); + Expression(); + } + } else { + ; + } + jj_consume_token(RCURLY); + } catch (Throwable jjte000) { +if (jjtc000) { + jjtree.clearNodeScope(jjtn000); + jjtc000 = false; + } else { + jjtree.popNode(); + } + if (jjte000 instanceof ParseException) { + throw (ParseException)jjte000; + } + if (jjte000 instanceof RuntimeException) { + throw (RuntimeException)jjte000; + } + throw (Error)jjte000; + } finally { +if (jjtc000) { + jjtree.closeNodeScope(jjtn000, true); + if (jjtree.nodeCreated()) { + jjtreeCloseNodeScope(jjtn000); + } + jjtn000.jjtSetLastToken(getToken(0)); + } + } +} + +/*************************************** + * Functions & Methods + ***************************************/ + final public +void Arguments() throws ParseException {/*@bgen(jjtree) Arguments */ + ASTArguments jjtn000 = new ASTArguments(JJTARGUMENTS); + boolean jjtc000 = true; + jjtree.openNodeScope(jjtn000); + jjtreeOpenNodeScope(jjtn000); + jjtn000.jjtSetFirstToken(getToken(1)); + try { + jj_consume_token(LPAREN); + if (jj_2_25(1)) { + Expression(); + label_18: + while (true) { + switch (jj_nt.kind) { + case COMMA:{ + break; + } + default: + break label_18; + } + jj_consume_token(COMMA); + Expression(); + } + } else { + ; + } + jj_consume_token(RPAREN); + } catch (Throwable jjte000) { +if (jjtc000) { + jjtree.clearNodeScope(jjtn000); + jjtc000 = false; + } else { + jjtree.popNode(); + } + if (jjte000 instanceof ParseException) { + throw (ParseException)jjte000; + } + if (jjte000 instanceof RuntimeException) { + throw (RuntimeException)jjte000; + } + throw (Error)jjte000; + } finally { +if (jjtc000) { + jjtree.closeNodeScope(jjtn000, true); + if (jjtree.nodeCreated()) { + jjtreeCloseNodeScope(jjtn000); + } + jjtn000.jjtSetLastToken(getToken(0)); + } + } +} + + final public void FunctionCallLookahead() throws ParseException { + if (jj_2_26(2147483647) && (isDeclaredNamespace(getToken(1), getToken(2)))) { + jj_consume_token(IDENTIFIER); + jj_consume_token(COLON); + jj_consume_token(IDENTIFIER); + jj_consume_token(LPAREN); + } else if (jj_2_27(2)) { + jj_consume_token(IDENTIFIER); + jj_consume_token(LPAREN); + } else if (jj_2_28(2)) { + jj_consume_token(REGISTER); + jj_consume_token(LPAREN); + } else { + jj_consume_token(-1); + throw new ParseException(); + } +} + + final public void FunctionCall() throws ParseException { + if (jj_2_29(2147483647) && (isDeclaredNamespace(getToken(1), getToken(2)))) { + NamespaceIdentifier(); +ASTFunctionNode jjtn001 = new ASTFunctionNode(JJTFUNCTIONNODE); + boolean jjtc001 = true; + jjtree.openNodeScope(jjtn001); + jjtreeOpenNodeScope(jjtn001); + jjtn001.jjtSetFirstToken(getToken(1)); + try { + Arguments(); + } catch (Throwable jjte001) { +if (jjtc001) { + jjtree.clearNodeScope(jjtn001); + jjtc001 = false; + } else { + jjtree.popNode(); + } + if (jjte001 instanceof ParseException) { + throw (ParseException)jjte001; + } + if (jjte001 instanceof RuntimeException) { + throw (RuntimeException)jjte001; + } + throw (Error)jjte001; + } finally { +if (jjtc001) { + jjtree.closeNodeScope(jjtn001, 2); + if (jjtree.nodeCreated()) { + jjtreeCloseNodeScope(jjtn001); + } + jjtn001.jjtSetLastToken(getToken(0)); + } + } + } else if (jj_2_30(2147483647)) { + Identifier(true); +ASTFunctionNode jjtn002 = new ASTFunctionNode(JJTFUNCTIONNODE); + boolean jjtc002 = true; + jjtree.openNodeScope(jjtn002); + jjtreeOpenNodeScope(jjtn002); + jjtn002.jjtSetFirstToken(getToken(1)); + try { + Arguments(); + } catch (Throwable jjte002) { +if (jjtc002) { + jjtree.clearNodeScope(jjtn002); + jjtc002 = false; + } else { + jjtree.popNode(); + } + if (jjte002 instanceof ParseException) { + throw (ParseException)jjte002; + } + if (jjte002 instanceof RuntimeException) { + throw (RuntimeException)jjte002; + } + throw (Error)jjte002; + } finally { +if (jjtc002) { + jjtree.closeNodeScope(jjtn002, 2); + if (jjtree.nodeCreated()) { + jjtreeCloseNodeScope(jjtn002); + } + jjtn002.jjtSetLastToken(getToken(0)); + } + } + } else { + jj_consume_token(-1); + throw new ParseException(); + } +} + + final public void Constructor() throws ParseException {/*@bgen(jjtree) ConstructorNode */ + ASTConstructorNode jjtn000 = new ASTConstructorNode(JJTCONSTRUCTORNODE); + boolean jjtc000 = true; + jjtree.openNodeScope(jjtn000); + jjtreeOpenNodeScope(jjtn000); + jjtn000.jjtSetFirstToken(getToken(1)); + try { + jj_consume_token(NEW); + jj_consume_token(LPAREN); + Expression(); + label_19: + while (true) { + switch (jj_nt.kind) { + case COMMA:{ + break; + } + default: + break label_19; + } + jj_consume_token(COMMA); + Expression(); + } + jj_consume_token(RPAREN); + } catch (Throwable jjte000) { +if (jjtc000) { + jjtree.clearNodeScope(jjtn000); + jjtc000 = false; + } else { + jjtree.popNode(); + } + if (jjte000 instanceof ParseException) { + throw (ParseException)jjte000; + } + if (jjte000 instanceof RuntimeException) { + throw (RuntimeException)jjte000; + } + throw (Error)jjte000; + } finally { +if (jjtc000) { + jjtree.closeNodeScope(jjtn000, true); + if (jjtree.nodeCreated()) { + jjtreeCloseNodeScope(jjtn000); + } + jjtn000.jjtSetLastToken(getToken(0)); + } + } +} + + final public void Parameter() throws ParseException {Token t; + t = jj_consume_token(IDENTIFIER); +declareParameter(t); +} + + final public void Parameters() throws ParseException { + jj_consume_token(LPAREN); + switch (jj_nt.kind) { + case VAR: + case IDENTIFIER:{ + switch (jj_nt.kind) { + case VAR:{ + jj_consume_token(VAR); + break; + } + default: + ; + } + Parameter(); + label_20: + while (true) { + switch (jj_nt.kind) { + case COMMA:{ + break; + } + default: + break label_20; + } + jj_consume_token(COMMA); + switch (jj_nt.kind) { + case VAR:{ + jj_consume_token(VAR); + break; + } + default: + ; + } + Parameter(); + } + break; + } + default: + ; + } + jj_consume_token(RPAREN); +} + + final public void LambdaLookahead() throws ParseException { + switch (jj_nt.kind) { + case FUNCTION:{ + jj_consume_token(FUNCTION); + Parameters(); + break; + } + case LPAREN:{ + Parameters(); + jj_consume_token(LAMBDA); + break; + } + case IDENTIFIER:{ + Parameter(); + jj_consume_token(LAMBDA); + break; + } + default: + jj_consume_token(-1); + throw new ParseException(); + } +} + + final public void Lambda() throws ParseException {/*@bgen(jjtree) JexlLambda */ + ASTJexlLambda jjtn000 = new ASTJexlLambda(JJTJEXLLAMBDA); + boolean jjtc000 = true; + jjtree.openNodeScope(jjtn000); + jjtreeOpenNodeScope(jjtn000); + jjtn000.jjtSetFirstToken(getToken(1));pushFrame(); + try { + switch (jj_nt.kind) { + case FUNCTION:{ +pushUnit(jjtn000); + jj_consume_token(FUNCTION); + Parameters(); + Block(); +jjtree.closeNodeScope(jjtn000, true); + jjtc000 = false; + if (jjtree.nodeCreated()) { + jjtreeCloseNodeScope(jjtn000); + } + jjtn000.jjtSetLastToken(getToken(0)); +popUnit(jjtn000); + break; + } + case LPAREN:{ +pushUnit(jjtn000); + Parameters(); + jj_consume_token(LAMBDA); + Block(); +jjtree.closeNodeScope(jjtn000, true); + jjtc000 = false; + if (jjtree.nodeCreated()) { + jjtreeCloseNodeScope(jjtn000); + } + jjtn000.jjtSetLastToken(getToken(0)); +popUnit(jjtn000); + break; + } + case IDENTIFIER:{ +pushUnit(jjtn000); + Parameter(); + jj_consume_token(LAMBDA); + Block(); +jjtree.closeNodeScope(jjtn000, true); + jjtc000 = false; + if (jjtree.nodeCreated()) { + jjtreeCloseNodeScope(jjtn000); + } + jjtn000.jjtSetLastToken(getToken(0)); +popUnit(jjtn000); + break; + } + default: + jj_consume_token(-1); + throw new ParseException(); + } + } catch (Throwable jjte000) { +if (jjtc000) { + jjtree.clearNodeScope(jjtn000); + jjtc000 = false; + } else { + jjtree.popNode(); + } + if (jjte000 instanceof ParseException) { + throw (ParseException)jjte000; + } + if (jjte000 instanceof RuntimeException) { + throw (RuntimeException)jjte000; + } + throw (Error)jjte000; + } finally { +if (jjtc000) { + jjtree.closeNodeScope(jjtn000, true); + if (jjtree.nodeCreated()) { + jjtreeCloseNodeScope(jjtn000); + } + jjtn000.jjtSetLastToken(getToken(0)); + } + } +} + +/*************************************** + * References + ***************************************/ + final public +Token dotName() throws ParseException {Token t ; + switch (jj_nt.kind) { + case DOT_IDENTIFIER:{ + t = jj_consume_token(DOT_IDENTIFIER); + break; + } + case IF:{ + t = jj_consume_token(IF); + break; + } + case ELSE:{ + t = jj_consume_token(ELSE); + break; + } + case FOR:{ + t = jj_consume_token(FOR); + break; + } + case WHILE:{ + t = jj_consume_token(WHILE); + break; + } + case DO:{ + t = jj_consume_token(DO); + break; + } + case NEW:{ + t = jj_consume_token(NEW); + break; + } + case EMPTY:{ + t = jj_consume_token(EMPTY); + break; + } + case SIZE:{ + t = jj_consume_token(SIZE); + break; + } + case TRUE:{ + t = jj_consume_token(TRUE); + break; + } + case FALSE:{ + t = jj_consume_token(FALSE); + break; + } + case NULL:{ + t = jj_consume_token(NULL); + break; + } + case _OR:{ + t = jj_consume_token(_OR); + break; + } + case _AND:{ + t = jj_consume_token(_AND); + break; + } + case NOT:{ + t = jj_consume_token(NOT); + break; + } + case NE:{ + t = jj_consume_token(NE); + break; + } + case EQ:{ + t = jj_consume_token(EQ); + break; + } + case GT:{ + t = jj_consume_token(GT); + break; + } + case GE:{ + t = jj_consume_token(GE); + break; + } + case LT:{ + t = jj_consume_token(LT); + break; + } + case LE:{ + t = jj_consume_token(LE); + break; + } + case VAR:{ + t = jj_consume_token(VAR); + break; + } + case FUNCTION:{ + t = jj_consume_token(FUNCTION); + break; + } + default: + jj_consume_token(-1); + throw new ParseException(); + } +{if ("" != null) return t ;} + throw new IllegalStateException ("Missing return statement in function"); +} + + final public void IdentifierAccess() throws ParseException {Token t; + switch (jj_nt.kind) { + case DOT:{ + jj_consume_token(DOT); + switch (jj_nt.kind) { + case IF: + case ELSE: + case FOR: + case WHILE: + case DO: + case NEW: + case VAR: + case EMPTY: + case SIZE: + case NULL: + case TRUE: + case FALSE: + case FUNCTION: + case _AND: + case _OR: + case EQ: + case NE: + case GT: + case GE: + case LT: + case LE: + case NOT: + case DOT_IDENTIFIER:{ + t = dotName(); +ASTIdentifierAccess jjtn001 = new ASTIdentifierAccess(JJTIDENTIFIERACCESS); + boolean jjtc001 = true; + jjtree.openNodeScope(jjtn001); + jjtreeOpenNodeScope(jjtn001); + jjtn001.jjtSetFirstToken(getToken(1)); + try { +jjtree.closeNodeScope(jjtn001, true); + jjtc001 = false; + if (jjtree.nodeCreated()) { + jjtreeCloseNodeScope(jjtn001); + } + jjtn001.jjtSetLastToken(getToken(0)); +jjtn001.setIdentifier(t.image); + } finally { +if (jjtc001) { + jjtree.closeNodeScope(jjtn001, true); + if (jjtree.nodeCreated()) { + jjtreeCloseNodeScope(jjtn001); + } + jjtn001.jjtSetLastToken(getToken(0)); + } + } + break; + } + case STRING_LITERAL:{ + t = jj_consume_token(STRING_LITERAL); +ASTIdentifierAccess jjtn002 = new ASTIdentifierAccess(JJTIDENTIFIERACCESS); + boolean jjtc002 = true; + jjtree.openNodeScope(jjtn002); + jjtreeOpenNodeScope(jjtn002); + jjtn002.jjtSetFirstToken(getToken(1)); + try { +jjtree.closeNodeScope(jjtn002, true); + jjtc002 = false; + if (jjtree.nodeCreated()) { + jjtreeCloseNodeScope(jjtn002); + } + jjtn002.jjtSetLastToken(getToken(0)); +jjtn002.setIdentifier(buildString(t.image, true)); + } finally { +if (jjtc002) { + jjtree.closeNodeScope(jjtn002, true); + if (jjtree.nodeCreated()) { + jjtreeCloseNodeScope(jjtn002); + } + jjtn002.jjtSetLastToken(getToken(0)); + } + } + break; + } + case JXLT_LITERAL:{ + t = jj_consume_token(JXLT_LITERAL); +ASTIdentifierAccessJxlt jjtn003 = new ASTIdentifierAccessJxlt(JJTIDENTIFIERACCESSJXLT); + boolean jjtc003 = true; + jjtree.openNodeScope(jjtn003); + jjtreeOpenNodeScope(jjtn003); + jjtn003.jjtSetFirstToken(getToken(1)); + try { +jjtree.closeNodeScope(jjtn003, true); + jjtc003 = false; + if (jjtree.nodeCreated()) { + jjtreeCloseNodeScope(jjtn003); + } + jjtn003.jjtSetLastToken(getToken(0)); +jjtn003.setIdentifier(buildString(t.image, true)); + } finally { +if (jjtc003) { + jjtree.closeNodeScope(jjtn003, true); + if (jjtree.nodeCreated()) { + jjtreeCloseNodeScope(jjtn003); + } + jjtn003.jjtSetLastToken(getToken(0)); + } + } + break; + } + default: + jj_consume_token(-1); + throw new ParseException(); + } + break; + } + case QDOT:{ + jj_consume_token(QDOT); + switch (jj_nt.kind) { + case IF: + case ELSE: + case FOR: + case WHILE: + case DO: + case NEW: + case VAR: + case EMPTY: + case SIZE: + case NULL: + case TRUE: + case FALSE: + case FUNCTION: + case _AND: + case _OR: + case EQ: + case NE: + case GT: + case GE: + case LT: + case LE: + case NOT: + case DOT_IDENTIFIER:{ + t = dotName(); +ASTIdentifierAccessSafe jjtn004 = new ASTIdentifierAccessSafe(JJTIDENTIFIERACCESSSAFE); + boolean jjtc004 = true; + jjtree.openNodeScope(jjtn004); + jjtreeOpenNodeScope(jjtn004); + jjtn004.jjtSetFirstToken(getToken(1)); + try { +jjtree.closeNodeScope(jjtn004, true); + jjtc004 = false; + if (jjtree.nodeCreated()) { + jjtreeCloseNodeScope(jjtn004); + } + jjtn004.jjtSetLastToken(getToken(0)); +jjtn004.setIdentifier(t.image); + } finally { +if (jjtc004) { + jjtree.closeNodeScope(jjtn004, true); + if (jjtree.nodeCreated()) { + jjtreeCloseNodeScope(jjtn004); + } + jjtn004.jjtSetLastToken(getToken(0)); + } + } + break; + } + case STRING_LITERAL:{ + t = jj_consume_token(STRING_LITERAL); +ASTIdentifierAccessSafe jjtn005 = new ASTIdentifierAccessSafe(JJTIDENTIFIERACCESSSAFE); + boolean jjtc005 = true; + jjtree.openNodeScope(jjtn005); + jjtreeOpenNodeScope(jjtn005); + jjtn005.jjtSetFirstToken(getToken(1)); + try { +jjtree.closeNodeScope(jjtn005, true); + jjtc005 = false; + if (jjtree.nodeCreated()) { + jjtreeCloseNodeScope(jjtn005); + } + jjtn005.jjtSetLastToken(getToken(0)); +jjtn005.setIdentifier(buildString(t.image, true)); + } finally { +if (jjtc005) { + jjtree.closeNodeScope(jjtn005, true); + if (jjtree.nodeCreated()) { + jjtreeCloseNodeScope(jjtn005); + } + jjtn005.jjtSetLastToken(getToken(0)); + } + } + break; + } + case JXLT_LITERAL:{ + t = jj_consume_token(JXLT_LITERAL); +ASTIdentifierAccessSafeJxlt jjtn006 = new ASTIdentifierAccessSafeJxlt(JJTIDENTIFIERACCESSSAFEJXLT); + boolean jjtc006 = true; + jjtree.openNodeScope(jjtn006); + jjtreeOpenNodeScope(jjtn006); + jjtn006.jjtSetFirstToken(getToken(1)); + try { +jjtree.closeNodeScope(jjtn006, true); + jjtc006 = false; + if (jjtree.nodeCreated()) { + jjtreeCloseNodeScope(jjtn006); + } + jjtn006.jjtSetLastToken(getToken(0)); +jjtn006.setIdentifier(buildString(t.image, true)); + } finally { +if (jjtc006) { + jjtree.closeNodeScope(jjtn006, true); + if (jjtree.nodeCreated()) { + jjtreeCloseNodeScope(jjtn006); + } + jjtn006.jjtSetLastToken(getToken(0)); + } + } + break; + } + default: + jj_consume_token(-1); + throw new ParseException(); + } + break; + } + default: + jj_consume_token(-1); + throw new ParseException(); + } +} + + final public void ArrayAccess() throws ParseException {/*@bgen(jjtree) ArrayAccess */ + ASTArrayAccess jjtn000 = new ASTArrayAccess(JJTARRAYACCESS); + boolean jjtc000 = true; + jjtree.openNodeScope(jjtn000); + jjtreeOpenNodeScope(jjtn000); + jjtn000.jjtSetFirstToken(getToken(1)); + try { + label_21: + while (true) { + jj_consume_token(LBRACKET); + Expression(); + jj_consume_token(RBRACKET); + switch (jj_nt.kind) { + case LBRACKET:{ + break; + } + default: + break label_21; + } + } + } catch (Throwable jjte000) { +if (jjtc000) { + jjtree.clearNodeScope(jjtn000); + jjtc000 = false; + } else { + jjtree.popNode(); + } + if (jjte000 instanceof ParseException) { + throw (ParseException)jjte000; + } + if (jjte000 instanceof RuntimeException) { + throw (RuntimeException)jjte000; + } + throw (Error)jjte000; + } finally { +if (jjtc000) { + jjtree.closeNodeScope(jjtn000, true); + if (jjtree.nodeCreated()) { + jjtreeCloseNodeScope(jjtn000); + } + jjtn000.jjtSetLastToken(getToken(0)); + } + } +} + + final public void MemberAccess() throws ParseException { + if (jj_2_31(2147483647)) { + ArrayAccess(); + } else if (jj_2_32(2147483647)) { + IdentifierAccess(); + } else if (jj_2_33(2147483647)) { + IdentifierAccess(); + } else { + jj_consume_token(-1); + throw new ParseException(); + } +} + + final public void ReferenceExpression() throws ParseException {/*@bgen(jjtree) #MethodNode(> 1) */ + ASTMethodNode jjtn000 = new ASTMethodNode(JJTMETHODNODE); + boolean jjtc000 = true; + jjtree.openNodeScope(jjtn000); + jjtreeOpenNodeScope(jjtn000); + jjtn000.jjtSetFirstToken(getToken(1)); + try { + jj_consume_token(LPAREN); + Expression(); +ASTReferenceExpression jjtn001 = new ASTReferenceExpression(JJTREFERENCEEXPRESSION); + boolean jjtc001 = true; + jjtree.openNodeScope(jjtn001); + jjtreeOpenNodeScope(jjtn001); + jjtn001.jjtSetFirstToken(getToken(1)); + try { + jj_consume_token(RPAREN); + } finally { +if (jjtc001) { + jjtree.closeNodeScope(jjtn001, 1); + if (jjtree.nodeCreated()) { + jjtreeCloseNodeScope(jjtn001); + } + jjtn001.jjtSetLastToken(getToken(0)); + } + } + label_22: + while (true) { + if (jj_2_34(2147483647)) { + } else { + break label_22; + } + Arguments(); + } + } catch (Throwable jjte000) { +if (jjtc000) { + jjtree.clearNodeScope(jjtn000); + jjtc000 = false; + } else { + jjtree.popNode(); + } + if (jjte000 instanceof ParseException) { + throw (ParseException)jjte000; + } + if (jjte000 instanceof RuntimeException) { + throw (RuntimeException)jjte000; + } + throw (Error)jjte000; + } finally { +if (jjtc000) { + jjtree.closeNodeScope(jjtn000, jjtree.nodeArity() > 1); + if (jjtree.nodeCreated()) { + jjtreeCloseNodeScope(jjtn000); + } + jjtn000.jjtSetLastToken(getToken(0)); + } + } +} + + final public void PrimaryExpression() throws ParseException { + if (jj_2_35(2147483647)) { + Lambda(); + } else if (jj_2_36(2147483647)) { + ReferenceExpression(); + } else if (jj_2_37(2147483647)) { + MapLiteral(); + } else if (jj_2_38(2147483647)) { + MapLiteral(); + } else if (jj_2_39(2147483647)) { + SetLiteral(); + } else if (jj_2_40(2147483647)) { + SetLiteral(); + } else if (jj_2_41(2147483647)) { + ArrayLiteral(); + } else if (jj_2_42(2147483647)) { + Constructor(); + } else if (jj_2_43(2147483647)) { + FunctionCall(); + } else { + switch (jj_nt.kind) { + case IDENTIFIER: + case REGISTER:{ + Identifier(true); + break; + } + case NULL: + case TRUE: + case FALSE: + case NAN_LITERAL: + case INTEGER_LITERAL: + case FLOAT_LITERAL: + case STRING_LITERAL: + case JXLT_LITERAL: + case REGEX_LITERAL:{ + Literal(); + break; + } + default: + jj_consume_token(-1); + throw new ParseException(); + } + } +} + + final public void MethodCall() throws ParseException { +ASTMethodNode jjtn001 = new ASTMethodNode(JJTMETHODNODE); + boolean jjtc001 = true; + jjtree.openNodeScope(jjtn001); + jjtreeOpenNodeScope(jjtn001); + jjtn001.jjtSetFirstToken(getToken(1)); + try { + MemberAccess(); + label_23: + while (true) { + Arguments(); + if (jj_2_44(2147483647)) { + } else { + break label_23; + } + } + } catch (Throwable jjte001) { +if (jjtc001) { + jjtree.clearNodeScope(jjtn001); + jjtc001 = false; + } else { + jjtree.popNode(); + } + if (jjte001 instanceof ParseException) { + throw (ParseException)jjte001; + } + if (jjte001 instanceof RuntimeException) { + throw (RuntimeException)jjte001; + } + throw (Error)jjte001; + } finally { +if (jjtc001) { + jjtree.closeNodeScope(jjtn001, jjtree.nodeArity() > 1); + if (jjtree.nodeCreated()) { + jjtreeCloseNodeScope(jjtn001); + } + jjtn001.jjtSetLastToken(getToken(0)); + } + } +} + + final public void MemberExpression() throws ParseException { + if (jj_2_45(2147483647)) { + MethodCall(); + } else { + switch (jj_nt.kind) { + case LBRACKET: + case DOT: + case QDOT:{ + MemberAccess(); + break; + } + default: + jj_consume_token(-1); + throw new ParseException(); + } + } +} + + final public void ValueExpression() throws ParseException { +ASTReference jjtn001 = new ASTReference(JJTREFERENCE); + boolean jjtc001 = true; + jjtree.openNodeScope(jjtn001); + jjtreeOpenNodeScope(jjtn001); + jjtn001.jjtSetFirstToken(getToken(1)); + try { + PrimaryExpression(); + label_24: + while (true) { + if (jj_2_46(2)) { + } else { + break label_24; + } + MemberExpression(); + } + } catch (Throwable jjte001) { +if (jjtc001) { + jjtree.clearNodeScope(jjtn001); + jjtc001 = false; + } else { + jjtree.popNode(); + } + if (jjte001 instanceof ParseException) { + throw (ParseException)jjte001; + } + if (jjte001 instanceof RuntimeException) { + throw (RuntimeException)jjte001; + } + throw (Error)jjte001; + } finally { +if (jjtc001) { + jjtree.closeNodeScope(jjtn001, jjtree.nodeArity() > 1); + if (jjtree.nodeCreated()) { + jjtreeCloseNodeScope(jjtn001); + } + jjtn001.jjtSetLastToken(getToken(0)); + } + } +} + + private boolean jj_2_1(int xla) + { + jj_la = xla; + jj_scanpos = token; + jj_lastpos = token; + try { return (!jj_3_1()); } + catch(LookaheadSuccess ls) { return true; } + } + + private boolean jj_2_2(int xla) + { + jj_la = xla; + jj_scanpos = token; + jj_lastpos = token; + try { return (!jj_3_2()); } + catch(LookaheadSuccess ls) { return true; } + } + + private boolean jj_2_3(int xla) + { + jj_la = xla; + jj_scanpos = token; + jj_lastpos = token; + try { return (!jj_3_3()); } + catch(LookaheadSuccess ls) { return true; } + } + + private boolean jj_2_4(int xla) + { + jj_la = xla; + jj_scanpos = token; + jj_lastpos = token; + try { return (!jj_3_4()); } + catch(LookaheadSuccess ls) { return true; } + } + + private boolean jj_2_5(int xla) + { + jj_la = xla; + jj_scanpos = token; + jj_lastpos = token; + try { return (!jj_3_5()); } + catch(LookaheadSuccess ls) { return true; } + } + + private boolean jj_2_6(int xla) + { + jj_la = xla; + jj_scanpos = token; + jj_lastpos = token; + try { return (!jj_3_6()); } + catch(LookaheadSuccess ls) { return true; } + } + + private boolean jj_2_7(int xla) + { + jj_la = xla; + jj_scanpos = token; + jj_lastpos = token; + try { return (!jj_3_7()); } + catch(LookaheadSuccess ls) { return true; } + } + + private boolean jj_2_8(int xla) + { + jj_la = xla; + jj_scanpos = token; + jj_lastpos = token; + try { return (!jj_3_8()); } + catch(LookaheadSuccess ls) { return true; } + } + + private boolean jj_2_9(int xla) + { + jj_la = xla; + jj_scanpos = token; + jj_lastpos = token; + try { return (!jj_3_9()); } + catch(LookaheadSuccess ls) { return true; } + } + + private boolean jj_2_10(int xla) + { + jj_la = xla; + jj_scanpos = token; + jj_lastpos = token; + try { return (!jj_3_10()); } + catch(LookaheadSuccess ls) { return true; } + } + + private boolean jj_2_11(int xla) + { + jj_la = xla; + jj_scanpos = token; + jj_lastpos = token; + try { return (!jj_3_11()); } + catch(LookaheadSuccess ls) { return true; } + } + + private boolean jj_2_12(int xla) + { + jj_la = xla; + jj_scanpos = token; + jj_lastpos = token; + try { return (!jj_3_12()); } + catch(LookaheadSuccess ls) { return true; } + } + + private boolean jj_2_13(int xla) + { + jj_la = xla; + jj_scanpos = token; + jj_lastpos = token; + try { return (!jj_3_13()); } + catch(LookaheadSuccess ls) { return true; } + } + + private boolean jj_2_14(int xla) + { + jj_la = xla; + jj_scanpos = token; + jj_lastpos = token; + try { return (!jj_3_14()); } + catch(LookaheadSuccess ls) { return true; } + } + + private boolean jj_2_15(int xla) + { + jj_la = xla; + jj_scanpos = token; + jj_lastpos = token; + try { return (!jj_3_15()); } + catch(LookaheadSuccess ls) { return true; } + } + + private boolean jj_2_16(int xla) + { + jj_la = xla; + jj_scanpos = token; + jj_lastpos = token; + try { return (!jj_3_16()); } + catch(LookaheadSuccess ls) { return true; } + } + + private boolean jj_2_17(int xla) + { + jj_la = xla; + jj_scanpos = token; + jj_lastpos = token; + try { return (!jj_3_17()); } + catch(LookaheadSuccess ls) { return true; } + } + + private boolean jj_2_18(int xla) + { + jj_la = xla; + jj_scanpos = token; + jj_lastpos = token; + try { return (!jj_3_18()); } + catch(LookaheadSuccess ls) { return true; } + } + + private boolean jj_2_19(int xla) + { + jj_la = xla; + jj_scanpos = token; + jj_lastpos = token; + try { return (!jj_3_19()); } + catch(LookaheadSuccess ls) { return true; } + } + + private boolean jj_2_20(int xla) + { + jj_la = xla; + jj_scanpos = token; + jj_lastpos = token; + try { return (!jj_3_20()); } + catch(LookaheadSuccess ls) { return true; } + } + + private boolean jj_2_21(int xla) + { + jj_la = xla; + jj_scanpos = token; + jj_lastpos = token; + try { return (!jj_3_21()); } + catch(LookaheadSuccess ls) { return true; } + } + + private boolean jj_2_22(int xla) + { + jj_la = xla; + jj_scanpos = token; + jj_lastpos = token; + try { return (!jj_3_22()); } + catch(LookaheadSuccess ls) { return true; } + } + + private boolean jj_2_23(int xla) + { + jj_la = xla; + jj_scanpos = token; + jj_lastpos = token; + try { return (!jj_3_23()); } + catch(LookaheadSuccess ls) { return true; } + } + + private boolean jj_2_24(int xla) + { + jj_la = xla; + jj_scanpos = token; + jj_lastpos = token; + try { return (!jj_3_24()); } + catch(LookaheadSuccess ls) { return true; } + } + + private boolean jj_2_25(int xla) + { + jj_la = xla; + jj_scanpos = token; + jj_lastpos = token; + try { return (!jj_3_25()); } + catch(LookaheadSuccess ls) { return true; } + } + + private boolean jj_2_26(int xla) + { + jj_la = xla; + jj_scanpos = token; + jj_lastpos = token; + try { return (!jj_3_26()); } + catch(LookaheadSuccess ls) { return true; } + } + + private boolean jj_2_27(int xla) + { + jj_la = xla; + jj_scanpos = token; + jj_lastpos = token; + try { return (!jj_3_27()); } + catch(LookaheadSuccess ls) { return true; } + } + + private boolean jj_2_28(int xla) + { + jj_la = xla; + jj_scanpos = token; + jj_lastpos = token; + try { return (!jj_3_28()); } + catch(LookaheadSuccess ls) { return true; } + } + + private boolean jj_2_29(int xla) + { + jj_la = xla; + jj_scanpos = token; + jj_lastpos = token; + try { return (!jj_3_29()); } + catch(LookaheadSuccess ls) { return true; } + } + + private boolean jj_2_30(int xla) + { + jj_la = xla; + jj_scanpos = token; + jj_lastpos = token; + try { return (!jj_3_30()); } + catch(LookaheadSuccess ls) { return true; } + } + + private boolean jj_2_31(int xla) + { + jj_la = xla; + jj_scanpos = token; + jj_lastpos = token; + try { return (!jj_3_31()); } + catch(LookaheadSuccess ls) { return true; } + } + + private boolean jj_2_32(int xla) + { + jj_la = xla; + jj_scanpos = token; + jj_lastpos = token; + try { return (!jj_3_32()); } + catch(LookaheadSuccess ls) { return true; } + } + + private boolean jj_2_33(int xla) + { + jj_la = xla; + jj_scanpos = token; + jj_lastpos = token; + try { return (!jj_3_33()); } + catch(LookaheadSuccess ls) { return true; } + } + + private boolean jj_2_34(int xla) + { + jj_la = xla; + jj_scanpos = token; + jj_lastpos = token; + try { return (!jj_3_34()); } + catch(LookaheadSuccess ls) { return true; } + } + + private boolean jj_2_35(int xla) + { + jj_la = xla; + jj_scanpos = token; + jj_lastpos = token; + try { return (!jj_3_35()); } + catch(LookaheadSuccess ls) { return true; } + } + + private boolean jj_2_36(int xla) + { + jj_la = xla; + jj_scanpos = token; + jj_lastpos = token; + try { return (!jj_3_36()); } + catch(LookaheadSuccess ls) { return true; } + } + + private boolean jj_2_37(int xla) + { + jj_la = xla; + jj_scanpos = token; + jj_lastpos = token; + try { return (!jj_3_37()); } + catch(LookaheadSuccess ls) { return true; } + } + + private boolean jj_2_38(int xla) + { + jj_la = xla; + jj_scanpos = token; + jj_lastpos = token; + try { return (!jj_3_38()); } + catch(LookaheadSuccess ls) { return true; } + } + + private boolean jj_2_39(int xla) + { + jj_la = xla; + jj_scanpos = token; + jj_lastpos = token; + try { return (!jj_3_39()); } + catch(LookaheadSuccess ls) { return true; } + } + + private boolean jj_2_40(int xla) + { + jj_la = xla; + jj_scanpos = token; + jj_lastpos = token; + try { return (!jj_3_40()); } + catch(LookaheadSuccess ls) { return true; } + } + + private boolean jj_2_41(int xla) + { + jj_la = xla; + jj_scanpos = token; + jj_lastpos = token; + try { return (!jj_3_41()); } + catch(LookaheadSuccess ls) { return true; } + } + + private boolean jj_2_42(int xla) + { + jj_la = xla; + jj_scanpos = token; + jj_lastpos = token; + try { return (!jj_3_42()); } + catch(LookaheadSuccess ls) { return true; } + } + + private boolean jj_2_43(int xla) + { + jj_la = xla; + jj_scanpos = token; + jj_lastpos = token; + try { return (!jj_3_43()); } + catch(LookaheadSuccess ls) { return true; } + } + + private boolean jj_2_44(int xla) + { + jj_la = xla; + jj_scanpos = token; + jj_lastpos = token; + try { return (!jj_3_44()); } + catch(LookaheadSuccess ls) { return true; } + } + + private boolean jj_2_45(int xla) + { + jj_la = xla; + jj_scanpos = token; + jj_lastpos = token; + try { return (!jj_3_45()); } + catch(LookaheadSuccess ls) { return true; } + } + + private boolean jj_2_46(int xla) + { + jj_la = xla; + jj_scanpos = token; + jj_lastpos = token; + try { return (!jj_3_46()); } + catch(LookaheadSuccess ls) { return true; } + } + + private boolean jj_3R_79() + { + if (jj_3R_99()) return true; + Token xsp; + xsp = jj_scanpos; + if (jj_3R_100()) jj_scanpos = xsp; + return false; + } + + private boolean jj_3R_100() + { + Token xsp; + xsp = jj_scanpos; + if (jj_3R_122()) { + jj_scanpos = xsp; + if (jj_3R_123()) { + jj_scanpos = xsp; + if (jj_3R_124()) return true; + } + } + return false; + } + + private boolean jj_3R_122() + { + if (jj_scan_token(QMARK)) return true; + if (jj_3R_26()) return true; + if (jj_scan_token(COLON)) return true; + if (jj_3R_26()) return true; + return false; + } + + private boolean jj_3R_123() + { + if (jj_scan_token(ELVIS)) return true; + if (jj_3R_26()) return true; + return false; + } + + private boolean jj_3R_124() + { + if (jj_scan_token(NULLP)) return true; + if (jj_3R_26()) return true; + return false; + } + + private boolean jj_3R_99() + { + if (jj_3R_120()) return true; + Token xsp; + while (true) { + xsp = jj_scanpos; + if (jj_3R_121()) { jj_scanpos = xsp; break; } + } + return false; + } + + private boolean jj_3R_121() + { + Token xsp; + xsp = jj_scanpos; + if (jj_scan_token(44)) { + jj_scanpos = xsp; + if (jj_scan_token(45)) return true; + } + if (jj_3R_120()) return true; + return false; + } + + private boolean jj_3R_120() + { + if (jj_3R_145()) return true; + Token xsp; + while (true) { + xsp = jj_scanpos; + if (jj_3R_146()) { jj_scanpos = xsp; break; } + } + return false; + } + + private boolean jj_3R_146() + { + Token xsp; + xsp = jj_scanpos; + if (jj_scan_token(42)) { + jj_scanpos = xsp; + if (jj_scan_token(43)) return true; + } + if (jj_3R_145()) return true; + return false; + } + + private boolean jj_3R_145() + { + if (jj_3R_162()) return true; + Token xsp; + while (true) { + xsp = jj_scanpos; + if (jj_3R_163()) { jj_scanpos = xsp; break; } + } + return false; + } + + private boolean jj_3R_163() + { + if (jj_scan_token(or)) return true; + if (jj_3R_162()) return true; + return false; + } + + private boolean jj_3R_162() + { + if (jj_3R_167()) return true; + Token xsp; + while (true) { + xsp = jj_scanpos; + if (jj_3R_168()) { jj_scanpos = xsp; break; } + } + return false; + } + + private boolean jj_3R_168() + { + if (jj_scan_token(xor)) return true; + if (jj_3R_167()) return true; + return false; + } + + private boolean jj_3R_167() + { + if (jj_3R_169()) return true; + Token xsp; + while (true) { + xsp = jj_scanpos; + if (jj_3R_170()) { jj_scanpos = xsp; break; } + } + return false; + } + + private boolean jj_3R_170() + { + if (jj_scan_token(and)) return true; + if (jj_3R_169()) return true; + return false; + } + + private boolean jj_3R_169() + { + if (jj_3R_171()) return true; + Token xsp; + xsp = jj_scanpos; + if (jj_3R_172()) jj_scanpos = xsp; + return false; + } + + private boolean jj_3R_172() + { + Token xsp; + xsp = jj_scanpos; + if (jj_3R_175()) { + jj_scanpos = xsp; + if (jj_3R_176()) { + jj_scanpos = xsp; + if (jj_3R_177()) return true; + } + } + return false; + } + + private boolean jj_3R_175() + { + Token xsp; + xsp = jj_scanpos; + if (jj_scan_token(46)) { + jj_scanpos = xsp; + if (jj_scan_token(47)) return true; + } + if (jj_3R_171()) return true; + return false; + } + + private boolean jj_3R_176() + { + Token xsp; + xsp = jj_scanpos; + if (jj_scan_token(48)) { + jj_scanpos = xsp; + if (jj_scan_token(49)) return true; + } + if (jj_3R_171()) return true; + return false; + } + + private boolean jj_3R_177() + { + if (jj_scan_token(range)) return true; + if (jj_3R_171()) return true; + return false; + } + + private boolean jj_3R_171() + { + if (jj_3R_173()) return true; + Token xsp; + xsp = jj_scanpos; + if (jj_3R_174()) jj_scanpos = xsp; + return false; + } + + private boolean jj_3R_174() + { + Token xsp; + xsp = jj_scanpos; + if (jj_3R_178()) { + jj_scanpos = xsp; + if (jj_3R_179()) { + jj_scanpos = xsp; + if (jj_3R_180()) { + jj_scanpos = xsp; + if (jj_3R_181()) { + jj_scanpos = xsp; + if (jj_3R_182()) { + jj_scanpos = xsp; + if (jj_3R_183()) { + jj_scanpos = xsp; + if (jj_3R_184()) { + jj_scanpos = xsp; + if (jj_3R_185()) { + jj_scanpos = xsp; + if (jj_3R_186()) { + jj_scanpos = xsp; + if (jj_3R_187()) return true; + } + } + } + } + } + } + } + } + } + return false; + } + + private boolean jj_3R_178() + { + Token xsp; + xsp = jj_scanpos; + if (jj_scan_token(54)) { + jj_scanpos = xsp; + if (jj_scan_token(55)) return true; + } + if (jj_3R_173()) return true; + return false; + } + + private boolean jj_3R_179() + { + Token xsp; + xsp = jj_scanpos; + if (jj_scan_token(50)) { + jj_scanpos = xsp; + if (jj_scan_token(51)) return true; + } + if (jj_3R_173()) return true; + return false; + } + + private boolean jj_3R_180() + { + Token xsp; + xsp = jj_scanpos; + if (jj_scan_token(56)) { + jj_scanpos = xsp; + if (jj_scan_token(57)) return true; + } + if (jj_3R_173()) return true; + return false; + } + + private boolean jj_3R_181() + { + Token xsp; + xsp = jj_scanpos; + if (jj_scan_token(52)) { + jj_scanpos = xsp; + if (jj_scan_token(53)) return true; + } + if (jj_3R_173()) return true; + return false; + } + + private boolean jj_3R_182() + { + if (jj_scan_token(req)) return true; + if (jj_3R_173()) return true; + return false; + } + + private boolean jj_3R_183() + { + if (jj_scan_token(rne)) return true; + if (jj_3R_173()) return true; + return false; + } + + private boolean jj_3R_184() + { + if (jj_scan_token(seq)) return true; + if (jj_3R_173()) return true; + return false; + } + + private boolean jj_3R_185() + { + if (jj_scan_token(sne)) return true; + if (jj_3R_173()) return true; + return false; + } + + private boolean jj_3R_186() + { + if (jj_scan_token(eeq)) return true; + if (jj_3R_173()) return true; + return false; + } + + private boolean jj_3R_187() + { + if (jj_scan_token(ene)) return true; + if (jj_3R_173()) return true; + return false; + } + + private boolean jj_3R_173() + { + if (jj_3R_57()) return true; + Token xsp; + while (true) { + xsp = jj_scanpos; + if (jj_3_19()) { jj_scanpos = xsp; break; } + } + return false; + } + + private boolean jj_3_19() + { + Token xsp; + xsp = jj_scanpos; + if (jj_3R_36()) { + jj_scanpos = xsp; + if (jj_3R_37()) return true; + } + return false; + } + + private boolean jj_3R_36() + { + if (jj_scan_token(plus)) return true; + if (jj_3R_57()) return true; + return false; + } + + private boolean jj_3R_37() + { + if (jj_scan_token(minus)) return true; + if (jj_3R_57()) return true; + return false; + } + + private boolean jj_3R_57() + { + if (jj_3R_80()) return true; + Token xsp; + while (true) { + xsp = jj_scanpos; + if (jj_3R_188()) { jj_scanpos = xsp; break; } + } + return false; + } + + private boolean jj_3R_188() + { + Token xsp; + xsp = jj_scanpos; + if (jj_3R_189()) { + jj_scanpos = xsp; + if (jj_3R_190()) { + jj_scanpos = xsp; + if (jj_3R_191()) return true; + } + } + return false; + } + + private boolean jj_3R_189() + { + if (jj_scan_token(mult)) return true; + if (jj_3R_80()) return true; + return false; + } + + private boolean jj_3R_190() + { + Token xsp; + xsp = jj_scanpos; + if (jj_scan_token(76)) { + jj_scanpos = xsp; + if (jj_scan_token(77)) return true; + } + if (jj_3R_80()) return true; + return false; + } + + private boolean jj_3R_191() + { + Token xsp; + xsp = jj_scanpos; + if (jj_scan_token(78)) { + jj_scanpos = xsp; + if (jj_scan_token(79)) return true; + } + if (jj_3R_80()) return true; + return false; + } + + private boolean jj_3R_80() + { + Token xsp; + xsp = jj_scanpos; + if (jj_3R_101()) { + jj_scanpos = xsp; + if (jj_3R_102()) { + jj_scanpos = xsp; + if (jj_3R_103()) { + jj_scanpos = xsp; + if (jj_3R_104()) { + jj_scanpos = xsp; + if (jj_3R_105()) { + jj_scanpos = xsp; + if (jj_3R_106()) { + jj_scanpos = xsp; + if (jj_3_20()) return true; + } + } + } + } + } + } + return false; + } + + private boolean jj_3R_101() + { + if (jj_scan_token(minus)) return true; + if (jj_3R_80()) return true; + return false; + } + + private boolean jj_3R_102() + { + if (jj_scan_token(plus)) return true; + if (jj_3R_80()) return true; + return false; + } + + private boolean jj_3R_103() + { + if (jj_scan_token(tilda)) return true; + if (jj_3R_80()) return true; + return false; + } + + private boolean jj_3R_104() + { + Token xsp; + xsp = jj_scanpos; + if (jj_scan_token(80)) { + jj_scanpos = xsp; + if (jj_scan_token(81)) return true; + } + if (jj_3R_80()) return true; + return false; + } + + private boolean jj_3R_105() + { + if (jj_scan_token(EMPTY)) return true; + if (jj_3R_80()) return true; + return false; + } + + private boolean jj_3R_106() + { + if (jj_scan_token(SIZE)) return true; + if (jj_3R_80()) return true; + return false; + } + + private boolean jj_3_20() + { + if (jj_3R_38()) return true; + return false; + } + + private boolean jj_3R_114() + { + Token xsp; + xsp = jj_scanpos; + if (jj_3R_130()) { + jj_scanpos = xsp; + if (jj_3R_131()) return true; + } + return false; + } + + private boolean jj_3R_130() + { + if (jj_scan_token(IDENTIFIER)) return true; + return false; + } + + private boolean jj_3R_131() + { + if (jj_scan_token(REGISTER)) return true; + return false; + } + + private boolean jj_3R_147() + { + if (jj_scan_token(IDENTIFIER)) return true; + if (jj_scan_token(COLON)) return true; + if (jj_scan_token(IDENTIFIER)) return true; + return false; + } + + private boolean jj_3R_115() + { + Token xsp; + xsp = jj_scanpos; + if (jj_3R_132()) { + jj_scanpos = xsp; + if (jj_3R_133()) { + jj_scanpos = xsp; + if (jj_3R_134()) { + jj_scanpos = xsp; + if (jj_3R_135()) { + jj_scanpos = xsp; + if (jj_3R_136()) { + jj_scanpos = xsp; + if (jj_3R_137()) { + jj_scanpos = xsp; + if (jj_3R_138()) { + jj_scanpos = xsp; + if (jj_3R_139()) return true; + } + } + } + } + } + } + } + return false; + } + + private boolean jj_3R_132() + { + if (jj_3R_148()) return true; + return false; + } + + private boolean jj_3R_133() + { + if (jj_3R_149()) return true; + return false; + } + + private boolean jj_3R_134() + { + if (jj_3R_150()) return true; + return false; + } + + private boolean jj_3R_135() + { + if (jj_3R_151()) return true; + return false; + } + + private boolean jj_3R_136() + { + if (jj_3R_152()) return true; + return false; + } + + private boolean jj_3R_137() + { + if (jj_3R_153()) return true; + return false; + } + + private boolean jj_3R_138() + { + if (jj_3R_154()) return true; + return false; + } + + private boolean jj_3R_139() + { + if (jj_3R_155()) return true; + return false; + } + + private boolean jj_3R_155() + { + if (jj_scan_token(NAN_LITERAL)) return true; + return false; + } + + private boolean jj_3R_154() + { + if (jj_scan_token(NULL)) return true; + return false; + } + + private boolean jj_3R_150() + { + Token xsp; + xsp = jj_scanpos; + if (jj_3R_164()) { + jj_scanpos = xsp; + if (jj_3R_165()) return true; + } + return false; + } + + private boolean jj_3R_164() + { + if (jj_scan_token(TRUE)) return true; + return false; + } + + private boolean jj_3R_165() + { + if (jj_scan_token(FALSE)) return true; + return false; + } + + private boolean jj_3R_148() + { + if (jj_scan_token(INTEGER_LITERAL)) return true; + return false; + } + + private boolean jj_3R_149() + { + if (jj_scan_token(FLOAT_LITERAL)) return true; + return false; + } + + private boolean jj_3R_152() + { + if (jj_scan_token(STRING_LITERAL)) return true; + return false; + } + + private boolean jj_3R_151() + { + if (jj_scan_token(JXLT_LITERAL)) return true; + return false; + } + + private boolean jj_3R_153() + { + if (jj_scan_token(REGEX_LITERAL)) return true; + return false; + } + + private boolean jj_3R_198() + { + if (jj_scan_token(ELIPSIS)) return true; + return false; + } + + private boolean jj_3R_111() + { + if (jj_scan_token(LBRACKET)) return true; + Token xsp; + xsp = jj_scanpos; + if (jj_3R_193()) { + jj_scanpos = xsp; + if (jj_3R_194()) return true; + } + if (jj_scan_token(RBRACKET)) return true; + return false; + } + + private boolean jj_3R_193() + { + if (jj_3R_198()) return true; + return false; + } + + private boolean jj_3R_194() + { + Token xsp; + xsp = jj_scanpos; + if (jj_3_22()) jj_scanpos = xsp; + xsp = jj_scanpos; + if (jj_3R_199()) jj_scanpos = xsp; + return false; + } + + private boolean jj_3_22() + { + if (jj_3R_26()) return true; + Token xsp; + while (true) { + xsp = jj_scanpos; + if (jj_3_21()) { jj_scanpos = xsp; break; } + } + return false; + } + + private boolean jj_3R_109() + { + if (jj_scan_token(LCURLY)) return true; + Token xsp; + xsp = jj_scanpos; + if (jj_3_23()) { + jj_scanpos = xsp; + if (jj_scan_token(34)) return true; + } + if (jj_scan_token(RCURLY)) return true; + return false; + } + + private boolean jj_3_23() + { + if (jj_3R_39()) return true; + Token xsp; + while (true) { + xsp = jj_scanpos; + if (jj_3R_196()) { jj_scanpos = xsp; break; } + } + return false; + } + + private boolean jj_3R_39() + { + if (jj_3R_26()) return true; + if (jj_scan_token(COLON)) return true; + if (jj_3R_26()) return true; + return false; + } + + private boolean jj_3_21() + { + if (jj_scan_token(COMMA)) return true; + if (jj_3R_26()) return true; + return false; + } + + private boolean jj_3R_110() + { + if (jj_scan_token(LCURLY)) return true; + Token xsp; + xsp = jj_scanpos; + if (jj_3_24()) jj_scanpos = xsp; + if (jj_scan_token(RCURLY)) return true; + return false; + } + + private boolean jj_3R_196() + { + if (jj_scan_token(COMMA)) return true; + if (jj_3R_39()) return true; + return false; + } + + private boolean jj_3_24() + { + if (jj_3R_26()) return true; + Token xsp; + while (true) { + xsp = jj_scanpos; + if (jj_3R_197()) { jj_scanpos = xsp; break; } + } + return false; + } + + private boolean jj_3R_97() + { + if (jj_scan_token(LPAREN)) return true; + Token xsp; + xsp = jj_scanpos; + if (jj_3_25()) jj_scanpos = xsp; + if (jj_scan_token(RPAREN)) return true; + return false; + } + + private boolean jj_3R_41() + { + Token xsp; + xsp = jj_scanpos; + jj_lookingAhead = true; + jj_semLA = isDeclaredNamespace(getToken(1), getToken(2)); + jj_lookingAhead = false; + if (!jj_semLA || jj_3R_62()) { + jj_scanpos = xsp; + if (jj_3_27()) { + jj_scanpos = xsp; + if (jj_3_28()) return true; + } + } + return false; + } + + private boolean jj_3R_62() + { + if (jj_scan_token(IDENTIFIER)) return true; + if (jj_scan_token(COLON)) return true; + if (jj_scan_token(IDENTIFIER)) return true; + if (jj_scan_token(LPAREN)) return true; + return false; + } + + private boolean jj_3_27() + { + if (jj_scan_token(IDENTIFIER)) return true; + if (jj_scan_token(LPAREN)) return true; + return false; + } + + private boolean jj_3_28() + { + if (jj_scan_token(REGISTER)) return true; + if (jj_scan_token(LPAREN)) return true; + return false; + } + + private boolean jj_3_25() + { + if (jj_3R_26()) return true; + Token xsp; + while (true) { + xsp = jj_scanpos; + if (jj_3R_144()) { jj_scanpos = xsp; break; } + } + return false; + } + + private boolean jj_3R_113() + { + Token xsp; + xsp = jj_scanpos; + jj_lookingAhead = true; + jj_semLA = isDeclaredNamespace(getToken(1), getToken(2)); + jj_lookingAhead = false; + if (!jj_semLA || jj_3R_128()) { + jj_scanpos = xsp; + if (jj_3R_129()) return true; + } + return false; + } + + private boolean jj_3R_128() + { + if (jj_3R_147()) return true; + if (jj_3R_97()) return true; + return false; + } + + private boolean jj_3_26() + { + if (jj_scan_token(IDENTIFIER)) return true; + if (jj_scan_token(COLON)) return true; + if (jj_scan_token(IDENTIFIER)) return true; + if (jj_scan_token(LPAREN)) return true; + return false; + } + + private boolean jj_3R_197() + { + if (jj_scan_token(COMMA)) return true; + if (jj_3R_26()) return true; + return false; + } + + private boolean jj_3R_129() + { + if (jj_3R_114()) return true; + if (jj_3R_97()) return true; + return false; + } + + private boolean jj_3R_112() + { + if (jj_scan_token(NEW)) return true; + if (jj_scan_token(LPAREN)) return true; + if (jj_3R_26()) return true; + Token xsp; + while (true) { + xsp = jj_scanpos; + if (jj_3R_195()) { jj_scanpos = xsp; break; } + } + if (jj_scan_token(RPAREN)) return true; + return false; + } + + private boolean jj_3_29() + { + if (jj_scan_token(IDENTIFIER)) return true; + if (jj_scan_token(COLON)) return true; + if (jj_scan_token(IDENTIFIER)) return true; + if (jj_scan_token(LPAREN)) return true; + return false; + } + + private boolean jj_3R_144() + { + if (jj_scan_token(COMMA)) return true; + if (jj_3R_26()) return true; + return false; + } + + private boolean jj_3_30() + { + if (jj_scan_token(IDENTIFIER)) return true; + if (jj_scan_token(LPAREN)) return true; + return false; + } + + private boolean jj_3R_199() + { + if (jj_scan_token(COMMA)) return true; + if (jj_3R_198()) return true; + return false; + } + + private boolean jj_3R_93() + { + if (jj_scan_token(IDENTIFIER)) return true; + return false; + } + + private boolean jj_3_1() + { + if (jj_3R_25()) return true; + return false; + } + + private boolean jj_3R_92() + { + if (jj_scan_token(LPAREN)) return true; + Token xsp; + xsp = jj_scanpos; + if (jj_3R_116()) jj_scanpos = xsp; + if (jj_scan_token(RPAREN)) return true; + return false; + } + + private boolean jj_3R_40() + { + Token xsp; + xsp = jj_scanpos; + if (jj_3R_59()) { + jj_scanpos = xsp; + if (jj_3R_60()) { + jj_scanpos = xsp; + if (jj_3R_61()) return true; + } + } + return false; + } + + private boolean jj_3R_59() + { + if (jj_scan_token(FUNCTION)) return true; + if (jj_3R_92()) return true; + return false; + } + + private boolean jj_3R_60() + { + if (jj_3R_92()) return true; + if (jj_scan_token(LAMBDA)) return true; + return false; + } + + private boolean jj_3_2() + { + if (jj_3R_26()) return true; + return false; + } + + private boolean jj_3R_61() + { + if (jj_3R_93()) return true; + if (jj_scan_token(LAMBDA)) return true; + return false; + } + + private boolean jj_3R_116() + { + Token xsp; + xsp = jj_scanpos; + if (jj_scan_token(15)) jj_scanpos = xsp; + if (jj_3R_93()) return true; + while (true) { + xsp = jj_scanpos; + if (jj_3R_140()) { jj_scanpos = xsp; break; } + } + return false; + } + + private boolean jj_3R_195() + { + if (jj_scan_token(COMMA)) return true; + if (jj_3R_26()) return true; + return false; + } + + private boolean jj_3R_107() + { + Token xsp; + xsp = jj_scanpos; + if (jj_3R_125()) { + jj_scanpos = xsp; + if (jj_3R_126()) { + jj_scanpos = xsp; + if (jj_3R_127()) return true; + } + } + return false; + } + + private boolean jj_3R_125() + { + if (jj_scan_token(FUNCTION)) return true; + if (jj_3R_92()) return true; + if (jj_3R_69()) return true; + return false; + } + + private boolean jj_3R_119() + { + if (jj_scan_token(ANNOTATION)) return true; + Token xsp; + xsp = jj_scanpos; + if (jj_3R_225()) jj_scanpos = xsp; + return false; + } + + private boolean jj_3R_126() + { + if (jj_3R_92()) return true; + if (jj_scan_token(LAMBDA)) return true; + if (jj_3R_69()) return true; + return false; + } + + private boolean jj_3R_127() + { + if (jj_3R_93()) return true; + if (jj_scan_token(LAMBDA)) return true; + if (jj_3R_69()) return true; + return false; + } + + private boolean jj_3R_67() + { + Token xsp; + if (jj_3R_98()) return true; + while (true) { + xsp = jj_scanpos; + if (jj_3R_98()) { jj_scanpos = xsp; break; } + } + xsp = jj_scanpos; + if (jj_3R_200()) { + jj_scanpos = xsp; + if (jj_3_5()) return true; + } + return false; + } + + private boolean jj_3R_98() + { + if (jj_3R_119()) return true; + return false; + } + + private boolean jj_3R_25() + { + Token xsp; + xsp = jj_scanpos; + if (jj_scan_token(33)) { + jj_scanpos = xsp; + if (jj_3R_44()) { + jj_scanpos = xsp; + if (jj_3R_45()) { + jj_scanpos = xsp; + if (jj_3R_46()) { + jj_scanpos = xsp; + if (jj_3R_47()) { + jj_scanpos = xsp; + if (jj_3R_48()) { + jj_scanpos = xsp; + if (jj_3R_49()) { + jj_scanpos = xsp; + if (jj_3R_50()) { + jj_scanpos = xsp; + if (jj_3R_51()) { + jj_scanpos = xsp; + if (jj_3R_52()) { + jj_scanpos = xsp; + if (jj_3R_53()) { + jj_scanpos = xsp; + if (jj_3R_54()) { + jj_scanpos = xsp; + if (jj_3R_55()) return true; + } + } + } + } + } + } + } + } + } + } + } + } + return false; + } + + private boolean jj_3R_44() + { + if (jj_3R_67()) return true; + return false; + } + + private boolean jj_3R_45() + { + if (jj_3R_68()) return true; + return false; + } + + private boolean jj_3R_46() + { + if (jj_3R_69()) return true; + return false; + } + + private boolean jj_3R_140() + { + if (jj_scan_token(COMMA)) return true; + Token xsp; + xsp = jj_scanpos; + if (jj_scan_token(15)) jj_scanpos = xsp; + if (jj_3R_93()) return true; + return false; + } + + private boolean jj_3_4() + { + if (jj_scan_token(ANNOTATION)) return true; + return false; + } + + private boolean jj_3R_47() + { + if (jj_3R_70()) return true; + return false; + } + + private boolean jj_3R_225() + { + if (jj_3R_97()) return true; + return false; + } + + private boolean jj_3R_48() + { + if (jj_3R_71()) return true; + return false; + } + + private boolean jj_3R_166() + { + Token xsp; + xsp = jj_scanpos; + if (jj_scan_token(89)) { + jj_scanpos = xsp; + if (jj_scan_token(9)) { + jj_scanpos = xsp; + if (jj_scan_token(10)) { + jj_scanpos = xsp; + if (jj_scan_token(11)) { + jj_scanpos = xsp; + if (jj_scan_token(12)) { + jj_scanpos = xsp; + if (jj_scan_token(13)) { + jj_scanpos = xsp; + if (jj_scan_token(14)) { + jj_scanpos = xsp; + if (jj_scan_token(16)) { + jj_scanpos = xsp; + if (jj_scan_token(17)) { + jj_scanpos = xsp; + if (jj_scan_token(19)) { + jj_scanpos = xsp; + if (jj_scan_token(20)) { + jj_scanpos = xsp; + if (jj_scan_token(18)) { + jj_scanpos = xsp; + if (jj_scan_token(45)) { + jj_scanpos = xsp; + if (jj_scan_token(43)) { + jj_scanpos = xsp; + if (jj_scan_token(81)) { + jj_scanpos = xsp; + if (jj_scan_token(49)) { + jj_scanpos = xsp; + if (jj_scan_token(47)) { + jj_scanpos = xsp; + if (jj_scan_token(51)) { + jj_scanpos = xsp; + if (jj_scan_token(53)) { + jj_scanpos = xsp; + if (jj_scan_token(55)) { + jj_scanpos = xsp; + if (jj_scan_token(57)) { + jj_scanpos = xsp; + if (jj_scan_token(15)) { + jj_scanpos = xsp; + if (jj_scan_token(22)) return true; + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + return false; + } + + private boolean jj_3R_49() + { + if (jj_3R_72()) return true; + return false; + } + + private boolean jj_3R_50() + { + if (jj_3R_73()) return true; + return false; + } + + private boolean jj_3R_51() + { + if (jj_3R_74()) return true; + return false; + } + + private boolean jj_3R_52() + { + if (jj_3R_75()) return true; + return false; + } + + private boolean jj_3R_53() + { + if (jj_3R_76()) return true; + return false; + } + + private boolean jj_3_6() + { + if (jj_scan_token(ANNOTATION)) return true; + return false; + } + + private boolean jj_3R_54() + { + if (jj_3R_77()) return true; + return false; + } + + private boolean jj_3_7() + { + if (jj_3R_26()) return true; + return false; + } + + private boolean jj_3R_55() + { + if (jj_3R_78()) return true; + return false; + } + + private boolean jj_3_3() + { + if (jj_scan_token(LPAREN)) return true; + return false; + } + + private boolean jj_3R_69() + { + if (jj_scan_token(LCURLY)) return true; + Token xsp; + while (true) { + xsp = jj_scanpos; + if (jj_3_8()) { jj_scanpos = xsp; break; } + } + if (jj_scan_token(RCURLY)) return true; + return false; + } + + private boolean jj_3R_118() + { + Token xsp; + xsp = jj_scanpos; + if (jj_3R_142()) { + jj_scanpos = xsp; + if (jj_3R_143()) return true; + } + return false; + } + + private boolean jj_3R_142() + { + if (jj_scan_token(DOT)) return true; + Token xsp; + xsp = jj_scanpos; + if (jj_3R_156()) { + jj_scanpos = xsp; + if (jj_3R_157()) { + jj_scanpos = xsp; + if (jj_3R_158()) return true; + } + } + return false; + } + + private boolean jj_3R_68() + { + if (jj_3R_26()) return true; + Token xsp; + while (true) { + xsp = jj_scanpos; + if (jj_3_9()) { jj_scanpos = xsp; break; } + } + xsp = jj_scanpos; + if (jj_scan_token(33)) jj_scanpos = xsp; + return false; + } + + private boolean jj_3R_156() + { + if (jj_3R_166()) return true; + return false; + } + + private boolean jj_3R_157() + { + if (jj_scan_token(STRING_LITERAL)) return true; + return false; + } + + private boolean jj_3R_143() + { + if (jj_scan_token(QDOT)) return true; + Token xsp; + xsp = jj_scanpos; + if (jj_3R_159()) { + jj_scanpos = xsp; + if (jj_3R_160()) { + jj_scanpos = xsp; + if (jj_3R_161()) return true; + } + } + return false; + } + + private boolean jj_3R_158() + { + if (jj_scan_token(JXLT_LITERAL)) return true; + return false; + } + + private boolean jj_3R_70() + { + if (jj_scan_token(IF)) return true; + if (jj_scan_token(LPAREN)) return true; + if (jj_3R_26()) return true; + if (jj_scan_token(RPAREN)) return true; + Token xsp; + xsp = jj_scanpos; + if (jj_3R_201()) { + jj_scanpos = xsp; + if (jj_3_10()) return true; + } + while (true) { + xsp = jj_scanpos; + if (jj_3_11()) { jj_scanpos = xsp; break; } + } + xsp = jj_scanpos; + if (jj_3R_202()) jj_scanpos = xsp; + return false; + } + + private boolean jj_3R_159() + { + if (jj_3R_166()) return true; + return false; + } + + private boolean jj_3_11() + { + if (jj_scan_token(ELSE)) return true; + if (jj_scan_token(IF)) return true; + if (jj_scan_token(LPAREN)) return true; + if (jj_3R_26()) return true; + if (jj_scan_token(RPAREN)) return true; + Token xsp; + xsp = jj_scanpos; + if (jj_3R_211()) { + jj_scanpos = xsp; + if (jj_3_12()) return true; + } + return false; + } + + private boolean jj_3R_160() + { + if (jj_scan_token(STRING_LITERAL)) return true; + return false; + } + + private boolean jj_3R_202() + { + if (jj_scan_token(ELSE)) return true; + Token xsp; + xsp = jj_scanpos; + if (jj_3R_212()) { + jj_scanpos = xsp; + if (jj_3_13()) return true; + } + return false; + } + + private boolean jj_3R_161() + { + if (jj_scan_token(JXLT_LITERAL)) return true; + return false; + } + + private boolean jj_3R_72() + { + if (jj_scan_token(WHILE)) return true; + if (jj_scan_token(LPAREN)) return true; + if (jj_3R_26()) return true; + if (jj_scan_token(RPAREN)) return true; + Token xsp; + xsp = jj_scanpos; + if (jj_3R_205()) { + jj_scanpos = xsp; + if (jj_3_14()) return true; + } + return false; + } + + private boolean jj_3R_200() + { + if (jj_3R_69()) return true; + return false; + } + + private boolean jj_3_9() + { + if (jj_3R_26()) return true; + return false; + } + + private boolean jj_3R_117() + { + Token xsp; + if (jj_3R_141()) return true; + while (true) { + xsp = jj_scanpos; + if (jj_3R_141()) { jj_scanpos = xsp; break; } + } + return false; + } + + private boolean jj_3R_141() + { + if (jj_scan_token(LBRACKET)) return true; + if (jj_3R_26()) return true; + if (jj_scan_token(RBRACKET)) return true; + return false; + } + + private boolean jj_3R_73() + { + if (jj_scan_token(DO)) return true; + Token xsp; + xsp = jj_scanpos; + if (jj_3R_206()) { + jj_scanpos = xsp; + if (jj_3_15()) return true; + } + if (jj_scan_token(WHILE)) return true; + if (jj_scan_token(LPAREN)) return true; + if (jj_3R_26()) return true; + if (jj_scan_token(RPAREN)) return true; + return false; + } + + private boolean jj_3R_63() + { + Token xsp; + xsp = jj_scanpos; + if (jj_3R_94()) { + jj_scanpos = xsp; + if (jj_3R_95()) { + jj_scanpos = xsp; + if (jj_3R_96()) return true; + } + } + return false; + } + + private boolean jj_3R_94() + { + if (jj_3R_117()) return true; + return false; + } + + private boolean jj_3R_95() + { + if (jj_3R_118()) return true; + return false; + } + + private boolean jj_3R_74() + { + if (jj_scan_token(RETURN)) return true; + if (jj_3R_68()) return true; + return false; + } + + private boolean jj_3R_96() + { + if (jj_3R_118()) return true; + return false; + } + + private boolean jj_3_8() + { + if (jj_3R_25()) return true; + return false; + } + + private boolean jj_3R_108() + { + if (jj_scan_token(LPAREN)) return true; + if (jj_3R_26()) return true; + if (jj_scan_token(RPAREN)) return true; + Token xsp; + while (true) { + xsp = jj_scanpos; + if (jj_3R_192()) { jj_scanpos = xsp; break; } + } + return false; + } + + private boolean jj_3_31() + { + if (jj_scan_token(LBRACKET)) return true; + return false; + } + + private boolean jj_3R_75() + { + if (jj_scan_token(CONTINUE)) return true; + return false; + } + + private boolean jj_3_32() + { + if (jj_scan_token(DOT)) return true; + return false; + } + + private boolean jj_3R_212() + { + if (jj_3R_69()) return true; + return false; + } + + private boolean jj_3_33() + { + if (jj_scan_token(QDOT)) return true; + return false; + } + + private boolean jj_3_5() + { + if (jj_3R_25()) return true; + return false; + } + + private boolean jj_3R_58() + { + Token xsp; + xsp = jj_scanpos; + if (jj_3R_81()) { + jj_scanpos = xsp; + if (jj_3R_82()) { + jj_scanpos = xsp; + if (jj_3R_83()) { + jj_scanpos = xsp; + if (jj_3R_84()) { + jj_scanpos = xsp; + if (jj_3R_85()) { + jj_scanpos = xsp; + if (jj_3R_86()) { + jj_scanpos = xsp; + if (jj_3R_87()) { + jj_scanpos = xsp; + if (jj_3R_88()) { + jj_scanpos = xsp; + if (jj_3R_89()) { + jj_scanpos = xsp; + if (jj_3R_90()) { + jj_scanpos = xsp; + if (jj_3R_91()) return true; + } + } + } + } + } + } + } + } + } + } + return false; + } + + private boolean jj_3R_81() + { + if (jj_3R_107()) return true; + return false; + } + + private boolean jj_3R_76() + { + if (jj_scan_token(BREAK)) return true; + return false; + } + + private boolean jj_3R_82() + { + if (jj_3R_108()) return true; + return false; + } + + private boolean jj_3R_83() + { + if (jj_3R_109()) return true; + return false; + } + + private boolean jj_3R_71() + { + if (jj_scan_token(FOR)) return true; + if (jj_scan_token(LPAREN)) return true; + if (jj_3R_203()) return true; + if (jj_scan_token(COLON)) return true; + if (jj_3R_26()) return true; + if (jj_scan_token(RPAREN)) return true; + Token xsp; + xsp = jj_scanpos; + if (jj_3R_204()) { + jj_scanpos = xsp; + if (jj_3_16()) return true; + } + return false; + } + + private boolean jj_3R_84() + { + if (jj_3R_109()) return true; + return false; + } + + private boolean jj_3R_85() + { + if (jj_3R_110()) return true; + return false; + } + + private boolean jj_3R_201() + { + if (jj_3R_69()) return true; + return false; + } + + private boolean jj_3R_206() + { + if (jj_3R_69()) return true; + return false; + } + + private boolean jj_3R_86() + { + if (jj_3R_110()) return true; + return false; + } + + private boolean jj_3_35() + { + if (jj_3R_40()) return true; + return false; + } + + private boolean jj_3R_87() + { + if (jj_3R_111()) return true; + return false; + } + + private boolean jj_3_36() + { + if (jj_scan_token(LPAREN)) return true; + return false; + } + + private boolean jj_3R_88() + { + if (jj_3R_112()) return true; + return false; + } + + private boolean jj_3R_204() + { + if (jj_3R_69()) return true; + return false; + } + + private boolean jj_3_37() + { + if (jj_scan_token(LCURLY)) return true; + if (jj_3R_26()) return true; + if (jj_scan_token(COLON)) return true; + return false; + } + + private boolean jj_3R_203() + { + Token xsp; + xsp = jj_scanpos; + if (jj_3R_213()) { + jj_scanpos = xsp; + if (jj_3R_214()) return true; + } + return false; + } + + private boolean jj_3R_213() + { + if (jj_scan_token(VAR)) return true; + if (jj_3R_207()) return true; + return false; + } + + private boolean jj_3R_89() + { + if (jj_3R_113()) return true; + return false; + } + + private boolean jj_3_38() + { + if (jj_scan_token(LCURLY)) return true; + if (jj_scan_token(COLON)) return true; + return false; + } + + private boolean jj_3R_214() + { + if (jj_3R_114()) return true; + return false; + } + + private boolean jj_3R_90() + { + if (jj_3R_114()) return true; + return false; + } + + private boolean jj_3_13() + { + if (jj_3R_25()) return true; + return false; + } + + private boolean jj_3_39() + { + if (jj_scan_token(LCURLY)) return true; + if (jj_3R_26()) return true; + Token xsp; + xsp = jj_scanpos; + if (jj_scan_token(35)) { + jj_scanpos = xsp; + if (jj_scan_token(30)) return true; + } + return false; + } + + private boolean jj_3R_91() + { + if (jj_3R_115()) return true; + return false; + } + + private boolean jj_3_40() + { + if (jj_scan_token(LCURLY)) return true; + if (jj_scan_token(RCURLY)) return true; + return false; + } + + private boolean jj_3R_42() + { + if (jj_3R_63()) return true; + Token xsp; + if (jj_3R_64()) return true; + while (true) { + xsp = jj_scanpos; + if (jj_3R_64()) { jj_scanpos = xsp; break; } + } + return false; + } + + private boolean jj_3R_77() + { + if (jj_scan_token(VAR)) return true; + if (jj_3R_207()) return true; + Token xsp; + xsp = jj_scanpos; + if (jj_3R_208()) jj_scanpos = xsp; + return false; + } + + private boolean jj_3_41() + { + if (jj_scan_token(LBRACKET)) return true; + return false; + } + + private boolean jj_3_42() + { + if (jj_scan_token(NEW)) return true; + return false; + } + + private boolean jj_3_43() + { + if (jj_3R_41()) return true; + return false; + } + + private boolean jj_3R_43() + { + Token xsp; + xsp = jj_scanpos; + if (jj_3R_65()) { + jj_scanpos = xsp; + if (jj_3R_66()) return true; + } + return false; + } + + private boolean jj_3R_65() + { + if (jj_3R_42()) return true; + return false; + } + + private boolean jj_3R_207() + { + if (jj_scan_token(IDENTIFIER)) return true; + return false; + } + + private boolean jj_3R_211() + { + if (jj_3R_69()) return true; + return false; + } + + private boolean jj_3_10() + { + if (jj_3R_25()) return true; + return false; + } + + private boolean jj_3_15() + { + if (jj_3R_25()) return true; + return false; + } + + private boolean jj_3R_38() + { + if (jj_3R_58()) return true; + Token xsp; + while (true) { + xsp = jj_scanpos; + if (jj_3_46()) { jj_scanpos = xsp; break; } + } + return false; + } + + private boolean jj_3R_78() + { + if (jj_scan_token(PRAGMA)) return true; + if (jj_3R_209()) return true; + if (jj_3R_210()) return true; + return false; + } + + private boolean jj_3_16() + { + if (jj_3R_25()) return true; + return false; + } + + private boolean jj_3_45() + { + if (jj_3R_42()) return true; + return false; + } + + private boolean jj_3R_64() + { + if (jj_3R_97()) return true; + return false; + } + + private boolean jj_3R_205() + { + if (jj_3R_69()) return true; + return false; + } + + private boolean jj_3R_208() + { + if (jj_scan_token(assign)) return true; + if (jj_3R_26()) return true; + return false; + } + + private boolean jj_3R_209() + { + Token xsp; + xsp = jj_scanpos; + if (jj_3R_215()) { + jj_scanpos = xsp; + if (jj_3R_216()) return true; + } + return false; + } + + private boolean jj_3R_215() + { + if (jj_scan_token(IDENTIFIER)) return true; + Token xsp; + while (true) { + xsp = jj_scanpos; + if (jj_3R_226()) { jj_scanpos = xsp; break; } + } + return false; + } + + private boolean jj_3_44() + { + if (jj_scan_token(LPAREN)) return true; + return false; + } + + private boolean jj_3R_192() + { + if (jj_3R_97()) return true; + return false; + } + + private boolean jj_3R_216() + { + if (jj_scan_token(DOT)) return true; + if (jj_scan_token(DOT_IDENTIFIER)) return true; + return false; + } + + private boolean jj_3_12() + { + if (jj_3R_25()) return true; + return false; + } + + private boolean jj_3_46() + { + if (jj_3R_43()) return true; + return false; + } + + private boolean jj_3R_210() + { + Token xsp; + xsp = jj_scanpos; + if (jj_3R_217()) { + jj_scanpos = xsp; + if (jj_3R_218()) { + jj_scanpos = xsp; + if (jj_3R_219()) { + jj_scanpos = xsp; + if (jj_3R_220()) { + jj_scanpos = xsp; + if (jj_3R_221()) { + jj_scanpos = xsp; + if (jj_3R_222()) { + jj_scanpos = xsp; + if (jj_3R_223()) { + jj_scanpos = xsp; + if (jj_3R_224()) return true; + } + } + } + } + } + } + } + return false; + } + + private boolean jj_3_34() + { + if (jj_scan_token(LPAREN)) return true; + return false; + } + + private boolean jj_3_14() + { + if (jj_3R_25()) return true; + return false; + } + + private boolean jj_3R_217() + { + if (jj_scan_token(INTEGER_LITERAL)) return true; + return false; + } + + private boolean jj_3R_218() + { + if (jj_scan_token(FLOAT_LITERAL)) return true; + return false; + } + + private boolean jj_3R_219() + { + if (jj_scan_token(STRING_LITERAL)) return true; + return false; + } + + private boolean jj_3R_220() + { + if (jj_3R_209()) return true; + return false; + } + + private boolean jj_3R_221() + { + if (jj_scan_token(TRUE)) return true; + return false; + } + + private boolean jj_3R_66() + { + if (jj_3R_63()) return true; + return false; + } + + private boolean jj_3R_222() + { + if (jj_scan_token(FALSE)) return true; + return false; + } + + private boolean jj_3R_223() + { + if (jj_scan_token(NULL)) return true; + return false; + } + + private boolean jj_3R_224() + { + if (jj_scan_token(NAN_LITERAL)) return true; + return false; + } + + private boolean jj_3R_26() + { + if (jj_3R_56()) return true; + return false; + } + + private boolean jj_3R_56() + { + if (jj_3R_79()) return true; + Token xsp; + while (true) { + xsp = jj_scanpos; + if (jj_3_18()) { jj_scanpos = xsp; break; } + } + return false; + } + + private boolean jj_3_18() + { + Token xsp; + xsp = jj_scanpos; + if (jj_3R_27()) { + jj_scanpos = xsp; + if (jj_3R_28()) { + jj_scanpos = xsp; + if (jj_3R_29()) { + jj_scanpos = xsp; + if (jj_3R_30()) { + jj_scanpos = xsp; + if (jj_3R_31()) { + jj_scanpos = xsp; + if (jj_3R_32()) { + jj_scanpos = xsp; + if (jj_3R_33()) { + jj_scanpos = xsp; + if (jj_3R_34()) { + jj_scanpos = xsp; + if (jj_3R_35()) return true; + } + } + } + } + } + } + } + } + return false; + } + + private boolean jj_3R_27() + { + if (jj_scan_token(plus_assign)) return true; + if (jj_3R_26()) return true; + return false; + } + + private boolean jj_3R_226() + { + if (jj_3R_209()) return true; + return false; + } + + private boolean jj_3R_28() + { + if (jj_scan_token(mult_assign)) return true; + if (jj_3R_26()) return true; + return false; + } + + private boolean jj_3R_29() + { + if (jj_scan_token(div_assign)) return true; + if (jj_3R_26()) return true; + return false; + } + + private boolean jj_3R_30() + { + if (jj_scan_token(mod_assign)) return true; + if (jj_3R_26()) return true; + return false; + } + + private boolean jj_3R_31() + { + if (jj_scan_token(and_assign)) return true; + if (jj_3R_26()) return true; + return false; + } + + private boolean jj_3_17() + { + if (jj_scan_token(DOT)) return true; + return false; + } + + private boolean jj_3R_32() + { + if (jj_scan_token(or_assign)) return true; + if (jj_3R_26()) return true; + return false; + } + + private boolean jj_3R_33() + { + if (jj_scan_token(xor_assign)) return true; + if (jj_3R_26()) return true; + return false; + } + + private boolean jj_3R_34() + { + if (jj_scan_token(minus_assign)) return true; + if (jj_3R_26()) return true; + return false; + } + + private boolean jj_3R_35() + { + if (jj_scan_token(assign)) return true; + if (jj_3R_26()) return true; + return false; + } + + /** Generated Token Manager. */ + public ParserTokenManager token_source; + SimpleCharStream jj_input_stream; + /** Current token. */ + public Token token; + /** Next token. */ + public Token jj_nt; + private Token jj_scanpos, jj_lastpos; + private int jj_la; + /** Whether we are looking ahead. */ + private boolean jj_lookingAhead = false; + private boolean jj_semLA; + + /** + * Constructor with InputStream. + * @param stream char stream + */ + public Parser(final Provider stream) { + jj_input_stream = new SimpleCharStream(stream, 1, 1); + token_source = new ParserTokenManager(jj_input_stream); + token = new Token(); + token.next = jj_nt = token_source.getNextToken(); + } + + /** + * Constructor with InputStream. + * @param sDSL String representation to be parsed + */ + public Parser(final String sDSL) { + this(new StringProvider(sDSL)); + } + + /** + * Reinitialise. + * @param sDSL String representation to be parsed + */ + public void ReInit(final String sDSL) { + ReInit(new StringProvider(sDSL)); + } + /** + * Reinitialise + * @param stream char stream + */ + public void ReInit(final Provider stream) { + if (jj_input_stream == null) { + jj_input_stream = new SimpleCharStream(stream, 1, 1); + } else { + jj_input_stream.reInit(stream, 1, 1); + } + if (token_source == null) { + token_source = new ParserTokenManager(jj_input_stream); + } + + token_source.ReInit(jj_input_stream); + token = new Token(); + token.next = jj_nt = token_source.getNextToken(); + } + + /** + * Constructor with generated Token Manager. + * @param tm Token manager to use + */ + public Parser(final ParserTokenManager tm) { + token_source = tm; + token = new Token(); + token.next = jj_nt = token_source.getNextToken(); + } + + /** + * Reinitialise + * @param tm Token manager to use + */ + public void ReInit(final ParserTokenManager tm) { + token_source = tm; + token = new Token(); + token.next = jj_nt = token_source.getNextToken(); + } + + private Token jj_consume_token(final int kind) throws ParseException { + final Token oldToken = token; + token = jj_nt; + if (token.next != null) + jj_nt = jj_nt.next; + else { + jj_nt.next = token_source.getNextToken(); + jj_nt = jj_nt.next; + } + if (token.kind == kind) { + return token; + } + jj_nt = token; + token = oldToken; + throw generateParseException(); + } + + private static final class LookaheadSuccess extends IllegalStateException {} + private final LookaheadSuccess jj_ls = new LookaheadSuccess(); + private boolean jj_scan_token(int kind) { + if (jj_scanpos == jj_lastpos) { + jj_la--; + if (jj_scanpos.next == null) { + jj_lastpos = jj_scanpos = jj_scanpos.next = token_source.getNextToken(); + } else { + jj_lastpos = jj_scanpos = jj_scanpos.next; + } + } else { + jj_scanpos = jj_scanpos.next; + } + if (jj_scanpos.kind != kind) return true; + if (jj_la == 0 && jj_scanpos == jj_lastpos) throw jj_ls; + return false; + } + + + /** + * @return the next Token. + */ + public final Token getNextToken() { + token = jj_nt; + if (token.next != null) + jj_nt = jj_nt.next; + else + jj_nt = jj_nt.next = token_source.getNextToken(); + return token; + } + + /** + * @param index index to be retrieved + * @return the specific Token. + */ + public final Token getToken(final int index) { + Token t = jj_lookingAhead ? jj_scanpos : token; + for (int i = 0; i < index; i++) { + if (t.next == null) + t.next = token_source.getNextToken(); + t = t.next; + } + return t; + } + + /** Generate ParseException. */ + public ParseException generateParseException() { + final Token errortok = token.next; + final int line = errortok.beginLine; + final int column = errortok.beginColumn; + final String mess = errortok.kind == 0 ? tokenImage[0] : errortok.image; + return new ParseException("Parse error at line " + line + ", column " + column + ". Encountered: " + mess); + } + + /** + * @return Always false. + */ + public final boolean trace_enabled() { + return false; + } + + /** Enable tracing. */ + public final void enable_tracing() {} + + /** Disable tracing. */ + public final void disable_tracing() {} + +} diff --git a/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/Parser.jjt b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/Parser.jjt new file mode 100644 index 0000000..a33f7a9 --- /dev/null +++ b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/Parser.jjt @@ -0,0 +1,949 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +options +{ + MULTI=true; + STATIC=false; + JAVA_TEMPLATE_TYPE="modern"; + VISITOR=true; + NODE_SCOPE_HOOK=true; + NODE_CLASS="JexlNode"; + UNICODE_INPUT=true; + KEEP_LINE_COLUMN=true; + TRACK_TOKENS=true; + CACHE_TOKENS=true; + ERROR_REPORTING=false; + // DEBUG_PARSER=true; + //DEBUG_TOKEN_MANAGER=true; +} + +PARSER_BEGIN(Parser) + +package org.apache.commons.jexl3.parser; + +import java.util.Collections; +import java.util.LinkedList; + +import org.apache.commons.jexl3.JexlInfo; +import org.apache.commons.jexl3.JexlFeatures; +import org.apache.commons.jexl3.JexlException; +import org.apache.commons.jexl3.internal.Scope; + +public final class Parser extends JexlParser +{ + public ASTJexlScript parse(JexlInfo jexlInfo, JexlFeatures jexlFeatures, String jexlSrc, Scope scope) { + JexlFeatures previous = getFeatures(); + try { + setFeatures(jexlFeatures); + // If registers are allowed, the default parser state has to be REGISTERS. + if (jexlFeatures.supportsRegister()) { + token_source.defaultLexState = REGISTERS; + } + // lets do the 'Unique Init' in here to be safe - it's a pain to remember + info = jexlInfo != null? jexlInfo : new JexlInfo(); + source = jexlSrc; + pragmas = null; + frame = scope; + ReInit(jexlSrc); + ASTJexlScript script = jexlFeatures.supportsScript()? JexlScript(scope) : JexlExpression(scope); + script.jjtSetValue(info.detach()); + script.setFeatures(jexlFeatures); + script.setPragmas(pragmas != null + ? Collections.unmodifiableMap(pragmas) + : Collections.emptyMap()); + return script; + } catch (TokenMgrException xtme) { + throw new JexlException.Tokenization(info, xtme).clean(); + } catch (ParseException xparse) { + Token errortok = errorToken(jj_lastpos, jj_scanpos, token.next, token); + throw new JexlException.Parsing(info.at(errortok.beginLine, errortok.beginColumn), errortok.image).clean(); + } finally { + token_source.defaultLexState = DEFAULT; + cleanup(previous); + jjtree.reset(); + } + } +} + +PARSER_END(Parser) + +TOKEN_MGR_DECLS : { + /** + * A stack of 1 for keeping state to deal with doted identifiers + */ + int dotLexState = DEFAULT; + + public void pushDot() { + dotLexState = curLexState; + curLexState = DOT_ID; + } + + public void popDot() { + if (curLexState == DOT_ID) { + curLexState = dotLexState; + dotLexState = defaultLexState; + } + } +} +/*************************************** + * Skip & Number literal tokens + ***************************************/ + +<*> SKIP : /* WHITE SPACE */ +{ + <"##" (~["\n","\r"])* ("\n" | "\r" | "\r\n")? > + | <"/*" (~["*"])* "*" ("*" | ~["*","/"] (~["*"])* "*")* "/"> + | <"//" (~["\n","\r"])* ("\n" | "\r" | "\r\n")? > + | " " + | "\t" + | "\n" + | "\r" + | "\f" +} + +<*> TOKEN : /* KEYWORDS */ +{ + < IF : "if" > { popDot(); } + | < ELSE : "else" > { popDot(); } + | < FOR : "for" > { popDot(); } + | < WHILE : "while" > { popDot(); } + | < DO : "do" > { popDot(); } + | < NEW : "new" > { popDot(); } + | < VAR : "var" > { popDot(); } + | < EMPTY : "empty" > { popDot(); } /* Revert state to default if was DOT_ID. */ + | < SIZE : "size" > { popDot(); } /* Revert state to default if was DOT_ID. */ + | < NULL : "null" > { popDot(); } + | < TRUE : "true" > { popDot(); } + | < FALSE : "false" > { popDot(); } + | < RETURN : "return" > { popDot(); } + | < FUNCTION : "function" > { popDot(); } + | < LAMBDA : "->" > + | < BREAK : "break" > { popDot(); } + | < CONTINUE : "continue" > { popDot(); } + | < PRAGMA : "#pragma" > { popDot(); } +} + +<*> TOKEN : { /* SEPARATORS */ + < LPAREN : "("> + | < RPAREN : ")"> + | < LCURLY : "{" > + | < RCURLY : "}" > + | < LBRACKET : "[" > + | < RBRACKET : "]" > + | < SEMICOL : ";" > + | < COLON : ":" > + | < COMMA : "," > + | < DOT : "." > { pushDot(); } /* Lexical state is now DOT_ID */ + | < QDOT : "?." > { pushDot(); } /* Lexical state is now DOT_ID */ + | < ELIPSIS : "..." > +} + +<*> TOKEN : { /* CONDITIONALS */ + < QMARK : "?" > + | < ELVIS : "?:" > + | < NULLP : "??" > + | < AND : "&&" > + | < _AND : "and" > { popDot(); } + | < OR : "||" > + | < _OR: "or" > { popDot(); } +} + +<*> TOKEN : { /* COMPARISONS */ + < eq : "==" > + | < EQ : "eq" > { popDot(); } + | < ne : "!=" > + | < NE : "ne" > { popDot(); } + | < gt : ">" > + | < GT : "gt" > { popDot(); } + | < ge : ">=" > + | < GE : "ge" > { popDot(); } + | < lt : "<" > + | < LT : "lt" > { popDot(); } + | < le : "<=" > + | < LE : "le" > { popDot(); } + | < req : "=~" > // regexp equal + | < rne : "!~" > // regexp not equal + | < seq : "=^" > // starts equal + | < eeq : "=$" > // ends equal + | < sne : "!^" > // start not equal + | < ene : "!$" > // ends not equal +} + +<*> TOKEN : { /* OPERATORS */ + < plus_assign : "+=" > + | < minus_assign : "-=" > + | < mult_assign : "*=" > + | < div_assign : "/=" > + | < mod_assign : "%=" > + | < and_assign : "&=" > + | < or_assign : "|=" > + | < xor_assign : "^=" > + + | < assign : "=" > + | < plus : "+" > + | < minus : "-" > + | < mult : "*" > + | < div : "/" > + | < DIV : "div" > { popDot(); } + | < mod : "%" > + | < MOD : "mod" > { popDot(); } + | < not : "!" > + | < NOT : "not" > { popDot(); } + | < and : "&" > + | < or : "|" > + | < xor : "^" > + + | < tilda : "~" > + | < range : ".." > +} + +/*************************************** + * Identifier & String tokens + ***************************************/ +<*> TOKEN : /* NaN */ +{ + < NAN_LITERAL : "NaN" > +} + +<*> TOKEN : /* ANNOTATION */ +{ + < ANNOTATION: "@" ( [ "0"-"9", "a"-"z", "A"-"Z", "_", "$" ])+ > +} + + TOKEN : /* IDENTIFIERS */ +{ + < DOT_IDENTIFIER: ( [ "0"-"9", "a"-"z", "A"-"Z", "_", "$", "@" ])+ > { popDot(); } /* Revert state to default. */ +} + + TOKEN : /* IDENTIFIERS */ +{ + < IDENTIFIER: (||)* > { matchedToken.image = StringParser.unescapeIdentifier(matchedToken.image); } +| + < #LETTER: [ "a"-"z", "A"-"Z", "_", "$", "@" ] > +| + < #DIGIT: [ "0"-"9"] > +| + < #ESCAPE: "\\" [" ", "'", "\"", "\\"] > +} + + TOKEN : /* REGISTERS: parser.ALLOW_REGISTER must be set to true before calling parse */ +{ + < REGISTER: "#" (["0"-"9"])+ > +} + + TOKEN : /* LITERALS */ +{ + ()? + | ()? + | ()? + > + | <#DECIMAL_LITERAL: ["1"-"9"] (["0"-"9"])*> + | <#HEX_LITERAL: "0" ["x","X"] (["0"-"9","a"-"f","A"-"F"])+> + | <#OCTAL_LITERAL: "0" (["0"-"7"])*> + | <#INT_SFX : ["l","L","h","H"]> +| +)? + | (["0"-"9"])+ (".")? () + | "." (["0"-"9"])+ () + | "#NaN" +> + | <#EXPONENT: ["e","E"] (["+","-"])? (["0"-"9"])+> + | <#FLT_CLS : ["f","F","d","D","b","B"]> + | <#FLT_SFX : ()? | > +} + +<*> TOKEN : +{ + < STRING_LITERAL: + "\"" (~["\"","\\","\n","\r","\u2028","\u2029"] | "\\" ~["\n","\r","\u2028","\u2029"])* "\"" + | + "'" (~["'","\\","\n","\r","\u2028","\u2029"] | "\\" ~["\n","\r","\u2028","\u2029"])* "'" + > { popDot(); } /* Revert state to default if was DOT_ID. */ +} + +<*> TOKEN : +{ + < JXLT_LITERAL: + "`" (~["`","\\"] | "\\" ~["\u0000"])* "`" + > { popDot(); } /* Revert state to default if was DOT_ID. */ +} + +<*> TOKEN : +{ + < REGEX_LITERAL: + "~" "/" (~["/","\n","\r","\u2028","\u2029"] | "\\" "/" )* "/" + > { popDot(); } /* Revert state to default if was DOT_ID. */ +} + +/*************************************** + * Statements + ***************************************/ + +ASTJexlScript JexlScript(Scope frame) : { + jjtThis.setScope(frame); +} +{ + { + pushUnit(jjtThis); + } + (Statement())* + { + popUnit(jjtThis); + return jjtThis.script(); + } +} + +ASTJexlScript JexlExpression(Scope frame) #JexlScript : { + jjtThis.setScope(frame); +} +{ + { + pushUnit(jjtThis); + } + ( Expression() )? + { + popUnit(jjtThis); + return jjtThis.script(); + } +} + +void Annotation() #Annotation : +{ + Token t; +} +{ + t= (LOOKAHEAD() Arguments() )? { jjtThis.setName(t.image); } +} + +void AnnotatedStatement() #AnnotatedStatement : {} + { + (LOOKAHEAD() Annotation())+ (LOOKAHEAD(1) Block() | Statement()) + } + +void Statement() #void : {} +{ + + | LOOKAHEAD() AnnotatedStatement() + | LOOKAHEAD(Expression()) ExpressionStatement() + | Block() + | IfStatement() + | ForeachStatement() + | WhileStatement() + | DoWhileStatement() + | ReturnStatement() + | Continue() + | Break() + | Var() + | Pragma() +} + +void Block() #Block : {} +{ + { pushUnit(jjtThis); } ( Statement() )* { popUnit(jjtThis); } +} + + +void ExpressionStatement() #void : {} +{ + Expression() (LOOKAHEAD(1) Expression() #Ambiguous(1))* (LOOKAHEAD(1) )? +} + + +void IfStatement() : {} +{ + Expression() (LOOKAHEAD(1) Block() | Statement()) + ( LOOKAHEAD(2) Expression() (LOOKAHEAD(1) Block() | Statement()) )* + ( LOOKAHEAD(1) (LOOKAHEAD(1) Block() | Statement()) )? +} + +void WhileStatement() : {} +{ + Expression() { loopCount += 1; } (LOOKAHEAD(1) Block() | Statement()) { loopCount -= 1; } +} + +void DoWhileStatement() : {} +{ + { loopCount += 1; } (LOOKAHEAD(1) Block() | Statement()) Expression() { loopCount -= 1; } +} + +void ReturnStatement() : {} +{ + ExpressionStatement() +} + +void Continue() #Continue : { + Token t; +} +{ + t= { if (loopCount == 0) { throwParsingException(null, t); } } +} + +void Break() #Break : { + Token t; +} +{ + t= { if (loopCount == 0) { throwParsingException(null, t); } } +} + +void ForeachStatement() : {} +{ + { pushUnit(jjtThis); } + ForEachVar() Expression() + { loopCount += 1; } + (LOOKAHEAD(1) Block() | Statement()) + { loopCount -= 1; popUnit(jjtThis); } +} + +void ForEachVar() #Reference : {} +{ + DeclareVar() +| + Identifier(true) +} + +void Var() #void : {} +{ + DeclareVar() (LOOKAHEAD(1) Expression() #Assignment(2))? +} + +void DeclareVar() #Var : +{ + Token t; +} +{ + t= { declareVariable(jjtThis, t); } +} + +void Pragma() #void : +{ + LinkedList lstr = new LinkedList(); + Object value; +} +{ + pragmaKey(lstr) value=pragmaValue() { declarePragma(stringify(lstr), value); } +} + +void pragmaKey(LinkedList lstr) #void : +{ + Token t; +} +{ + t= { lstr.add(t.image); } ( LOOKAHEAD() pragmaKey(lstr) )* + | + t= { lstr.add(t.image); } +} + +Object pragmaValue() #void : +{ +Token v; +LinkedList lstr = new LinkedList(); +Object result; +} +{ + ( + LOOKAHEAD(1) v= { result = NumberParser.parseInteger(v.image); } + | LOOKAHEAD(1) v= { result = NumberParser.parseDouble(v.image); } + | LOOKAHEAD(1) v= { result = Parser.buildString(v.image, true); } + | LOOKAHEAD(1) pragmaKey(lstr) { result = stringify(lstr); } + | LOOKAHEAD(1) { result = true; } + | LOOKAHEAD(1) { result = false; } + | LOOKAHEAD(1) { result = null; } + | LOOKAHEAD(1) { result = Double.NaN; } + ) + { + return result; + } +} + + +/*************************************** + * Expression syntax + ***************************************/ + +void Expression() #void : {} +{ + AssignmentExpression() +} + +void AssignmentExpression() #void : {} +{ + ConditionalExpression() + ( LOOKAHEAD(2) ( + Expression() #SetAddNode(2) + | + Expression() #SetMultNode(2) + | + Expression() #SetDivNode(2) + | + Expression() #SetModNode(2) + | + Expression() #SetAndNode(2) + | + Expression() #SetOrNode(2) + | + Expression() #SetXorNode(2) + | + Expression() #SetSubNode(2) + | + Expression() #Assignment(2) + ) )* +} + +/*************************************** + * Conditional & relational + ***************************************/ + +void ConditionalExpression() #void : {} +{ + ConditionalOrExpression() + ( + Expression() Expression() #TernaryNode(3) + | + Expression() #TernaryNode(2) + | + Expression() #NullpNode(2) + )? +} + +void ConditionalOrExpression() #void : {} +{ + ConditionalAndExpression() + ( (|<_OR>) ConditionalAndExpression() #OrNode(2) )* +} + +void ConditionalAndExpression() #void : {} +{ + InclusiveOrExpression() + ( (|<_AND>) InclusiveOrExpression() #AndNode(2) )* +} + +void InclusiveOrExpression() #void : {} +{ + ExclusiveOrExpression() + ( ExclusiveOrExpression() #BitwiseOrNode(2) )* +} + +void ExclusiveOrExpression() #void : {} +{ + AndExpression() + ( AndExpression() #BitwiseXorNode(2) )* +} + +void AndExpression() #void : {} +{ + EqualityExpression() + ( EqualityExpression() #BitwiseAndNode(2) )* +} + +void EqualityExpression() #void : {} +{ + RelationalExpression() + ( + ( | ) RelationalExpression() #EQNode(2) + | + ( | ) RelationalExpression() #NENode(2) + | + RelationalExpression() #RangeNode(2) // range + )? +} + +void RelationalExpression() #void : {} +{ + AdditiveExpression() + ( + ( |) AdditiveExpression() #LTNode(2) + | + ( | ) AdditiveExpression() #GTNode(2) + | + ( | ) AdditiveExpression() #LENode(2) + | + ( | ) AdditiveExpression() #GENode(2) + | + AdditiveExpression() #ERNode(2) // equals regexp + | + AdditiveExpression() #NRNode(2) // not equals regexp + | + AdditiveExpression() #SWNode(2) // starts with + | + AdditiveExpression() #NSWNode(2) // not starts with + | + AdditiveExpression() #EWNode(2) // ends with + | + AdditiveExpression() #NEWNode(2) // not ends with + )? +} + +/*************************************** + * Arithmetic + ***************************************/ + +void AdditiveExpression() #void : {} +{ + MultiplicativeExpression() + ( LOOKAHEAD(2) ( + MultiplicativeExpression() #AddNode(2) + | + MultiplicativeExpression() #SubNode(2) + ) )* +} + +void MultiplicativeExpression() #void : {} +{ + UnaryExpression() + ( + UnaryExpression() #MulNode(2) + | + (

|
) UnaryExpression() #DivNode(2) + | + (|) UnaryExpression() #ModNode(2) + )* +} + +void UnaryExpression() #void : {} +{ + UnaryExpression() #UnaryMinusNode(1) + | + UnaryExpression() #UnaryPlusNode(1) + | + UnaryExpression() #BitwiseComplNode(1) + | + (|) UnaryExpression() #NotNode(1) + | + UnaryExpression() #EmptyFunction(1) + | + UnaryExpression() #SizeFunction(1) + | + ValueExpression() +} + +/*************************************** + * Identifier & Literals + ***************************************/ + +void Identifier(boolean top) : +{ + Token t; +} +{ + t= { jjtThis.setSymbol(top? checkVariable(jjtThis, t.image) : t.image); } +| + t= { jjtThis.setSymbol(t.image); } +} + + +void NamespaceIdentifier() #NamespaceIdentifier : +{ + Token ns; + Token id; +} +{ + ns= id= { jjtThis.setNamespace(ns.image, id.image); } +} + +void Literal() #void : +{ + Token t; +} +{ + IntegerLiteral() +| + FloatLiteral() +| + BooleanLiteral() +| + JxltLiteral() +| + StringLiteral() +| + RegexLiteral() +| + NullLiteral() +| + NaNLiteral() +} + +void NaNLiteral() #NumberLiteral : +{} +{ + { jjtThis.setReal("NaN"); } +} + +void NullLiteral() : {} +{ + +} + +void BooleanLiteral() #void : +{} +{ + #TrueNode +| + #FalseNode +} + +void IntegerLiteral() #NumberLiteral : +{ + Token t; +} +{ + t= + { jjtThis.setNatural(t.image); } +} + + +void FloatLiteral() #NumberLiteral: +{ + Token t; +} +{ + t= + { jjtThis.setReal(t.image); } +} + +void StringLiteral() : +{ + Token t; +} +{ + t= + { jjtThis.setLiteral(Parser.buildString(t.image, true)); } +} + + +void JxltLiteral() #JxltLiteral : +{ + Token t; +} +{ + t= + { jjtThis.setLiteral(Parser.buildString(t.image, true)); } +} + +void RegexLiteral() : +{ + Token t; +} +{ + t= + { jjtThis.setLiteral(Parser.buildRegex(t.image)); } +} + + +void ExtendedLiteral() #ExtendedLiteral : {} +{ + +} + +void ArrayLiteral() : {} +{ + + ( + LOOKAHEAD(1) ExtendedLiteral() + | + (Expression() (LOOKAHEAD(2) Expression() )*)? ( ExtendedLiteral())? + ) + +} + +void MapLiteral() : {} +{ + + ( + MapEntry() ( MapEntry() )* + | + + ) +} + +void MapEntry() : {} +{ + Expression() Expression() +} + +void SetLiteral() : {} +{ + (Expression() ( Expression() )*)? +} + + +/*************************************** + * Functions & Methods + ***************************************/ + +void Arguments() #Arguments : {} +{ + (Expression() ( Expression())* )? +} + +void FunctionCallLookahead() #void : {} +{ + LOOKAHEAD( , { isDeclaredNamespace(getToken(1), getToken(2)) }) + | + LOOKAHEAD(2) + | + LOOKAHEAD(2) +} + +void FunctionCall() #void : {} +{ + LOOKAHEAD( , { isDeclaredNamespace(getToken(1), getToken(2)) }) NamespaceIdentifier() Arguments() #FunctionNode(2) + | + LOOKAHEAD( ) Identifier(true) Arguments() #FunctionNode(2) +} + +void Constructor() #ConstructorNode : {} +{ + Expression() ( Expression() )* +} + +void Parameter() #void : +{ + Token t; +} +{ + t= { declareParameter(t); } +} + +void Parameters() #void : {} +{ + [()? Parameter() ( ()? Parameter())* ] +} + + +void LambdaLookahead() #void : {} +{ + Parameters() + | + Parameters() + | + Parameter() +} + +void Lambda() #JexlLambda : +{ + pushFrame(); +} +{ + { pushUnit(jjtThis); } Parameters() Block() { popUnit(jjtThis); } + | + { pushUnit(jjtThis); } Parameters() Block() { popUnit(jjtThis); } + | + { pushUnit(jjtThis); } Parameter() Block() { popUnit(jjtThis); } +} + + + +/*************************************** + * References + ***************************************/ + +Token dotName() #void : +{ + Token t ; +} +{ + ( t = | t= | t= | t= | t= | t= | t=| t= | t= | t= | t= | t= + | t=<_OR> | t=<_AND>| t= | t= | t= | t= | t= | t= | t= + | t= | t= ) { return t ;} +} + +void IdentifierAccess() #void : +{ + Token t; +} +{ + ( + t=dotName() { jjtThis.setIdentifier(t.image); } #IdentifierAccess + | + t= { jjtThis.setIdentifier(Parser.buildString(t.image, true)); } #IdentifierAccess + | + t= { jjtThis.setIdentifier(Parser.buildString(t.image, true)); } #IdentifierAccessJxlt + ) + | + ( + t=dotName() { jjtThis.setIdentifier(t.image); } #IdentifierAccessSafe + | + t= { jjtThis.setIdentifier(Parser.buildString(t.image, true)); } #IdentifierAccessSafe + | + t= { jjtThis.setIdentifier(Parser.buildString(t.image, true)); } #IdentifierAccessSafeJxlt + ) +} + +void ArrayAccess() : {} +{ + (LOOKAHEAD(1) Expression() )+ +} + +void MemberAccess() #void : {} +{ + LOOKAHEAD() ArrayAccess() + | + LOOKAHEAD() IdentifierAccess() + | + LOOKAHEAD() IdentifierAccess() +} + +void ReferenceExpression() #MethodNode(>1) : {} +{ + Expression() #ReferenceExpression(1) ( LOOKAHEAD() Arguments() )* +} + +void PrimaryExpression() #void : {} +{ + LOOKAHEAD( LambdaLookahead() ) Lambda() + | + LOOKAHEAD( ) ReferenceExpression() + | + LOOKAHEAD( Expression() ) MapLiteral() + | + LOOKAHEAD( ) MapLiteral() + | + LOOKAHEAD( Expression() ( | )) SetLiteral() + | + LOOKAHEAD( ) SetLiteral() + | + LOOKAHEAD( ) ArrayLiteral() + | + LOOKAHEAD( ) Constructor() + | + LOOKAHEAD( FunctionCallLookahead() ) FunctionCall() + | + Identifier(true) + | + Literal() +} + +void MethodCall() #void : {} +{ + (MemberAccess() (LOOKAHEAD() Arguments())+) #MethodNode(>1) +} + + +void MemberExpression() #void : {} +{ + LOOKAHEAD(MethodCall()) MethodCall() | MemberAccess() +} + +void ValueExpression() #void : {} +{ + ( PrimaryExpression() ( LOOKAHEAD(2) MemberExpression() )*) #Reference(>1) +} + diff --git a/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ParserConstants.java b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ParserConstants.java new file mode 100644 index 0000000..320f44d --- /dev/null +++ b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ParserConstants.java @@ -0,0 +1,328 @@ +/* Generated by: JJTree&ParserGeneratorCC: Do not edit this line. ParserConstants.java */ +package aiyh.utils.tool.org.apache.commons.jexl3.parser; + + +/** + * Token literal values and constants. + * Generated by com.helger.pgcc.output.java.OtherFilesGenJava#start() + */ +public interface ParserConstants { + + /** End of File. */ + int EOF = 0; + /** RegularExpression Id. */ + int IF = 9; + /** RegularExpression Id. */ + int ELSE = 10; + /** RegularExpression Id. */ + int FOR = 11; + /** RegularExpression Id. */ + int WHILE = 12; + /** RegularExpression Id. */ + int DO = 13; + /** RegularExpression Id. */ + int NEW = 14; + /** RegularExpression Id. */ + int VAR = 15; + /** RegularExpression Id. */ + int EMPTY = 16; + /** RegularExpression Id. */ + int SIZE = 17; + /** RegularExpression Id. */ + int NULL = 18; + /** RegularExpression Id. */ + int TRUE = 19; + /** RegularExpression Id. */ + int FALSE = 20; + /** RegularExpression Id. */ + int RETURN = 21; + /** RegularExpression Id. */ + int FUNCTION = 22; + /** RegularExpression Id. */ + int LAMBDA = 23; + /** RegularExpression Id. */ + int BREAK = 24; + /** RegularExpression Id. */ + int CONTINUE = 25; + /** RegularExpression Id. */ + int PRAGMA = 26; + /** RegularExpression Id. */ + int LPAREN = 27; + /** RegularExpression Id. */ + int RPAREN = 28; + /** RegularExpression Id. */ + int LCURLY = 29; + /** RegularExpression Id. */ + int RCURLY = 30; + /** RegularExpression Id. */ + int LBRACKET = 31; + /** RegularExpression Id. */ + int RBRACKET = 32; + /** RegularExpression Id. */ + int SEMICOL = 33; + /** RegularExpression Id. */ + int COLON = 34; + /** RegularExpression Id. */ + int COMMA = 35; + /** RegularExpression Id. */ + int DOT = 36; + /** RegularExpression Id. */ + int QDOT = 37; + /** RegularExpression Id. */ + int ELIPSIS = 38; + /** RegularExpression Id. */ + int QMARK = 39; + /** RegularExpression Id. */ + int ELVIS = 40; + /** RegularExpression Id. */ + int NULLP = 41; + /** RegularExpression Id. */ + int AND = 42; + /** RegularExpression Id. */ + int _AND = 43; + /** RegularExpression Id. */ + int OR = 44; + /** RegularExpression Id. */ + int _OR = 45; + /** RegularExpression Id. */ + int eq = 46; + /** RegularExpression Id. */ + int EQ = 47; + /** RegularExpression Id. */ + int ne = 48; + /** RegularExpression Id. */ + int NE = 49; + /** RegularExpression Id. */ + int gt = 50; + /** RegularExpression Id. */ + int GT = 51; + /** RegularExpression Id. */ + int ge = 52; + /** RegularExpression Id. */ + int GE = 53; + /** RegularExpression Id. */ + int lt = 54; + /** RegularExpression Id. */ + int LT = 55; + /** RegularExpression Id. */ + int le = 56; + /** RegularExpression Id. */ + int LE = 57; + /** RegularExpression Id. */ + int req = 58; + /** RegularExpression Id. */ + int rne = 59; + /** RegularExpression Id. */ + int seq = 60; + /** RegularExpression Id. */ + int eeq = 61; + /** RegularExpression Id. */ + int sne = 62; + /** RegularExpression Id. */ + int ene = 63; + /** RegularExpression Id. */ + int plus_assign = 64; + /** RegularExpression Id. */ + int minus_assign = 65; + /** RegularExpression Id. */ + int mult_assign = 66; + /** RegularExpression Id. */ + int div_assign = 67; + /** RegularExpression Id. */ + int mod_assign = 68; + /** RegularExpression Id. */ + int and_assign = 69; + /** RegularExpression Id. */ + int or_assign = 70; + /** RegularExpression Id. */ + int xor_assign = 71; + /** RegularExpression Id. */ + int assign = 72; + /** RegularExpression Id. */ + int plus = 73; + /** RegularExpression Id. */ + int minus = 74; + /** RegularExpression Id. */ + int mult = 75; + /** RegularExpression Id. */ + int div = 76; + /** RegularExpression Id. */ + int DIV = 77; + /** RegularExpression Id. */ + int mod = 78; + /** RegularExpression Id. */ + int MOD = 79; + /** RegularExpression Id. */ + int not = 80; + /** RegularExpression Id. */ + int NOT = 81; + /** RegularExpression Id. */ + int and = 82; + /** RegularExpression Id. */ + int or = 83; + /** RegularExpression Id. */ + int xor = 84; + /** RegularExpression Id. */ + int tilda = 85; + /** RegularExpression Id. */ + int range = 86; + /** RegularExpression Id. */ + int NAN_LITERAL = 87; + /** RegularExpression Id. */ + int ANNOTATION = 88; + /** RegularExpression Id. */ + int DOT_IDENTIFIER = 89; + /** RegularExpression Id. */ + int IDENTIFIER = 90; + /** RegularExpression Id. */ + int LETTER = 91; + /** RegularExpression Id. */ + int DIGIT = 92; + /** RegularExpression Id. */ + int ESCAPE = 93; + /** RegularExpression Id. */ + int REGISTER = 94; + /** RegularExpression Id. */ + int INTEGER_LITERAL = 95; + /** RegularExpression Id. */ + int DECIMAL_LITERAL = 96; + /** RegularExpression Id. */ + int HEX_LITERAL = 97; + /** RegularExpression Id. */ + int OCTAL_LITERAL = 98; + /** RegularExpression Id. */ + int INT_SFX = 99; + /** RegularExpression Id. */ + int FLOAT_LITERAL = 100; + /** RegularExpression Id. */ + int EXPONENT = 101; + /** RegularExpression Id. */ + int FLT_CLS = 102; + /** RegularExpression Id. */ + int FLT_SFX = 103; + /** RegularExpression Id. */ + int STRING_LITERAL = 104; + /** RegularExpression Id. */ + int JXLT_LITERAL = 105; + /** RegularExpression Id. */ + int REGEX_LITERAL = 106; + + /** Lexical state. */ + int DEFAULT = 0; + /** Lexical state. */ + int DOT_ID = 1; + /** Lexical state. */ + int REGISTERS = 2; + + /** Literal token values. */ + String[] tokenImage = { + "", + "", + "", + "", + "\" \"", + "\"\\t\"", + "\"\\n\"", + "\"\\r\"", + "\"\\f\"", + "\"if\"", + "\"else\"", + "\"for\"", + "\"while\"", + "\"do\"", + "\"new\"", + "\"var\"", + "\"empty\"", + "\"size\"", + "\"null\"", + "\"true\"", + "\"false\"", + "\"return\"", + "\"function\"", + "\"->\"", + "\"break\"", + "\"continue\"", + "\"#pragma\"", + "\"(\"", + "\")\"", + "\"{\"", + "\"}\"", + "\"[\"", + "\"]\"", + "\";\"", + "\":\"", + "\",\"", + "\".\"", + "\"?.\"", + "\"...\"", + "\"?\"", + "\"?:\"", + "\"??\"", + "\"&&\"", + "\"and\"", + "\"||\"", + "\"or\"", + "\"==\"", + "\"eq\"", + "\"!=\"", + "\"ne\"", + "\">\"", + "\"gt\"", + "\">=\"", + "\"ge\"", + "\"<\"", + "\"lt\"", + "\"<=\"", + "\"le\"", + "\"=~\"", + "\"!~\"", + "\"=^\"", + "\"=$\"", + "\"!^\"", + "\"!$\"", + "\"+=\"", + "\"-=\"", + "\"*=\"", + "\"/=\"", + "\"%=\"", + "\"&=\"", + "\"|=\"", + "\"^=\"", + "\"=\"", + "\"+\"", + "\"-\"", + "\"*\"", + "\"/\"", + "\"div\"", + "\"%\"", + "\"mod\"", + "\"!\"", + "\"not\"", + "\"&\"", + "\"|\"", + "\"^\"", + "\"~\"", + "\"..\"", + "\"NaN\"", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + }; + +} diff --git a/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ParserDefaultVisitor.java b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ParserDefaultVisitor.java new file mode 100644 index 0000000..8fbaa61 --- /dev/null +++ b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ParserDefaultVisitor.java @@ -0,0 +1,23 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package aiyh.utils.tool.org.apache.commons.jexl3.parser; + +/** + * This class only exists to prevent JJTree from generating it, since it + * expects {@link ParserVisitor} to be an interface, not an abstract class. + */ +class ParserDefaultVisitor { } diff --git a/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ParserTokenManager.java b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ParserTokenManager.java new file mode 100644 index 0000000..1363c09 --- /dev/null +++ b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ParserTokenManager.java @@ -0,0 +1,3723 @@ +/* ParserTokenManager.java */ +/* Generated by: JJTree&ParserGeneratorCC: Do not edit this line. ParserTokenManager.java */ +package aiyh.utils.tool.org.apache.commons.jexl3.parser; + +/** Token Manager. */ +@SuppressWarnings ("unused") +public class ParserTokenManager implements ParserConstants { + /** + * A stack of 1 for keeping state to deal with doted identifiers + */ + int dotLexState = DEFAULT; + + public void pushDot() { + dotLexState = curLexState; + curLexState = DOT_ID; + } + + public void popDot() { + if (curLexState == DOT_ID) { + curLexState = dotLexState; + dotLexState = defaultLexState; + } + } +private final int jjStopStringLiteralDfa_0(int pos, long active0, long active1){ + switch (pos) + { + case 0: + if ((active0 & 0x2aaa800037ffe00L) != 0x0L || (active1 & 0x82a000L) != 0x0L) + { + jjmatchedKind = 90; + return 72; + } + if ((active0 & 0x5000000000L) != 0x0L || (active1 & 0x400000L) != 0x0L) + return 10; + if ((active1 & 0x1008L) != 0x0L) + return 61; + if ((active1 & 0x200000L) != 0x0L) + return 31; + if ((active0 & 0x4000000L) != 0x0L) + return 52; + return -1; + case 1: + if ((active0 & 0x2aaa00000006200L) != 0x0L) + return 72; + if ((active0 & 0x800037f9c00L) != 0x0L || (active1 & 0x82a000L) != 0x0L) + { + if (jjmatchedPos != 1) + { + jjmatchedKind = 90; + jjmatchedPos = 1; + } + return 72; + } + return -1; + case 2: + if ((active0 & 0x8000000c800L) != 0x0L || (active1 & 0x82a000L) != 0x0L) + return 72; + if ((active0 & 0x37f1400L) != 0x0L) + { + jjmatchedKind = 90; + jjmatchedPos = 2; + return 72; + } + return -1; + case 3: + if ((active0 & 0x3711000L) != 0x0L) + { + jjmatchedKind = 90; + jjmatchedPos = 3; + return 72; + } + if ((active0 & 0xe0400L) != 0x0L) + return 72; + return -1; + case 4: + if ((active0 & 0x2600000L) != 0x0L) + { + jjmatchedKind = 90; + jjmatchedPos = 4; + return 72; + } + if ((active0 & 0x1111000L) != 0x0L) + return 72; + return -1; + case 5: + if ((active0 & 0x200000L) != 0x0L) + return 72; + if ((active0 & 0x2400000L) != 0x0L) + { + jjmatchedKind = 90; + jjmatchedPos = 5; + return 72; + } + return -1; + case 6: + if ((active0 & 0x2400000L) != 0x0L) + { + jjmatchedKind = 90; + jjmatchedPos = 6; + return 72; + } + return -1; + default : + return -1; + } +} +private final int jjStartNfa_0(int pos, long active0, long active1){ + return jjMoveNfa_0(jjStopStringLiteralDfa_0(pos, active0, active1), pos + 1); +} +private int jjStopAtPos(int pos, int kind) +{ + jjmatchedKind = kind; + jjmatchedPos = pos; + return pos + 1; +} +private int jjMoveStringLiteralDfa0_0(){ + switch(curChar) + { + case '!': + jjmatchedKind = 80; + return jjMoveStringLiteralDfa1_0(0xc801000000000000L, 0x0L); + case '#': + return jjMoveStringLiteralDfa1_0(0x4000000L, 0x0L); + case '%': + jjmatchedKind = 78; + return jjMoveStringLiteralDfa1_0(0x0L, 0x10L); + case '&': + jjmatchedKind = 82; + return jjMoveStringLiteralDfa1_0(0x40000000000L, 0x20L); + case '(': + return jjStopAtPos(0, 27); + case ')': + return jjStopAtPos(0, 28); + case '*': + jjmatchedKind = 75; + return jjMoveStringLiteralDfa1_0(0x0L, 0x4L); + case '+': + jjmatchedKind = 73; + return jjMoveStringLiteralDfa1_0(0x0L, 0x1L); + case ',': + return jjStopAtPos(0, 35); + case '-': + jjmatchedKind = 74; + return jjMoveStringLiteralDfa1_0(0x800000L, 0x2L); + case '.': + jjmatchedKind = 36; + return jjMoveStringLiteralDfa1_0(0x4000000000L, 0x400000L); + case '/': + jjmatchedKind = 76; + return jjMoveStringLiteralDfa1_0(0x0L, 0x8L); + case ':': + return jjStopAtPos(0, 34); + case ';': + return jjStopAtPos(0, 33); + case '<': + jjmatchedKind = 54; + return jjMoveStringLiteralDfa1_0(0x100000000000000L, 0x0L); + case '=': + jjmatchedKind = 72; + return jjMoveStringLiteralDfa1_0(0x3400400000000000L, 0x0L); + case '>': + jjmatchedKind = 50; + return jjMoveStringLiteralDfa1_0(0x10000000000000L, 0x0L); + case '?': + jjmatchedKind = 39; + return jjMoveStringLiteralDfa1_0(0x32000000000L, 0x0L); + case 'N': + return jjMoveStringLiteralDfa1_0(0x0L, 0x800000L); + case '[': + return jjStopAtPos(0, 31); + case ']': + return jjStopAtPos(0, 32); + case '^': + jjmatchedKind = 84; + return jjMoveStringLiteralDfa1_0(0x0L, 0x80L); + case 'a': + return jjMoveStringLiteralDfa1_0(0x80000000000L, 0x0L); + case 'b': + return jjMoveStringLiteralDfa1_0(0x1000000L, 0x0L); + case 'c': + return jjMoveStringLiteralDfa1_0(0x2000000L, 0x0L); + case 'd': + return jjMoveStringLiteralDfa1_0(0x2000L, 0x2000L); + case 'e': + return jjMoveStringLiteralDfa1_0(0x800000010400L, 0x0L); + case 'f': + return jjMoveStringLiteralDfa1_0(0x500800L, 0x0L); + case 'g': + return jjMoveStringLiteralDfa1_0(0x28000000000000L, 0x0L); + case 'i': + return jjMoveStringLiteralDfa1_0(0x200L, 0x0L); + case 'l': + return jjMoveStringLiteralDfa1_0(0x280000000000000L, 0x0L); + case 'm': + return jjMoveStringLiteralDfa1_0(0x0L, 0x8000L); + case 'n': + return jjMoveStringLiteralDfa1_0(0x2000000044000L, 0x20000L); + case 'o': + return jjMoveStringLiteralDfa1_0(0x200000000000L, 0x0L); + case 'r': + return jjMoveStringLiteralDfa1_0(0x200000L, 0x0L); + case 's': + return jjMoveStringLiteralDfa1_0(0x20000L, 0x0L); + case 't': + return jjMoveStringLiteralDfa1_0(0x80000L, 0x0L); + case 'v': + return jjMoveStringLiteralDfa1_0(0x8000L, 0x0L); + case 'w': + return jjMoveStringLiteralDfa1_0(0x1000L, 0x0L); + case '{': + return jjStopAtPos(0, 29); + case '|': + jjmatchedKind = 83; + return jjMoveStringLiteralDfa1_0(0x100000000000L, 0x40L); + case '}': + return jjStopAtPos(0, 30); + case '~': + return jjStartNfaWithStates_0(0, 85, 31); + default : + return jjMoveNfa_0(0, 0); + } +} +private int jjMoveStringLiteralDfa1_0(long active0, long active1){ + try { curChar = input_stream.readChar(); } + catch(java.io.IOException e) { + jjStopStringLiteralDfa_0(0, active0, active1); + return 1; + } + switch(curChar) + { + case '$': + if ((active0 & 0x2000000000000000L) != 0x0L) + return jjStopAtPos(1, 61); + else if ((active0 & 0x8000000000000000L) != 0x0L) + return jjStopAtPos(1, 63); + break; + case '&': + if ((active0 & 0x40000000000L) != 0x0L) + return jjStopAtPos(1, 42); + break; + case '.': + if ((active0 & 0x2000000000L) != 0x0L) + return jjStopAtPos(1, 37); + else if ((active1 & 0x400000L) != 0x0L) + { + jjmatchedKind = 86; + jjmatchedPos = 1; + } + return jjMoveStringLiteralDfa2_0(active0, 0x4000000000L, active1, 0x0L); + case ':': + if ((active0 & 0x10000000000L) != 0x0L) + return jjStopAtPos(1, 40); + break; + case '=': + if ((active0 & 0x400000000000L) != 0x0L) + return jjStopAtPos(1, 46); + else if ((active0 & 0x1000000000000L) != 0x0L) + return jjStopAtPos(1, 48); + else if ((active0 & 0x10000000000000L) != 0x0L) + return jjStopAtPos(1, 52); + else if ((active0 & 0x100000000000000L) != 0x0L) + return jjStopAtPos(1, 56); + else if ((active1 & 0x1L) != 0x0L) + return jjStopAtPos(1, 64); + else if ((active1 & 0x2L) != 0x0L) + return jjStopAtPos(1, 65); + else if ((active1 & 0x4L) != 0x0L) + return jjStopAtPos(1, 66); + else if ((active1 & 0x8L) != 0x0L) + return jjStopAtPos(1, 67); + else if ((active1 & 0x10L) != 0x0L) + return jjStopAtPos(1, 68); + else if ((active1 & 0x20L) != 0x0L) + return jjStopAtPos(1, 69); + else if ((active1 & 0x40L) != 0x0L) + return jjStopAtPos(1, 70); + else if ((active1 & 0x80L) != 0x0L) + return jjStopAtPos(1, 71); + break; + case '>': + if ((active0 & 0x800000L) != 0x0L) + return jjStopAtPos(1, 23); + break; + case '?': + if ((active0 & 0x20000000000L) != 0x0L) + return jjStopAtPos(1, 41); + break; + case '^': + if ((active0 & 0x1000000000000000L) != 0x0L) + return jjStopAtPos(1, 60); + else if ((active0 & 0x4000000000000000L) != 0x0L) + return jjStopAtPos(1, 62); + break; + case 'a': + return jjMoveStringLiteralDfa2_0(active0, 0x108000L, active1, 0x800000L); + case 'e': + if ((active0 & 0x2000000000000L) != 0x0L) + { + jjmatchedKind = 49; + jjmatchedPos = 1; + } + else if ((active0 & 0x20000000000000L) != 0x0L) + return jjStartNfaWithStates_0(1, 53, 72); + else if ((active0 & 0x200000000000000L) != 0x0L) + return jjStartNfaWithStates_0(1, 57, 72); + return jjMoveStringLiteralDfa2_0(active0, 0x204000L, active1, 0x0L); + case 'f': + if ((active0 & 0x200L) != 0x0L) + return jjStartNfaWithStates_0(1, 9, 72); + break; + case 'h': + return jjMoveStringLiteralDfa2_0(active0, 0x1000L, active1, 0x0L); + case 'i': + return jjMoveStringLiteralDfa2_0(active0, 0x20000L, active1, 0x2000L); + case 'l': + return jjMoveStringLiteralDfa2_0(active0, 0x400L, active1, 0x0L); + case 'm': + return jjMoveStringLiteralDfa2_0(active0, 0x10000L, active1, 0x0L); + case 'n': + return jjMoveStringLiteralDfa2_0(active0, 0x80000000000L, active1, 0x0L); + case 'o': + if ((active0 & 0x2000L) != 0x0L) + return jjStartNfaWithStates_0(1, 13, 72); + return jjMoveStringLiteralDfa2_0(active0, 0x2000800L, active1, 0x28000L); + case 'p': + return jjMoveStringLiteralDfa2_0(active0, 0x4000000L, active1, 0x0L); + case 'q': + if ((active0 & 0x800000000000L) != 0x0L) + return jjStartNfaWithStates_0(1, 47, 72); + break; + case 'r': + if ((active0 & 0x200000000000L) != 0x0L) + return jjStartNfaWithStates_0(1, 45, 72); + return jjMoveStringLiteralDfa2_0(active0, 0x1080000L, active1, 0x0L); + case 't': + if ((active0 & 0x8000000000000L) != 0x0L) + return jjStartNfaWithStates_0(1, 51, 72); + else if ((active0 & 0x80000000000000L) != 0x0L) + return jjStartNfaWithStates_0(1, 55, 72); + break; + case 'u': + return jjMoveStringLiteralDfa2_0(active0, 0x440000L, active1, 0x0L); + case '|': + if ((active0 & 0x100000000000L) != 0x0L) + return jjStopAtPos(1, 44); + break; + case '~': + if ((active0 & 0x400000000000000L) != 0x0L) + return jjStopAtPos(1, 58); + else if ((active0 & 0x800000000000000L) != 0x0L) + return jjStopAtPos(1, 59); + break; + default : + break; + } + return jjStartNfa_0(0, active0, active1); +} +private int jjMoveStringLiteralDfa2_0(long old0, long active0, long old1, long active1){ + if (((active0 &= old0) | (active1 &= old1)) == 0L) + return jjStartNfa_0(0, old0, old1); + try { curChar = input_stream.readChar(); } + catch(java.io.IOException e) { + jjStopStringLiteralDfa_0(1, active0, active1); + return 2; + } + switch(curChar) + { + case '.': + if ((active0 & 0x4000000000L) != 0x0L) + return jjStopAtPos(2, 38); + break; + case 'N': + if ((active1 & 0x800000L) != 0x0L) + return jjStartNfaWithStates_0(2, 87, 72); + break; + case 'd': + if ((active0 & 0x80000000000L) != 0x0L) + return jjStartNfaWithStates_0(2, 43, 72); + else if ((active1 & 0x8000L) != 0x0L) + return jjStartNfaWithStates_0(2, 79, 72); + break; + case 'e': + return jjMoveStringLiteralDfa3_0(active0, 0x1000000L, active1, 0x0L); + case 'i': + return jjMoveStringLiteralDfa3_0(active0, 0x1000L, active1, 0x0L); + case 'l': + return jjMoveStringLiteralDfa3_0(active0, 0x140000L, active1, 0x0L); + case 'n': + return jjMoveStringLiteralDfa3_0(active0, 0x2400000L, active1, 0x0L); + case 'p': + return jjMoveStringLiteralDfa3_0(active0, 0x10000L, active1, 0x0L); + case 'r': + if ((active0 & 0x800L) != 0x0L) + return jjStartNfaWithStates_0(2, 11, 72); + else if ((active0 & 0x8000L) != 0x0L) + return jjStartNfaWithStates_0(2, 15, 72); + return jjMoveStringLiteralDfa3_0(active0, 0x4000000L, active1, 0x0L); + case 's': + return jjMoveStringLiteralDfa3_0(active0, 0x400L, active1, 0x0L); + case 't': + if ((active1 & 0x20000L) != 0x0L) + return jjStartNfaWithStates_0(2, 81, 72); + return jjMoveStringLiteralDfa3_0(active0, 0x200000L, active1, 0x0L); + case 'u': + return jjMoveStringLiteralDfa3_0(active0, 0x80000L, active1, 0x0L); + case 'v': + if ((active1 & 0x2000L) != 0x0L) + return jjStartNfaWithStates_0(2, 77, 72); + break; + case 'w': + if ((active0 & 0x4000L) != 0x0L) + return jjStartNfaWithStates_0(2, 14, 72); + break; + case 'z': + return jjMoveStringLiteralDfa3_0(active0, 0x20000L, active1, 0x0L); + default : + break; + } + return jjStartNfa_0(1, active0, active1); +} +private int jjMoveStringLiteralDfa3_0(long old0, long active0, long old1, long active1){ + if (((active0 &= old0) | (active1 &= old1)) == 0L) + return jjStartNfa_0(1, old0, old1); + try { curChar = input_stream.readChar(); } + catch(java.io.IOException e) { + jjStopStringLiteralDfa_0(2, active0, 0L); + return 3; + } + switch(curChar) + { + case 'a': + return jjMoveStringLiteralDfa4_0(active0, 0x5000000L); + case 'c': + return jjMoveStringLiteralDfa4_0(active0, 0x400000L); + case 'e': + if ((active0 & 0x400L) != 0x0L) + return jjStartNfaWithStates_0(3, 10, 72); + else if ((active0 & 0x20000L) != 0x0L) + return jjStartNfaWithStates_0(3, 17, 72); + else if ((active0 & 0x80000L) != 0x0L) + return jjStartNfaWithStates_0(3, 19, 72); + break; + case 'l': + if ((active0 & 0x40000L) != 0x0L) + return jjStartNfaWithStates_0(3, 18, 72); + return jjMoveStringLiteralDfa4_0(active0, 0x1000L); + case 's': + return jjMoveStringLiteralDfa4_0(active0, 0x100000L); + case 't': + return jjMoveStringLiteralDfa4_0(active0, 0x2010000L); + case 'u': + return jjMoveStringLiteralDfa4_0(active0, 0x200000L); + default : + break; + } + return jjStartNfa_0(2, active0, 0L); +} +private int jjMoveStringLiteralDfa4_0(long old0, long active0){ + if (((active0 &= old0)) == 0L) + return jjStartNfa_0(2, old0, 0L); + try { curChar = input_stream.readChar(); } + catch(java.io.IOException e) { + jjStopStringLiteralDfa_0(3, active0, 0L); + return 4; + } + switch(curChar) + { + case 'e': + if ((active0 & 0x1000L) != 0x0L) + return jjStartNfaWithStates_0(4, 12, 72); + else if ((active0 & 0x100000L) != 0x0L) + return jjStartNfaWithStates_0(4, 20, 72); + break; + case 'g': + return jjMoveStringLiteralDfa5_0(active0, 0x4000000L); + case 'i': + return jjMoveStringLiteralDfa5_0(active0, 0x2000000L); + case 'k': + if ((active0 & 0x1000000L) != 0x0L) + return jjStartNfaWithStates_0(4, 24, 72); + break; + case 'r': + return jjMoveStringLiteralDfa5_0(active0, 0x200000L); + case 't': + return jjMoveStringLiteralDfa5_0(active0, 0x400000L); + case 'y': + if ((active0 & 0x10000L) != 0x0L) + return jjStartNfaWithStates_0(4, 16, 72); + break; + default : + break; + } + return jjStartNfa_0(3, active0, 0L); +} +private int jjMoveStringLiteralDfa5_0(long old0, long active0){ + if (((active0 &= old0)) == 0L) + return jjStartNfa_0(3, old0, 0L); + try { curChar = input_stream.readChar(); } + catch(java.io.IOException e) { + jjStopStringLiteralDfa_0(4, active0, 0L); + return 5; + } + switch(curChar) + { + case 'i': + return jjMoveStringLiteralDfa6_0(active0, 0x400000L); + case 'm': + return jjMoveStringLiteralDfa6_0(active0, 0x4000000L); + case 'n': + if ((active0 & 0x200000L) != 0x0L) + return jjStartNfaWithStates_0(5, 21, 72); + return jjMoveStringLiteralDfa6_0(active0, 0x2000000L); + default : + break; + } + return jjStartNfa_0(4, active0, 0L); +} +private int jjMoveStringLiteralDfa6_0(long old0, long active0){ + if (((active0 &= old0)) == 0L) + return jjStartNfa_0(4, old0, 0L); + try { curChar = input_stream.readChar(); } + catch(java.io.IOException e) { + jjStopStringLiteralDfa_0(5, active0, 0L); + return 6; + } + switch(curChar) + { + case 'a': + if ((active0 & 0x4000000L) != 0x0L) + return jjStopAtPos(6, 26); + break; + case 'o': + return jjMoveStringLiteralDfa7_0(active0, 0x400000L); + case 'u': + return jjMoveStringLiteralDfa7_0(active0, 0x2000000L); + default : + break; + } + return jjStartNfa_0(5, active0, 0L); +} +private int jjMoveStringLiteralDfa7_0(long old0, long active0){ + if (((active0 &= old0)) == 0L) + return jjStartNfa_0(5, old0, 0L); + try { curChar = input_stream.readChar(); } + catch(java.io.IOException e) { + jjStopStringLiteralDfa_0(6, active0, 0L); + return 7; + } + switch(curChar) + { + case 'e': + if ((active0 & 0x2000000L) != 0x0L) + return jjStartNfaWithStates_0(7, 25, 72); + break; + case 'n': + if ((active0 & 0x400000L) != 0x0L) + return jjStartNfaWithStates_0(7, 22, 72); + break; + default : + break; + } + return jjStartNfa_0(6, active0, 0L); +} +private int jjStartNfaWithStates_0(int pos, int kind, int state) +{ + jjmatchedKind = kind; + jjmatchedPos = pos; + try { curChar = input_stream.readChar(); } + catch(java.io.IOException e) { return pos + 1; } + return jjMoveNfa_0(state, pos + 1); +} +static final long[] jjbitVec0 = { + 0xfffffffefffffffeL, 0xffffffffffffffffL, 0xffffffffffffffffL, 0xffffffffffffffffL +}; +static final long[] jjbitVec2 = { + 0x0L, 0x0L, 0xffffffffffffffffL, 0xffffffffffffffffL +}; +static final long[] jjbitVec3 = { + 0xfffffcffffffffffL, 0xffffffffffffffffL, 0xffffffffffffffffL, 0xffffffffffffffffL +}; +static final long[] jjbitVec4 = { + 0xfffffffffffffffeL, 0xffffffffffffffffL, 0xffffffffffffffffL, 0xffffffffffffffffL +}; +private int jjMoveNfa_0(int startState, int curPos) +{ + int startsAt = 0; + jjnewStateCnt = 72; + int i = 1; + jjstateSet[0] = startState; + int kind = 0x7fffffff; + for (;;) + { + if (++jjround == 0x7fffffff) + ReInitRounds(); + if (curChar < 64) + { + long l = 1L << curChar; + do + { + switch(jjstateSet[--i]) + { + case 61: + if (curChar == 47) + { + if (kind > 3) + kind = 3; + { jjCheckNAddStates(0, 2); } + } + else if (curChar == 42) + { jjCheckNAddTwoStates(62, 63); } + break; + case 0: + if ((0x3ff000000000000L & l) != 0x0L) + { jjCheckNAddStates(3, 8); } + else if (curChar == 47) + { jjAddStates(9, 10); } + else if (curChar == 35) + { jjAddStates(11, 12); } + else if (curChar == 39) + { jjCheckNAddStates(13, 15); } + else if (curChar == 34) + { jjCheckNAddStates(16, 18); } + else if (curChar == 46) + { jjCheckNAdd(10); } + else if (curChar == 36) + { + if (kind > 90) + kind = 90; + { jjCheckNAddTwoStates(3, 4); } + } + if ((0x3fe000000000000L & l) != 0x0L) + { + if (kind > 95) + kind = 95; + { jjCheckNAddTwoStates(7, 8); } + } + else if (curChar == 48) + { + if (kind > 95) + kind = 95; + { jjCheckNAddStates(19, 21); } + } + break; + case 72: + case 3: + if ((0x3ff001000000000L & l) == 0x0L) + break; + if (kind > 90) + kind = 90; + { jjCheckNAddTwoStates(3, 4); } + break; + case 52: + if (curChar != 35) + break; + if (kind > 1) + kind = 1; + { jjCheckNAddStates(22, 24); } + break; + case 1: + if ((0x3ff001000000000L & l) == 0x0L) + break; + if (kind > 88) + kind = 88; + jjstateSet[jjnewStateCnt++] = 1; + break; + case 2: + if (curChar != 36) + break; + if (kind > 90) + kind = 90; + { jjCheckNAddTwoStates(3, 4); } + break; + case 5: + if ((0x8500000000L & l) == 0x0L) + break; + if (kind > 90) + kind = 90; + { jjCheckNAddTwoStates(3, 4); } + break; + case 6: + if ((0x3fe000000000000L & l) == 0x0L) + break; + if (kind > 95) + kind = 95; + { jjCheckNAddTwoStates(7, 8); } + break; + case 7: + if ((0x3ff000000000000L & l) == 0x0L) + break; + if (kind > 95) + kind = 95; + { jjCheckNAddTwoStates(7, 8); } + break; + case 9: + if (curChar == 46) + { jjCheckNAdd(10); } + break; + case 10: + if ((0x3ff000000000000L & l) != 0x0L) + { jjCheckNAddStates(25, 27); } + break; + case 12: + if ((0x280000000000L & l) != 0x0L) + { jjCheckNAdd(13); } + break; + case 13: + if ((0x3ff000000000000L & l) == 0x0L) + break; + if (kind > 100) + kind = 100; + { jjCheckNAddTwoStates(13, 14); } + break; + case 15: + if (curChar == 34) + { jjCheckNAddStates(16, 18); } + break; + case 16: + if ((0xfffffffbffffdbffL & l) != 0x0L) + { jjCheckNAddStates(16, 18); } + break; + case 18: + if ((0xffffffffffffdbffL & l) != 0x0L) + { jjCheckNAddStates(16, 18); } + break; + case 19: + if (curChar == 34 && kind > 104) + kind = 104; + break; + case 20: + if (curChar == 39) + { jjCheckNAddStates(13, 15); } + break; + case 21: + if ((0xffffff7fffffdbffL & l) != 0x0L) + { jjCheckNAddStates(13, 15); } + break; + case 23: + if ((0xffffffffffffdbffL & l) != 0x0L) + { jjCheckNAddStates(13, 15); } + break; + case 24: + if (curChar == 39 && kind > 104) + kind = 104; + break; + case 26: + { jjCheckNAddStates(28, 30); } + break; + case 28: + if ((0xfffffffffffffffeL & l) != 0x0L) + { jjCheckNAddStates(28, 30); } + break; + case 31: + if (curChar == 47) + { jjCheckNAddStates(31, 33); } + break; + case 32: + if ((0xffff7fffffffdbffL & l) != 0x0L) + { jjCheckNAddStates(31, 33); } + break; + case 34: + if (curChar == 47 && kind > 106) + kind = 106; + break; + case 35: + if ((0x3ff000000000000L & l) != 0x0L) + { jjCheckNAddStates(3, 8); } + break; + case 36: + if ((0x3ff000000000000L & l) != 0x0L) + { jjCheckNAddTwoStates(36, 37); } + break; + case 37: + if (curChar == 46) + { jjCheckNAdd(38); } + break; + case 38: + if ((0x3ff000000000000L & l) == 0x0L) + break; + if (kind > 100) + kind = 100; + { jjCheckNAddStates(34, 36); } + break; + case 40: + if ((0x280000000000L & l) != 0x0L) + { jjCheckNAdd(41); } + break; + case 41: + if ((0x3ff000000000000L & l) == 0x0L) + break; + if (kind > 100) + kind = 100; + { jjCheckNAddTwoStates(41, 14); } + break; + case 42: + if ((0x3ff000000000000L & l) != 0x0L) + { jjCheckNAddStates(37, 40); } + break; + case 43: + if (curChar == 46) + { jjCheckNAddTwoStates(44, 14); } + break; + case 45: + if ((0x280000000000L & l) != 0x0L) + { jjCheckNAdd(46); } + break; + case 46: + if ((0x3ff000000000000L & l) == 0x0L) + break; + if (kind > 100) + kind = 100; + { jjCheckNAddTwoStates(46, 14); } + break; + case 47: + if (curChar != 48) + break; + if (kind > 95) + kind = 95; + { jjCheckNAddStates(19, 21); } + break; + case 49: + if ((0x3ff000000000000L & l) == 0x0L) + break; + if (kind > 95) + kind = 95; + { jjCheckNAddTwoStates(49, 8); } + break; + case 50: + if ((0xff000000000000L & l) == 0x0L) + break; + if (kind > 95) + kind = 95; + { jjCheckNAddTwoStates(50, 8); } + break; + case 51: + if (curChar == 35) + { jjAddStates(11, 12); } + break; + case 53: + if ((0xffffffffffffdbffL & l) == 0x0L) + break; + if (kind > 1) + kind = 1; + { jjCheckNAddStates(22, 24); } + break; + case 54: + if ((0x2400L & l) != 0x0L && kind > 1) + kind = 1; + break; + case 55: + if (curChar == 10 && kind > 1) + kind = 1; + break; + case 56: + if (curChar == 13) + jjstateSet[jjnewStateCnt++] = 55; + break; + case 60: + if (curChar == 47) + { jjAddStates(9, 10); } + break; + case 62: + if ((0xfffffbffffffffffL & l) != 0x0L) + { jjCheckNAddTwoStates(62, 63); } + break; + case 63: + if (curChar == 42) + { jjCheckNAddStates(41, 43); } + break; + case 64: + if ((0xffff7bffffffffffL & l) != 0x0L) + { jjCheckNAddTwoStates(65, 63); } + break; + case 65: + if ((0xfffffbffffffffffL & l) != 0x0L) + { jjCheckNAddTwoStates(65, 63); } + break; + case 66: + if (curChar == 47 && kind > 2) + kind = 2; + break; + case 67: + if (curChar != 47) + break; + if (kind > 3) + kind = 3; + { jjCheckNAddStates(0, 2); } + break; + case 68: + if ((0xffffffffffffdbffL & l) == 0x0L) + break; + if (kind > 3) + kind = 3; + { jjCheckNAddStates(0, 2); } + break; + case 69: + if ((0x2400L & l) != 0x0L && kind > 3) + kind = 3; + break; + case 70: + if (curChar == 10 && kind > 3) + kind = 3; + break; + case 71: + if (curChar == 13) + jjstateSet[jjnewStateCnt++] = 70; + break; + default : break; + } + } while(i != startsAt); + } + else if (curChar < 128) + { + long l = 1L << (curChar & 077); + do + { + switch(jjstateSet[--i]) + { + case 0: + if ((0x7fffffe87ffffffL & l) != 0x0L) + { + if (kind > 90) + kind = 90; + { jjCheckNAddTwoStates(3, 4); } + } + else if (curChar == 126) + { jjCheckNAdd(31); } + else if (curChar == 96) + { jjCheckNAddStates(28, 30); } + if (curChar == 64) + { jjCheckNAdd(1); } + break; + case 72: + if ((0x7fffffe87ffffffL & l) != 0x0L) + { + if (kind > 90) + kind = 90; + { jjCheckNAddTwoStates(3, 4); } + } + else if (curChar == 92) + jjstateSet[jjnewStateCnt++] = 5; + break; + case 52: + if (curChar == 78) + jjstateSet[jjnewStateCnt++] = 58; + break; + case 1: + if ((0x7fffffe87fffffeL & l) == 0x0L) + break; + if (kind > 88) + kind = 88; + { jjCheckNAdd(1); } + break; + case 2: + if ((0x7fffffe87ffffffL & l) == 0x0L) + break; + if (kind > 90) + kind = 90; + { jjCheckNAddTwoStates(3, 4); } + break; + case 3: + if ((0x7fffffe87ffffffL & l) == 0x0L) + break; + if (kind > 90) + kind = 90; + { jjCheckNAddTwoStates(3, 4); } + break; + case 4: + if (curChar == 92) + jjstateSet[jjnewStateCnt++] = 5; + break; + case 5: + if (curChar != 92) + break; + if (kind > 90) + kind = 90; + { jjCheckNAddTwoStates(3, 4); } + break; + case 8: + if ((0x110000001100L & l) != 0x0L && kind > 95) + kind = 95; + break; + case 11: + if ((0x2000000020L & l) != 0x0L) + { jjAddStates(44, 45); } + break; + case 14: + if ((0x5400000054L & l) != 0x0L && kind > 100) + kind = 100; + break; + case 16: + if ((0xffffffffefffffffL & l) != 0x0L) + { jjCheckNAddStates(16, 18); } + break; + case 17: + if (curChar == 92) + jjstateSet[jjnewStateCnt++] = 18; + break; + case 18: + { jjCheckNAddStates(16, 18); } + break; + case 21: + if ((0xffffffffefffffffL & l) != 0x0L) + { jjCheckNAddStates(13, 15); } + break; + case 22: + if (curChar == 92) + jjstateSet[jjnewStateCnt++] = 23; + break; + case 23: + { jjCheckNAddStates(13, 15); } + break; + case 25: + if (curChar == 96) + { jjCheckNAddStates(28, 30); } + break; + case 26: + if ((0xfffffffeefffffffL & l) != 0x0L) + { jjCheckNAddStates(28, 30); } + break; + case 27: + if (curChar == 92) + jjstateSet[jjnewStateCnt++] = 28; + break; + case 28: + { jjCheckNAddStates(28, 30); } + break; + case 29: + if (curChar == 96 && kind > 105) + kind = 105; + break; + case 30: + if (curChar == 126) + { jjCheckNAdd(31); } + break; + case 32: + { jjAddStates(31, 33); } + break; + case 33: + if (curChar == 92) + { jjCheckNAdd(31); } + break; + case 39: + if ((0x2000000020L & l) != 0x0L) + { jjAddStates(46, 47); } + break; + case 44: + if ((0x2000000020L & l) != 0x0L) + { jjAddStates(48, 49); } + break; + case 48: + if ((0x100000001000000L & l) != 0x0L) + { jjCheckNAdd(49); } + break; + case 49: + if ((0x7e0000007eL & l) == 0x0L) + break; + if (kind > 95) + kind = 95; + { jjCheckNAddTwoStates(49, 8); } + break; + case 53: + if (kind > 1) + kind = 1; + { jjAddStates(22, 24); } + break; + case 57: + if (curChar == 78 && kind > 100) + kind = 100; + break; + case 58: + if (curChar == 97) + jjstateSet[jjnewStateCnt++] = 57; + break; + case 62: + { jjCheckNAddTwoStates(62, 63); } + break; + case 64: + case 65: + { jjCheckNAddTwoStates(65, 63); } + break; + case 68: + if (kind > 3) + kind = 3; + { jjAddStates(0, 2); } + break; + default : break; + } + } while(i != startsAt); + } + else + { + int hiByte = (curChar >> 8); + int i1 = hiByte >> 6; + long l1 = 1L << (hiByte & 077); + int i2 = (curChar & 0xff) >> 6; + long l2 = 1L << (curChar & 077); + do + { + switch(jjstateSet[--i]) + { + case 16: + case 18: + if (jjCanMove_0(hiByte, i1, i2, l1, l2)) + { jjCheckNAddStates(16, 18); } + break; + case 21: + case 23: + if (jjCanMove_0(hiByte, i1, i2, l1, l2)) + { jjCheckNAddStates(13, 15); } + break; + case 26: + case 28: + if (jjCanMove_1(hiByte, i1, i2, l1, l2)) + { jjCheckNAddStates(28, 30); } + break; + case 32: + if (jjCanMove_0(hiByte, i1, i2, l1, l2)) + { jjAddStates(31, 33); } + break; + case 53: + if (!jjCanMove_1(hiByte, i1, i2, l1, l2)) + break; + if (kind > 1) + kind = 1; + { jjAddStates(22, 24); } + break; + case 62: + if (jjCanMove_1(hiByte, i1, i2, l1, l2)) + { jjCheckNAddTwoStates(62, 63); } + break; + case 64: + case 65: + if (jjCanMove_1(hiByte, i1, i2, l1, l2)) + { jjCheckNAddTwoStates(65, 63); } + break; + case 68: + if (!jjCanMove_1(hiByte, i1, i2, l1, l2)) + break; + if (kind > 3) + kind = 3; + { jjAddStates(0, 2); } + break; + default : if (i1 == 0 || l1 == 0 || i2 == 0 || l2 == 0) break; else break; + } + } while(i != startsAt); + } + if (kind != 0x7fffffff) + { + jjmatchedKind = kind; + jjmatchedPos = curPos; + kind = 0x7fffffff; + } + ++curPos; + i = jjnewStateCnt; + jjnewStateCnt = startsAt; + startsAt = 72 - jjnewStateCnt; + if (i == startsAt) + return curPos; + try { curChar = input_stream.readChar(); } + catch(java.io.IOException e) { return curPos; } + } +} +private final int jjStopStringLiteralDfa_1(int pos, long active0, long active1){ + switch (pos) + { + case 0: + if ((active1 & 0x1008L) != 0x0L) + return 30; + if ((active1 & 0x200000L) != 0x0L) + return 25; + if ((active0 & 0x2aaa800037ffe00L) != 0x0L || (active1 & 0x82a000L) != 0x0L) + { + jjmatchedKind = 89; + return 8; + } + if ((active0 & 0x4000000L) != 0x0L) + return 0; + return -1; + case 1: + if ((active0 & 0x2aaa00000006200L) != 0x0L) + return 8; + if ((active0 & 0x800037f9c00L) != 0x0L || (active1 & 0x82a000L) != 0x0L) + { + if (jjmatchedPos != 1) + { + jjmatchedKind = 89; + jjmatchedPos = 1; + } + return 8; + } + return -1; + case 2: + if ((active0 & 0x8000000c800L) != 0x0L || (active1 & 0x82a000L) != 0x0L) + return 8; + if ((active0 & 0x37f1400L) != 0x0L) + { + jjmatchedKind = 89; + jjmatchedPos = 2; + return 8; + } + return -1; + case 3: + if ((active0 & 0xe0400L) != 0x0L) + return 8; + if ((active0 & 0x3711000L) != 0x0L) + { + jjmatchedKind = 89; + jjmatchedPos = 3; + return 8; + } + return -1; + case 4: + if ((active0 & 0x2600000L) != 0x0L) + { + jjmatchedKind = 89; + jjmatchedPos = 4; + return 8; + } + if ((active0 & 0x1111000L) != 0x0L) + return 8; + return -1; + case 5: + if ((active0 & 0x200000L) != 0x0L) + return 8; + if ((active0 & 0x2400000L) != 0x0L) + { + jjmatchedKind = 89; + jjmatchedPos = 5; + return 8; + } + return -1; + case 6: + if ((active0 & 0x2400000L) != 0x0L) + { + jjmatchedKind = 89; + jjmatchedPos = 6; + return 8; + } + return -1; + default : + return -1; + } +} +private final int jjStartNfa_1(int pos, long active0, long active1){ + return jjMoveNfa_1(jjStopStringLiteralDfa_1(pos, active0, active1), pos + 1); +} +private int jjMoveStringLiteralDfa0_1(){ + switch(curChar) + { + case '!': + jjmatchedKind = 80; + return jjMoveStringLiteralDfa1_1(0xc801000000000000L, 0x0L); + case '#': + return jjMoveStringLiteralDfa1_1(0x4000000L, 0x0L); + case '%': + jjmatchedKind = 78; + return jjMoveStringLiteralDfa1_1(0x0L, 0x10L); + case '&': + jjmatchedKind = 82; + return jjMoveStringLiteralDfa1_1(0x40000000000L, 0x20L); + case '(': + return jjStopAtPos(0, 27); + case ')': + return jjStopAtPos(0, 28); + case '*': + jjmatchedKind = 75; + return jjMoveStringLiteralDfa1_1(0x0L, 0x4L); + case '+': + jjmatchedKind = 73; + return jjMoveStringLiteralDfa1_1(0x0L, 0x1L); + case ',': + return jjStopAtPos(0, 35); + case '-': + jjmatchedKind = 74; + return jjMoveStringLiteralDfa1_1(0x800000L, 0x2L); + case '.': + jjmatchedKind = 36; + return jjMoveStringLiteralDfa1_1(0x4000000000L, 0x400000L); + case '/': + jjmatchedKind = 76; + return jjMoveStringLiteralDfa1_1(0x0L, 0x8L); + case ':': + return jjStopAtPos(0, 34); + case ';': + return jjStopAtPos(0, 33); + case '<': + jjmatchedKind = 54; + return jjMoveStringLiteralDfa1_1(0x100000000000000L, 0x0L); + case '=': + jjmatchedKind = 72; + return jjMoveStringLiteralDfa1_1(0x3400400000000000L, 0x0L); + case '>': + jjmatchedKind = 50; + return jjMoveStringLiteralDfa1_1(0x10000000000000L, 0x0L); + case '?': + jjmatchedKind = 39; + return jjMoveStringLiteralDfa1_1(0x32000000000L, 0x0L); + case 'N': + return jjMoveStringLiteralDfa1_1(0x0L, 0x800000L); + case '[': + return jjStopAtPos(0, 31); + case ']': + return jjStopAtPos(0, 32); + case '^': + jjmatchedKind = 84; + return jjMoveStringLiteralDfa1_1(0x0L, 0x80L); + case 'a': + return jjMoveStringLiteralDfa1_1(0x80000000000L, 0x0L); + case 'b': + return jjMoveStringLiteralDfa1_1(0x1000000L, 0x0L); + case 'c': + return jjMoveStringLiteralDfa1_1(0x2000000L, 0x0L); + case 'd': + return jjMoveStringLiteralDfa1_1(0x2000L, 0x2000L); + case 'e': + return jjMoveStringLiteralDfa1_1(0x800000010400L, 0x0L); + case 'f': + return jjMoveStringLiteralDfa1_1(0x500800L, 0x0L); + case 'g': + return jjMoveStringLiteralDfa1_1(0x28000000000000L, 0x0L); + case 'i': + return jjMoveStringLiteralDfa1_1(0x200L, 0x0L); + case 'l': + return jjMoveStringLiteralDfa1_1(0x280000000000000L, 0x0L); + case 'm': + return jjMoveStringLiteralDfa1_1(0x0L, 0x8000L); + case 'n': + return jjMoveStringLiteralDfa1_1(0x2000000044000L, 0x20000L); + case 'o': + return jjMoveStringLiteralDfa1_1(0x200000000000L, 0x0L); + case 'r': + return jjMoveStringLiteralDfa1_1(0x200000L, 0x0L); + case 's': + return jjMoveStringLiteralDfa1_1(0x20000L, 0x0L); + case 't': + return jjMoveStringLiteralDfa1_1(0x80000L, 0x0L); + case 'v': + return jjMoveStringLiteralDfa1_1(0x8000L, 0x0L); + case 'w': + return jjMoveStringLiteralDfa1_1(0x1000L, 0x0L); + case '{': + return jjStopAtPos(0, 29); + case '|': + jjmatchedKind = 83; + return jjMoveStringLiteralDfa1_1(0x100000000000L, 0x40L); + case '}': + return jjStopAtPos(0, 30); + case '~': + return jjStartNfaWithStates_1(0, 85, 25); + default : + return jjMoveNfa_1(5, 0); + } +} +private int jjMoveStringLiteralDfa1_1(long active0, long active1){ + try { curChar = input_stream.readChar(); } + catch(java.io.IOException e) { + jjStopStringLiteralDfa_1(0, active0, active1); + return 1; + } + switch(curChar) + { + case '$': + if ((active0 & 0x2000000000000000L) != 0x0L) + return jjStopAtPos(1, 61); + else if ((active0 & 0x8000000000000000L) != 0x0L) + return jjStopAtPos(1, 63); + break; + case '&': + if ((active0 & 0x40000000000L) != 0x0L) + return jjStopAtPos(1, 42); + break; + case '.': + if ((active0 & 0x2000000000L) != 0x0L) + return jjStopAtPos(1, 37); + else if ((active1 & 0x400000L) != 0x0L) + { + jjmatchedKind = 86; + jjmatchedPos = 1; + } + return jjMoveStringLiteralDfa2_1(active0, 0x4000000000L, active1, 0x0L); + case ':': + if ((active0 & 0x10000000000L) != 0x0L) + return jjStopAtPos(1, 40); + break; + case '=': + if ((active0 & 0x400000000000L) != 0x0L) + return jjStopAtPos(1, 46); + else if ((active0 & 0x1000000000000L) != 0x0L) + return jjStopAtPos(1, 48); + else if ((active0 & 0x10000000000000L) != 0x0L) + return jjStopAtPos(1, 52); + else if ((active0 & 0x100000000000000L) != 0x0L) + return jjStopAtPos(1, 56); + else if ((active1 & 0x1L) != 0x0L) + return jjStopAtPos(1, 64); + else if ((active1 & 0x2L) != 0x0L) + return jjStopAtPos(1, 65); + else if ((active1 & 0x4L) != 0x0L) + return jjStopAtPos(1, 66); + else if ((active1 & 0x8L) != 0x0L) + return jjStopAtPos(1, 67); + else if ((active1 & 0x10L) != 0x0L) + return jjStopAtPos(1, 68); + else if ((active1 & 0x20L) != 0x0L) + return jjStopAtPos(1, 69); + else if ((active1 & 0x40L) != 0x0L) + return jjStopAtPos(1, 70); + else if ((active1 & 0x80L) != 0x0L) + return jjStopAtPos(1, 71); + break; + case '>': + if ((active0 & 0x800000L) != 0x0L) + return jjStopAtPos(1, 23); + break; + case '?': + if ((active0 & 0x20000000000L) != 0x0L) + return jjStopAtPos(1, 41); + break; + case '^': + if ((active0 & 0x1000000000000000L) != 0x0L) + return jjStopAtPos(1, 60); + else if ((active0 & 0x4000000000000000L) != 0x0L) + return jjStopAtPos(1, 62); + break; + case 'a': + return jjMoveStringLiteralDfa2_1(active0, 0x108000L, active1, 0x800000L); + case 'e': + if ((active0 & 0x2000000000000L) != 0x0L) + { + jjmatchedKind = 49; + jjmatchedPos = 1; + } + else if ((active0 & 0x20000000000000L) != 0x0L) + return jjStartNfaWithStates_1(1, 53, 8); + else if ((active0 & 0x200000000000000L) != 0x0L) + return jjStartNfaWithStates_1(1, 57, 8); + return jjMoveStringLiteralDfa2_1(active0, 0x204000L, active1, 0x0L); + case 'f': + if ((active0 & 0x200L) != 0x0L) + return jjStartNfaWithStates_1(1, 9, 8); + break; + case 'h': + return jjMoveStringLiteralDfa2_1(active0, 0x1000L, active1, 0x0L); + case 'i': + return jjMoveStringLiteralDfa2_1(active0, 0x20000L, active1, 0x2000L); + case 'l': + return jjMoveStringLiteralDfa2_1(active0, 0x400L, active1, 0x0L); + case 'm': + return jjMoveStringLiteralDfa2_1(active0, 0x10000L, active1, 0x0L); + case 'n': + return jjMoveStringLiteralDfa2_1(active0, 0x80000000000L, active1, 0x0L); + case 'o': + if ((active0 & 0x2000L) != 0x0L) + return jjStartNfaWithStates_1(1, 13, 8); + return jjMoveStringLiteralDfa2_1(active0, 0x2000800L, active1, 0x28000L); + case 'p': + return jjMoveStringLiteralDfa2_1(active0, 0x4000000L, active1, 0x0L); + case 'q': + if ((active0 & 0x800000000000L) != 0x0L) + return jjStartNfaWithStates_1(1, 47, 8); + break; + case 'r': + if ((active0 & 0x200000000000L) != 0x0L) + return jjStartNfaWithStates_1(1, 45, 8); + return jjMoveStringLiteralDfa2_1(active0, 0x1080000L, active1, 0x0L); + case 't': + if ((active0 & 0x8000000000000L) != 0x0L) + return jjStartNfaWithStates_1(1, 51, 8); + else if ((active0 & 0x80000000000000L) != 0x0L) + return jjStartNfaWithStates_1(1, 55, 8); + break; + case 'u': + return jjMoveStringLiteralDfa2_1(active0, 0x440000L, active1, 0x0L); + case '|': + if ((active0 & 0x100000000000L) != 0x0L) + return jjStopAtPos(1, 44); + break; + case '~': + if ((active0 & 0x400000000000000L) != 0x0L) + return jjStopAtPos(1, 58); + else if ((active0 & 0x800000000000000L) != 0x0L) + return jjStopAtPos(1, 59); + break; + default : + break; + } + return jjStartNfa_1(0, active0, active1); +} +private int jjMoveStringLiteralDfa2_1(long old0, long active0, long old1, long active1){ + if (((active0 &= old0) | (active1 &= old1)) == 0L) + return jjStartNfa_1(0, old0, old1); + try { curChar = input_stream.readChar(); } + catch(java.io.IOException e) { + jjStopStringLiteralDfa_1(1, active0, active1); + return 2; + } + switch(curChar) + { + case '.': + if ((active0 & 0x4000000000L) != 0x0L) + return jjStopAtPos(2, 38); + break; + case 'N': + if ((active1 & 0x800000L) != 0x0L) + return jjStartNfaWithStates_1(2, 87, 8); + break; + case 'd': + if ((active0 & 0x80000000000L) != 0x0L) + return jjStartNfaWithStates_1(2, 43, 8); + else if ((active1 & 0x8000L) != 0x0L) + return jjStartNfaWithStates_1(2, 79, 8); + break; + case 'e': + return jjMoveStringLiteralDfa3_1(active0, 0x1000000L, active1, 0x0L); + case 'i': + return jjMoveStringLiteralDfa3_1(active0, 0x1000L, active1, 0x0L); + case 'l': + return jjMoveStringLiteralDfa3_1(active0, 0x140000L, active1, 0x0L); + case 'n': + return jjMoveStringLiteralDfa3_1(active0, 0x2400000L, active1, 0x0L); + case 'p': + return jjMoveStringLiteralDfa3_1(active0, 0x10000L, active1, 0x0L); + case 'r': + if ((active0 & 0x800L) != 0x0L) + return jjStartNfaWithStates_1(2, 11, 8); + else if ((active0 & 0x8000L) != 0x0L) + return jjStartNfaWithStates_1(2, 15, 8); + return jjMoveStringLiteralDfa3_1(active0, 0x4000000L, active1, 0x0L); + case 's': + return jjMoveStringLiteralDfa3_1(active0, 0x400L, active1, 0x0L); + case 't': + if ((active1 & 0x20000L) != 0x0L) + return jjStartNfaWithStates_1(2, 81, 8); + return jjMoveStringLiteralDfa3_1(active0, 0x200000L, active1, 0x0L); + case 'u': + return jjMoveStringLiteralDfa3_1(active0, 0x80000L, active1, 0x0L); + case 'v': + if ((active1 & 0x2000L) != 0x0L) + return jjStartNfaWithStates_1(2, 77, 8); + break; + case 'w': + if ((active0 & 0x4000L) != 0x0L) + return jjStartNfaWithStates_1(2, 14, 8); + break; + case 'z': + return jjMoveStringLiteralDfa3_1(active0, 0x20000L, active1, 0x0L); + default : + break; + } + return jjStartNfa_1(1, active0, active1); +} +private int jjMoveStringLiteralDfa3_1(long old0, long active0, long old1, long active1){ + if (((active0 &= old0) | (active1 &= old1)) == 0L) + return jjStartNfa_1(1, old0, old1); + try { curChar = input_stream.readChar(); } + catch(java.io.IOException e) { + jjStopStringLiteralDfa_1(2, active0, 0L); + return 3; + } + switch(curChar) + { + case 'a': + return jjMoveStringLiteralDfa4_1(active0, 0x5000000L); + case 'c': + return jjMoveStringLiteralDfa4_1(active0, 0x400000L); + case 'e': + if ((active0 & 0x400L) != 0x0L) + return jjStartNfaWithStates_1(3, 10, 8); + else if ((active0 & 0x20000L) != 0x0L) + return jjStartNfaWithStates_1(3, 17, 8); + else if ((active0 & 0x80000L) != 0x0L) + return jjStartNfaWithStates_1(3, 19, 8); + break; + case 'l': + if ((active0 & 0x40000L) != 0x0L) + return jjStartNfaWithStates_1(3, 18, 8); + return jjMoveStringLiteralDfa4_1(active0, 0x1000L); + case 's': + return jjMoveStringLiteralDfa4_1(active0, 0x100000L); + case 't': + return jjMoveStringLiteralDfa4_1(active0, 0x2010000L); + case 'u': + return jjMoveStringLiteralDfa4_1(active0, 0x200000L); + default : + break; + } + return jjStartNfa_1(2, active0, 0L); +} +private int jjMoveStringLiteralDfa4_1(long old0, long active0){ + if (((active0 &= old0)) == 0L) + return jjStartNfa_1(2, old0, 0L); + try { curChar = input_stream.readChar(); } + catch(java.io.IOException e) { + jjStopStringLiteralDfa_1(3, active0, 0L); + return 4; + } + switch(curChar) + { + case 'e': + if ((active0 & 0x1000L) != 0x0L) + return jjStartNfaWithStates_1(4, 12, 8); + else if ((active0 & 0x100000L) != 0x0L) + return jjStartNfaWithStates_1(4, 20, 8); + break; + case 'g': + return jjMoveStringLiteralDfa5_1(active0, 0x4000000L); + case 'i': + return jjMoveStringLiteralDfa5_1(active0, 0x2000000L); + case 'k': + if ((active0 & 0x1000000L) != 0x0L) + return jjStartNfaWithStates_1(4, 24, 8); + break; + case 'r': + return jjMoveStringLiteralDfa5_1(active0, 0x200000L); + case 't': + return jjMoveStringLiteralDfa5_1(active0, 0x400000L); + case 'y': + if ((active0 & 0x10000L) != 0x0L) + return jjStartNfaWithStates_1(4, 16, 8); + break; + default : + break; + } + return jjStartNfa_1(3, active0, 0L); +} +private int jjMoveStringLiteralDfa5_1(long old0, long active0){ + if (((active0 &= old0)) == 0L) + return jjStartNfa_1(3, old0, 0L); + try { curChar = input_stream.readChar(); } + catch(java.io.IOException e) { + jjStopStringLiteralDfa_1(4, active0, 0L); + return 5; + } + switch(curChar) + { + case 'i': + return jjMoveStringLiteralDfa6_1(active0, 0x400000L); + case 'm': + return jjMoveStringLiteralDfa6_1(active0, 0x4000000L); + case 'n': + if ((active0 & 0x200000L) != 0x0L) + return jjStartNfaWithStates_1(5, 21, 8); + return jjMoveStringLiteralDfa6_1(active0, 0x2000000L); + default : + break; + } + return jjStartNfa_1(4, active0, 0L); +} +private int jjMoveStringLiteralDfa6_1(long old0, long active0){ + if (((active0 &= old0)) == 0L) + return jjStartNfa_1(4, old0, 0L); + try { curChar = input_stream.readChar(); } + catch(java.io.IOException e) { + jjStopStringLiteralDfa_1(5, active0, 0L); + return 6; + } + switch(curChar) + { + case 'a': + if ((active0 & 0x4000000L) != 0x0L) + return jjStopAtPos(6, 26); + break; + case 'o': + return jjMoveStringLiteralDfa7_1(active0, 0x400000L); + case 'u': + return jjMoveStringLiteralDfa7_1(active0, 0x2000000L); + default : + break; + } + return jjStartNfa_1(5, active0, 0L); +} +private int jjMoveStringLiteralDfa7_1(long old0, long active0){ + if (((active0 &= old0)) == 0L) + return jjStartNfa_1(5, old0, 0L); + try { curChar = input_stream.readChar(); } + catch(java.io.IOException e) { + jjStopStringLiteralDfa_1(6, active0, 0L); + return 7; + } + switch(curChar) + { + case 'e': + if ((active0 & 0x2000000L) != 0x0L) + return jjStartNfaWithStates_1(7, 25, 8); + break; + case 'n': + if ((active0 & 0x400000L) != 0x0L) + return jjStartNfaWithStates_1(7, 22, 8); + break; + default : + break; + } + return jjStartNfa_1(6, active0, 0L); +} +private int jjStartNfaWithStates_1(int pos, int kind, int state) +{ + jjmatchedKind = kind; + jjmatchedPos = pos; + try { curChar = input_stream.readChar(); } + catch(java.io.IOException e) { return pos + 1; } + return jjMoveNfa_1(state, pos + 1); +} +private int jjMoveNfa_1(int startState, int curPos) +{ + int startsAt = 0; + jjnewStateCnt = 41; + int i = 1; + jjstateSet[0] = startState; + int kind = 0x7fffffff; + for (;;) + { + if (++jjround == 0x7fffffff) + ReInitRounds(); + if (curChar < 64) + { + long l = 1L << curChar; + do + { + switch(jjstateSet[--i]) + { + case 5: + if ((0x3ff001000000000L & l) != 0x0L) + { + if (kind > 89) + kind = 89; + { jjCheckNAdd(8); } + } + else if (curChar == 47) + { jjAddStates(50, 51); } + else if (curChar == 39) + { jjCheckNAddStates(52, 54); } + else if (curChar == 34) + { jjCheckNAddStates(55, 57); } + else if (curChar == 35) + jjstateSet[jjnewStateCnt++] = 0; + break; + case 30: + if (curChar == 47) + { + if (kind > 3) + kind = 3; + { jjCheckNAddStates(58, 60); } + } + else if (curChar == 42) + { jjCheckNAddTwoStates(31, 32); } + break; + case 0: + if (curChar != 35) + break; + if (kind > 1) + kind = 1; + { jjCheckNAddStates(61, 63); } + break; + case 1: + if ((0xffffffffffffdbffL & l) == 0x0L) + break; + if (kind > 1) + kind = 1; + { jjCheckNAddStates(61, 63); } + break; + case 2: + if ((0x2400L & l) != 0x0L && kind > 1) + kind = 1; + break; + case 3: + if (curChar == 10 && kind > 1) + kind = 1; + break; + case 4: + if (curChar == 13) + jjstateSet[jjnewStateCnt++] = 3; + break; + case 7: + if ((0x3ff001000000000L & l) == 0x0L) + break; + if (kind > 88) + kind = 88; + jjstateSet[jjnewStateCnt++] = 7; + break; + case 8: + if ((0x3ff001000000000L & l) == 0x0L) + break; + if (kind > 89) + kind = 89; + { jjCheckNAdd(8); } + break; + case 9: + if (curChar == 34) + { jjCheckNAddStates(55, 57); } + break; + case 10: + if ((0xfffffffbffffdbffL & l) != 0x0L) + { jjCheckNAddStates(55, 57); } + break; + case 12: + if ((0xffffffffffffdbffL & l) != 0x0L) + { jjCheckNAddStates(55, 57); } + break; + case 13: + if (curChar == 34 && kind > 104) + kind = 104; + break; + case 14: + if (curChar == 39) + { jjCheckNAddStates(52, 54); } + break; + case 15: + if ((0xffffff7fffffdbffL & l) != 0x0L) + { jjCheckNAddStates(52, 54); } + break; + case 17: + if ((0xffffffffffffdbffL & l) != 0x0L) + { jjCheckNAddStates(52, 54); } + break; + case 18: + if (curChar == 39 && kind > 104) + kind = 104; + break; + case 20: + { jjCheckNAddStates(64, 66); } + break; + case 22: + if ((0xfffffffffffffffeL & l) != 0x0L) + { jjCheckNAddStates(64, 66); } + break; + case 25: + if (curChar == 47) + { jjCheckNAddStates(67, 69); } + break; + case 26: + if ((0xffff7fffffffdbffL & l) != 0x0L) + { jjCheckNAddStates(67, 69); } + break; + case 28: + if (curChar == 47 && kind > 106) + kind = 106; + break; + case 29: + if (curChar == 47) + { jjAddStates(50, 51); } + break; + case 31: + if ((0xfffffbffffffffffL & l) != 0x0L) + { jjCheckNAddTwoStates(31, 32); } + break; + case 32: + if (curChar == 42) + { jjCheckNAddStates(70, 72); } + break; + case 33: + if ((0xffff7bffffffffffL & l) != 0x0L) + { jjCheckNAddTwoStates(34, 32); } + break; + case 34: + if ((0xfffffbffffffffffL & l) != 0x0L) + { jjCheckNAddTwoStates(34, 32); } + break; + case 35: + if (curChar == 47 && kind > 2) + kind = 2; + break; + case 36: + if (curChar != 47) + break; + if (kind > 3) + kind = 3; + { jjCheckNAddStates(58, 60); } + break; + case 37: + if ((0xffffffffffffdbffL & l) == 0x0L) + break; + if (kind > 3) + kind = 3; + { jjCheckNAddStates(58, 60); } + break; + case 38: + if ((0x2400L & l) != 0x0L && kind > 3) + kind = 3; + break; + case 39: + if (curChar == 10 && kind > 3) + kind = 3; + break; + case 40: + if (curChar == 13) + jjstateSet[jjnewStateCnt++] = 39; + break; + default : break; + } + } while(i != startsAt); + } + else if (curChar < 128) + { + long l = 1L << (curChar & 077); + do + { + switch(jjstateSet[--i]) + { + case 5: + if ((0x7fffffe87ffffffL & l) != 0x0L) + { + if (kind > 89) + kind = 89; + { jjCheckNAdd(8); } + } + else if (curChar == 126) + { jjCheckNAdd(25); } + else if (curChar == 96) + { jjCheckNAddStates(64, 66); } + if (curChar == 64) + { jjCheckNAdd(7); } + break; + case 1: + if (kind > 1) + kind = 1; + { jjAddStates(61, 63); } + break; + case 6: + if (curChar == 64) + { jjCheckNAdd(7); } + break; + case 7: + if ((0x7fffffe87fffffeL & l) == 0x0L) + break; + if (kind > 88) + kind = 88; + { jjCheckNAdd(7); } + break; + case 8: + if ((0x7fffffe87ffffffL & l) == 0x0L) + break; + if (kind > 89) + kind = 89; + { jjCheckNAdd(8); } + break; + case 10: + if ((0xffffffffefffffffL & l) != 0x0L) + { jjCheckNAddStates(55, 57); } + break; + case 11: + if (curChar == 92) + jjstateSet[jjnewStateCnt++] = 12; + break; + case 12: + { jjCheckNAddStates(55, 57); } + break; + case 15: + if ((0xffffffffefffffffL & l) != 0x0L) + { jjCheckNAddStates(52, 54); } + break; + case 16: + if (curChar == 92) + jjstateSet[jjnewStateCnt++] = 17; + break; + case 17: + { jjCheckNAddStates(52, 54); } + break; + case 19: + if (curChar == 96) + { jjCheckNAddStates(64, 66); } + break; + case 20: + if ((0xfffffffeefffffffL & l) != 0x0L) + { jjCheckNAddStates(64, 66); } + break; + case 21: + if (curChar == 92) + jjstateSet[jjnewStateCnt++] = 22; + break; + case 22: + { jjCheckNAddStates(64, 66); } + break; + case 23: + if (curChar == 96 && kind > 105) + kind = 105; + break; + case 24: + if (curChar == 126) + { jjCheckNAdd(25); } + break; + case 26: + { jjAddStates(67, 69); } + break; + case 27: + if (curChar == 92) + { jjCheckNAdd(25); } + break; + case 31: + { jjCheckNAddTwoStates(31, 32); } + break; + case 33: + case 34: + { jjCheckNAddTwoStates(34, 32); } + break; + case 37: + if (kind > 3) + kind = 3; + { jjAddStates(58, 60); } + break; + default : break; + } + } while(i != startsAt); + } + else + { + int hiByte = (curChar >> 8); + int i1 = hiByte >> 6; + long l1 = 1L << (hiByte & 077); + int i2 = (curChar & 0xff) >> 6; + long l2 = 1L << (curChar & 077); + do + { + switch(jjstateSet[--i]) + { + case 1: + if (!jjCanMove_1(hiByte, i1, i2, l1, l2)) + break; + if (kind > 1) + kind = 1; + { jjAddStates(61, 63); } + break; + case 10: + case 12: + if (jjCanMove_0(hiByte, i1, i2, l1, l2)) + { jjCheckNAddStates(55, 57); } + break; + case 15: + case 17: + if (jjCanMove_0(hiByte, i1, i2, l1, l2)) + { jjCheckNAddStates(52, 54); } + break; + case 20: + case 22: + if (jjCanMove_1(hiByte, i1, i2, l1, l2)) + { jjCheckNAddStates(64, 66); } + break; + case 26: + if (jjCanMove_0(hiByte, i1, i2, l1, l2)) + { jjAddStates(67, 69); } + break; + case 31: + if (jjCanMove_1(hiByte, i1, i2, l1, l2)) + { jjCheckNAddTwoStates(31, 32); } + break; + case 33: + case 34: + if (jjCanMove_1(hiByte, i1, i2, l1, l2)) + { jjCheckNAddTwoStates(34, 32); } + break; + case 37: + if (!jjCanMove_1(hiByte, i1, i2, l1, l2)) + break; + if (kind > 3) + kind = 3; + { jjAddStates(58, 60); } + break; + default : if (i1 == 0 || l1 == 0 || i2 == 0 || l2 == 0) break; else break; + } + } while(i != startsAt); + } + if (kind != 0x7fffffff) + { + jjmatchedKind = kind; + jjmatchedPos = curPos; + kind = 0x7fffffff; + } + ++curPos; + i = jjnewStateCnt; + jjnewStateCnt = startsAt; + startsAt = 41 - jjnewStateCnt; + if (i == startsAt) + return curPos; + try { curChar = input_stream.readChar(); } + catch(java.io.IOException e) { return curPos; } + } +} +private final int jjStopStringLiteralDfa_2(int pos, long active0, long active1){ + switch (pos) + { + case 0: + if ((active0 & 0x2aaa800037ffe00L) != 0x0L || (active1 & 0x82a000L) != 0x0L) + { + jjmatchedKind = 90; + return 74; + } + if ((active0 & 0x4000000L) != 0x0L) + return 54; + if ((active0 & 0x5000000000L) != 0x0L || (active1 & 0x400000L) != 0x0L) + return 12; + if ((active1 & 0x200000L) != 0x0L) + return 33; + if ((active1 & 0x1008L) != 0x0L) + return 63; + return -1; + case 1: + if ((active0 & 0x2aaa00000006200L) != 0x0L) + return 74; + if ((active0 & 0x800037f9c00L) != 0x0L || (active1 & 0x82a000L) != 0x0L) + { + if (jjmatchedPos != 1) + { + jjmatchedKind = 90; + jjmatchedPos = 1; + } + return 74; + } + return -1; + case 2: + if ((active0 & 0x8000000c800L) != 0x0L || (active1 & 0x82a000L) != 0x0L) + return 74; + if ((active0 & 0x37f1400L) != 0x0L) + { + jjmatchedKind = 90; + jjmatchedPos = 2; + return 74; + } + return -1; + case 3: + if ((active0 & 0x3711000L) != 0x0L) + { + jjmatchedKind = 90; + jjmatchedPos = 3; + return 74; + } + if ((active0 & 0xe0400L) != 0x0L) + return 74; + return -1; + case 4: + if ((active0 & 0x2600000L) != 0x0L) + { + jjmatchedKind = 90; + jjmatchedPos = 4; + return 74; + } + if ((active0 & 0x1111000L) != 0x0L) + return 74; + return -1; + case 5: + if ((active0 & 0x200000L) != 0x0L) + return 74; + if ((active0 & 0x2400000L) != 0x0L) + { + jjmatchedKind = 90; + jjmatchedPos = 5; + return 74; + } + return -1; + case 6: + if ((active0 & 0x2400000L) != 0x0L) + { + jjmatchedKind = 90; + jjmatchedPos = 6; + return 74; + } + return -1; + default : + return -1; + } +} +private final int jjStartNfa_2(int pos, long active0, long active1){ + return jjMoveNfa_2(jjStopStringLiteralDfa_2(pos, active0, active1), pos + 1); +} +private int jjMoveStringLiteralDfa0_2(){ + switch(curChar) + { + case '!': + jjmatchedKind = 80; + return jjMoveStringLiteralDfa1_2(0xc801000000000000L, 0x0L); + case '#': + return jjMoveStringLiteralDfa1_2(0x4000000L, 0x0L); + case '%': + jjmatchedKind = 78; + return jjMoveStringLiteralDfa1_2(0x0L, 0x10L); + case '&': + jjmatchedKind = 82; + return jjMoveStringLiteralDfa1_2(0x40000000000L, 0x20L); + case '(': + return jjStopAtPos(0, 27); + case ')': + return jjStopAtPos(0, 28); + case '*': + jjmatchedKind = 75; + return jjMoveStringLiteralDfa1_2(0x0L, 0x4L); + case '+': + jjmatchedKind = 73; + return jjMoveStringLiteralDfa1_2(0x0L, 0x1L); + case ',': + return jjStopAtPos(0, 35); + case '-': + jjmatchedKind = 74; + return jjMoveStringLiteralDfa1_2(0x800000L, 0x2L); + case '.': + jjmatchedKind = 36; + return jjMoveStringLiteralDfa1_2(0x4000000000L, 0x400000L); + case '/': + jjmatchedKind = 76; + return jjMoveStringLiteralDfa1_2(0x0L, 0x8L); + case ':': + return jjStopAtPos(0, 34); + case ';': + return jjStopAtPos(0, 33); + case '<': + jjmatchedKind = 54; + return jjMoveStringLiteralDfa1_2(0x100000000000000L, 0x0L); + case '=': + jjmatchedKind = 72; + return jjMoveStringLiteralDfa1_2(0x3400400000000000L, 0x0L); + case '>': + jjmatchedKind = 50; + return jjMoveStringLiteralDfa1_2(0x10000000000000L, 0x0L); + case '?': + jjmatchedKind = 39; + return jjMoveStringLiteralDfa1_2(0x32000000000L, 0x0L); + case 'N': + return jjMoveStringLiteralDfa1_2(0x0L, 0x800000L); + case '[': + return jjStopAtPos(0, 31); + case ']': + return jjStopAtPos(0, 32); + case '^': + jjmatchedKind = 84; + return jjMoveStringLiteralDfa1_2(0x0L, 0x80L); + case 'a': + return jjMoveStringLiteralDfa1_2(0x80000000000L, 0x0L); + case 'b': + return jjMoveStringLiteralDfa1_2(0x1000000L, 0x0L); + case 'c': + return jjMoveStringLiteralDfa1_2(0x2000000L, 0x0L); + case 'd': + return jjMoveStringLiteralDfa1_2(0x2000L, 0x2000L); + case 'e': + return jjMoveStringLiteralDfa1_2(0x800000010400L, 0x0L); + case 'f': + return jjMoveStringLiteralDfa1_2(0x500800L, 0x0L); + case 'g': + return jjMoveStringLiteralDfa1_2(0x28000000000000L, 0x0L); + case 'i': + return jjMoveStringLiteralDfa1_2(0x200L, 0x0L); + case 'l': + return jjMoveStringLiteralDfa1_2(0x280000000000000L, 0x0L); + case 'm': + return jjMoveStringLiteralDfa1_2(0x0L, 0x8000L); + case 'n': + return jjMoveStringLiteralDfa1_2(0x2000000044000L, 0x20000L); + case 'o': + return jjMoveStringLiteralDfa1_2(0x200000000000L, 0x0L); + case 'r': + return jjMoveStringLiteralDfa1_2(0x200000L, 0x0L); + case 's': + return jjMoveStringLiteralDfa1_2(0x20000L, 0x0L); + case 't': + return jjMoveStringLiteralDfa1_2(0x80000L, 0x0L); + case 'v': + return jjMoveStringLiteralDfa1_2(0x8000L, 0x0L); + case 'w': + return jjMoveStringLiteralDfa1_2(0x1000L, 0x0L); + case '{': + return jjStopAtPos(0, 29); + case '|': + jjmatchedKind = 83; + return jjMoveStringLiteralDfa1_2(0x100000000000L, 0x40L); + case '}': + return jjStopAtPos(0, 30); + case '~': + return jjStartNfaWithStates_2(0, 85, 33); + default : + return jjMoveNfa_2(0, 0); + } +} +private int jjMoveStringLiteralDfa1_2(long active0, long active1){ + try { curChar = input_stream.readChar(); } + catch(java.io.IOException e) { + jjStopStringLiteralDfa_2(0, active0, active1); + return 1; + } + switch(curChar) + { + case '$': + if ((active0 & 0x2000000000000000L) != 0x0L) + return jjStopAtPos(1, 61); + else if ((active0 & 0x8000000000000000L) != 0x0L) + return jjStopAtPos(1, 63); + break; + case '&': + if ((active0 & 0x40000000000L) != 0x0L) + return jjStopAtPos(1, 42); + break; + case '.': + if ((active0 & 0x2000000000L) != 0x0L) + return jjStopAtPos(1, 37); + else if ((active1 & 0x400000L) != 0x0L) + { + jjmatchedKind = 86; + jjmatchedPos = 1; + } + return jjMoveStringLiteralDfa2_2(active0, 0x4000000000L, active1, 0x0L); + case ':': + if ((active0 & 0x10000000000L) != 0x0L) + return jjStopAtPos(1, 40); + break; + case '=': + if ((active0 & 0x400000000000L) != 0x0L) + return jjStopAtPos(1, 46); + else if ((active0 & 0x1000000000000L) != 0x0L) + return jjStopAtPos(1, 48); + else if ((active0 & 0x10000000000000L) != 0x0L) + return jjStopAtPos(1, 52); + else if ((active0 & 0x100000000000000L) != 0x0L) + return jjStopAtPos(1, 56); + else if ((active1 & 0x1L) != 0x0L) + return jjStopAtPos(1, 64); + else if ((active1 & 0x2L) != 0x0L) + return jjStopAtPos(1, 65); + else if ((active1 & 0x4L) != 0x0L) + return jjStopAtPos(1, 66); + else if ((active1 & 0x8L) != 0x0L) + return jjStopAtPos(1, 67); + else if ((active1 & 0x10L) != 0x0L) + return jjStopAtPos(1, 68); + else if ((active1 & 0x20L) != 0x0L) + return jjStopAtPos(1, 69); + else if ((active1 & 0x40L) != 0x0L) + return jjStopAtPos(1, 70); + else if ((active1 & 0x80L) != 0x0L) + return jjStopAtPos(1, 71); + break; + case '>': + if ((active0 & 0x800000L) != 0x0L) + return jjStopAtPos(1, 23); + break; + case '?': + if ((active0 & 0x20000000000L) != 0x0L) + return jjStopAtPos(1, 41); + break; + case '^': + if ((active0 & 0x1000000000000000L) != 0x0L) + return jjStopAtPos(1, 60); + else if ((active0 & 0x4000000000000000L) != 0x0L) + return jjStopAtPos(1, 62); + break; + case 'a': + return jjMoveStringLiteralDfa2_2(active0, 0x108000L, active1, 0x800000L); + case 'e': + if ((active0 & 0x2000000000000L) != 0x0L) + { + jjmatchedKind = 49; + jjmatchedPos = 1; + } + else if ((active0 & 0x20000000000000L) != 0x0L) + return jjStartNfaWithStates_2(1, 53, 74); + else if ((active0 & 0x200000000000000L) != 0x0L) + return jjStartNfaWithStates_2(1, 57, 74); + return jjMoveStringLiteralDfa2_2(active0, 0x204000L, active1, 0x0L); + case 'f': + if ((active0 & 0x200L) != 0x0L) + return jjStartNfaWithStates_2(1, 9, 74); + break; + case 'h': + return jjMoveStringLiteralDfa2_2(active0, 0x1000L, active1, 0x0L); + case 'i': + return jjMoveStringLiteralDfa2_2(active0, 0x20000L, active1, 0x2000L); + case 'l': + return jjMoveStringLiteralDfa2_2(active0, 0x400L, active1, 0x0L); + case 'm': + return jjMoveStringLiteralDfa2_2(active0, 0x10000L, active1, 0x0L); + case 'n': + return jjMoveStringLiteralDfa2_2(active0, 0x80000000000L, active1, 0x0L); + case 'o': + if ((active0 & 0x2000L) != 0x0L) + return jjStartNfaWithStates_2(1, 13, 74); + return jjMoveStringLiteralDfa2_2(active0, 0x2000800L, active1, 0x28000L); + case 'p': + return jjMoveStringLiteralDfa2_2(active0, 0x4000000L, active1, 0x0L); + case 'q': + if ((active0 & 0x800000000000L) != 0x0L) + return jjStartNfaWithStates_2(1, 47, 74); + break; + case 'r': + if ((active0 & 0x200000000000L) != 0x0L) + return jjStartNfaWithStates_2(1, 45, 74); + return jjMoveStringLiteralDfa2_2(active0, 0x1080000L, active1, 0x0L); + case 't': + if ((active0 & 0x8000000000000L) != 0x0L) + return jjStartNfaWithStates_2(1, 51, 74); + else if ((active0 & 0x80000000000000L) != 0x0L) + return jjStartNfaWithStates_2(1, 55, 74); + break; + case 'u': + return jjMoveStringLiteralDfa2_2(active0, 0x440000L, active1, 0x0L); + case '|': + if ((active0 & 0x100000000000L) != 0x0L) + return jjStopAtPos(1, 44); + break; + case '~': + if ((active0 & 0x400000000000000L) != 0x0L) + return jjStopAtPos(1, 58); + else if ((active0 & 0x800000000000000L) != 0x0L) + return jjStopAtPos(1, 59); + break; + default : + break; + } + return jjStartNfa_2(0, active0, active1); +} +private int jjMoveStringLiteralDfa2_2(long old0, long active0, long old1, long active1){ + if (((active0 &= old0) | (active1 &= old1)) == 0L) + return jjStartNfa_2(0, old0, old1); + try { curChar = input_stream.readChar(); } + catch(java.io.IOException e) { + jjStopStringLiteralDfa_2(1, active0, active1); + return 2; + } + switch(curChar) + { + case '.': + if ((active0 & 0x4000000000L) != 0x0L) + return jjStopAtPos(2, 38); + break; + case 'N': + if ((active1 & 0x800000L) != 0x0L) + return jjStartNfaWithStates_2(2, 87, 74); + break; + case 'd': + if ((active0 & 0x80000000000L) != 0x0L) + return jjStartNfaWithStates_2(2, 43, 74); + else if ((active1 & 0x8000L) != 0x0L) + return jjStartNfaWithStates_2(2, 79, 74); + break; + case 'e': + return jjMoveStringLiteralDfa3_2(active0, 0x1000000L, active1, 0x0L); + case 'i': + return jjMoveStringLiteralDfa3_2(active0, 0x1000L, active1, 0x0L); + case 'l': + return jjMoveStringLiteralDfa3_2(active0, 0x140000L, active1, 0x0L); + case 'n': + return jjMoveStringLiteralDfa3_2(active0, 0x2400000L, active1, 0x0L); + case 'p': + return jjMoveStringLiteralDfa3_2(active0, 0x10000L, active1, 0x0L); + case 'r': + if ((active0 & 0x800L) != 0x0L) + return jjStartNfaWithStates_2(2, 11, 74); + else if ((active0 & 0x8000L) != 0x0L) + return jjStartNfaWithStates_2(2, 15, 74); + return jjMoveStringLiteralDfa3_2(active0, 0x4000000L, active1, 0x0L); + case 's': + return jjMoveStringLiteralDfa3_2(active0, 0x400L, active1, 0x0L); + case 't': + if ((active1 & 0x20000L) != 0x0L) + return jjStartNfaWithStates_2(2, 81, 74); + return jjMoveStringLiteralDfa3_2(active0, 0x200000L, active1, 0x0L); + case 'u': + return jjMoveStringLiteralDfa3_2(active0, 0x80000L, active1, 0x0L); + case 'v': + if ((active1 & 0x2000L) != 0x0L) + return jjStartNfaWithStates_2(2, 77, 74); + break; + case 'w': + if ((active0 & 0x4000L) != 0x0L) + return jjStartNfaWithStates_2(2, 14, 74); + break; + case 'z': + return jjMoveStringLiteralDfa3_2(active0, 0x20000L, active1, 0x0L); + default : + break; + } + return jjStartNfa_2(1, active0, active1); +} +private int jjMoveStringLiteralDfa3_2(long old0, long active0, long old1, long active1){ + if (((active0 &= old0) | (active1 &= old1)) == 0L) + return jjStartNfa_2(1, old0, old1); + try { curChar = input_stream.readChar(); } + catch(java.io.IOException e) { + jjStopStringLiteralDfa_2(2, active0, 0L); + return 3; + } + switch(curChar) + { + case 'a': + return jjMoveStringLiteralDfa4_2(active0, 0x5000000L); + case 'c': + return jjMoveStringLiteralDfa4_2(active0, 0x400000L); + case 'e': + if ((active0 & 0x400L) != 0x0L) + return jjStartNfaWithStates_2(3, 10, 74); + else if ((active0 & 0x20000L) != 0x0L) + return jjStartNfaWithStates_2(3, 17, 74); + else if ((active0 & 0x80000L) != 0x0L) + return jjStartNfaWithStates_2(3, 19, 74); + break; + case 'l': + if ((active0 & 0x40000L) != 0x0L) + return jjStartNfaWithStates_2(3, 18, 74); + return jjMoveStringLiteralDfa4_2(active0, 0x1000L); + case 's': + return jjMoveStringLiteralDfa4_2(active0, 0x100000L); + case 't': + return jjMoveStringLiteralDfa4_2(active0, 0x2010000L); + case 'u': + return jjMoveStringLiteralDfa4_2(active0, 0x200000L); + default : + break; + } + return jjStartNfa_2(2, active0, 0L); +} +private int jjMoveStringLiteralDfa4_2(long old0, long active0){ + if (((active0 &= old0)) == 0L) + return jjStartNfa_2(2, old0, 0L); + try { curChar = input_stream.readChar(); } + catch(java.io.IOException e) { + jjStopStringLiteralDfa_2(3, active0, 0L); + return 4; + } + switch(curChar) + { + case 'e': + if ((active0 & 0x1000L) != 0x0L) + return jjStartNfaWithStates_2(4, 12, 74); + else if ((active0 & 0x100000L) != 0x0L) + return jjStartNfaWithStates_2(4, 20, 74); + break; + case 'g': + return jjMoveStringLiteralDfa5_2(active0, 0x4000000L); + case 'i': + return jjMoveStringLiteralDfa5_2(active0, 0x2000000L); + case 'k': + if ((active0 & 0x1000000L) != 0x0L) + return jjStartNfaWithStates_2(4, 24, 74); + break; + case 'r': + return jjMoveStringLiteralDfa5_2(active0, 0x200000L); + case 't': + return jjMoveStringLiteralDfa5_2(active0, 0x400000L); + case 'y': + if ((active0 & 0x10000L) != 0x0L) + return jjStartNfaWithStates_2(4, 16, 74); + break; + default : + break; + } + return jjStartNfa_2(3, active0, 0L); +} +private int jjMoveStringLiteralDfa5_2(long old0, long active0){ + if (((active0 &= old0)) == 0L) + return jjStartNfa_2(3, old0, 0L); + try { curChar = input_stream.readChar(); } + catch(java.io.IOException e) { + jjStopStringLiteralDfa_2(4, active0, 0L); + return 5; + } + switch(curChar) + { + case 'i': + return jjMoveStringLiteralDfa6_2(active0, 0x400000L); + case 'm': + return jjMoveStringLiteralDfa6_2(active0, 0x4000000L); + case 'n': + if ((active0 & 0x200000L) != 0x0L) + return jjStartNfaWithStates_2(5, 21, 74); + return jjMoveStringLiteralDfa6_2(active0, 0x2000000L); + default : + break; + } + return jjStartNfa_2(4, active0, 0L); +} +private int jjMoveStringLiteralDfa6_2(long old0, long active0){ + if (((active0 &= old0)) == 0L) + return jjStartNfa_2(4, old0, 0L); + try { curChar = input_stream.readChar(); } + catch(java.io.IOException e) { + jjStopStringLiteralDfa_2(5, active0, 0L); + return 6; + } + switch(curChar) + { + case 'a': + if ((active0 & 0x4000000L) != 0x0L) + return jjStopAtPos(6, 26); + break; + case 'o': + return jjMoveStringLiteralDfa7_2(active0, 0x400000L); + case 'u': + return jjMoveStringLiteralDfa7_2(active0, 0x2000000L); + default : + break; + } + return jjStartNfa_2(5, active0, 0L); +} +private int jjMoveStringLiteralDfa7_2(long old0, long active0){ + if (((active0 &= old0)) == 0L) + return jjStartNfa_2(5, old0, 0L); + try { curChar = input_stream.readChar(); } + catch(java.io.IOException e) { + jjStopStringLiteralDfa_2(6, active0, 0L); + return 7; + } + switch(curChar) + { + case 'e': + if ((active0 & 0x2000000L) != 0x0L) + return jjStartNfaWithStates_2(7, 25, 74); + break; + case 'n': + if ((active0 & 0x400000L) != 0x0L) + return jjStartNfaWithStates_2(7, 22, 74); + break; + default : + break; + } + return jjStartNfa_2(6, active0, 0L); +} +private int jjStartNfaWithStates_2(int pos, int kind, int state) +{ + jjmatchedKind = kind; + jjmatchedPos = pos; + try { curChar = input_stream.readChar(); } + catch(java.io.IOException e) { return pos + 1; } + return jjMoveNfa_2(state, pos + 1); +} +private int jjMoveNfa_2(int startState, int curPos) +{ + int startsAt = 0; + jjnewStateCnt = 74; + int i = 1; + jjstateSet[0] = startState; + int kind = 0x7fffffff; + for (;;) + { + if (++jjround == 0x7fffffff) + ReInitRounds(); + if (curChar < 64) + { + long l = 1L << curChar; + do + { + switch(jjstateSet[--i]) + { + case 0: + if ((0x3ff000000000000L & l) != 0x0L) + { jjCheckNAddStates(73, 78); } + else if (curChar == 47) + { jjAddStates(79, 80); } + else if (curChar == 35) + { jjAddStates(81, 82); } + else if (curChar == 39) + { jjCheckNAddStates(83, 85); } + else if (curChar == 34) + { jjCheckNAddStates(86, 88); } + else if (curChar == 46) + { jjCheckNAdd(12); } + else if (curChar == 36) + { + if (kind > 90) + kind = 90; + { jjCheckNAddTwoStates(3, 4); } + } + if ((0x3fe000000000000L & l) != 0x0L) + { + if (kind > 95) + kind = 95; + { jjCheckNAddTwoStates(9, 10); } + } + else if (curChar == 48) + { + if (kind > 95) + kind = 95; + { jjCheckNAddStates(89, 91); } + } + else if (curChar == 35) + { jjCheckNAdd(7); } + break; + case 63: + if (curChar == 47) + { + if (kind > 3) + kind = 3; + { jjCheckNAddStates(92, 94); } + } + else if (curChar == 42) + { jjCheckNAddTwoStates(64, 65); } + break; + case 54: + if ((0x3ff000000000000L & l) != 0x0L) + { + if (kind > 94) + kind = 94; + { jjCheckNAdd(7); } + } + else if (curChar == 35) + { + if (kind > 1) + kind = 1; + { jjCheckNAddStates(95, 97); } + } + break; + case 74: + case 3: + if ((0x3ff001000000000L & l) == 0x0L) + break; + if (kind > 90) + kind = 90; + { jjCheckNAddTwoStates(3, 4); } + break; + case 1: + if ((0x3ff001000000000L & l) == 0x0L) + break; + if (kind > 88) + kind = 88; + jjstateSet[jjnewStateCnt++] = 1; + break; + case 2: + if (curChar != 36) + break; + if (kind > 90) + kind = 90; + { jjCheckNAddTwoStates(3, 4); } + break; + case 5: + if ((0x8500000000L & l) == 0x0L) + break; + if (kind > 90) + kind = 90; + { jjCheckNAddTwoStates(3, 4); } + break; + case 6: + if (curChar == 35) + { jjCheckNAdd(7); } + break; + case 7: + if ((0x3ff000000000000L & l) == 0x0L) + break; + if (kind > 94) + kind = 94; + { jjCheckNAdd(7); } + break; + case 8: + if ((0x3fe000000000000L & l) == 0x0L) + break; + if (kind > 95) + kind = 95; + { jjCheckNAddTwoStates(9, 10); } + break; + case 9: + if ((0x3ff000000000000L & l) == 0x0L) + break; + if (kind > 95) + kind = 95; + { jjCheckNAddTwoStates(9, 10); } + break; + case 11: + if (curChar == 46) + { jjCheckNAdd(12); } + break; + case 12: + if ((0x3ff000000000000L & l) != 0x0L) + { jjCheckNAddStates(98, 100); } + break; + case 14: + if ((0x280000000000L & l) != 0x0L) + { jjCheckNAdd(15); } + break; + case 15: + if ((0x3ff000000000000L & l) == 0x0L) + break; + if (kind > 100) + kind = 100; + { jjCheckNAddTwoStates(15, 16); } + break; + case 17: + if (curChar == 34) + { jjCheckNAddStates(86, 88); } + break; + case 18: + if ((0xfffffffbffffdbffL & l) != 0x0L) + { jjCheckNAddStates(86, 88); } + break; + case 20: + if ((0xffffffffffffdbffL & l) != 0x0L) + { jjCheckNAddStates(86, 88); } + break; + case 21: + if (curChar == 34 && kind > 104) + kind = 104; + break; + case 22: + if (curChar == 39) + { jjCheckNAddStates(83, 85); } + break; + case 23: + if ((0xffffff7fffffdbffL & l) != 0x0L) + { jjCheckNAddStates(83, 85); } + break; + case 25: + if ((0xffffffffffffdbffL & l) != 0x0L) + { jjCheckNAddStates(83, 85); } + break; + case 26: + if (curChar == 39 && kind > 104) + kind = 104; + break; + case 28: + { jjCheckNAddStates(101, 103); } + break; + case 30: + if ((0xfffffffffffffffeL & l) != 0x0L) + { jjCheckNAddStates(101, 103); } + break; + case 33: + if (curChar == 47) + { jjCheckNAddStates(104, 106); } + break; + case 34: + if ((0xffff7fffffffdbffL & l) != 0x0L) + { jjCheckNAddStates(104, 106); } + break; + case 36: + if (curChar == 47 && kind > 106) + kind = 106; + break; + case 37: + if ((0x3ff000000000000L & l) != 0x0L) + { jjCheckNAddStates(73, 78); } + break; + case 38: + if ((0x3ff000000000000L & l) != 0x0L) + { jjCheckNAddTwoStates(38, 39); } + break; + case 39: + if (curChar == 46) + { jjCheckNAdd(40); } + break; + case 40: + if ((0x3ff000000000000L & l) == 0x0L) + break; + if (kind > 100) + kind = 100; + { jjCheckNAddStates(107, 109); } + break; + case 42: + if ((0x280000000000L & l) != 0x0L) + { jjCheckNAdd(43); } + break; + case 43: + if ((0x3ff000000000000L & l) == 0x0L) + break; + if (kind > 100) + kind = 100; + { jjCheckNAddTwoStates(43, 16); } + break; + case 44: + if ((0x3ff000000000000L & l) != 0x0L) + { jjCheckNAddStates(110, 113); } + break; + case 45: + if (curChar == 46) + { jjCheckNAddTwoStates(46, 16); } + break; + case 47: + if ((0x280000000000L & l) != 0x0L) + { jjCheckNAdd(48); } + break; + case 48: + if ((0x3ff000000000000L & l) == 0x0L) + break; + if (kind > 100) + kind = 100; + { jjCheckNAddTwoStates(48, 16); } + break; + case 49: + if (curChar != 48) + break; + if (kind > 95) + kind = 95; + { jjCheckNAddStates(89, 91); } + break; + case 51: + if ((0x3ff000000000000L & l) == 0x0L) + break; + if (kind > 95) + kind = 95; + { jjCheckNAddTwoStates(51, 10); } + break; + case 52: + if ((0xff000000000000L & l) == 0x0L) + break; + if (kind > 95) + kind = 95; + { jjCheckNAddTwoStates(52, 10); } + break; + case 53: + if (curChar == 35) + { jjAddStates(81, 82); } + break; + case 55: + if ((0xffffffffffffdbffL & l) == 0x0L) + break; + if (kind > 1) + kind = 1; + { jjCheckNAddStates(95, 97); } + break; + case 56: + if ((0x2400L & l) != 0x0L && kind > 1) + kind = 1; + break; + case 57: + if (curChar == 10 && kind > 1) + kind = 1; + break; + case 58: + if (curChar == 13) + jjstateSet[jjnewStateCnt++] = 57; + break; + case 62: + if (curChar == 47) + { jjAddStates(79, 80); } + break; + case 64: + if ((0xfffffbffffffffffL & l) != 0x0L) + { jjCheckNAddTwoStates(64, 65); } + break; + case 65: + if (curChar == 42) + { jjCheckNAddStates(114, 116); } + break; + case 66: + if ((0xffff7bffffffffffL & l) != 0x0L) + { jjCheckNAddTwoStates(67, 65); } + break; + case 67: + if ((0xfffffbffffffffffL & l) != 0x0L) + { jjCheckNAddTwoStates(67, 65); } + break; + case 68: + if (curChar == 47 && kind > 2) + kind = 2; + break; + case 69: + if (curChar != 47) + break; + if (kind > 3) + kind = 3; + { jjCheckNAddStates(92, 94); } + break; + case 70: + if ((0xffffffffffffdbffL & l) == 0x0L) + break; + if (kind > 3) + kind = 3; + { jjCheckNAddStates(92, 94); } + break; + case 71: + if ((0x2400L & l) != 0x0L && kind > 3) + kind = 3; + break; + case 72: + if (curChar == 10 && kind > 3) + kind = 3; + break; + case 73: + if (curChar == 13) + jjstateSet[jjnewStateCnt++] = 72; + break; + default : break; + } + } while(i != startsAt); + } + else if (curChar < 128) + { + long l = 1L << (curChar & 077); + do + { + switch(jjstateSet[--i]) + { + case 0: + if ((0x7fffffe87ffffffL & l) != 0x0L) + { + if (kind > 90) + kind = 90; + { jjCheckNAddTwoStates(3, 4); } + } + else if (curChar == 126) + { jjCheckNAdd(33); } + else if (curChar == 96) + { jjCheckNAddStates(101, 103); } + if (curChar == 64) + { jjCheckNAdd(1); } + break; + case 54: + if (curChar == 78) + jjstateSet[jjnewStateCnt++] = 60; + break; + case 74: + if ((0x7fffffe87ffffffL & l) != 0x0L) + { + if (kind > 90) + kind = 90; + { jjCheckNAddTwoStates(3, 4); } + } + else if (curChar == 92) + jjstateSet[jjnewStateCnt++] = 5; + break; + case 1: + if ((0x7fffffe87fffffeL & l) == 0x0L) + break; + if (kind > 88) + kind = 88; + { jjCheckNAdd(1); } + break; + case 2: + if ((0x7fffffe87ffffffL & l) == 0x0L) + break; + if (kind > 90) + kind = 90; + { jjCheckNAddTwoStates(3, 4); } + break; + case 3: + if ((0x7fffffe87ffffffL & l) == 0x0L) + break; + if (kind > 90) + kind = 90; + { jjCheckNAddTwoStates(3, 4); } + break; + case 4: + if (curChar == 92) + jjstateSet[jjnewStateCnt++] = 5; + break; + case 5: + if (curChar != 92) + break; + if (kind > 90) + kind = 90; + { jjCheckNAddTwoStates(3, 4); } + break; + case 10: + if ((0x110000001100L & l) != 0x0L && kind > 95) + kind = 95; + break; + case 13: + if ((0x2000000020L & l) != 0x0L) + { jjAddStates(117, 118); } + break; + case 16: + if ((0x5400000054L & l) != 0x0L && kind > 100) + kind = 100; + break; + case 18: + if ((0xffffffffefffffffL & l) != 0x0L) + { jjCheckNAddStates(86, 88); } + break; + case 19: + if (curChar == 92) + jjstateSet[jjnewStateCnt++] = 20; + break; + case 20: + { jjCheckNAddStates(86, 88); } + break; + case 23: + if ((0xffffffffefffffffL & l) != 0x0L) + { jjCheckNAddStates(83, 85); } + break; + case 24: + if (curChar == 92) + jjstateSet[jjnewStateCnt++] = 25; + break; + case 25: + { jjCheckNAddStates(83, 85); } + break; + case 27: + if (curChar == 96) + { jjCheckNAddStates(101, 103); } + break; + case 28: + if ((0xfffffffeefffffffL & l) != 0x0L) + { jjCheckNAddStates(101, 103); } + break; + case 29: + if (curChar == 92) + jjstateSet[jjnewStateCnt++] = 30; + break; + case 30: + { jjCheckNAddStates(101, 103); } + break; + case 31: + if (curChar == 96 && kind > 105) + kind = 105; + break; + case 32: + if (curChar == 126) + { jjCheckNAdd(33); } + break; + case 34: + { jjAddStates(104, 106); } + break; + case 35: + if (curChar == 92) + { jjCheckNAdd(33); } + break; + case 41: + if ((0x2000000020L & l) != 0x0L) + { jjAddStates(119, 120); } + break; + case 46: + if ((0x2000000020L & l) != 0x0L) + { jjAddStates(121, 122); } + break; + case 50: + if ((0x100000001000000L & l) != 0x0L) + { jjCheckNAdd(51); } + break; + case 51: + if ((0x7e0000007eL & l) == 0x0L) + break; + if (kind > 95) + kind = 95; + { jjCheckNAddTwoStates(51, 10); } + break; + case 55: + if (kind > 1) + kind = 1; + { jjAddStates(95, 97); } + break; + case 59: + if (curChar == 78 && kind > 100) + kind = 100; + break; + case 60: + if (curChar == 97) + jjstateSet[jjnewStateCnt++] = 59; + break; + case 64: + { jjCheckNAddTwoStates(64, 65); } + break; + case 66: + case 67: + { jjCheckNAddTwoStates(67, 65); } + break; + case 70: + if (kind > 3) + kind = 3; + { jjAddStates(92, 94); } + break; + default : break; + } + } while(i != startsAt); + } + else + { + int hiByte = (curChar >> 8); + int i1 = hiByte >> 6; + long l1 = 1L << (hiByte & 077); + int i2 = (curChar & 0xff) >> 6; + long l2 = 1L << (curChar & 077); + do + { + switch(jjstateSet[--i]) + { + case 18: + case 20: + if (jjCanMove_0(hiByte, i1, i2, l1, l2)) + { jjCheckNAddStates(86, 88); } + break; + case 23: + case 25: + if (jjCanMove_0(hiByte, i1, i2, l1, l2)) + { jjCheckNAddStates(83, 85); } + break; + case 28: + case 30: + if (jjCanMove_1(hiByte, i1, i2, l1, l2)) + { jjCheckNAddStates(101, 103); } + break; + case 34: + if (jjCanMove_0(hiByte, i1, i2, l1, l2)) + { jjAddStates(104, 106); } + break; + case 55: + if (!jjCanMove_1(hiByte, i1, i2, l1, l2)) + break; + if (kind > 1) + kind = 1; + { jjAddStates(95, 97); } + break; + case 64: + if (jjCanMove_1(hiByte, i1, i2, l1, l2)) + { jjCheckNAddTwoStates(64, 65); } + break; + case 66: + case 67: + if (jjCanMove_1(hiByte, i1, i2, l1, l2)) + { jjCheckNAddTwoStates(67, 65); } + break; + case 70: + if (!jjCanMove_1(hiByte, i1, i2, l1, l2)) + break; + if (kind > 3) + kind = 3; + { jjAddStates(92, 94); } + break; + default : if (i1 == 0 || l1 == 0 || i2 == 0 || l2 == 0) break; else break; + } + } while(i != startsAt); + } + if (kind != 0x7fffffff) + { + jjmatchedKind = kind; + jjmatchedPos = curPos; + kind = 0x7fffffff; + } + ++curPos; + i = jjnewStateCnt; + jjnewStateCnt = startsAt; + startsAt = 74 - jjnewStateCnt; + if (i == startsAt) + return curPos; + try { curChar = input_stream.readChar(); } + catch(java.io.IOException e) { return curPos; } + } +} + +/** Token literal values. */ +public static final String[] jjstrLiteralImages = { +"", null, null, null, null, null, null, null, null, "\151\146", +"\145\154\163\145", "\146\157\162", "\167\150\151\154\145", "\144\157", "\156\145\167", +"\166\141\162", "\145\155\160\164\171", "\163\151\172\145", "\156\165\154\154", +"\164\162\165\145", "\146\141\154\163\145", "\162\145\164\165\162\156", +"\146\165\156\143\164\151\157\156", "\55\76", "\142\162\145\141\153", "\143\157\156\164\151\156\165\145", +"\43\160\162\141\147\155\141", "\50", "\51", "\173", "\175", "\133", "\135", "\73", "\72", "\54", "\56", +"\77\56", "\56\56\56", "\77", "\77\72", "\77\77", "\46\46", "\141\156\144", "\174\174", +"\157\162", "\75\75", "\145\161", "\41\75", "\156\145", "\76", "\147\164", "\76\75", +"\147\145", "\74", "\154\164", "\74\75", "\154\145", "\75\176", "\41\176", "\75\136", +"\75\44", "\41\136", "\41\44", "\53\75", "\55\75", "\52\75", "\57\75", "\45\75", +"\46\75", "\174\75", "\136\75", "\75", "\53", "\55", "\52", "\57", "\144\151\166", "\45", +"\155\157\144", "\41", "\156\157\164", "\46", "\174", "\136", "\176", "\56\56", "\116\141\116", +null, null, null, null, null, null, null, null, null, null, null, null, null, null, +null, null, null, null, null, }; +protected Token jjFillToken() +{ + final Token t; + final String curTokenImage; + final int beginLine; + final int endLine; + final int beginColumn; + final int endColumn; + String im = jjstrLiteralImages[jjmatchedKind]; + curTokenImage = im == null ? input_stream.getImage() : im; + beginLine = input_stream.getBeginLine(); + beginColumn = input_stream.getBeginColumn(); + endLine = input_stream.getEndLine(); + endColumn = input_stream.getEndColumn(); + t = Token.newToken(jjmatchedKind); + t.kind = jjmatchedKind; + t.image = curTokenImage; + + t.beginLine = beginLine; + t.endLine = endLine; + t.beginColumn = beginColumn; + t.endColumn = endColumn; + + return t; +} +static final int[] jjnextStates = { + 68, 69, 71, 36, 37, 42, 43, 44, 14, 61, 67, 52, 59, 21, 22, 24, + 16, 17, 19, 48, 50, 8, 53, 54, 56, 10, 11, 14, 26, 27, 29, 32, + 33, 34, 38, 39, 14, 42, 43, 44, 14, 63, 64, 66, 12, 13, 40, 41, + 45, 46, 30, 36, 15, 16, 18, 10, 11, 13, 37, 38, 40, 1, 2, 4, + 20, 21, 23, 26, 27, 28, 32, 33, 35, 38, 39, 44, 45, 46, 16, 63, + 69, 54, 61, 23, 24, 26, 18, 19, 21, 50, 52, 10, 70, 71, 73, 55, + 56, 58, 12, 13, 16, 28, 29, 31, 34, 35, 36, 40, 41, 16, 44, 45, + 46, 16, 65, 66, 68, 14, 15, 42, 43, 47, 48, +}; +private static final boolean jjCanMove_0(int hiByte, int i1, int i2, long l1, long l2) +{ + switch(hiByte) + { + case 0: + return ((jjbitVec2[i2] & l2) != 0L); + case 32: + return ((jjbitVec3[i2] & l2) != 0L); + default : + if ((jjbitVec0[i1] & l1) != 0L) + return true; + return false; + } +} +private static final boolean jjCanMove_1(int hiByte, int i1, int i2, long l1, long l2) +{ + switch(hiByte) + { + case 0: + return ((jjbitVec2[i2] & l2) != 0L); + default : + if ((jjbitVec4[i1] & l1) != 0L) + return true; + return false; + } +} + +int curLexState = 0; +int defaultLexState = 0; +int jjnewStateCnt; +int jjround; +int jjmatchedPos; +int jjmatchedKind; + +/** Get the next Token. */ +public Token getNextToken() +{ + Token matchedToken; + int curPos = 0; + + EOFLoop: + for (;;) + { + try + { + curChar = input_stream.beginToken(); + } + catch(final Exception e) + { + jjmatchedKind = 0; + jjmatchedPos = -1; + matchedToken = jjFillToken(); + return matchedToken; + } + image = jjimage; + image.setLength(0); + jjimageLen = 0; + + switch(curLexState) + { + case 0: + try { + input_stream.backup(0); + while (curChar <= 32 && (0x100003600L & (1L << curChar)) != 0x0L) + curChar = input_stream.beginToken(); + } + catch (final java.io.IOException e1) { + continue EOFLoop; + } + jjmatchedKind = 0x7fffffff; + jjmatchedPos = 0; + curPos = jjMoveStringLiteralDfa0_0(); + break; + case 1: + try { + input_stream.backup(0); + while (curChar <= 32 && (0x100003600L & (1L << curChar)) != 0x0L) + curChar = input_stream.beginToken(); + } + catch (final java.io.IOException e1) { + continue EOFLoop; + } + jjmatchedKind = 0x7fffffff; + jjmatchedPos = 0; + curPos = jjMoveStringLiteralDfa0_1(); + break; + case 2: + try { + input_stream.backup(0); + while (curChar <= 32 && (0x100003600L & (1L << curChar)) != 0x0L) + curChar = input_stream.beginToken(); + } + catch (final java.io.IOException e1) { + continue EOFLoop; + } + jjmatchedKind = 0x7fffffff; + jjmatchedPos = 0; + curPos = jjMoveStringLiteralDfa0_2(); + break; + } + if (jjmatchedKind != 0x7fffffff) + { + if (jjmatchedPos + 1 < curPos) + input_stream.backup(curPos - jjmatchedPos - 1); + if ((jjtoToken[jjmatchedKind >> 6] & (1L << (jjmatchedKind & 077))) != 0L) + { + matchedToken = jjFillToken(); + TokenLexicalActions(matchedToken); + if (jjnewLexState[jjmatchedKind] != -1) + curLexState = jjnewLexState[jjmatchedKind]; + return matchedToken; + } + else + { + if (jjnewLexState[jjmatchedKind] != -1) + curLexState = jjnewLexState[jjmatchedKind]; + continue EOFLoop; + } + } + int error_line = input_stream.getEndLine(); + int error_column = input_stream.getEndColumn(); + String error_after = null; + boolean EOFSeen = false; + try { + input_stream.readChar(); + input_stream.backup(1); + } + catch (final java.io.IOException e1) { + EOFSeen = true; + error_after = curPos <= 1 ? "" : input_stream.getImage(); + if (curChar == '\n' || curChar == '\r') { + error_line++; + error_column = 0; + } + else + error_column++; + } + if (!EOFSeen) { + input_stream.backup(1); + error_after = curPos <= 1 ? "" : input_stream.getImage(); + } + throw new TokenMgrException(EOFSeen, curLexState, error_line, error_column, error_after, curChar, TokenMgrException.LEXICAL_ERROR); + } +} + +void SkipLexicalActions(Token matchedToken) +{ + switch(jjmatchedKind) + { + default : + break; + } +} +void MoreLexicalActions() +{ + jjimageLen += (lengthOfMatch = jjmatchedPos + 1); + switch(jjmatchedKind) + { + default : + break; + } +} +void TokenLexicalActions(Token matchedToken) +{ + switch(jjmatchedKind) + { + case 9 : + image.append(jjstrLiteralImages[9]); + lengthOfMatch = jjstrLiteralImages[9].length(); + popDot(); + break; + case 10 : + image.append(jjstrLiteralImages[10]); + lengthOfMatch = jjstrLiteralImages[10].length(); + popDot(); + break; + case 11 : + image.append(jjstrLiteralImages[11]); + lengthOfMatch = jjstrLiteralImages[11].length(); + popDot(); + break; + case 12 : + image.append(jjstrLiteralImages[12]); + lengthOfMatch = jjstrLiteralImages[12].length(); + popDot(); + break; + case 13 : + image.append(jjstrLiteralImages[13]); + lengthOfMatch = jjstrLiteralImages[13].length(); + popDot(); + break; + case 14 : + image.append(jjstrLiteralImages[14]); + lengthOfMatch = jjstrLiteralImages[14].length(); + popDot(); + break; + case 15 : + image.append(jjstrLiteralImages[15]); + lengthOfMatch = jjstrLiteralImages[15].length(); + popDot(); + break; + case 16 : + image.append(jjstrLiteralImages[16]); + lengthOfMatch = jjstrLiteralImages[16].length(); + popDot(); + break; + case 17 : + image.append(jjstrLiteralImages[17]); + lengthOfMatch = jjstrLiteralImages[17].length(); + popDot(); + break; + case 18 : + image.append(jjstrLiteralImages[18]); + lengthOfMatch = jjstrLiteralImages[18].length(); + popDot(); + break; + case 19 : + image.append(jjstrLiteralImages[19]); + lengthOfMatch = jjstrLiteralImages[19].length(); + popDot(); + break; + case 20 : + image.append(jjstrLiteralImages[20]); + lengthOfMatch = jjstrLiteralImages[20].length(); + popDot(); + break; + case 21 : + image.append(jjstrLiteralImages[21]); + lengthOfMatch = jjstrLiteralImages[21].length(); + popDot(); + break; + case 22 : + image.append(jjstrLiteralImages[22]); + lengthOfMatch = jjstrLiteralImages[22].length(); + popDot(); + break; + case 24 : + image.append(jjstrLiteralImages[24]); + lengthOfMatch = jjstrLiteralImages[24].length(); + popDot(); + break; + case 25 : + image.append(jjstrLiteralImages[25]); + lengthOfMatch = jjstrLiteralImages[25].length(); + popDot(); + break; + case 26 : + image.append(jjstrLiteralImages[26]); + lengthOfMatch = jjstrLiteralImages[26].length(); + popDot(); + break; + case 36 : + image.append(jjstrLiteralImages[36]); + lengthOfMatch = jjstrLiteralImages[36].length(); + pushDot(); + break; + case 37 : + image.append(jjstrLiteralImages[37]); + lengthOfMatch = jjstrLiteralImages[37].length(); + pushDot(); + break; + case 43 : + image.append(jjstrLiteralImages[43]); + lengthOfMatch = jjstrLiteralImages[43].length(); + popDot(); + break; + case 45 : + image.append(jjstrLiteralImages[45]); + lengthOfMatch = jjstrLiteralImages[45].length(); + popDot(); + break; + case 47 : + image.append(jjstrLiteralImages[47]); + lengthOfMatch = jjstrLiteralImages[47].length(); + popDot(); + break; + case 49 : + image.append(jjstrLiteralImages[49]); + lengthOfMatch = jjstrLiteralImages[49].length(); + popDot(); + break; + case 51 : + image.append(jjstrLiteralImages[51]); + lengthOfMatch = jjstrLiteralImages[51].length(); + popDot(); + break; + case 53 : + image.append(jjstrLiteralImages[53]); + lengthOfMatch = jjstrLiteralImages[53].length(); + popDot(); + break; + case 55 : + image.append(jjstrLiteralImages[55]); + lengthOfMatch = jjstrLiteralImages[55].length(); + popDot(); + break; + case 57 : + image.append(jjstrLiteralImages[57]); + lengthOfMatch = jjstrLiteralImages[57].length(); + popDot(); + break; + case 77 : + image.append(jjstrLiteralImages[77]); + lengthOfMatch = jjstrLiteralImages[77].length(); + popDot(); + break; + case 79 : + image.append(jjstrLiteralImages[79]); + lengthOfMatch = jjstrLiteralImages[79].length(); + popDot(); + break; + case 81 : + image.append(jjstrLiteralImages[81]); + lengthOfMatch = jjstrLiteralImages[81].length(); + popDot(); + break; + case 89 : + image.append(input_stream.getSuffix(jjimageLen + (lengthOfMatch = jjmatchedPos + 1))); + popDot(); + break; + case 90 : + image.append(input_stream.getSuffix(jjimageLen + (lengthOfMatch = jjmatchedPos + 1))); + matchedToken.image = StringParser.unescapeIdentifier(matchedToken.image); + break; + case 104 : + image.append(input_stream.getSuffix(jjimageLen + (lengthOfMatch = jjmatchedPos + 1))); + popDot(); + break; + case 105 : + image.append(input_stream.getSuffix(jjimageLen + (lengthOfMatch = jjmatchedPos + 1))); + popDot(); + break; + case 106 : + image.append(input_stream.getSuffix(jjimageLen + (lengthOfMatch = jjmatchedPos + 1))); + popDot(); + break; + default : + break; + } +} +private void jjCheckNAdd(int state) +{ + if (jjrounds[state] != jjround) + { + jjstateSet[jjnewStateCnt++] = state; + jjrounds[state] = jjround; + } +} +private void jjAddStates(int start, int end) +{ + do { + jjstateSet[jjnewStateCnt++] = jjnextStates[start]; + } while (start++ != end); +} +private void jjCheckNAddTwoStates(int state1, int state2) +{ + jjCheckNAdd(state1); + jjCheckNAdd(state2); +} + +private void jjCheckNAddStates(int start, int end) +{ + do { + jjCheckNAdd(jjnextStates[start]); + } while (start++ != end); +} + + /** Constructor. */ + public ParserTokenManager(SimpleCharStream stream){ + input_stream = stream; + } + + /** Constructor. */ + public ParserTokenManager (SimpleCharStream stream, int lexState){ + ReInit(stream); + SwitchTo(lexState); + } + + /** Reinitialise parser. */ + + public void ReInit(SimpleCharStream stream) + { + + + jjmatchedPos = + jjnewStateCnt = + 0; + curLexState = defaultLexState; + input_stream = stream; + ReInitRounds(); + } + + private void ReInitRounds() + { + int i; + jjround = 0x80000001; + for (i = 74; i-- > 0;) + jjrounds[i] = 0x80000000; + } + + /** Reinitialise parser. */ + public void ReInit(SimpleCharStream stream, int lexState) + { + ReInit(stream); + SwitchTo(lexState); + } + + /** Switch to specified lex state. */ + public void SwitchTo(int lexState) + { + if (lexState >= 3 || lexState < 0) + throw new TokenMgrException("Error: Ignoring invalid lexical state : " + lexState + ". State unchanged.", TokenMgrException.INVALID_LEXICAL_STATE); + else + curLexState = lexState; + } + + +/** Lexer state names. */ +public static final String[] lexStateNames = { + "DEFAULT", + "DOT_ID", + "REGISTERS", +}; + +/** Lex State array. */ +public static final int[] jjnewLexState = { + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, +}; +static final long[] jjtoToken = { + 0xfffffffffffffe01L, 0x710c7ffffffL, +}; +static final long[] jjtoSkip = { + 0x1feL, 0x0L, +}; +static final long[] jjtoSpecial = { + 0x0L, 0x0L, +}; +static final long[] jjtoMore = { + 0x0L, 0x0L, +}; + protected SimpleCharStream input_stream; + + private final int[] jjrounds = new int[74]; + private final int[] jjstateSet = new int[2 * 74]; + private final StringBuilder jjimage = new StringBuilder(); + private StringBuilder image = jjimage; + private int jjimageLen; + private int lengthOfMatch; + protected int curChar; +} diff --git a/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ParserTreeConstants.java b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ParserTreeConstants.java new file mode 100644 index 0000000..97a6a7a --- /dev/null +++ b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ParserTreeConstants.java @@ -0,0 +1,171 @@ +/* Generated by: ParserGeneratorCC: Do not edit this line. ParserTreeConstants.java Version 1.1.3 */ +package aiyh.utils.tool.org.apache.commons.jexl3.parser; + +public interface ParserTreeConstants +{ + public int JJTJEXLSCRIPT = 0; + public int JJTANNOTATION = 1; + public int JJTANNOTATEDSTATEMENT = 2; + public int JJTVOID = 3; + public int JJTBLOCK = 4; + public int JJTAMBIGUOUS = 5; + public int JJTIFSTATEMENT = 6; + public int JJTWHILESTATEMENT = 7; + public int JJTDOWHILESTATEMENT = 8; + public int JJTRETURNSTATEMENT = 9; + public int JJTCONTINUE = 10; + public int JJTBREAK = 11; + public int JJTFOREACHSTATEMENT = 12; + public int JJTREFERENCE = 13; + public int JJTASSIGNMENT = 14; + public int JJTVAR = 15; + public int JJTSETADDNODE = 16; + public int JJTSETMULTNODE = 17; + public int JJTSETDIVNODE = 18; + public int JJTSETMODNODE = 19; + public int JJTSETANDNODE = 20; + public int JJTSETORNODE = 21; + public int JJTSETXORNODE = 22; + public int JJTSETSUBNODE = 23; + public int JJTTERNARYNODE = 24; + public int JJTNULLPNODE = 25; + public int JJTORNODE = 26; + public int JJTANDNODE = 27; + public int JJTBITWISEORNODE = 28; + public int JJTBITWISEXORNODE = 29; + public int JJTBITWISEANDNODE = 30; + public int JJTEQNODE = 31; + public int JJTNENODE = 32; + public int JJTRANGENODE = 33; + public int JJTLTNODE = 34; + public int JJTGTNODE = 35; + public int JJTLENODE = 36; + public int JJTGENODE = 37; + public int JJTERNODE = 38; + public int JJTNRNODE = 39; + public int JJTSWNODE = 40; + public int JJTNSWNODE = 41; + public int JJTEWNODE = 42; + public int JJTNEWNODE = 43; + public int JJTADDNODE = 44; + public int JJTSUBNODE = 45; + public int JJTMULNODE = 46; + public int JJTDIVNODE = 47; + public int JJTMODNODE = 48; + public int JJTUNARYMINUSNODE = 49; + public int JJTUNARYPLUSNODE = 50; + public int JJTBITWISECOMPLNODE = 51; + public int JJTNOTNODE = 52; + public int JJTEMPTYFUNCTION = 53; + public int JJTSIZEFUNCTION = 54; + public int JJTIDENTIFIER = 55; + public int JJTNAMESPACEIDENTIFIER = 56; + public int JJTNUMBERLITERAL = 57; + public int JJTNULLLITERAL = 58; + public int JJTTRUENODE = 59; + public int JJTFALSENODE = 60; + public int JJTSTRINGLITERAL = 61; + public int JJTJXLTLITERAL = 62; + public int JJTREGEXLITERAL = 63; + public int JJTEXTENDEDLITERAL = 64; + public int JJTARRAYLITERAL = 65; + public int JJTMAPLITERAL = 66; + public int JJTMAPENTRY = 67; + public int JJTSETLITERAL = 68; + public int JJTARGUMENTS = 69; + public int JJTFUNCTIONNODE = 70; + public int JJTCONSTRUCTORNODE = 71; + public int JJTJEXLLAMBDA = 72; + public int JJTIDENTIFIERACCESS = 73; + public int JJTIDENTIFIERACCESSJXLT = 74; + public int JJTIDENTIFIERACCESSSAFE = 75; + public int JJTIDENTIFIERACCESSSAFEJXLT = 76; + public int JJTARRAYACCESS = 77; + public int JJTMETHODNODE = 78; + public int JJTREFERENCEEXPRESSION = 79; + + + public String[] jjtNodeName = { + "JexlScript", + "Annotation", + "AnnotatedStatement", + "void", + "Block", + "Ambiguous", + "IfStatement", + "WhileStatement", + "DoWhileStatement", + "ReturnStatement", + "Continue", + "Break", + "ForeachStatement", + "Reference", + "Assignment", + "Var", + "SetAddNode", + "SetMultNode", + "SetDivNode", + "SetModNode", + "SetAndNode", + "SetOrNode", + "SetXorNode", + "SetSubNode", + "TernaryNode", + "NullpNode", + "OrNode", + "AndNode", + "BitwiseOrNode", + "BitwiseXorNode", + "BitwiseAndNode", + "EQNode", + "NENode", + "RangeNode", + "LTNode", + "GTNode", + "LENode", + "GENode", + "ERNode", + "NRNode", + "SWNode", + "NSWNode", + "EWNode", + "NEWNode", + "AddNode", + "SubNode", + "MulNode", + "DivNode", + "ModNode", + "UnaryMinusNode", + "UnaryPlusNode", + "BitwiseComplNode", + "NotNode", + "EmptyFunction", + "SizeFunction", + "Identifier", + "NamespaceIdentifier", + "NumberLiteral", + "NullLiteral", + "TrueNode", + "FalseNode", + "StringLiteral", + "JxltLiteral", + "RegexLiteral", + "ExtendedLiteral", + "ArrayLiteral", + "MapLiteral", + "MapEntry", + "SetLiteral", + "Arguments", + "FunctionNode", + "ConstructorNode", + "JexlLambda", + "IdentifierAccess", + "IdentifierAccessJxlt", + "IdentifierAccessSafe", + "IdentifierAccessSafeJxlt", + "ArrayAccess", + "MethodNode", + "ReferenceExpression", + }; +} +/* ParserGeneratorCC - OriginalChecksum=194118043a083a98065a1e2c89cf8496 (do not edit this line) */ diff --git a/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ParserVisitor.java b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ParserVisitor.java new file mode 100644 index 0000000..628d2dd --- /dev/null +++ b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/ParserVisitor.java @@ -0,0 +1,188 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package aiyh.utils.tool.org.apache.commons.jexl3.parser; + +/** + * Fully abstract to avoid public interface exposition. + */ +public abstract class ParserVisitor { + /** + * Unused, satisfy ParserVisitor interface. + * @param node a node + * @param data the data + * @return does not return + */ + protected final Object visit(final SimpleNode node, final Object data) { + throw new UnsupportedOperationException(node.getClass().getSimpleName() + " : not supported yet."); + } + + /** + * Unused, should throw in Parser. + * @param node a node + * @param data the data + * @return does not return + */ + protected final Object visit(final ASTAmbiguous node, final Object data) { + throw new UnsupportedOperationException("unexpected type of node"); + } + + protected abstract Object visit(ASTJexlScript node, Object data); + + protected abstract Object visit(ASTBlock node, Object data); + + protected abstract Object visit(ASTIfStatement node, Object data); + + protected abstract Object visit(ASTWhileStatement node, Object data); + + protected abstract Object visit(ASTDoWhileStatement node, Object data); + + protected abstract Object visit(ASTContinue node, Object data); + + protected abstract Object visit(ASTBreak node, Object data); + + protected abstract Object visit(ASTForeachStatement node, Object data); + + protected abstract Object visit(ASTReturnStatement node, Object data); + + protected abstract Object visit(ASTAssignment node, Object data); + + protected abstract Object visit(ASTVar node, Object data); + + protected abstract Object visit(ASTReference node, Object data); + + protected abstract Object visit(ASTTernaryNode node, Object data); + + protected abstract Object visit(ASTNullpNode node, Object data); + + protected abstract Object visit(ASTOrNode node, Object data); + + protected abstract Object visit(ASTAndNode node, Object data); + + protected abstract Object visit(ASTBitwiseOrNode node, Object data); + + protected abstract Object visit(ASTBitwiseXorNode node, Object data); + + protected abstract Object visit(ASTBitwiseAndNode node, Object data); + + protected abstract Object visit(ASTEQNode node, Object data); + + protected abstract Object visit(ASTNENode node, Object data); + + protected abstract Object visit(ASTLTNode node, Object data); + + protected abstract Object visit(ASTGTNode node, Object data); + + protected abstract Object visit(ASTLENode node, Object data); + + protected abstract Object visit(ASTGENode node, Object data); + + protected abstract Object visit(ASTERNode node, Object data); + + protected abstract Object visit(ASTNRNode node, Object data); + + protected abstract Object visit(ASTSWNode node, Object data); + + protected abstract Object visit(ASTNSWNode node, Object data); + + protected abstract Object visit(ASTEWNode node, Object data); + + protected abstract Object visit(ASTNEWNode node, Object data); + + protected abstract Object visit(ASTAddNode node, Object data); + + protected abstract Object visit(ASTSubNode node, Object data); + + protected abstract Object visit(ASTMulNode node, Object data); + + protected abstract Object visit(ASTDivNode node, Object data); + + protected abstract Object visit(ASTModNode node, Object data); + + protected abstract Object visit(ASTUnaryMinusNode node, Object data); + + protected abstract Object visit(ASTUnaryPlusNode node, Object data); + + protected abstract Object visit(ASTBitwiseComplNode node, Object data); + + protected abstract Object visit(ASTNotNode node, Object data); + + protected abstract Object visit(ASTIdentifier node, Object data); + + protected abstract Object visit(ASTNullLiteral node, Object data); + + protected abstract Object visit(ASTTrueNode node, Object data); + + protected abstract Object visit(ASTFalseNode node, Object data); + + protected abstract Object visit(ASTNumberLiteral node, Object data); + + protected abstract Object visit(ASTStringLiteral node, Object data); + + protected abstract Object visit(ASTRegexLiteral node, Object data); + + protected abstract Object visit(ASTSetLiteral node, Object data); + + protected abstract Object visit(ASTExtendedLiteral node, Object data); + + protected abstract Object visit(ASTArrayLiteral node, Object data); + + protected abstract Object visit(ASTRangeNode node, Object data); + + protected abstract Object visit(ASTMapLiteral node, Object data); + + protected abstract Object visit(ASTMapEntry node, Object data); + + protected abstract Object visit(ASTEmptyFunction node, Object data); + + protected abstract Object visit(ASTSizeFunction node, Object data); + + protected abstract Object visit(ASTFunctionNode node, Object data); + + protected abstract Object visit(ASTMethodNode node, Object data); + + protected abstract Object visit(ASTConstructorNode node, Object data); + + protected abstract Object visit(ASTArrayAccess node, Object data); + + protected abstract Object visit(ASTIdentifierAccess node, Object data); + + protected abstract Object visit(ASTArguments node, Object data); + + protected abstract Object visit(ASTReferenceExpression node, Object data); + + protected abstract Object visit(ASTSetAddNode node, Object data); + + protected abstract Object visit(ASTSetSubNode node, Object data); + + protected abstract Object visit(ASTSetMultNode node, Object data); + + protected abstract Object visit(ASTSetDivNode node, Object data); + + protected abstract Object visit(ASTSetModNode node, Object data); + + protected abstract Object visit(ASTSetAndNode node, Object data); + + protected abstract Object visit(ASTSetOrNode node, Object data); + + protected abstract Object visit(ASTSetXorNode node, Object data); + + protected abstract Object visit(ASTJxltLiteral node, Object data); + + protected abstract Object visit(ASTAnnotation node, Object data); + + protected abstract Object visit(ASTAnnotatedStatement node, Object data); +} diff --git a/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/Provider.java b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/Provider.java new file mode 100644 index 0000000..b83424a --- /dev/null +++ b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/Provider.java @@ -0,0 +1,25 @@ +/* Generated by: ParserGeneratorCC: Do not edit this line. Provider.java Version 1.1 */ +/* ParserGeneratorCCOptions:KEEP_LINE_COLUMN=true */ +package aiyh.utils.tool.org.apache.commons.jexl3.parser; + +import java.io.IOException; + +/** + * Abstract interface for reading from a stream. + * The buffering should be done internally. + */ +public interface Provider extends java.io.Closeable +{ + /** + * Reads characters into an array + * + * @param aDest Destination buffer. May not be null. + * @param nOfs Offset at which to start storing characters. Must be ≥ 0. + * @param nLen The maximum possible number of characters to read. Must be ≥ 0. + * @return The number of characters read, or -1 at the end of the stream + * @exception IOException if reading fails + */ + int read (char [] aDest, int nOfs, int nLen) throws IOException; +} + +/* ParserGeneratorCC - OriginalChecksum=79842b13b96be0e2dfb1b63e055ed16a (do not edit this line) */ diff --git a/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/SimpleCharStream.java b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/SimpleCharStream.java new file mode 100644 index 0000000..363edd6 --- /dev/null +++ b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/SimpleCharStream.java @@ -0,0 +1,74 @@ +/* Generated by: ParserGeneratorCC: Do not edit this line. SimpleCharStream.java Version 1.1 */ +/* ParserGeneratorCCOptions:SUPPORT_CLASS_VISIBILITY_PUBLIC=true */ +package aiyh.utils.tool.org.apache.commons.jexl3.parser; + +/** + * An implementation of interface CharStream, where the stream is assumed to + * contain only ASCII characters (without unicode processing). + */ +public +class SimpleCharStream extends AbstractCharStream +{ + private Provider inputStream; + + @Override + protected int streamRead (final char[] aBuf, final int nOfs, final int nLen) throws java.io.IOException + { + return inputStream.read (aBuf, nOfs, nLen); + } + + @Override + protected void streamClose() throws java.io.IOException + { + inputStream.close (); + } + + /** Constructor. */ + public SimpleCharStream(final Provider dstream, + final int startline, + final int startcolumn, + final int buffersize) + { + super (startline, startcolumn, buffersize); + inputStream = dstream; + } + + /** Constructor. */ + public SimpleCharStream(final Provider dstream, + final int startline, + final int startcolumn) + { + this(dstream, startline, startcolumn, DEFAULT_BUF_SIZE); + } + + /** Constructor. */ + public SimpleCharStream(final Provider dstream) + { + this(dstream, 1, 1, DEFAULT_BUF_SIZE); + } + + /** Reinitialise. */ + public void reInit(final Provider dstream, + final int startline, + final int startcolumn, + final int buffersize) + { + inputStream = dstream; + super.reInit (startline, startcolumn, buffersize); + } + + /** Reinitialise. */ + public void reInit(final Provider dstream, + final int startline, + final int startcolumn) + { + reInit(dstream, startline, startcolumn, DEFAULT_BUF_SIZE); + } + + /** Reinitialise. */ + public void reInit(final Provider dstream) + { + reInit(dstream, 1, 1, DEFAULT_BUF_SIZE); + } +} +/* ParserGeneratorCC - OriginalChecksum=542026dd48959c684c77c4bfaffe536a (do not edit this line) */ diff --git a/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/SimpleNode.java b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/SimpleNode.java new file mode 100644 index 0000000..5eb7c08 --- /dev/null +++ b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/SimpleNode.java @@ -0,0 +1,200 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package aiyh.utils.tool.org.apache.commons.jexl3.parser; + +/** + * A class originally generated by JJTree with the following JavaCCOptions: + * MULTI=true,NODE_USES_PARSER=true,VISITOR=true,TRACK_TOKENS=false,NODE_PREFIX=AST,NODE_EXTENDS=,NODE_FACTORY= + * + * Works around issue https://javacc.dev.java.net/issues/show_bug.cgi?id=227 + * As soon as this issue if fixed and the maven plugin uses the correct version of Javacc, this + * class can go away. + * + * The technical goal is to ensure every reference made in the parser was to a JexlNode; unfortunately, + * as in javacc 4.1, it still uses a SimpleNode reference in the generated ParserVisitor. + * Besides, there is no need to keep the parser around in the node. + * + * The functional goal is to a allow a volatile value in the node + * so it can serve as a last evaluation cache even in multi-threaded executions. + */ +public class SimpleNode implements Node { + /** The parent node. */ + private JexlNode parent; + /** The array of children nodes. */ + private JexlNode[] children; + /** The node type id. */ + protected final int id; + /** volatile value so it can be used as a last evaluation cache. */ + private volatile Object value; + + /** + * Creates a SimpleNode instance. + * @param i the node type identifier + */ + public SimpleNode(final int i) { + id = i; + } + + /** + * Creates a SimpleNode instance. + * @param p the parser instance + * @param i the node type identifier + */ + public SimpleNode(final Parser p, final int i) { + this(i); + } + + @Override + public void jjtOpen() { + } + + @Override + public void jjtClose() { + } + + /** + * Sets this node's parent. + * @param n the parent + */ + @Override + public void jjtSetParent(final Node n) { + parent = (JexlNode) n; + } + + /** + * Gets this node's parent. + * @return the parent node + */ + @Override + public JexlNode jjtGetParent() { + return parent; + } + + /** Adds a child node. + * @param n the child node + * @param i the child offset + */ + @Override + public void jjtAddChild(final Node n, final int i) { + if (children == null) { + children = new JexlNode[i + 1]; + } else if (i >= children.length) { + final JexlNode[] c = new JexlNode[i + 1]; + System.arraycopy(children, 0, c, 0, children.length); + children = c; + } + children[i] = (JexlNode) n; + } + + // For use by ASTJexlScript only + void jjtSetChildren(final JexlNode[] jexlNodes) { + children = jexlNodes; + } + + /** + * Gets a child of this node. + * @param i the child offset + * @return the child node + */ + @Override + public JexlNode jjtGetChild(final int i) { + return children[i]; + } + + /** + * Gets this node number of children. + * @return the number of children + */ + @Override + public int jjtGetNumChildren() { + return (children == null) ? 0 : children.length; + } + + /** Sets this node value. + * @param value + */ + public void jjtSetValue(final Object value) { + this.value = value; + } + + /** Gets this node value. + * @return value + */ + public Object jjtGetValue() { + return value; + } + + /** + * Accept the visitor. + * @param visitor the visitor + * @param data contextual data + * @return result of visit + **/ + @Override + public Object jjtAccept(final ParserVisitor visitor, final Object data) { + return visitor.visit(this, data); + } + + /** + * Accept the visitor on all this node's children. + * @param visitor the visitor + * @param data contextual data + * @return result of visit + **/ + public Object childrenAccept(final ParserVisitor visitor, final Object data) { + if (children != null) { + for (final JexlNode child : children) { + child.jjtAccept(visitor, data); + } + } + return data; + } + + /* You can override these two methods in subclasses of SimpleNode to + customize the way the JexlNode appears when the tree is dumped. If + your output uses more than one line you should override + toString(String), otherwise overriding toString() is probably all + you need to do. */ + @Override + public String toString() { + return ParserTreeConstants.jjtNodeName[id]; + } + + public String toString(final String prefix) { + return prefix + toString(); + } + + /* Override this method if you want to customize how the JexlNode dumps + out its children. */ + public void dump(final String prefix) { + System.out.println(toString(prefix)); + if (children != null) { + for (final SimpleNode n : children) { + if (n != null) { + n.dump(prefix + " "); + } + } + } + } + + @Override + public int getId() { + return id; + } +} + +/* JavaCC - OriginalChecksum=7dff880883d088a37c1e3197e4b455a0 (do not edit this line) */ diff --git a/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/StreamProvider.java b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/StreamProvider.java new file mode 100644 index 0000000..5595225 --- /dev/null +++ b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/StreamProvider.java @@ -0,0 +1,57 @@ +/* Generated by: ParserGeneratorCC: Do not edit this line. StreamProvider.java Version 1.1 */ +/* ParserGeneratorCCOptions:KEEP_LINE_COLUMN=true */ +package aiyh.utils.tool.org.apache.commons.jexl3.parser; + + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.Reader; + +/** + * NOTE : This generated class can be safely deleted if installing in a GWT installation (use StringProvider instead) + */ +public class StreamProvider implements Provider +{ + private Reader m_aReader; + + public StreamProvider(final InputStream stream, final String charsetName) throws IOException + { + this (new BufferedReader (new InputStreamReader (stream, charsetName))); + } + + public StreamProvider(final InputStream stream, final java.nio.charset.Charset charset) + { + this (new BufferedReader (new InputStreamReader (stream, charset))); + } + + public StreamProvider (final Reader reader) + { + m_aReader = reader; + } + + public int read (final char[] aDest, final int nOfs, final int nLen) throws IOException + { + int result = m_aReader.read(aDest, nOfs, nLen); + + /* CBA -- Added 2014/03/29 -- + This logic allows the generated Java code to be easily translated to C# (via sharpen) - + as in C# 0 represents end of file, and in Java, -1 represents end of file + See : http://msdn.microsoft.com/en-us/library/9kstw824(v=vs.110).aspx + ** Technically, this is not required for java but the overhead is extremely low compared to the code generation benefits. + */ + if (result == 0) + if (nOfs < aDest.length && nLen > 0) + result = -1; + + return result; + } + + public void close () throws IOException + { + if (m_aReader != null) + m_aReader.close(); + } +} +/* ParserGeneratorCC - OriginalChecksum=f4cc4504a630d11129ad9cbe176ddd39 (do not edit this line) */ diff --git a/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/StringParser.java b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/StringParser.java new file mode 100644 index 0000000..b1ec1c8 --- /dev/null +++ b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/StringParser.java @@ -0,0 +1,327 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package aiyh.utils.tool.org.apache.commons.jexl3.parser; + +/** + * Common constant strings utilities. + *

+ * This package methods read JEXL string literals and handle escaping through the + * 'backslash' (ie: \) character. Escaping is used to neutralize string delimiters (the single + * and double quotes) and read Unicode hexadecimal encoded characters. + *

+ *

+ * The only escapable characters are the single and double quotes - ''' and '"' -, + * a Unicode sequence starting with 'u' followed by 4 hexadecimals and + * the backslash character - '\' - itself. + *

+ *

+ * A sequence where '\' occurs before any non-escapable character or sequence has no effect, the + * sequence output being the same as the input. + *

+ */ +public class StringParser { + /** Default constructor. */ + public StringParser() { + } + + /** + * Builds a string, handles escaping through '\' syntax. + * @param str the string to build from + * @param eatsep whether the separator, the first character, should be considered + * @return the built string + */ + public static String buildString(final CharSequence str, final boolean eatsep) { + return buildString(str, eatsep, true); + } + + /** + * Builds a template, does not escape characters. + * @param str the string to build from + * @param eatsep whether the separator, the first character, should be considered + * @return the built string + */ + public static String buildTemplate(final CharSequence str, final boolean eatsep) { + return buildString(str, eatsep, false); + } + + /** + * Builds a string, handles escaping through '\' syntax. + * @param str the string to build from + * @param eatsep whether the separator, the first character, should be considered + * @param esc whether escape characters are interpreted or escaped + * @return the built string + */ + private static String buildString(final CharSequence str, final boolean eatsep, final boolean esc) { + final StringBuilder strb = new StringBuilder(str.length()); + final char sep = eatsep ? str.charAt(0) : 0; + final int end = str.length() - (eatsep ? 1 : 0); + final int begin = (eatsep ? 1 : 0); + read(strb, str, begin, end, sep, esc); + return strb.toString(); + } + + /** + * Builds a regex pattern string, handles escaping '/' through '\/' syntax. + * @param str the string to build from + * @return the built string + */ + public static String buildRegex(final CharSequence str) { + return buildString(str.subSequence(1, str.length()), true); + } + + /** + * Read the remainder of a string till a given separator, + * handles escaping through '\' syntax. + * @param strb the destination buffer to copy characters into + * @param str the origin + * @param index the offset into the origin + * @param sep the separator, single or double quote, marking end of string + * @return the offset in origin + */ + public static int readString(final StringBuilder strb, final CharSequence str, final int index, final char sep) { + return read(strb, str, index, str.length(), sep, true); + } + /** The length of an escaped unicode sequence. */ + private static final int UCHAR_LEN = 4; + + /** + * Read the remainder of a string till a given separator, + * handles escaping through '\' syntax. + * @param strb the destination buffer to copy characters into + * @param str the origin + * @param begin the relative offset in str to begin reading + * @param end the relative offset in str to end reading + * @param sep the separator, single or double quote, marking end of string + * @param esc whether escape characters are interpreted or escaped + * @return the last character offset handled in origin + */ + private static int read(final StringBuilder strb, final CharSequence str, final int begin, final int end, final char sep, final boolean esc) { + boolean escape = false; + int index = begin; + for (; index < end; ++index) { + final char c = str.charAt(index); + if (escape) { + if (c == 'u' && (index + UCHAR_LEN) < end && readUnicodeChar(strb, str, index + 1) > 0) { + index += UCHAR_LEN; + } else { + // if c is not an escapable character, re-emmit the backslash before it + final boolean notSeparator = sep == 0 ? c != '\'' && c != '"' : c != sep; + if (notSeparator && c != '\\') { + if (!esc) { + strb.append('\\').append(c); + } else { + switch (c) { + // http://es5.github.io/x7.html#x7.8.4 + case 'b': + strb.append('\b'); + break; // backspace \u0008 + case 't': + strb.append('\t'); + break; // horizontal tab \u0009 + case 'n': + strb.append('\n'); + break; // line feed \u000A + // We don't support vertical tab. If needed, the unicode (\u000B) should be used instead + case 'f': + strb.append('\f'); + break; // form feed \u000C + case 'r': + strb.append('\r'); + break; // carriage return \u000D + default: + strb.append('\\').append(c); + } + } + } else { + strb.append(c); + } + } + escape = false; + continue; + } + if (c == '\\') { + escape = true; + continue; + } + strb.append(c); + if (c == sep) { + break; + } + } + return index; + } + /** Initial shift value for composing a Unicode char from 4 nibbles (16 - 4). */ + private static final int SHIFT = 12; + /** The base 10 offset used to convert hexa characters to decimal. */ + private static final int BASE10 = 10; + + /** + * Reads a Unicode escape character. + * @param strb the builder to write the character to + * @param str the sequence + * @param begin the begin offset in sequence (after the '\\u') + * @return 0 if char could not be read, 4 otherwise + */ + private static int readUnicodeChar(final StringBuilder strb, final CharSequence str, final int begin) { + char xc = 0; + int bits = SHIFT; + int value = 0; + for (int offset = 0; offset < UCHAR_LEN; ++offset) { + final char c = str.charAt(begin + offset); + if (c >= '0' && c <= '9') { + value = (c - '0'); + } else if (c >= 'a' && c <= 'h') { + value = (c - 'a' + BASE10); + } else if (c >= 'A' && c <= 'H') { + value = (c - 'A' + BASE10); + } else { + return 0; + } + xc |= value << bits; + bits -= UCHAR_LEN; + } + strb.append(xc); + return UCHAR_LEN; + } + /** The last 7bits ascii character. */ + private static final char LAST_ASCII = 127; + /** The first printable 7bits ascii character. */ + private static final char FIRST_ASCII = 32; + + /** + * Escapes a String representation, expand non-ASCII characters as Unicode escape sequence. + * @param delim the delimiter character + * @param str the string to escape + * @return the escaped representation + */ + public static String escapeString(final String str, final char delim) { + if (str == null) { + return null; + } + final int length = str.length(); + final StringBuilder strb = new StringBuilder(length + 2); + strb.append(delim); + for (int i = 0; i < length; ++i) { + final char c = str.charAt(i); + switch (c) { + case 0: + continue; + case '\b': + strb.append("\\b"); + break; + case '\t': + strb.append("\\t"); + break; + case '\n': + strb.append("\\n"); + break; + case '\f': + strb.append("\\f"); + break; + case '\r': + strb.append("\\r"); + break; + case '\"': + strb.append("\\\""); + break; + case '\'': + strb.append("\\\'"); + break; + case '\\': + strb.append("\\\\"); + break; + default: + if (c >= FIRST_ASCII && c <= LAST_ASCII) { + strb.append(c); + } else { + // convert to Unicode escape sequence + strb.append('\\'); + strb.append('u'); + final String hex = Integer.toHexString(c); + for (int h = hex.length(); h < UCHAR_LEN; ++h) { + strb.append('0'); + } + strb.append(hex); + } + } + } + strb.append(delim); + return strb.toString(); + } + + /** + * Remove escape char ('\') from an identifier. + * @param str the identifier escaped string, ie with a backslash before space, quote, double-quote and backslash + * @return the string with no '\\' character + */ + public static String unescapeIdentifier(final String str) { + StringBuilder strb = null; + if (str != null) { + int n = 0; + final int last = str.length(); + while (n < last) { + final char c = str.charAt(n); + if (c == '\\') { + if (strb == null) { + strb = new StringBuilder(last); + strb.append(str.substring(0, n)); + } + } else if (strb != null) { + strb.append(c); + } + n += 1; + } + } + return strb == null ? str : strb.toString(); + } + + /** + * Adds a escape char ('\') where needed in a string form of an ide + * @param str the identifier un-escaped string + * @return the string with added backslash character before space, quote, double-quote and backslash + */ + public static String escapeIdentifier(final String str) { + StringBuilder strb = null; + if (str != null) { + int n = 0; + final int last = str.length(); + while (n < last) { + final char c = str.charAt(n); + switch (c) { + case ' ': + case '\'': + case '"': + case '\\': { + if (strb == null) { + strb = new StringBuilder(last); + strb.append(str.substring(0, n)); + } + strb.append('\\'); + strb.append(c); + break; + } + default: + if (strb != null) { + strb.append(c); + } + } + n += 1; + } + } + return strb == null ? str : strb.toString(); + } +} diff --git a/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/StringProvider.java b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/StringProvider.java new file mode 100644 index 0000000..36876c3 --- /dev/null +++ b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/StringProvider.java @@ -0,0 +1,43 @@ +/* Generated by: ParserGeneratorCC: Do not edit this line. StringProvider.java Version 1.1 */ +/* ParserGeneratorCCOptions:KEEP_LINE_COLUMN=true */ +package aiyh.utils.tool.org.apache.commons.jexl3.parser; + + +import java.io.IOException; + +public class StringProvider implements Provider +{ + private String m_sStr; + private int m_nPos = 0; + private final int m_nLen; + + public StringProvider(final String sStr) + { + m_sStr = sStr; + m_nLen = sStr.length(); + } + + public int read (final char[] aDest, final int nOfs, final int nLen) throws IOException + { + final int nLeft = m_nLen - m_nPos; + if (nLeft <= 0) + return -1; + + int nCharsRead = aDest.length - nOfs; + if (nLen < nCharsRead) + nCharsRead = nLen; + if (nLeft < nCharsRead) + nCharsRead = nLeft; + + m_sStr.getChars(m_nPos, m_nPos + nCharsRead, aDest, nOfs); + m_nPos += nCharsRead; + + return nCharsRead; + } + + public void close() + { + m_sStr = null; + } +} +/* ParserGeneratorCC - OriginalChecksum=38d0917156eee4ceaf568285a30e6123 (do not edit this line) */ diff --git a/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/Token.java b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/Token.java new file mode 100644 index 0000000..81db71c --- /dev/null +++ b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/Token.java @@ -0,0 +1,132 @@ +/* Generated by: ParserGeneratorCC: Do not edit this line. Token.java Version 1.1 */ +/* ParserGeneratorCCOptions:TOKEN_EXTENDS=,KEEP_LINE_COLUMN=true,SUPPORT_CLASS_VISIBILITY_PUBLIC=true */ +package aiyh.utils.tool.org.apache.commons.jexl3.parser; + +/** + * Describes the input token stream. + */ + +public class Token +implements java.io.Serializable { + /** + * The version identifier for this Serializable class. + * Increment only if the serialized form of the + * class changes. + */ + private static final long serialVersionUID = 1L; + + /** + * An integer that describes the kind of this token. This numbering + * system is determined by JavaCCParser, and a table of these numbers is + * stored in the file ...Constants.java. + */ + public int kind; + + /** The line number of the first character of this Token. */ + public int beginLine; + /** The column number of the first character of this Token. */ + public int beginColumn; + /** The line number of the last character of this Token. */ + public int endLine; + /** The column number of the last character of this Token. */ + public int endColumn; + + /** + * The string image of the token. + */ + public String image; + + /** + * A reference to the next regular (non-special) token from the input + * stream. If this is the last token from the input stream, or if the + * token manager has not read tokens beyond this one, this field is + * set to null. This is true only if this token is also a regular + * token. Otherwise, see below for a description of the contents of + * this field. + */ + public Token next; + + /** + * This field is used to access special tokens that occur prior to this + * token, but after the immediately preceding regular (non-special) token. + * If there are no such special tokens, this field is set to null. + * When there are more than one such special token, this field refers + * to the last of these special tokens, which in turn refers to the next + * previous special token through its specialToken field, and so on + * until the first special token (whose specialToken field is null). + * The next fields of special tokens refer to other special tokens that + * immediately follow it (without an intervening regular token). If there + * is no such token, this field is null. + */ + public Token specialToken; + + /** + * No-argument constructor + */ + public Token() {} + + /** + * Constructs a new token for the specified Image. + */ + public Token(final int nKind) + { + this(nKind, null); + } + + /** + * Constructs a new token for the specified Image and Kind. + */ + public Token(final int nKind, final String sImage) + { + this.kind = nKind; + this.image = sImage; + } + + /** + * An optional attribute value of the Token. + * Tokens which are not used as syntactic sugar will often contain + * meaningful values that will be used later on by the compiler or + * interpreter. This attribute value is often different from the image. + * Any subclass of Token that actually wants to return a non-null value can + * override this method as appropriate. + */ + public Object getValue() { + return null; + } + + /** + * Returns the image. + */ + @Override + public String toString() + { + return image; + } + + /** + * Returns a new Token object, by default. However, if you want, you + * can create and return subclass objects based on the value of ofKind. + * Simply add the cases to the switch for all those special cases. + * For example, if you have a subclass of Token called IDToken that + * you want to create if ofKind is ID, simply add something like : + * + * case MyParserConstants.ID : return new IDToken(ofKind, image); + * + * to the following switch statement. Then you can cast matchedToken + * variable to the appropriate type and use sit in your lexical actions. + */ + public static Token newToken(int ofKind, String image) + { + switch(ofKind) + { + default : return new Token(ofKind, image); + } + } + + public static Token newToken(int ofKind) + { + return newToken(ofKind, null); + } + +} +/* ParserGeneratorCC - OriginalChecksum=5e5f51d13d543320fa2b6a4c81ab6bfe (do not edit this line) */ diff --git a/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/TokenMgrException.java b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/TokenMgrException.java new file mode 100644 index 0000000..edb3a55 --- /dev/null +++ b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/TokenMgrException.java @@ -0,0 +1,184 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package aiyh.utils.tool.org.apache.commons.jexl3.parser; + +/** + * Token Manager Error. + */ +public class TokenMgrException extends RuntimeException implements JavaccError { + /** + * The version identifier for this Serializable class. + * Increment only if the serialized form of the + * class changes. + */ + private static final long serialVersionUID = 1L; + + /* + * Ordinals for various reasons why an Error of this type can be thrown. + */ + /** + * Lexical error occurred. + */ + public static final int LEXICAL_ERROR = 0; + /** + * An attempt was made to create a second instance of a static token manager. + */ + public static final int STATIC_LEXER_ERROR = 1; + /** + * Tried to change to an invalid lexical state. + */ + public static final int INVALID_LEXICAL_STATE = 2; + /** + * Detected (and bailed out of) an infinite loop in the token manager. + */ + public static final int LOOP_DETECTED = 3; + /** + * Indicates the reason why the exception is thrown. It will have + * one of the above 4 values. + */ + private final int errorCode; + /** + * The lexer state. + */ + @SuppressWarnings("unused") // not read currently + private int state; + /** + * The current character. + */ + private char current; + /** + * Last correct input before error occurs. + */ + private String after; + /** + * Whether eof was reached whilst expecting more input. + */ + private boolean eof; + /** + * Error line. + */ + private int line; + /** + * Error column. + */ + private int column; + + /** + * Returns a detailed message for the Error when it is thrown by the + * token manager to indicate a lexical error. + * @return the message + */ + @Override + public String getMessage() { + return ("Lexical error at line " + + line + ", column " + + column + ". Encountered: " + + (eof ? " " + : (StringParser.escapeString(String.valueOf(current), '"')) + " (" + (int) current + "), ") + + "after : " + StringParser.escapeString(after, '"')); + } + + + /** Constructor with message and reason. */ + public TokenMgrException(final String message, final int reason) { + super(message); + errorCode = reason; + } + + /** Full Constructor. */ + public TokenMgrException(final boolean EOFSeen, final int lexState, final int errorLine, final int errorColumn, final String errorAfter, final int curChar, final int reason) { + eof = EOFSeen; + state = lexState; + line = errorLine; + column = errorColumn; + after = errorAfter; + current = (char) curChar; + errorCode = reason; + } + + /** + * Gets the reason why the exception is thrown. + * @return one of the 4 lexical error codes + */ + public int getErrorCode() { + return errorCode; + } + + @Override + public int getLine() { + return line; + } + + @Override + public int getColumn() { + return column; + } + + @Override + public String getAfter() { + return after; + } + + /*** + * Replaces unprintable characters by their espaced (or unicode escaped) + * equivalents in the given string + */ + protected static String addEscapes(final String str) { + final StringBuilder retval = new StringBuilder(); + char ch; + for (int i = 0; i < str.length(); i++) { + switch (str.charAt(i)) + { + case 0 : + continue; + case '\b': + retval.append("//b"); + continue; + case '\t': + retval.append("//t"); + continue; + case '\n': + retval.append("//n"); + continue; + case '\f': + retval.append("//f"); + continue; + case '\r': + retval.append("//r"); + continue; + case '\"': + retval.append("//\""); + continue; + case '\'': + retval.append("//\'"); + continue; + case '/': + retval.append("////"); + continue; + default: + if ((ch = str.charAt(i)) < 0x20 || ch > 0x7e) { + final String s = "0000" + Integer.toString(ch, 16); + retval.append("//u").append(s.substring(s.length() - 4)); + } else { + retval.append(ch); + } + continue; + } + } + return retval.toString(); + } +} diff --git a/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/package.html b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/package.html new file mode 100644 index 0000000..d0a46ca --- /dev/null +++ b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/parser/package.html @@ -0,0 +1,31 @@ + + + + Package Documentation for org.apache.commons.jexl3.parser Package + + +

+ Contains the Parser for JEXL script. +

+

+ This internal package is not intended for public usage and there is no + guarantee that its public classes or methods will remain as is in subsequent + versions. +

+ + diff --git a/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/scripting/JexlScriptEngine.java b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/scripting/JexlScriptEngine.java new file mode 100644 index 0000000..43eaa6b --- /dev/null +++ b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/scripting/JexlScriptEngine.java @@ -0,0 +1,397 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package aiyh.utils.tool.org.apache.commons.jexl3.scripting; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.PrintWriter; +import java.io.Reader; +import java.io.Writer; + +import javax.script.AbstractScriptEngine; +import javax.script.Bindings; +import javax.script.Compilable; +import javax.script.CompiledScript; +import javax.script.ScriptContext; +import javax.script.ScriptEngine; +import javax.script.ScriptEngineFactory; +import javax.script.ScriptException; +import javax.script.SimpleBindings; + +import aiyh.utils.tool.org.apache.commons.jexl3.JexlBuilder; +import aiyh.utils.tool.org.apache.commons.jexl3.JexlContext; +import aiyh.utils.tool.org.apache.commons.jexl3.JexlEngine; +import aiyh.utils.tool.org.apache.commons.jexl3.JexlScript; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * Implements the JEXL ScriptEngine for JSF-223. + *

+ * This implementation gives access to both ENGINE_SCOPE and GLOBAL_SCOPE bindings. + * When a JEXL script accesses a variable for read or write, + * this implementation checks first ENGINE and then GLOBAL scope. + * The first one found is used. + * If no variable is found, and the JEXL script is writing to a variable, + * it will be stored in the ENGINE scope. + *

+ *

+ * The implementation also creates the "JEXL" script object as an instance of the + * class {@link JexlScriptObject} for access to utility methods and variables. + *

+ * See + * Java Scripting API + * Javadoc. + * + * @since 2.0 + */ +public class JexlScriptEngine extends AbstractScriptEngine implements Compilable { + + /** The logger. */ + private static final Log LOG = LogFactory.getLog(JexlScriptEngine.class); + + /** The shared expression cache size. */ + private static final int CACHE_SIZE = 512; + + /** Reserved key for context (mandated by JSR-223). */ + public static final String CONTEXT_KEY = "context"; + + /** Reserved key for JexlScriptObject. */ + public static final String JEXL_OBJECT_KEY = "JEXL"; + + /** The JexlScriptObject instance. */ + private final JexlScriptObject jexlObject; + + /** The factory which created this instance. */ + private final ScriptEngineFactory parentFactory; + + /** The JEXL EL engine. */ + private final JexlEngine jexlEngine; + + /** + * Default constructor. + * + *

Only intended for use when not using a factory. + * Sets the factory to {@link JexlScriptEngineFactory}.

+ */ + public JexlScriptEngine() { + this(FactorySingletonHolder.DEFAULT_FACTORY); + } + + /** + * Implements engine and engine context properties for use by JEXL scripts. + * Those properties are always bound to the default engine scope context. + * + *

The following properties are defined:

+ * + *
    + *
  • in - refers to the engine scope reader that defaults to reading System.err
  • + *
  • out - refers the engine scope writer that defaults to writing in System.out
  • + *
  • err - refers to the engine scope writer that defaults to writing in System.err
  • + *
  • logger - the JexlScriptEngine logger
  • + *
  • System - the System.class
  • + *
+ * + * @since 2.0 + */ + public class JexlScriptObject { + + /** + * Gives access to the underlying JEXL engine shared between all ScriptEngine instances. + *

Although this allows to manipulate various engine flags (lenient, debug, cache...) + * for all JexlScriptEngine instances, you probably should only do so + * if you are in strict control and sole user of the JEXL scripting feature.

+ * + * @return the shared underlying JEXL engine + */ + public JexlEngine getEngine() { + return jexlEngine; + } + + /** + * Gives access to the engine scope output writer (defaults to System.out). + * + * @return the engine output writer + */ + public PrintWriter getOut() { + final Writer out = context.getWriter(); + if (out instanceof PrintWriter) { + return (PrintWriter) out; + } + if (out != null) { + return new PrintWriter(out, true); + } + return null; + } + + /** + * Gives access to the engine scope error writer (defaults to System.err). + * + * @return the engine error writer + */ + public PrintWriter getErr() { + final Writer error = context.getErrorWriter(); + if (error instanceof PrintWriter) { + return (PrintWriter) error; + } + if (error != null) { + return new PrintWriter(error, true); + } + return null; + } + + /** + * Gives access to the engine scope input reader (defaults to System.in). + * + * @return the engine input reader + */ + public Reader getIn() { + return context.getReader(); + } + + /** + * Gives access to System class. + * + * @return System.class + */ + public Class getSystem() { + return System.class; + } + + /** + * Gives access to the engine logger. + * + * @return the JexlScriptEngine logger + */ + public Log getLogger() { + return LOG; + } + } + + + /** + * Create a scripting engine using the supplied factory. + * + * @param factory the factory which created this instance. + * @throws NullPointerException if factory is null + */ + public JexlScriptEngine(final ScriptEngineFactory factory) { + if (factory == null) { + throw new NullPointerException("ScriptEngineFactory must not be null"); + } + parentFactory = factory; + jexlEngine = EngineSingletonHolder.DEFAULT_ENGINE; + jexlObject = new JexlScriptObject(); + } + + @Override + public Bindings createBindings() { + return new SimpleBindings(); + } + + @Override + public Object eval(final Reader reader, final ScriptContext context) throws ScriptException { + // This is mandated by JSR-223 (see SCR.5.5.2 Methods) + if (reader == null || context == null) { + throw new NullPointerException("script and context must be non-null"); + } + return eval(readerToString(reader), context); + } + + @Override + public Object eval(final String script, final ScriptContext context) throws ScriptException { + // This is mandated by JSR-223 (see SCR.5.5.2 Methods) + if (script == null || context == null) { + throw new NullPointerException("script and context must be non-null"); + } + // This is mandated by JSR-223 (end of section SCR.4.3.4.1.2 - JexlScript Execution) + context.setAttribute(CONTEXT_KEY, context, ScriptContext.ENGINE_SCOPE); + try { + final JexlScript jexlScript = jexlEngine.createScript(script); + final JexlContext ctxt = new JexlContextWrapper(context); + return jexlScript.execute(ctxt); + } catch (final Exception e) { + throw new ScriptException(e.toString()); + } + } + + @Override + public ScriptEngineFactory getFactory() { + return parentFactory; + } + + @Override + public CompiledScript compile(final String script) throws ScriptException { + // This is mandated by JSR-223 + if (script == null) { + throw new NullPointerException("script must be non-null"); + } + try { + final JexlScript jexlScript = jexlEngine.createScript(script); + return new JexlCompiledScript(jexlScript); + } catch (final Exception e) { + throw new ScriptException(e.toString()); + } + } + + @Override + public CompiledScript compile(final Reader script) throws ScriptException { + // This is mandated by JSR-223 + if (script == null) { + throw new NullPointerException("script must be non-null"); + } + return compile(readerToString(script)); + } + + /** + * Read from a reader into a local buffer and return a String with + * the contents of the reader. + * + * @param scriptReader to be read. + * @return the contents of the reader as a String. + * @throws ScriptException on any error reading the reader. + */ + private static String readerToString(final Reader scriptReader) throws ScriptException { + final StringBuilder buffer = new StringBuilder(); + BufferedReader reader; + if (scriptReader instanceof BufferedReader) { + reader = (BufferedReader) scriptReader; + } else { + reader = new BufferedReader(scriptReader); + } + try { + String line; + while ((line = reader.readLine()) != null) { + buffer.append(line).append('\n'); + } + return buffer.toString(); + } catch (final IOException e) { + throw new ScriptException(e); + } + } + + /** + * Holds singleton JexlScriptEngineFactory (IODH). + */ + private static class FactorySingletonHolder { + /** non instantiable. */ + private FactorySingletonHolder() {} + + /** The engine factory singleton instance. */ + private static final JexlScriptEngineFactory DEFAULT_FACTORY = new JexlScriptEngineFactory(); + } + + /** + * Holds singleton JexlScriptEngine (IODH). + *

A single JEXL engine and JexlUberspect is shared by all instances of JexlScriptEngine.

+ */ + private static class EngineSingletonHolder { + /** non instantiable. */ + private EngineSingletonHolder() {} + + /** The JEXL engine singleton instance. */ + private static final JexlEngine DEFAULT_ENGINE = new JexlBuilder().logger(LOG).cache(CACHE_SIZE).create(); + } + + /** + * Wrapper to help convert a JSR-223 ScriptContext into a JexlContext. + * + * Current implementation only gives access to ENGINE_SCOPE binding. + */ + private final class JexlContextWrapper implements JexlContext { + /** The wrapped script context. */ + private final ScriptContext scriptContext; + + /** + * Creates a context wrapper. + * + * @param theContext the engine context. + */ + private JexlContextWrapper (final ScriptContext theContext){ + scriptContext = theContext; + } + + @Override + public Object get(final String name) { + final Object o = scriptContext.getAttribute(name); + if (JEXL_OBJECT_KEY.equals(name)) { + if (o != null) { + LOG.warn("JEXL is a reserved variable name, user defined value is ignored"); + } + return jexlObject; + } + return o; + } + + @Override + public void set(final String name, final Object value) { + int scope = scriptContext.getAttributesScope(name); + if (scope == -1) { // not found, default to engine + scope = ScriptContext.ENGINE_SCOPE; + } + scriptContext.getBindings(scope).put(name , value); + } + + @Override + public boolean has(final String name) { + final Bindings bnd = scriptContext.getBindings(ScriptContext.ENGINE_SCOPE); + return bnd.containsKey(name); + } + + } + + /** + * Wrapper to help convert a JEXL JexlScript into a JSR-223 CompiledScript. + */ + private final class JexlCompiledScript extends CompiledScript { + /** The underlying JEXL expression instance. */ + private final JexlScript script; + + /** + * Creates an instance. + * + * @param theScript to wrap + */ + private JexlCompiledScript(final JexlScript theScript) { + script = theScript; + } + + @Override + public String toString() { + return script.getSourceText(); + } + + @Override + public Object eval(final ScriptContext context) throws ScriptException { + // This is mandated by JSR-223 (end of section SCR.4.3.4.1.2 - JexlScript Execution) + context.setAttribute(CONTEXT_KEY, context, ScriptContext.ENGINE_SCOPE); + try { + final JexlContext ctxt = new JexlContextWrapper(context); + return script.execute(ctxt); + } catch (final Exception e) { + throw new ScriptException(e.toString()); + } + } + + @Override + public ScriptEngine getEngine() { + return JexlScriptEngine.this; + } + } +} diff --git a/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/scripting/JexlScriptEngineFactory.java b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/scripting/JexlScriptEngineFactory.java new file mode 100644 index 0000000..52e8f16 --- /dev/null +++ b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/scripting/JexlScriptEngineFactory.java @@ -0,0 +1,153 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package aiyh.utils.tool.org.apache.commons.jexl3.scripting; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import javax.script.ScriptEngine; +import javax.script.ScriptEngineFactory; +import aiyh.utils.tool.org.apache.commons.jexl3.parser.StringParser; + +/** + * Implements the JEXL ScriptEngineFactory for JSF-223. + *

+ * Supports the following:
+ * Language short names: "JEXL", "Jexl", "jexl", "JEXL2", "Jexl2", "jexl2", "JEXL3", "Jexl3", "jexl3"
+ * File Extensions: ".jexl", ".jexl2", ".jexl3"
+ * "jexl3" etc. were added for engineVersion="3.0". + *

+ *

+ * See + * Java Scripting API + * Javadoc. + * + * @since 2.0 + */ +public class JexlScriptEngineFactory implements ScriptEngineFactory { + + @Override + public String getEngineName() { + return "JEXL Engine"; + } + + @Override + public String getEngineVersion() { + return "3.2"; // ensure this is updated if function changes are made to this class + } + + @Override + public String getLanguageName() { + return "JEXL"; + } + + @Override + public String getLanguageVersion() { + return "3.2"; // TODO this should be derived from the actual version + } + + @Override + public String getMethodCallSyntax(final String obj, final String m, final String... args) { + final StringBuilder sb = new StringBuilder(); + sb.append(obj); + sb.append('.'); + sb.append(m); + sb.append('('); + boolean needComma = false; + for(final String arg : args){ + if (needComma) { + sb.append(','); + } + sb.append(arg); + needComma = true; + } + sb.append(')'); + return sb.toString(); + } + + @Override + public List getExtensions() { + return Collections.unmodifiableList(Arrays.asList("jexl", "jexl2", "jexl3")); + } + + @Override + public List getMimeTypes() { + return Collections.unmodifiableList(Arrays.asList("application/x-jexl", + "application/x-jexl2", + "application/x-jexl3")); + } + + @Override + public List getNames() { + return Collections.unmodifiableList(Arrays.asList("JEXL", "Jexl", "jexl", + "JEXL2", "Jexl2", "jexl2", + "JEXL3", "Jexl3", "jexl3")); + } + + @Override + public String getOutputStatement(final String toDisplay) { + if (toDisplay == null) { + return "JEXL.out.print(null)"; + } + return "JEXL.out.print("+StringParser.escapeString(toDisplay, '\'')+")"; + } + + @Override + public Object getParameter(final String key) { + switch (key) { + case ScriptEngine.ENGINE: + return getEngineName(); + case ScriptEngine.ENGINE_VERSION: + return getEngineVersion(); + case ScriptEngine.NAME: + return getNames(); + case ScriptEngine.LANGUAGE: + return getLanguageName(); + case ScriptEngine.LANGUAGE_VERSION: + return getLanguageVersion(); + case "THREADING": + /* + * To implement multithreading, the scripting engine context (inherited from AbstractScriptEngine) + * would need to be made thread-safe; so would the setContext/getContext methods. + * It is easier to share the underlying Uberspect and JEXL engine instance, especially + * with an expression cache. + */ + default: + return null; + } + } + + @Override + public String getProgram(final String... statements) { + final StringBuilder sb = new StringBuilder(); + for(final String statement : statements){ + sb.append(statement.trim()); + if (!statement.endsWith(";")){ + sb.append(';'); + } + } + return sb.toString(); + } + + @Override + public ScriptEngine getScriptEngine() { + return new JexlScriptEngine(this); + } + +} diff --git a/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/scripting/Main.java b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/scripting/Main.java new file mode 100644 index 0000000..ae3ec25 --- /dev/null +++ b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/scripting/Main.java @@ -0,0 +1,91 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package aiyh.utils.tool.org.apache.commons.jexl3.scripting; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileInputStream; +import java.io.InputStreamReader; + +import java.nio.charset.Charset; +import javax.script.ScriptEngine; +import javax.script.ScriptException; + +/** + * Test application for JexlScriptEngine (JSR-223 implementation). + * + * @since 2.0 + */ +public class Main { + + /** + * Reads an input. + * + * @param charset the charset or null for default charset + * @param fileName the file name or null for stdin + * @return the reader + * @throws Exception if anything goes wrong + */ + static BufferedReader read(final Charset charset, final String fileName) throws Exception { + return new BufferedReader( + new InputStreamReader( + fileName == null + ? System.in + : new FileInputStream(new File(fileName)), + charset == null + ? Charset.defaultCharset() + : charset)); + } + + /** + * Test application for JexlScriptEngine (JSR-223 implementation). + * + * If a single argument is present, it is treated as a file name of a JEXL + * script to be evaluated. Any exceptions terminate the application. + * + * Otherwise, lines are read from standard input and evaluated. + * ScriptExceptions are logged, and do not cause the application to exit. + * This is done so that interactive testing is easier. + * + * @param args (optional) file name to evaluate. Stored in the args variable. + * + * @throws Exception if parsing or IO fail + */ + public static void main(final String[] args) throws Exception { + final JexlScriptEngineFactory fac = new JexlScriptEngineFactory(); + final ScriptEngine engine = fac.getScriptEngine(); + engine.put("args", args); + if (args.length == 1){ + final Object value = engine.eval(read(null, args[0])); + System.out.println("Return value: "+value); + } else { + final BufferedReader console = read(null, null); + String line; + System.out.print("> "); + while(null != (line=console.readLine())){ + try { + final Object value = engine.eval(line); + System.out.println("Return value: "+value); + } catch (final ScriptException e) { + System.out.println(e.getLocalizedMessage()); + } + System.out.print("> "); + } + } + } +} diff --git a/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/scripting/package.html b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/scripting/package.html new file mode 100644 index 0000000..9ac1016 --- /dev/null +++ b/src/main/java/aiyh/utils/tool/org/apache/commons/jexl3/scripting/package.html @@ -0,0 +1,31 @@ + + + + Package Documentation for org.apache.commons.jexl3.scripting Package + + +

+ Contains the JSR-223 Scripting Engine for JEXL script. +

+

+ This internal package is not intended for public usage and there is no + guarantee that its public classes or methods will remain as is in subsequent + versions. +

+ + diff --git a/src/main/java/weaver/youhong/ai/pcn/actioin/ccworkflow/CCWorkflowAction.java b/src/main/java/weaver/youhong/ai/pcn/actioin/ccworkflow/CCWorkflowAction.java index 039ccb3..0aacab6 100644 --- a/src/main/java/weaver/youhong/ai/pcn/actioin/ccworkflow/CCWorkflowAction.java +++ b/src/main/java/weaver/youhong/ai/pcn/actioin/ccworkflow/CCWorkflowAction.java @@ -111,6 +111,7 @@ public class CCWorkflowAction extends SafeCusBaseAction { updateMap.put("WORKFLOWID", workflowId); updateMap.put("WORKFLOWTYPE", workflowType); updateMap.put("USERTYPE", 0); + updateMap.put("NODEID", nodId); updateMap.put("AGENTORBYAGENTID", -1); updateMap.put("AGENTTYPE", 0); updateMap.put("SHOWORDER", ++showOrder); @@ -126,6 +127,7 @@ public class CCWorkflowAction extends SafeCusBaseAction { batchList.add(updateMap); } String sql = builderSql(batchList.get(0)); + log.info("解析sql: " + sql); boolean flag = mapper.insertCurrentOperator(sql, batchList); if (!flag) { throw new CustomerException("流程抄送失败!"); @@ -157,10 +159,14 @@ public class CCWorkflowAction extends SafeCusBaseAction { sb.append("insert into workflow_currentoperator ("); for (Map.Entry entry : map.entrySet()) { sb.append(entry.getKey()).append(","); - valueSb.append("#{item.").append(entry.getKey()).append("},"); + valueSb.append("#{item.") + .append(entry.getKey()) + .append("},"); } sb.deleteCharAt(sb.length() - 1); - valueSb.deleteCharAt(sb.length() - 1); + valueSb.deleteCharAt(valueSb.length() - 1); + valueSb.append(")"); + sb.append(")"); sb.append(" values ").append(valueSb); return sb.toString(); } diff --git a/src/test/java/youhong/ai/mymapper/ParseSqlTest.java b/src/test/java/youhong/ai/mymapper/ParseSqlTest.java index c384060..102bc68 100644 --- a/src/test/java/youhong/ai/mymapper/ParseSqlTest.java +++ b/src/test/java/youhong/ai/mymapper/ParseSqlTest.java @@ -22,13 +22,13 @@ public class ParseSqlTest extends BaseTest { @Test public void test() { String sql = "select $t{table} into set field = #{field} from table :my-where{\n" + - "\t:my-for:item=\"item\":index=\"index\":collection=\"ids\":open=\"\":separator=\",\":close=\"\\\":nullable{\n" + + "\t:my-for:item=\"item\":index=\"index\":collection=\"ids.split(',')\":open=\"\":separator=\",\":close=\"\\\":nullable{\n" + "\t\tand $t{index} = #{item}\n" + "\t}\n" + "} order by abase = #{order} $t{desc}"; ParseSqlUtil parseSqlUtil = new ParseSqlUtil(); Map param = new HashMap<>(); - param.put("ids", Arrays.asList("var1", "var2", "var3")); + param.put("ids", "var1,var2,var3"); param.put("table", "hhhtable"); param.put("field", "field"); param.put("order", "order"); diff --git a/src/test/java/youhong/ai/mymapper/command/commandImpl/MyForCommand.java b/src/test/java/youhong/ai/mymapper/command/commandImpl/MyForCommand.java index 796a97c..1302dc8 100644 --- a/src/test/java/youhong/ai/mymapper/command/commandImpl/MyForCommand.java +++ b/src/test/java/youhong/ai/mymapper/command/commandImpl/MyForCommand.java @@ -12,6 +12,7 @@ import youhong.ai.mymapper.command.properties.AbstractCommandProperties; import youhong.ai.mymapper.command.properties.MyForProperties; import youhong.ai.mymapper.util.ParamValueUtil; import youhong.ai.mymapper.util.ParseSqlUtil; +import youhong.ai.mymapper.util.ScriptUtil; import java.util.*; import java.util.function.BiFunction; @@ -44,15 +45,27 @@ public class MyForCommand implements ISqlCommand { if (StringUtil.isNullOrEmpty(collection)) { throw new NullPointerException("collection attribute is can not to be null or empty! "); } - boolean valueFunInvoke = false; + /*boolean valueFunInvoke = false; String funName = ""; if (collection.endsWith(")")) { char[] chars = collection.toCharArray(); funName = findInvokeFun(chars); collection = collection.replace("." + funName, ""); valueFunInvoke = true; + }*/ + Object value; + try { + Map tempMap = ParseSqlUtil.PARSE_TEMP_PARAM_LOCALE.get(); + if (!Objects.isNull(tempMap)) { + params.putAll(tempMap); + } + value = ScriptUtil.invokeScript(collection, params); + } catch (Exception e) { + value = ParamValueUtil.getValueByKeyStr(collection, params); + } + if (Objects.isNull(value)) { + value = ParamValueUtil.getValueByKeyStr(collection, params); } - Object value = ParamValueUtil.getValueByKeyStr(collection, params); if (Objects.isNull(value)) { if (properties.getNullable()) { return null; @@ -60,7 +73,7 @@ public class MyForCommand implements ISqlCommand { throw new NullPointerException("collection value is null: " + collection); } } - if (valueFunInvoke) { + /*if (valueFunInvoke) { if (value instanceof String) { value = invokeFun(value, funName); if (value.getClass().isArray()) { @@ -69,6 +82,11 @@ public class MyForCommand implements ISqlCommand { return listCommand(collect, properties); } } + }*/ + if (value.getClass().isArray()) { + Object[] valueArr = (Object[]) value; + List collect = Arrays.stream(valueArr).collect(Collectors.toList()); + return listCommand(collect, properties); } if (value instanceof List) { return listCommand(value, properties); @@ -76,7 +94,7 @@ public class MyForCommand implements ISqlCommand { if (value instanceof Map) { return mapCommand(value, properties); } - return null; + throw new CustomerException("Uncertain type! " + value.getClass()); } private Object invokeFun(Object value, String funName) { diff --git a/src/test/java/youhong/ai/mymapper/command/commandImpl/MyIfCommand.java b/src/test/java/youhong/ai/mymapper/command/commandImpl/MyIfCommand.java new file mode 100644 index 0000000..f1442c2 --- /dev/null +++ b/src/test/java/youhong/ai/mymapper/command/commandImpl/MyIfCommand.java @@ -0,0 +1,16 @@ +package youhong.ai.mymapper.command.commandImpl; + +import youhong.ai.mymapper.command.annotation.SqlCommand; +import youhong.ai.mymapper.command.constant.CommandConsTant; + +/** + *

if指令

+ * + *

create: 2023/3/3 23:19

+ * + * @author youHong.ai + */ +@SqlCommand(CommandConsTant.IF) +public class MyIfCommand { + +} diff --git a/src/test/java/youhong/ai/mymapper/command/constant/CommandConsTant.java b/src/test/java/youhong/ai/mymapper/command/constant/CommandConsTant.java index 4fcd63b..e9b427f 100644 --- a/src/test/java/youhong/ai/mymapper/command/constant/CommandConsTant.java +++ b/src/test/java/youhong/ai/mymapper/command/constant/CommandConsTant.java @@ -38,4 +38,6 @@ public class CommandConsTant { public static final String SET = "set"; + public static final String IF = "if"; + } diff --git a/src/test/java/youhong/ai/mymapper/command/properties/MyIfProperties.java b/src/test/java/youhong/ai/mymapper/command/properties/MyIfProperties.java new file mode 100644 index 0000000..ffa5d5d --- /dev/null +++ b/src/test/java/youhong/ai/mymapper/command/properties/MyIfProperties.java @@ -0,0 +1,11 @@ +package youhong.ai.mymapper.command.properties; + +/** + *

if指令参数

+ * + *

create: 2023/3/3 23:20

+ * + * @author youHong.ai + */ +public class MyIfProperties extends AbstractCommandProperties { +} diff --git a/src/test/java/youhong/ai/mymapper/util/AbstractCommandPropertiesFactory.java b/src/test/java/youhong/ai/mymapper/util/AbstractCommandPropertiesFactory.java index 4ac329b..a7d2937 100644 --- a/src/test/java/youhong/ai/mymapper/util/AbstractCommandPropertiesFactory.java +++ b/src/test/java/youhong/ai/mymapper/util/AbstractCommandPropertiesFactory.java @@ -4,6 +4,7 @@ import aiyh.utils.annotation.MethodRuleNo; import youhong.ai.mymapper.command.constant.CommandConsTant; import youhong.ai.mymapper.command.properties.AbstractCommandProperties; import youhong.ai.mymapper.command.properties.MyForProperties; +import youhong.ai.mymapper.command.properties.MyIfProperties; import youhong.ai.mymapper.command.properties.MyWhereProperties; import java.util.List; @@ -37,4 +38,14 @@ public class AbstractCommandPropertiesFactory { return commandProperties; } + @MethodRuleNo(name = CommandConsTant.IF, desc = "if 指令参数解析") + private AbstractCommandProperties getIfProperties(List commandItemList) { + AbstractCommandProperties commandProperties = new MyIfProperties(); + for (int i = 1; i < commandItemList.size(); i++) { + String item = commandItemList.get(i); + CommandPropUtil.setValueString(commandProperties, item); + } + return commandProperties; + } + } diff --git a/src/test/java/youhong/ai/mymapper/util/CommandPropUtil.java b/src/test/java/youhong/ai/mymapper/util/CommandPropUtil.java index 1ec1b40..c5e4c8d 100644 --- a/src/test/java/youhong/ai/mymapper/util/CommandPropUtil.java +++ b/src/test/java/youhong/ai/mymapper/util/CommandPropUtil.java @@ -86,7 +86,7 @@ public class CommandPropUtil { try { field = aClass.getDeclaredField(fieldName); } catch (NoSuchFieldException e) { - throw new CustomerException(e); + throw new CustomerException("Uncertain attribute of :" + fieldName); } Method setMethod = findMethod(aClass, fieldName, field); if (split.length == 1) { diff --git a/src/test/java/youhong/ai/mymapper/util/ScriptUtil.java b/src/test/java/youhong/ai/mymapper/util/ScriptUtil.java new file mode 100644 index 0000000..48d38e0 --- /dev/null +++ b/src/test/java/youhong/ai/mymapper/util/ScriptUtil.java @@ -0,0 +1,25 @@ +package youhong.ai.mymapper.util; + +import aiyh.utils.tool.org.apache.commons.jexl3.*; + +import java.util.Map; + +/** + *

脚本工具

+ * + *

create: 2023/3/3 23:03

+ * + * @author youHong.ai + */ +public class ScriptUtil { + private static final JexlEngine jexl = new JexlBuilder().create(); + + public static Object invokeScript(String script, Map params) { + JexlContext jc = new MapContext(); + for (Map.Entry entry : params.entrySet()) { + jc.set(entry.getKey(), entry.getValue()); + } + JexlExpression expression = jexl.createExpression(script); + return expression.evaluate(jc); + } +} diff --git a/src/test/java/youhong/ai/pcn/UtilTest.java b/src/test/java/youhong/ai/pcn/UtilTest.java index 18ef3f7..ef1b54e 100644 --- a/src/test/java/youhong/ai/pcn/UtilTest.java +++ b/src/test/java/youhong/ai/pcn/UtilTest.java @@ -2,6 +2,7 @@ package youhong.ai.pcn; import aiyh.utils.GenerateFileUtil; import aiyh.utils.Util; +import aiyh.utils.tool.org.apache.commons.jexl3.*; import basetest.BaseTest; import com.alibaba.fastjson.JSON; import ebu7common.youhong.ai.bean.Builder; @@ -17,6 +18,8 @@ import weaver.youhong.ai.pcn.actioin.sendemail.SendEmailToExternalPersonnelActio import youhong.ai.pcn.mapper.TransTestMapper; import youhong.ai.pcn.pojo.Student; +import java.util.ArrayList; +import java.util.Arrays; import java.util.HashMap; import java.util.List; @@ -162,4 +165,26 @@ public class UtilTest extends BaseTest { System.out.println('\t'); System.out.println(' '); } + + + @Test + public void testGroovy() { + // GroovyScriptFactory groovyScriptFactory = new GroovyScriptFactory(); + // ExpressionParser parser = new SpelExpressionParser(); + // Expression expression = parser.parseExpression("#ids != null and #test == \"haha\""); + // EvaluationContext context = new StandardEvaluationContext(); + // context.setVariable("ids", new ArrayList<>()); + // context.setVariable("test", "haha"); + // Object value = expression.getValue(context); + // System.out.println(value); + JexlEngine jexl = new JexlBuilder().create(); + JexlContext jc = new MapContext(); + jc.set("verifyStatus", 1); + jc.set("test", "1,2,3,4"); + jc.set("array", new ArrayList<>()); + JexlExpression expression = jexl.createExpression("test.split(',')"); + Object result = expression.evaluate(jc); + System.out.println(Arrays.toString((Object[]) result)); + + } }