/*
 * 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.apache.commons.configuration2.interpol;

import java.util.ArrayList;
import java.util.Objects;

import org.apache.commons.configuration2.ex.ConfigurationRuntimeException;
import org.apache.commons.configuration2.io.ConfigurationLogger;
import org.apache.commons.jexl2.Expression;
import org.apache.commons.jexl2.JexlContext;
import org.apache.commons.jexl2.JexlEngine;
import org.apache.commons.jexl2.MapContext;
import org.apache.commons.lang3.ClassUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.text.StringSubstitutor;
import org.apache.commons.text.lookup.StringLookup;

Lookup that allows expressions to be evaluated.
    ExprLookup.Variables vars = new ExprLookup.Variables();
    vars.add(new ExprLookup.Variable("String", org.apache.commons.lang.StringUtils.class));
    vars.add(new ExprLookup.Variable("Util", new Utility("Hello")));
    vars.add(new ExprLookup.Variable("System", "Class:java.lang.System"));
    XMLConfiguration config = new XMLConfiguration(TEST_FILE);
    config.setLogger(log);
    ExprLookup lookup = new ExprLookup(vars);
    lookup.setConfiguration(config);
    String str = lookup.lookup("'$[element] ' + String.trimToEmpty('$[space.description]')");
In the example above TEST_FILE contains xml that looks like:
<configuration>
  <element>value</element>
  <space xml:space="preserve">
    <description xml:space="default">     Some text      </description>
  </space>
</configuration>
The result will be "value Some text". This lookup uses Apache Commons Jexl and requires that the dependency be added to any projects which use this.
Since:1.7
/** * Lookup that allows expressions to be evaluated. * * <pre> * ExprLookup.Variables vars = new ExprLookup.Variables(); * vars.add(new ExprLookup.Variable("String", org.apache.commons.lang.StringUtils.class)); * vars.add(new ExprLookup.Variable("Util", new Utility("Hello"))); * vars.add(new ExprLookup.Variable("System", "Class:java.lang.System")); * XMLConfiguration config = new XMLConfiguration(TEST_FILE); * config.setLogger(log); * ExprLookup lookup = new ExprLookup(vars); * lookup.setConfiguration(config); * String str = lookup.lookup("'$[element] ' + String.trimToEmpty('$[space.description]')"); * </pre> * * In the example above TEST_FILE contains xml that looks like: * <pre> * &lt;configuration&gt; * &lt;element&gt;value&lt;/element&gt; * &lt;space xml:space="preserve"&gt; * &lt;description xml:space="default"&gt; Some text &lt;/description&gt; * &lt;/space&gt; * &lt;/configuration&gt; * </pre> * * The result will be "value Some text". * * This lookup uses Apache Commons Jexl and requires that the dependency be added to any * projects which use this. * * @since 1.7 */
public class ExprLookup implements Lookup {
Prefix to identify a Java Class object
/** Prefix to identify a Java Class object */
private static final String CLASS = "Class:";
The default prefix for subordinate lookup expressions
/** The default prefix for subordinate lookup expressions */
private static final String DEFAULT_PREFIX = "$[";
The default suffix for subordinate lookup expressions
/** The default suffix for subordinate lookup expressions */
private static final String DEFAULT_SUFFIX = "]";
The ConfigurationInterpolator used by this object.
/** The ConfigurationInterpolator used by this object. */
private ConfigurationInterpolator interpolator;
The StringSubstitutor for performing replace operations.
/** The StringSubstitutor for performing replace operations. */
private StringSubstitutor substitutor;
The logger used by this instance.
/** The logger used by this instance. */
private ConfigurationLogger logger;
The engine.
/** The engine. */
private final JexlEngine engine = new JexlEngine();
The variables maintained by this object.
/** The variables maintained by this object. */
private Variables variables;
The String to use to start subordinate lookup expressions
/** The String to use to start subordinate lookup expressions */
private String prefixMatcher = DEFAULT_PREFIX;
The String to use to terminate subordinate lookup expressions
/** The String to use to terminate subordinate lookup expressions */
private String suffixMatcher = DEFAULT_SUFFIX;
The default constructor. Will get used when the Lookup is constructed via configuration.
/** * The default constructor. Will get used when the Lookup is constructed via * configuration. */
public ExprLookup() { }
Constructor for use by applications.
Params:
  • list – The list of objects to be accessible in expressions.
/** * Constructor for use by applications. * @param list The list of objects to be accessible in expressions. */
public ExprLookup(final Variables list) { setVariables(list); }
Constructor for use by applications.
Params:
  • list – The list of objects to be accessible in expressions.
  • prefix – The prefix to use for subordinate lookups.
  • suffix – The suffix to use for subordinate lookups.
/** * Constructor for use by applications. * @param list The list of objects to be accessible in expressions. * @param prefix The prefix to use for subordinate lookups. * @param suffix The suffix to use for subordinate lookups. */
public ExprLookup(final Variables list, final String prefix, final String suffix) { this(list); setVariablePrefixMatcher(prefix); setVariableSuffixMatcher(suffix); }
Set the prefix to use to identify subordinate expressions. This cannot be the same as the prefix used for the primary expression.
Params:
  • prefix – The String identifying the beginning of the expression.
/** * Set the prefix to use to identify subordinate expressions. This cannot be the * same as the prefix used for the primary expression. * @param prefix The String identifying the beginning of the expression. */
public void setVariablePrefixMatcher(final String prefix) { prefixMatcher = prefix; }
Set the suffix to use to identify subordinate expressions. This cannot be the same as the suffix used for the primary expression.
Params:
  • suffix – The String identifying the end of the expression.
/** * Set the suffix to use to identify subordinate expressions. This cannot be the * same as the suffix used for the primary expression. * @param suffix The String identifying the end of the expression. */
public void setVariableSuffixMatcher(final String suffix) { suffixMatcher = suffix; }
Add the Variables that will be accessible within expressions.
Params:
  • list – The list of Variables.
/** * Add the Variables that will be accessible within expressions. * @param list The list of Variables. */
public void setVariables(final Variables list) { variables = new Variables(list); }
Returns the list of Variables that are accessible within expressions. This method returns a copy of the variables managed by this lookup; so modifying this object has no impact on this lookup.
Returns:the List of Variables that are accessible within expressions.
/** * Returns the list of Variables that are accessible within expressions. * This method returns a copy of the variables managed by this lookup; so * modifying this object has no impact on this lookup. * * @return the List of Variables that are accessible within expressions. */
public Variables getVariables() { return new Variables(variables); }
Returns the logger used by this object.
Returns:the Log
Since:2.0
/** * Returns the logger used by this object. * * @return the {@code Log} * @since 2.0 */
public ConfigurationLogger getLogger() { return logger; }
Sets the logger to be used by this object. If no logger is passed in, no log output is generated.
Params:
  • logger – the Log
Since:2.0
/** * Sets the logger to be used by this object. If no logger is passed in, no * log output is generated. * * @param logger the {@code Log} * @since 2.0 */
public void setLogger(final ConfigurationLogger logger) { this.logger = logger; }
Returns the ConfigurationInterpolator used by this object.
Returns:the ConfigurationInterpolator
Since:2.0
/** * Returns the {@code ConfigurationInterpolator} used by this object. * * @return the {@code ConfigurationInterpolator} * @since 2.0 */
public ConfigurationInterpolator getInterpolator() { return interpolator; }
Sets the ConfigurationInterpolator to be used by this object.
Params:
  • interpolator – the ConfigurationInterpolator (may be null)
Since:2.0
/** * Sets the {@code ConfigurationInterpolator} to be used by this object. * * @param interpolator the {@code ConfigurationInterpolator} (may be * <b>null</b>) * @since 2.0 */
public void setInterpolator(final ConfigurationInterpolator interpolator) { this.interpolator = interpolator; installSubstitutor(interpolator); }
Evaluates the expression.
Params:
  • var – The expression.
Returns:The String result of the expression.
/** * Evaluates the expression. * @param var The expression. * @return The String result of the expression. */
@Override public String lookup(final String var) { if (substitutor == null) { return var; } String result = substitutor.replace(var); try { final Expression exp = engine.createExpression(result); final Object exprResult = exp.evaluate(createContext()); result = exprResult != null ? String.valueOf(exprResult) : null; } catch (final Exception e) { final ConfigurationLogger l = getLogger(); if (l != null) { l.debug("Error encountered evaluating " + result + ": " + e); } } return result; }
Creates a StringSubstitutor object which uses the passed in ConfigurationInterpolator as lookup object.
Params:
  • ip – the ConfigurationInterpolator to be used
/** * Creates a {@code StringSubstitutor} object which uses the passed in * {@code ConfigurationInterpolator} as lookup object. * * @param ip the {@code ConfigurationInterpolator} to be used */
private void installSubstitutor(final ConfigurationInterpolator ip) { if (ip == null) { substitutor = null; } else { final StringLookup variableResolver = key -> Objects.toString(ip.resolve(key), null); substitutor = new StringSubstitutor(variableResolver, prefixMatcher, suffixMatcher, StringSubstitutor.DEFAULT_ESCAPE); } }
Creates a new JexlContext and initializes it with the variables managed by this Lookup object.
Returns:the newly created context
/** * Creates a new {@code JexlContext} and initializes it with the variables * managed by this Lookup object. * * @return the newly created context */
private JexlContext createContext() { final JexlContext ctx = new MapContext(); initializeContext(ctx); return ctx; }
Initializes the specified context with the variables managed by this Lookup object.
Params:
  • ctx – the context to be initialized
/** * Initializes the specified context with the variables managed by this * Lookup object. * * @param ctx the context to be initialized */
private void initializeContext(final JexlContext ctx) { for (final Variable var : variables) { ctx.set(var.getName(), var.getValue()); } }
List wrapper used to allow the Variables list to be created as beans in DefaultConfigurationBuilder.
/** * List wrapper used to allow the Variables list to be created as beans in * DefaultConfigurationBuilder. */
public static class Variables extends ArrayList<Variable> {
The serial version UID.
/** * The serial version UID. */
private static final long serialVersionUID = 20111205L;
Creates a new empty instance of Variables.
/** * Creates a new empty instance of {@code Variables}. */
public Variables() { super(); }
Creates a new instance of Variables and copies the content of the given object.
Params:
  • vars – the Variables object to be copied
/** * Creates a new instance of {@code Variables} and copies the content of * the given object. * * @param vars the {@code Variables} object to be copied */
public Variables(final Variables vars) { super(vars); } public Variable getVariable() { return size() > 0 ? get(size() - 1) : null; } }
The key and corresponding object that will be made available to the JexlContext for use in expressions.
/** * The key and corresponding object that will be made available to the * JexlContext for use in expressions. */
public static class Variable {
The name to be used in expressions
/** The name to be used in expressions */
private String key;
The object to be accessed in expressions
/** The object to be accessed in expressions */
private Object value; public Variable() { } public Variable(final String name, final Object value) { setName(name); setValue(value); } public String getName() { return key; } public void setName(final String name) { this.key = name; } public Object getValue() { return value; } public void setValue(final Object value) throws ConfigurationRuntimeException { try { if (!(value instanceof String)) { this.value = value; return; } final String val = (String) value; final String name = StringUtils.removeStartIgnoreCase(val, CLASS); final Class<?> clazz = ClassUtils.getClass(name); if (name.length() == val.length()) { this.value = clazz.newInstance(); } else { this.value = clazz; } } catch (final Exception e) { throw new ConfigurationRuntimeException("Unable to create " + value, e); } } } }