| /******************************************************************************* |
| * 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 org.ofbiz.base.util.string; |
| |
| import java.io.Serializable; |
| import java.math.BigDecimal; |
| import java.util.ArrayList; |
| import java.util.HashMap; |
| import java.util.Locale; |
| import java.util.Map; |
| import java.util.TimeZone; |
| |
| import javax.el.PropertyNotFoundException; |
| |
| import org.ofbiz.base.lang.IsEmpty; |
| import org.ofbiz.base.lang.SourceMonitored; |
| import org.ofbiz.base.util.Debug; |
| import org.ofbiz.base.util.ObjectType; |
| import org.ofbiz.base.util.ScriptUtil; |
| import org.ofbiz.base.util.UtilDateTime; |
| import org.ofbiz.base.util.UtilFormatOut; |
| import org.ofbiz.base.util.UtilGenerics; |
| import org.ofbiz.base.util.UtilMisc; |
| import org.ofbiz.base.util.UtilValidate; |
| import org.ofbiz.base.util.cache.UtilCache; |
| |
| /** Expands String values that contain Unified Expression Language (JSR 245) |
| * syntax. This class also supports the execution of bsh scripts by using the |
| * 'bsh:' prefix, and Groovy scripts by using the 'groovy:' prefix. |
| * Further it is possible to control the output by specifying the suffix |
| * '?currency(XXX)' to format the output according to the supplied locale |
| * and specified (XXX) currency.<p>This class extends the UEL by allowing |
| * nested expressions.</p> |
| */ |
| @SourceMonitored |
| @SuppressWarnings("serial") |
| public abstract class FlexibleStringExpander implements Serializable, IsEmpty { |
| |
| public static final String module = FlexibleStringExpander.class.getName(); |
| public static final String openBracket = "${"; |
| public static final String closeBracket = "}"; |
| protected static final UtilCache<Key, FlexibleStringExpander> exprCache = UtilCache.createUtilCache("flexibleStringExpander.ExpressionCache"); |
| protected static final FlexibleStringExpander nullExpr = new ConstSimpleElem(new char[0]); |
| |
| /** |
| * Returns <code>true</code> if <code>fse</code> contains a <code>String</code> constant. |
| * @param fse The <code>FlexibleStringExpander</code> to test |
| * @return <code>true</code> if <code>fse</code> contains a <code>String</code> constant |
| */ |
| public static boolean containsConstant(FlexibleStringExpander fse) { |
| if (fse instanceof ConstSimpleElem || fse instanceof ConstOffsetElem) { |
| return true; |
| } |
| if (fse instanceof Elements) { |
| Elements fseElements = (Elements) fse; |
| for (FlexibleStringExpander childElement : fseElements.childElems) { |
| if (containsConstant(childElement)) { |
| return true; |
| } |
| } |
| } |
| return false; |
| } |
| |
| /** |
| * Returns <code>true</code> if <code>fse</code> contains an expression. |
| * @param fse The <code>FlexibleStringExpander</code> to test |
| * @return <code>true</code> if <code>fse</code> contains an expression |
| */ |
| public static boolean containsExpression(FlexibleStringExpander fse) { |
| return !(fse instanceof ConstSimpleElem); |
| } |
| |
| /** |
| * Returns <code>true</code> if <code>fse</code> contains a script. |
| * @param fse The <code>FlexibleStringExpander</code> to test |
| * @return <code>true</code> if <code>fse</code> contains a script |
| */ |
| public static boolean containsScript(FlexibleStringExpander fse) { |
| if (fse instanceof ScriptElem) { |
| return true; |
| } |
| if (fse instanceof Elements) { |
| Elements fseElements = (Elements) fse; |
| for (FlexibleStringExpander childElement : fseElements.childElems) { |
| if (containsScript(childElement)) { |
| return true; |
| } |
| } |
| } |
| return false; |
| } |
| |
| /** Evaluate an expression and return the result as a <code>String</code>. |
| * Null expressions return <code>null</code>. |
| * A null <code>context</code> argument will return the original expression. |
| * <p>Note that the behavior of this method is not the same as using |
| * <code>FlexibleStringExpander.getInstance(expression).expandString(context)</code> |
| * because it returns <code>null</code> when given a null <code>expression</code> |
| * argument, and |
| * <code>FlexibleStringExpander.getInstance(expression).expandString(context)</code> |
| * returns an empty <code>String</code>.</p> |
| * |
| * @param expression The original expression |
| * @param context The evaluation context |
| * @return The original expression's evaluation result as a <code>String</code> |
| */ |
| public static String expandString(String expression, Map<String, ? extends Object> context) { |
| return expandString(expression, context, null, null); |
| } |
| |
| /** Evaluate an expression and return the result as a <code>String</code>. |
| * Null expressions return <code>null</code>. |
| * A null <code>context</code> argument will return the original expression. |
| * <p>Note that the behavior of this method is not the same as using |
| * <code>FlexibleStringExpander.getInstance(expression).expandString(context, locale)</code> |
| * because it returns <code>null</code> when given a null <code>expression</code> |
| * argument, and |
| * <code>FlexibleStringExpander.getInstance(expression).expandString(context, locale)</code> |
| * returns an empty <code>String</code>.</p> |
| * |
| * @param expression The original expression |
| * @param context The evaluation context |
| * @param locale The locale to be used for localization |
| * @return The original expression's evaluation result as a <code>String</code> |
| */ |
| public static String expandString(String expression, Map<String, ? extends Object> context, Locale locale) { |
| return expandString(expression, context, null, locale); |
| } |
| |
| /** Evaluate an expression and return the result as a <code>String</code>. |
| * Null expressions return <code>null</code>. |
| * A null <code>context</code> argument will return the original expression. |
| * <p>Note that the behavior of this method is not the same as using |
| * <code>FlexibleStringExpander.getInstance(expression).expandString(context, timeZone locale)</code> |
| * because it returns <code>null</code> when given a null <code>expression</code> |
| * argument, and |
| * <code>FlexibleStringExpander.getInstance(expression).expandString(context, timeZone, locale)</code> |
| * returns an empty <code>String</code>.</p> |
| * |
| * @param expression The original expression |
| * @param context The evaluation context |
| * @param timeZone The time zone to be used for localization |
| * @param locale The locale to be used for localization |
| * @return The original expression's evaluation result as a <code>String</code> |
| */ |
| public static String expandString(String expression, Map<String, ? extends Object> context, TimeZone timeZone, Locale locale) { |
| if (expression == null) { |
| return ""; |
| } |
| if (context == null || !expression.contains(openBracket)) { |
| return expression; |
| } |
| FlexibleStringExpander fse = FlexibleStringExpander.getInstance(expression); |
| return fse.expandString(context, timeZone, locale); |
| } |
| |
| /** Returns a <code>FlexibleStringExpander</code> object. <p>A null or |
| * empty argument will return a <code>FlexibleStringExpander</code> |
| * object that represents an empty expression. That object is a shared |
| * singleton, so there is no memory or performance penalty in using it.</p> |
| * <p>If the method is passed a <code>String</code> argument that doesn't |
| * contain an expression, the <code>FlexibleStringExpander</code> object |
| * that is returned does not perform any evaluations on the original |
| * <code>String</code> - any methods that return a <code>String</code> |
| * will return the original <code>String</code>. The object returned by |
| * this method is very compact - taking less memory than the original |
| * <code>String</code>.</p> |
| * |
| * @param expression The original expression |
| * @return A <code>FlexibleStringExpander</code> instance |
| */ |
| public static FlexibleStringExpander getInstance(String expression) { |
| return getInstance(expression, true); |
| } |
| |
| /* Returns a <code>FlexibleStringExpander</code> object. <p>A null or |
| * empty argument will return a <code>FlexibleStringExpander</code> |
| * object that represents an empty expression. That object is a shared |
| * singleton, so there is no memory or performance penalty in using it.</p> |
| * <p>If the method is passed a <code>String</code> argument that doesn't |
| * contain an expression, the <code>FlexibleStringExpander</code> object |
| * that is returned does not perform any evaluations on the original |
| * <code>String</code> - any methods that return a <code>String</code> |
| * will return the original <code>String</code>. The object returned by |
| * this method is very compact - taking less memory than the original |
| * <code>String</code>.</p> |
| * |
| * @param expression The original expression |
| * @param useCache whether to store things into a global cache |
| * @return A <code>FlexibleStringExpander</code> instance |
| */ |
| public static FlexibleStringExpander getInstance(String expression, boolean useCache) { |
| if (UtilValidate.isEmpty(expression)) { |
| return nullExpr; |
| } |
| return getInstance(expression, expression.toCharArray(), 0, expression.length(), useCache); |
| } |
| |
| private static FlexibleStringExpander getInstance(String expression, char[] chars, int offset, int length, boolean useCache) { |
| if (length == 0) { |
| return nullExpr; |
| } |
| if (!useCache) { |
| return parse(chars, offset, length); |
| } |
| // Remove the next nine lines to cache all expressions |
| if (!expression.contains(openBracket)) { |
| if (chars.length == length) { |
| return new ConstSimpleElem(chars); |
| } else { |
| return new ConstOffsetElem(chars, offset, length); |
| } |
| } |
| Key key = chars.length == length ? new SimpleKey(chars) : new OffsetKey(chars, offset, length); |
| FlexibleStringExpander fse = exprCache.get(key); |
| if (fse == null) { |
| exprCache.put(key, parse(chars, offset, length)); |
| fse = exprCache.get(key); |
| } |
| return fse; |
| } |
| |
| private static abstract class Key { |
| @Override |
| public final boolean equals(Object o) { |
| // No class test here, nor null, as this class is only used |
| // internally |
| return toString().equals(o.toString()); |
| } |
| |
| @Override |
| public final int hashCode() { |
| return toString().hashCode(); |
| } |
| } |
| |
| private static final class SimpleKey extends Key { |
| private final char[] chars; |
| |
| protected SimpleKey(char[] chars) { |
| this.chars = chars; |
| } |
| |
| @Override |
| public String toString() { |
| return new String(chars); |
| } |
| } |
| |
| private static final class OffsetKey extends Key { |
| private final char[] chars; |
| private final int offset; |
| private final int length; |
| |
| protected OffsetKey(char[] chars, int offset, int length) { |
| this.chars = chars; |
| this.offset = offset; |
| this.length = length; |
| } |
| |
| @Override |
| public String toString() { |
| return new String(chars, offset, length); |
| } |
| } |
| |
| private static FlexibleStringExpander parse(char[] chars, int offset, int length) { |
| FlexibleStringExpander[] strElems = getStrElems(chars, offset, length); |
| if (strElems.length == 1) { |
| return strElems[0]; |
| } else { |
| return new Elements(chars, offset, length, strElems); |
| } |
| } |
| |
| protected static FlexibleStringExpander[] getStrElems(char[] chars, int offset, int length) { |
| String expression = new String(chars, 0, length + offset); |
| int start = expression.indexOf(openBracket, offset); |
| if (start == -1) { |
| return new FlexibleStringExpander[] { new ConstOffsetElem(chars, offset, length) }; |
| } |
| int origLen = length; |
| ArrayList<FlexibleStringExpander> strElems = new ArrayList<FlexibleStringExpander>(); |
| int currentInd = offset; |
| int end = -1; |
| while (start != -1) { |
| end = expression.indexOf(closeBracket, start); |
| if (end == -1) { |
| Debug.logWarning("Found a ${ without a closing } (curly-brace) in the String: " + expression, module); |
| break; |
| } |
| // Check for escaped expression |
| boolean escapedExpression = (start - 1 >= 0 && expression.charAt(start - 1) == '\\'); |
| if (start > currentInd) { |
| // append everything from the current index to the start of the expression |
| strElems.add(new ConstOffsetElem(chars, currentInd, (escapedExpression ? start -1 : start) - currentInd)); |
| } |
| if (expression.indexOf("bsh:", start + 2) == start + 2 && !escapedExpression) { |
| // checks to see if this starts with a "bsh:", if so treat the rest of the expression as a bsh scriptlet |
| strElems.add(new ScriptElem(chars, start, Math.min(end + 1, start + length) - start, start + 6, end - start - 6)); |
| } else if (expression.indexOf("groovy:", start + 2) == start + 2 && !escapedExpression) { |
| // checks to see if this starts with a "groovy:", if so treat the rest of the expression as a groovy scriptlet |
| strElems.add(new ScriptElem(chars, start, Math.min(end + 1, start + length) - start, start + 9, end - start - 9)); |
| } else { |
| // Scan for matching closing bracket |
| int ptr = expression.indexOf(openBracket, start + 2); |
| while (ptr != -1 && end != -1 && ptr < end) { |
| end = expression.indexOf(closeBracket, end + 1); |
| ptr = expression.indexOf(openBracket, ptr + 2); |
| } |
| if (end == -1) { |
| end = origLen; |
| } |
| // Evaluation sequence is important - do not change it |
| if (escapedExpression) { |
| strElems.add(new ConstOffsetElem(chars, start, end + 1 - start)); |
| } else { |
| String subExpression = expression.substring(start + 2, end); |
| int currencyPos = subExpression.indexOf("?currency("); |
| int closeParen = currencyPos > 0 ? subExpression.indexOf(")", currencyPos + 10) : -1; |
| if (closeParen != -1) { |
| strElems.add(new CurrElem(chars, start, Math.min(end + 1, start + length) - start, start + 2, end - start - 1)); |
| } else if (subExpression.contains(openBracket)) { |
| strElems.add(new NestedVarElem(chars, start, Math.min(end + 1, start + length) - start, start + 2, Math.min(end - 2, start + length) - start)); |
| } else { |
| strElems.add(new VarElem(chars, start, Math.min(end + 1, start + length) - start, start + 2, Math.min(end - 2, start + length) - start)); |
| } |
| } |
| } |
| // reset the current index to after the expression, and the start to the beginning of the next expression |
| currentInd = end + 1; |
| if (currentInd > origLen + offset) { |
| currentInd = origLen + offset; |
| } |
| start = expression.indexOf(openBracket, currentInd); |
| } |
| // append the rest of the original string, ie after the last expression |
| if (currentInd < origLen + offset) { |
| strElems.add(new ConstOffsetElem(chars, currentInd, offset + length - currentInd)); |
| } |
| return strElems.toArray(new FlexibleStringExpander[strElems.size()]); |
| } |
| |
| // Note: a character array is used instead of a String to keep the memory footprint small. |
| protected final char[] chars; |
| protected int hint = 20; |
| |
| protected FlexibleStringExpander(char[] chars) { |
| this.chars = chars; |
| } |
| |
| protected abstract Object get(Map<String, ? extends Object> context, TimeZone timeZone, Locale locale); |
| |
| private static Locale getLocale(Locale locale, Map<String, ? extends Object> context) { |
| if (locale == null) { |
| locale = (Locale) context.get("locale"); |
| if (locale == null && context.containsKey("autoUserLogin")) { |
| Map<String, Object> autoUserLogin = UtilGenerics.cast(context.get("autoUserLogin")); |
| locale = UtilMisc.ensureLocale(autoUserLogin.get("lastLocale")); |
| } |
| if (locale == null) { |
| locale = Locale.getDefault(); |
| } |
| } |
| return locale; |
| } |
| |
| private static TimeZone getTimeZone(TimeZone timeZone, Map<String, ? extends Object> context) { |
| if (timeZone == null) { |
| timeZone = (TimeZone) context.get("timeZone"); |
| if (timeZone == null && context.containsKey("autoUserLogin")) { |
| Map<String, String> autoUserLogin = UtilGenerics.cast(context.get("autoUserLogin")); |
| timeZone = UtilDateTime.toTimeZone(autoUserLogin.get("lastTimeZone")); |
| } |
| if (timeZone == null) { |
| timeZone = TimeZone.getDefault(); |
| } |
| } |
| return timeZone; |
| } |
| |
| /** Evaluate this object's expression and return the result as a <code>String</code>. |
| * Null or empty expressions return an empty <code>String</code>. |
| * A <code>null context</code> argument will return the original expression. |
| * |
| * @param context The evaluation context |
| * @return This object's expression result as a <code>String</code> |
| */ |
| public String expandString(Map<String, ? extends Object> context) { |
| return this.expandString(context, null, null); |
| } |
| |
| /** Evaluate this object's expression and return the result as a <code>String</code>. |
| * Null or empty expressions return an empty <code>String</code>. |
| * A <code>null context</code> argument will return the original expression. |
| * |
| * @param context The evaluation context |
| * @param locale The locale to be used for localization |
| * @return This object's expression result as a <code>String</code> |
| */ |
| public String expandString(Map<String, ? extends Object> context, Locale locale) { |
| return this.expandString(context, null, locale); |
| } |
| |
| /** Evaluate this object's expression and return the result as a <code>String</code>. |
| * Null or empty expressions return an empty <code>String</code>. |
| * A <code>null context</code> argument will return the original expression. |
| * |
| * @param context The evaluation context |
| * @param timeZone The time zone to be used for localization |
| * @param locale The locale to be used for localization |
| * @return This object's expression result as a <code>String</code> |
| */ |
| public String expandString(Map<String, ? extends Object> context, TimeZone timeZone, Locale locale) { |
| if (context == null) { |
| return this.toString(); |
| } |
| timeZone = getTimeZone(timeZone, context); |
| locale = getLocale(locale, context); |
| Object obj = get(context, timeZone, locale); |
| StringBuilder buffer = new StringBuilder(this.hint); |
| try { |
| if (obj != null) { |
| if (obj instanceof String) { |
| buffer.append(obj); |
| } else { |
| buffer.append(ObjectType.simpleTypeConvert(obj, "String", null, timeZone, locale, true)); |
| } |
| } |
| } catch (Exception e) { |
| buffer.append(obj); |
| } |
| if (buffer.length() > this.hint) { |
| this.hint = buffer.length(); |
| } |
| return buffer.toString(); |
| } |
| |
| /** Evaluate this object's expression and return the result as an <code>Object</code>. |
| * Null or empty expressions return an empty <code>String</code>. |
| * A <code>null context</code> argument will return the original expression. |
| * |
| * @param context The evaluation context |
| * @return This object's expression result as a <code>String</code> |
| */ |
| public Object expand(Map<String, ? extends Object> context) { |
| return this.expand(context, null, null); |
| } |
| |
| /** Evaluate this object's expression and return the result as an <code>Object</code>. |
| * Null or empty expressions return an empty <code>String</code>. |
| * A <code>null context</code> argument will return the original expression. |
| * |
| * @param context The evaluation context |
| * @param locale The locale to be used for localization |
| * @return This object's expression result as a <code>String</code> |
| */ |
| public Object expand(Map<String, ? extends Object> context, Locale locale) { |
| return this.expand(context, null, locale); |
| } |
| |
| /** Evaluate this object's expression and return the result as an <code>Object</code>. |
| * Null or empty expressions return an empty <code>String</code>. |
| * A <code>null context</code> argument will return the original expression. |
| * |
| * @param context The evaluation context |
| * @param timeZone The time zone to be used for localization |
| * @param locale The locale to be used for localization |
| * @return This object's expression result as a <code>String</code> |
| */ |
| public Object expand(Map<String, ? extends Object> context, TimeZone timeZone, Locale locale) { |
| if (context == null) { |
| return null; |
| } |
| return get(context, getTimeZone(timeZone, context), getLocale(locale, context)); |
| } |
| |
| /** Returns a copy of the original expression. |
| * |
| * @return The original expression |
| */ |
| public abstract String getOriginal(); |
| |
| /** Returns <code>true</code> if the original expression is empty |
| * or <code>null</code>. |
| * |
| * @return <code>true</code> if the original expression is empty |
| * or <code>null</code> |
| */ |
| public abstract boolean isEmpty(); |
| |
| /** Returns a copy of the original expression. |
| * |
| * @return The original expression |
| */ |
| @Override |
| public String toString() { |
| return this.getOriginal(); |
| } |
| |
| protected static abstract class ArrayOffsetString extends FlexibleStringExpander { |
| protected final int offset; |
| protected final int length; |
| |
| protected ArrayOffsetString(char[] chars, int offset, int length) { |
| super(chars); |
| this.offset = offset; |
| this.length = length; |
| } |
| @Override |
| public boolean isEmpty() { |
| // This is always false; the complex child classes can't be |
| // empty, as they contain at least ${; constant elements |
| // with a length of 0 will never be created. |
| return false; |
| } |
| @Override |
| public String getOriginal() { |
| return new String(this.chars, this.offset, this.length); |
| } |
| } |
| |
| /** An object that represents a <code>String</code> constant portion of an expression. */ |
| protected static class ConstSimpleElem extends FlexibleStringExpander { |
| protected ConstSimpleElem(char[] chars) { |
| super(chars); |
| } |
| |
| @Override |
| public boolean isEmpty() { |
| return this.chars.length == 0; |
| } |
| |
| @Override |
| public String getOriginal() { |
| return new String(this.chars); |
| } |
| |
| @Override |
| public String expandString(Map<String, ? extends Object> context, TimeZone timeZone, Locale locale) { |
| return getOriginal(); |
| } |
| |
| @Override |
| protected Object get(Map<String, ? extends Object> context, TimeZone timeZone, Locale locale) { |
| return isEmpty() ? null : getOriginal(); |
| } |
| } |
| |
| /** An object that represents a <code>String</code> constant portion of an expression. */ |
| protected static class ConstOffsetElem extends ArrayOffsetString { |
| protected ConstOffsetElem(char[] chars, int offset, int length) { |
| super(chars, offset, length); |
| } |
| |
| @Override |
| protected Object get(Map<String, ? extends Object> context, TimeZone timeZone, Locale locale) { |
| return getOriginal(); |
| } |
| |
| @Override |
| public String expandString(Map<String, ? extends Object> context, TimeZone timeZone, Locale locale) { |
| return new String(this.chars, this.offset, this.length); |
| } |
| } |
| |
| /** An object that represents a currency portion of an expression. */ |
| protected static class CurrElem extends ArrayOffsetString { |
| protected final char[] valueStr; |
| protected final FlexibleStringExpander codeExpr; |
| |
| protected CurrElem(char[] chars, int offset, int length, int parseStart, int parseLength) { |
| super(chars, offset, length); |
| String parse = new String(chars, parseStart, parseLength); |
| int currencyPos = parse.indexOf("?currency("); |
| int closeParen = parse.indexOf(")", currencyPos + 10); |
| this.codeExpr = FlexibleStringExpander.getInstance(parse, chars, parseStart + currencyPos + 10, closeParen - currencyPos - 10, true); |
| this.valueStr = openBracket.concat(parse.substring(0, currencyPos)).concat(closeBracket).toCharArray(); |
| } |
| |
| @Override |
| protected Object get(Map<String, ? extends Object> context, TimeZone timeZone, Locale locale) { |
| try { |
| Object obj = UelUtil.evaluate(context, new String(this.valueStr)); |
| if (obj != null) { |
| String currencyCode = this.codeExpr.expandString(context, timeZone, locale); |
| return UtilFormatOut.formatCurrency(new BigDecimal(obj.toString()), currencyCode, locale); |
| } |
| } catch (PropertyNotFoundException e) { |
| if (Debug.verboseOn()) { |
| Debug.logVerbose("Error evaluating expression: " + e, module); |
| } |
| } catch (Exception e) { |
| Debug.logError("Error evaluating expression: " + e, module); |
| } |
| return null; |
| } |
| } |
| |
| /** A container object that contains expression fragments. */ |
| protected static class Elements extends ArrayOffsetString { |
| protected final FlexibleStringExpander[] childElems; |
| |
| protected Elements(char[] chars, int offset, int length, FlexibleStringExpander[] childElems) { |
| super(chars, offset, length); |
| this.childElems = childElems; |
| } |
| |
| @Override |
| protected Object get(Map<String, ? extends Object> context, TimeZone timeZone, Locale locale) { |
| StringBuilder buffer = new StringBuilder(); |
| for (FlexibleStringExpander child : this.childElems) { |
| buffer.append(child.expandString(context, timeZone, locale)); |
| } |
| return buffer.toString(); |
| } |
| } |
| |
| /** An object that represents a <code>${[groovy|bsh]:}</code> expression. */ |
| protected static class ScriptElem extends ArrayOffsetString { |
| private final String language; |
| private final int parseStart; |
| private final int parseLength; |
| private final String script; |
| protected final Class<?> parsedScript; |
| |
| protected ScriptElem(char[] chars, int offset, int length, int parseStart, int parseLength) { |
| super(chars, offset, length); |
| this.language = new String(this.chars, offset + 2, parseStart - offset - 3); |
| this.parseStart = parseStart; |
| this.parseLength = parseLength; |
| this.script = new String(this.chars, this.parseStart, this.parseLength); |
| this.parsedScript = ScriptUtil.parseScript(this.language, this.script); |
| } |
| |
| @Override |
| protected Object get(Map<String, ? extends Object> context, TimeZone timeZone, Locale locale) { |
| try { |
| Map <String, Object> contextCopy = new HashMap<String, Object>(context); |
| Object obj = ScriptUtil.evaluate(this.language, this.script, this.parsedScript, contextCopy); |
| if (obj != null) { |
| return obj; |
| } else { |
| if (Debug.verboseOn()) { |
| Debug.logVerbose("Scriptlet evaluated to null [" + this + "].", module); |
| } |
| } |
| } catch (Exception e) { |
| Debug.logWarning(e, "Error evaluating scriptlet [" + this + "]; error was: " + e, module); |
| } |
| return null; |
| } |
| } |
| |
| /** An object that represents a nested expression. */ |
| protected static class NestedVarElem extends ArrayOffsetString { |
| protected final FlexibleStringExpander[] childElems; |
| |
| protected NestedVarElem(char[] chars, int offset, int length, int parseStart, int parseLength) { |
| super(chars, offset, length); |
| this.childElems = getStrElems(chars, parseStart, parseLength); |
| if (length > this.hint) { |
| this.hint = length; |
| } |
| } |
| |
| @Override |
| protected Object get(Map<String, ? extends Object> context, TimeZone timeZone, Locale locale) { |
| StringBuilder expr = new StringBuilder(this.hint); |
| for (FlexibleStringExpander child : this.childElems) { |
| expr.append(child.expandString(context, timeZone, locale)); |
| } |
| if (expr.length() == 0) { |
| return ""; |
| } |
| try { |
| return UelUtil.evaluate(context, openBracket.concat(expr.toString()).concat(closeBracket)); |
| } catch (PropertyNotFoundException e) { |
| if (Debug.verboseOn()) { |
| Debug.logVerbose("Error evaluating expression: " + e, module); |
| } |
| } catch (Exception e) { |
| Debug.logError("Error evaluating expression: " + e, module); |
| } |
| return ""; |
| } |
| } |
| |
| /** An object that represents a simple, non-nested expression. */ |
| protected static class VarElem extends ArrayOffsetString { |
| protected final char[] bracketedOriginal; |
| |
| protected VarElem(char[] chars, int offset, int length, int parseStart, int parseLength) { |
| super(chars, offset, length); |
| this.bracketedOriginal = openBracket.concat(UelUtil.prepareExpression(new String(chars, parseStart, parseLength))).concat(closeBracket).toCharArray(); |
| } |
| |
| @Override |
| protected Object get(Map<String, ? extends Object> context, TimeZone timeZone, Locale locale) { |
| Object obj = null; |
| try { |
| obj = UelUtil.evaluate(context, new String(this.bracketedOriginal)); |
| } catch (PropertyNotFoundException e) { |
| if (Debug.verboseOn()) { |
| Debug.logVerbose("Error evaluating expression " + this + ": " + e, module); |
| } |
| } catch (Exception e) { |
| Debug.logError("Error evaluating expression " + this + ": " + e, module); |
| } |
| return obj; |
| } |
| } |
| } |