Coverage Report - org.apache.commons.configuration.interpol.ExprLookup
 
Classes in this File Line Coverage Branch Coverage Complexity
ExprLookup
70%
26/37
100%
2/2
1,45
ExprLookup$Variable
86%
20/23
75%
3/4
1,45
ExprLookup$Variables
57%
4/7
0%
0/2
1,45
 
 1  
 /*
 2  
  * Licensed to the Apache Software Foundation (ASF) under one or more
 3  
  * contributor license agreements.  See the NOTICE file distributed with
 4  
  * this work for additional information regarding copyright ownership.
 5  
  * The ASF licenses this file to You under the Apache License, Version 2.0
 6  
  * (the "License"); you may not use this file except in compliance with
 7  
  * the License.  You may obtain a copy of the License at
 8  
  *
 9  
  *     http://www.apache.org/licenses/LICENSE-2.0
 10  
  *
 11  
  * Unless required by applicable law or agreed to in writing, software
 12  
  * distributed under the License is distributed on an "AS IS" BASIS,
 13  
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 14  
  * See the License for the specific language governing permissions and
 15  
  * limitations under the License.
 16  
  */
 17  
 package org.apache.commons.configuration.interpol;
 18  
 
 19  
 import java.util.ArrayList;
 20  
 
 21  
 import org.apache.commons.configuration.AbstractConfiguration;
 22  
 import org.apache.commons.configuration.ConfigurationRuntimeException;
 23  
 import org.apache.commons.jexl2.Expression;
 24  
 import org.apache.commons.jexl2.JexlContext;
 25  
 import org.apache.commons.jexl2.JexlEngine;
 26  
 import org.apache.commons.jexl2.MapContext;
 27  
 import org.apache.commons.lang.ClassUtils;
 28  
 import org.apache.commons.lang.StringUtils;
 29  
 import org.apache.commons.lang.text.StrLookup;
 30  
 import org.apache.commons.lang.text.StrSubstitutor;
 31  
 
 32  
 /**
 33  
  * Lookup that allows expressions to be evaluated.
 34  
  *
 35  
  * <pre>
 36  
  *     ExprLookup.Variables vars = new ExprLookup.Variables();
 37  
  *     vars.add(new ExprLookup.Variable("String", org.apache.commons.lang.StringUtils.class));
 38  
  *     vars.add(new ExprLookup.Variable("Util", new Utility("Hello")));
 39  
  *     vars.add(new ExprLookup.Variable("System", "Class:java.lang.System"));
 40  
  *     XMLConfiguration config = new XMLConfiguration(TEST_FILE);
 41  
  *     config.setLogger(log);
 42  
  *     ExprLookup lookup = new ExprLookup(vars);
 43  
  *     lookup.setConfiguration(config);
 44  
  *     String str = lookup.lookup("'$[element] ' + String.trimToEmpty('$[space.description]')");
 45  
  * </pre>
 46  
  *
 47  
  * In the example above TEST_FILE contains xml that looks like:
 48  
  * <pre>
 49  
  * &lt;configuration&gt;
 50  
  *   &lt;element&gt;value&lt;/element&gt;
 51  
  *   &lt;space xml:space="preserve"&gt;
 52  
  *     &lt;description xml:space="default"&gt;     Some text      &lt;/description&gt;
 53  
  *   &lt;/space&gt;
 54  
  * &lt;/configuration&gt;
 55  
  * </pre>
 56  
  *
 57  
  * The result will be "value Some text".
 58  
  *
 59  
  * This lookup uses Apache Commons Jexl and requires that the dependency be added to any
 60  
  * projects which use this.
 61  
  *
 62  
  * @since 1.7
 63  
  * @author <a
 64  
  * href="http://commons.apache.org/configuration/team-list.html">Commons Configuration team</a>
 65  
  * @version $Id: ExprLookup.java 1234539 2012-01-22 16:19:15Z oheger $
 66  
  */
 67  
 public class ExprLookup extends StrLookup
 68  
 {
 69  
     /** Prefix to identify a Java Class object */
 70  
     private static final String CLASS = "Class:";
 71  
 
 72  
     /** The default prefix for subordinate lookup expressions */
 73  
     private static final String DEFAULT_PREFIX = "$[";
 74  
 
 75  
     /** The default suffix for subordinate lookup expressions */
 76  
     private static final String DEFAULT_SUFFIX = "]";
 77  
 
 78  
     /** Configuration being operated on */
 79  
     private AbstractConfiguration configuration;
 80  
 
 81  
     /** The engine. */
 82  2
     private final JexlEngine engine = new JexlEngine();
 83  
 
 84  
     /** The variables maintained by this object. */
 85  
     private Variables variables;
 86  
 
 87  
     /** The String to use to start subordinate lookup expressions */
 88  2
     private String prefixMatcher = DEFAULT_PREFIX;
 89  
 
 90  
     /** The String to use to terminate subordinate lookup expressions */
 91  2
     private String suffixMatcher = DEFAULT_SUFFIX;
 92  
 
 93  
     /**
 94  
      * The default constructor. Will get used when the Lookup is constructed via
 95  
      * configuration.
 96  
      */
 97  
     public ExprLookup()
 98  1
     {
 99  1
     }
 100  
 
 101  
     /**
 102  
      * Constructor for use by applications.
 103  
      * @param list The list of objects to be accessible in expressions.
 104  
      */
 105  
     public ExprLookup(Variables list)
 106  1
     {
 107  1
         setVariables(list);
 108  1
     }
 109  
 
 110  
     /**
 111  
      * Constructor for use by applications.
 112  
      * @param list The list of objects to be accessible in expressions.
 113  
      * @param prefix The prefix to use for subordinate lookups.
 114  
      * @param suffix The suffix to use for subordinate lookups.
 115  
      */
 116  
     public ExprLookup(Variables list, String prefix, String suffix)
 117  
     {
 118  0
         this(list);
 119  0
         setVariablePrefixMatcher(prefix);
 120  0
         setVariableSuffixMatcher(suffix);
 121  0
     }
 122  
 
 123  
     /**
 124  
      * Set the prefix to use to identify subordinate expressions. This cannot be the
 125  
      * same as the prefix used for the primary expression.
 126  
      * @param prefix The String identifying the beginning of the expression.
 127  
      */
 128  
     public void setVariablePrefixMatcher(String prefix)
 129  
     {
 130  0
         prefixMatcher = prefix;
 131  0
     }
 132  
 
 133  
 
 134  
     /**
 135  
      * Set the suffix to use to identify subordinate expressions. This cannot be the
 136  
      * same as the suffix used for the primary expression.
 137  
      * @param suffix The String identifying the end of the expression.
 138  
      */
 139  
     public void setVariableSuffixMatcher(String suffix)
 140  
     {
 141  0
         suffixMatcher = suffix;
 142  0
     }
 143  
 
 144  
     /**
 145  
      * Add the Variables that will be accessible within expressions.
 146  
      * @param list The list of Variables.
 147  
      */
 148  
     public void setVariables(Variables list)
 149  
     {
 150  2
         variables = new Variables(list);
 151  2
     }
 152  
 
 153  
     /**
 154  
      * Returns the list of Variables that are accessible within expressions.
 155  
      * @return the List of Variables that are accessible within expressions.
 156  
      */
 157  
     public Variables getVariables()
 158  
     {
 159  0
         return null;
 160  
     }
 161  
 
 162  
     /**
 163  
      * Set the configuration to be used to interpolate subordinate expressions.
 164  
      * @param config The Configuration.
 165  
      */
 166  
     public void setConfiguration(AbstractConfiguration config)
 167  
     {
 168  2
         this.configuration = config;
 169  2
     }
 170  
 
 171  
     /**
 172  
      * Evaluates the expression.
 173  
      * @param var The expression.
 174  
      * @return The String result of the expression.
 175  
      */
 176  
     @Override
 177  
     public String lookup(String var)
 178  
     {
 179  6
         ConfigurationInterpolator interp = configuration.getInterpolator();
 180  6
         StrSubstitutor subst = new StrSubstitutor(interp, prefixMatcher, suffixMatcher,
 181  
                 StrSubstitutor.DEFAULT_ESCAPE);
 182  
 
 183  6
         String result = subst.replace(var);
 184  
 
 185  
         try
 186  
         {
 187  6
             Expression exp = engine.createExpression(result);
 188  6
             result = (String) exp.evaluate(createContext());
 189  
         }
 190  0
         catch (Exception e)
 191  
         {
 192  0
             configuration.getLogger().debug("Error encountered evaluating " + result, e);
 193  6
         }
 194  
 
 195  6
         return result;
 196  
     }
 197  
 
 198  
     /**
 199  
      * Creates a new {@code JexlContext} and initializes it with the variables
 200  
      * managed by this Lookup object.
 201  
      *
 202  
      * @return the newly created context
 203  
      */
 204  
     private JexlContext createContext()
 205  
     {
 206  6
         JexlContext ctx = new MapContext();
 207  6
         initializeContext(ctx);
 208  6
         return ctx;
 209  
     }
 210  
 
 211  
     /**
 212  
      * Initializes the specified context with the variables managed by this
 213  
      * Lookup object.
 214  
      *
 215  
      * @param ctx the context to be initialized
 216  
      */
 217  
     private void initializeContext(JexlContext ctx)
 218  
     {
 219  6
         for (Variable var : variables)
 220  
         {
 221  14
             ctx.set(var.getName(), var.getValue());
 222  14
         }
 223  6
     }
 224  
 
 225  
     /**
 226  
      * List wrapper used to allow the Variables list to be created as beans in
 227  
      * DefaultConfigurationBuilder.
 228  
      */
 229  
     public static class Variables extends ArrayList<Variable>
 230  
     {
 231  
         /**
 232  
          * The serial version UID.
 233  
          */
 234  
         private static final long serialVersionUID = 20111205L;
 235  
 
 236  
         /**
 237  
          * Creates a new empty instance of {@code Variables}.
 238  
          */
 239  
         public Variables()
 240  
         {
 241  2
             super();
 242  2
         }
 243  
 
 244  
         /**
 245  
          * Creates a new instance of {@code Variables} and copies the content of
 246  
          * the given object.
 247  
          *
 248  
          * @param vars the {@code Variables} object to be copied
 249  
          */
 250  
         public Variables(Variables vars)
 251  
         {
 252  2
             super(vars);
 253  2
         }
 254  
 
 255  
         public Variable getVariable()
 256  
         {
 257  0
             if (size() > 0)
 258  
             {
 259  0
                 return get(size() - 1);
 260  
             }
 261  
             else
 262  
             {
 263  0
                 return null;
 264  
             }
 265  
         }
 266  
 
 267  
     }
 268  
 
 269  
     /**
 270  
      * The key and corresponding object that will be made available to the
 271  
      * JexlContext for use in expressions.
 272  
      */
 273  
     public static class Variable
 274  
     {
 275  
         /** The name to be used in expressions */
 276  
         private String key;
 277  
 
 278  
         /** The object to be accessed in expressions */
 279  
         private Object value;
 280  
 
 281  
         public Variable()
 282  2
         {
 283  2
         }
 284  
 
 285  
         public Variable(String name, Object value)
 286  3
         {
 287  3
             setName(name);
 288  3
             setValue(value);
 289  3
         }
 290  
 
 291  
         public String getName()
 292  
         {
 293  14
             return key;
 294  
         }
 295  
 
 296  
         public void setName(String name)
 297  
         {
 298  5
             this.key = name;
 299  5
         }
 300  
 
 301  
         public Object getValue()
 302  
         {
 303  14
             return value;
 304  
         }
 305  
 
 306  
         public void setValue(Object value) throws ConfigurationRuntimeException
 307  
         {
 308  
             try
 309  
             {
 310  5
                 if (!(value instanceof String))
 311  
                 {
 312  2
                     this.value = value;
 313  2
                     return;
 314  
                 }
 315  3
                 String val = (String) value;
 316  3
                 String name = StringUtils.removeStartIgnoreCase(val, CLASS);
 317  3
                 Class<?> clazz = ClassUtils.getClass(name);
 318  3
                 if (name.length() == val.length())
 319  
                 {
 320  0
                     this.value = clazz.newInstance();
 321  
                 }
 322  
                 else
 323  
                 {
 324  3
                     this.value = clazz;
 325  
                 }
 326  
             }
 327  0
             catch (Exception e)
 328  
             {
 329  0
                 throw new ConfigurationRuntimeException("Unable to create " + value, e);
 330  3
             }
 331  
 
 332  3
         }
 333  
     }
 334  
 }