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 * <configuration> 051 * <element>value</element> 052 * <space xml:space="preserve"> 053 * <description xml:space="default"> Some text </description> 054 * </space> 055 * </configuration> 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 }