    001    /*
    002     * Licensed to the Apache Software Foundation (ASF) under one or more
    003     * contributor license agreements.  See the NOTICE file distributed with
    004     * this work for additional information regarding copyright ownership.
    005     * The ASF licenses this file to You under the Apache License, Version 2.0
    006     * (the "License"); you may not use this file except in compliance with
    007     * the License.  You may obtain a copy of the License at
    008     *
    009     *     http://www.apache.org/licenses/LICENSE-2.0
    010     *
    011     * Unless required by applicable law or agreed to in writing, software
    012     * distributed under the License is distributed on an "AS IS" BASIS,
    013     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    014     * See the License for the specific language governing permissions and
    015     * limitations under the License.
    016     */
    017    package org.apache.commons.configuration.interpol;
    018
    019    import org.apache.commons.lang.text.StrLookup;
    020    import org.apache.commons.lang.text.StrSubstitutor;
    021    import org.apache.commons.lang.StringUtils;
    022    import org.apache.commons.lang.ClassUtils;
    023    import org.apache.commons.configuration.AbstractConfiguration;
    024    import org.apache.commons.configuration.ConfigurationRuntimeException;
    025    import org.apache.commons.jexl.JexlHelper;
    026    import org.apache.commons.jexl.JexlContext;
    027    import org.apache.commons.jexl.Expression;
    028    import org.apache.commons.jexl.ExpressionFactory;
    029
    030    import java.util.Iterator;
    031    import java.util.ArrayList;
    032
    033    /**
    034     * Lookup that allows expressions to be evaluated.
    035     *
    036     * <pre>
    037     *     ExprLookup.Variables vars = new ExprLookup.Variables();
    038     *     vars.add(new ExprLookup.Variable("String", org.apache.commons.lang.StringUtils.class));
    039     *     vars.add(new ExprLookup.Variable("Util", new Utility("Hello")));
    040     *     vars.add(new ExprLookup.Variable("System", "Class:java.lang.System"));
    041     *     XMLConfiguration config = new XMLConfiguration(TEST_FILE);
    042     *     config.setLogger(log);
    043     *     ExprLookup lookup = new ExprLookup(vars);
    044     *     lookup.setConfiguration(config);
    045     *     String str = lookup.lookup("'$[element] ' + String.trimToEmpty('$[space.description]')");
    046     * </pre>
    047     *
    048     * In the example above TEST_FILE contains xml that looks like:
    049     * <pre>
    050     * &lt;configuration&gt;
    051     *   &lt;element&gt;value&lt;/element&gt;
    052     *   &lt;space xml:space="preserve"&gt;
    053     *     &lt;description xml:space="default"&gt;     Some text      &lt;/description&gt;
    054     *   &lt;/space&gt;
    055     * &lt;/configuration&gt;
    056     * </pre>
    057     *
    058     * The result will be "value Some text".
    059     *
    060     * This lookup uses Apache Commons Jexl and requires that the dependency be added to any
    061     * projects which use this.
    062     *
    063     * @since 1.7
    064     * @author <a
    065     * href="http://commons.apache.org/configuration/team-list.html">Commons Configuration team</a>
    066     * @version $Id: ExprLookup.java 766914 2009-04-20 23:38:49Z rgoers $
    067     */
    068    public class ExprLookup extends StrLookup
    069    {
    070        /** Prefix to identify a Java Class object */
    071        private static final String CLASS = "Class:";
    072
    073        /** The default prefix for subordinate lookup expressions */
    074        private static final String DEFAULT_PREFIX = "$[";
    075
    076        /** The default suffix for subordinate lookup expressions */
    077        private static final String DEFAULT_SUFFIX = "]";
    078
    079        /** Configuration being operated on */
    080        private AbstractConfiguration configuration;
    081
    082        /** The JexlContext */
    083        private JexlContext context = JexlHelper.createContext();
    084
    085        /** The String to use to start subordinate lookup expressions */
    086        private String prefixMatcher = DEFAULT_PREFIX;
    087
    088        /** The String to use to terminate subordinate lookup expressions */
    089        private String suffixMatcher = DEFAULT_SUFFIX;
    090
    091        /**
    092         * The default constructor. Will get used when the Lookup is constructed via
    093         * configuration.
    094         */
    095        public ExprLookup()
    096        {
    097        }
    098
    099        /**
    100         * Constructor for use by applications.
    101         * @param list The list of objects to be accessible in expressions.
    102         */
    103        public ExprLookup(Variables list)
    104        {
    105            setVariables(list);
    106        }
    107
    108        /**
    109         * Constructor for use by applications.
    110         * @param list The list of objects to be accessible in expressions.
    111         * @param prefix The prefix to use for subordinate lookups.
    112         * @param suffix The suffix to use for subordinate lookups.
    113         */
    114        public ExprLookup(Variables list, String prefix, String suffix)
    115        {
    116            this(list);
    117            setVariablePrefixMatcher(prefix);
    118            setVariableSuffixMatcher(suffix);
    119        }
    120
    121        /**
    122         * Set the prefix to use to identify subordinate expressions. This cannot be the
    123         * same as the prefix used for the primary expression.
    124         * @param prefix The String identifying the beginning of the expression.
    125         */
    126        public void setVariablePrefixMatcher(String prefix)
    127        {
    128            prefixMatcher = prefix;
    129        }
    130
    131
    132        /**
    133         * Set the suffix to use to identify subordinate expressions. This cannot be the
    134         * same as the suffix used for the primary expression.
    135         * @param suffix The String identifying the end of the expression.
    136         */
    137        public void setVariableSuffixMatcher(String suffix)
    138        {
    139            suffixMatcher = suffix;
    140        }
    141
    142        /**
    143         * Add the Variables that will be accessible within expressions.
    144         * @param list The list of Variables.
    145         */
    146        public void setVariables(Variables list)
    147        {
    148            Iterator iter = list.iterator();
    149            while (iter.hasNext())
    150            {
    151                Variable var = (Variable) iter.next();
    152                context.getVars().put(var.getName(), var.getValue());
    153            }
    154        }
    155
    156        /**
    157         * Returns the list of Variables that are accessible within expressions.
    158         * @return the List of Variables that are accessible within expressions.
    159         */
    160        public Variables getVariables()
    161        {
    162            return null;
    163        }
    164
    165        /**
    166         * Set the configuration to be used to interpolate subordinate expressiosn.
    167         * @param config The Configuration.
    168         */
    169        public void setConfiguration(AbstractConfiguration config)
    170        {
    171            this.configuration = config;
    172        }
    173
    174        /**
    175         * Evaluates the expression.
    176         * @param var The expression.
    177         * @return The String result of the expression.
    178         */
    179        public String lookup(String var)
    180        {
    181            ConfigurationInterpolator interp = configuration.getInterpolator();
    182            StrSubstitutor subst = new StrSubstitutor(interp, prefixMatcher, suffixMatcher,
    183                    StrSubstitutor.DEFAULT_ESCAPE);
    184
    185            String result = subst.replace(var);
    186
    187            try
    188            {
    189                Expression exp = ExpressionFactory.createExpression(result);
    190                result = (String) exp.evaluate(context);
    191            }
    192            catch (Exception e)
    193            {
    194                configuration.getLogger().debug("Error encountered evaluating " + result, e);
    195            }
    196
    197            return result;
    198        }
    199
    200        /**
    201         * List wrapper used to allow the Variables list to be created as beans in
    202         * DefaultConfigurationBuilder.
    203         */
    204        public static class Variables extends ArrayList
    205        {
    206            /*
    207            public void setVariable(Variable var)
    208            {
    209                add(var);
    210            } */
    211
    212            public Variable getVariable()
    213            {
    214                if (size() > 0)
    215                {
    216                    return (Variable) get(size() - 1);
    217                }
    218                else
    219                {
    220                    return null;
    221                }
    222            }
    223
    224        }
    225
    226        /**
    227         * The key and corresponding object that will be made available to the
    228         * JexlContext for use in expressions.
    229         */
    230        public static class Variable
    231        {
    232            /** The name to be used in expressions */
    233            private String key;
    234
    235            /** The object to be accessed in expressions */
    236            private Object value;
    237
    238            public Variable()
    239            {
    240            }
    241
    242            public Variable(String name, Object value)
    243            {
    244                setName(name);
    245                setValue(value);
    246            }
    247
    248            public String getName()
    249            {
    250                return key;
    251            }
    252
    253            public void setName(String name)
    254            {
    255                this.key = name;
    256            }
    257
    258            public Object getValue()
    259            {
    260                return value;
    261            }
    262
    263            public void setValue(Object value) throws ConfigurationRuntimeException
    264            {
    265                try
    266                {
    267                    if (!(value instanceof String))
    268                    {
    269                        this.value = value;
    270                        return;
    271                    }
    272                    String val = (String) value;
    273                    String name = StringUtils.removeStartIgnoreCase(val, CLASS);
    274                    Class clazz = ClassUtils.getClass(name);
    275                    if (name.length() == val.length())
    276                    {
    277                        this.value = clazz.newInstance();
    278                    }
    279                    else
    280                    {
    281                        this.value = clazz;
    282                    }
    283                }
    284                catch (Exception e)
    285                {
    286                    throw new ConfigurationRuntimeException("Unable to create " + value, e);
    287                }
    288
    289            }
    290        }
    291    }