| Classes in this File | Line Coverage | Branch Coverage | Complexity | ||||
| ConstantLookup |
|
| 3.0;3 |
| 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.lang.reflect.Field; | |
| 20 | import java.util.HashMap; | |
| 21 | import java.util.Map; | |
| 22 | ||
| 23 | import org.apache.commons.lang.ClassUtils; | |
| 24 | import org.apache.commons.lang.text.StrLookup; | |
| 25 | import org.apache.commons.logging.Log; | |
| 26 | import org.apache.commons.logging.LogFactory; | |
| 27 | ||
| 28 | /** | |
| 29 | * <p> | |
| 30 | * A specialized lookup implementation that allows access to constant fields of | |
| 31 | * classes. | |
| 32 | * </p> | |
| 33 | * <p> | |
| 34 | * Sometimes it is necessary in a configuration file to refer to a constant | |
| 35 | * defined in a class. This can be done with this lookup implementation. | |
| 36 | * Variable names passed in must be of the form | |
| 37 | * <code>mypackage.MyClass.FIELD</code>. The <code>lookup()</code> method | |
| 38 | * will split the passed in string at the last dot, separating the fully | |
| 39 | * qualified class name and the name of the constant (i.e. <strong>static final</strong>) | |
| 40 | * member field. Then the class is loaded and the field's value is obtained | |
| 41 | * using reflection. | |
| 42 | * </p> | |
| 43 | * <p> | |
| 44 | * Once retrieved values are cached for fast access. This class is thread-safe. | |
| 45 | * It can be used as a standard (i.e. global) lookup object and serve multiple | |
| 46 | * clients concurrently. | |
| 47 | * </p> | |
| 48 | * | |
| 49 | * @version $Id: ConstantLookup.java 588329 2007-10-25 20:01:31Z oheger $ | |
| 50 | * @since 1.4 | |
| 51 | * @author <a | |
| 52 | * href="http://commons.apache.org/configuration/team-list.html">Commons | |
| 53 | * Configuration team</a> | |
| 54 | */ | |
| 55 | 63 | public class ConstantLookup extends StrLookup |
| 56 | { | |
| 57 | /** Constant for the field separator. */ | |
| 58 | private static final char FIELD_SEPRATOR = '.'; | |
| 59 | ||
| 60 | /** An internally used cache for already retrieved values. */ | |
| 61 | 55 | private static Map constantCache = new HashMap(); |
| 62 | ||
| 63 | /** The logger. */ | |
| 64 | 63 | private Log log = LogFactory.getLog(getClass()); |
| 65 | ||
| 66 | /** | |
| 67 | * Tries to resolve the specified variable. The passed in variable name is | |
| 68 | * interpreted as the name of a <b>static final</b> member field of a | |
| 69 | * class. If the value has already been obtained, it can be retrieved from | |
| 70 | * an internal cache. Otherwise this method will invoke the | |
| 71 | * <code>resolveField()</code> method and pass in the name of the class | |
| 72 | * and the field. | |
| 73 | * | |
| 74 | * @param var the name of the variable to be resolved | |
| 75 | * @return the value of this variable or <b>null</b> if it cannot be | |
| 76 | * resolved | |
| 77 | */ | |
| 78 | public String lookup(String var) | |
| 79 | { | |
| 80 | 17 | if (var == null) |
| 81 | { | |
| 82 | 1 | return null; |
| 83 | } | |
| 84 | ||
| 85 | String result; | |
| 86 | 16 | synchronized (constantCache) |
| 87 | { | |
| 88 | 16 | result = (String) constantCache.get(var); |
| 89 | 16 | } |
| 90 | 16 | if (result != null) |
| 91 | { | |
| 92 | 6 | return result; |
| 93 | } | |
| 94 | ||
| 95 | 10 | int fieldPos = var.lastIndexOf(FIELD_SEPRATOR); |
| 96 | 10 | if (fieldPos < 0) |
| 97 | { | |
| 98 | 1 | return null; |
| 99 | } | |
| 100 | try | |
| 101 | { | |
| 102 | 9 | Object value = resolveField(var.substring(0, fieldPos), var |
| 103 | .substring(fieldPos + 1)); | |
| 104 | 6 | if (value != null) |
| 105 | { | |
| 106 | 6 | synchronized (constantCache) |
| 107 | { | |
| 108 | // In worst case, the value will be fetched multiple times | |
| 109 | // because of this lax synchronisation, but for constant | |
| 110 | // values this shouldn't be a problem. | |
| 111 | 6 | constantCache.put(var, String.valueOf(value)); |
| 112 | 6 | } |
| 113 | 6 | result = value.toString(); |
| 114 | } | |
| 115 | } | |
| 116 | 3 | catch (Exception ex) |
| 117 | { | |
| 118 | 3 | log.warn("Could not obtain value for variable " + var, ex); |
| 119 | 6 | } |
| 120 | ||
| 121 | 9 | return result; |
| 122 | } | |
| 123 | ||
| 124 | /** | |
| 125 | * Clears the shared cache with the so far resolved constants. | |
| 126 | */ | |
| 127 | public static void clear() | |
| 128 | { | |
| 129 | 8 | synchronized (constantCache) |
| 130 | { | |
| 131 | 8 | constantCache.clear(); |
| 132 | 8 | } |
| 133 | 8 | } |
| 134 | ||
| 135 | /** | |
| 136 | * Determines the value of the specified constant member field of a class. | |
| 137 | * This implementation will call <code>fetchClass()</code> to obtain the | |
| 138 | * <code>java.lang.Class</code> object for the target class. Then it will | |
| 139 | * use reflection to obtain the field's value. For this to work the field | |
| 140 | * must be accessable. | |
| 141 | * | |
| 142 | * @param className the name of the class | |
| 143 | * @param fieldName the name of the member field of that class to read | |
| 144 | * @return the field's value | |
| 145 | * @throws Exception if an error occurs | |
| 146 | */ | |
| 147 | protected Object resolveField(String className, String fieldName) | |
| 148 | throws Exception | |
| 149 | { | |
| 150 | 9 | Class clazz = fetchClass(className); |
| 151 | 8 | Field field = clazz.getField(fieldName); |
| 152 | 6 | return field.get(null); |
| 153 | } | |
| 154 | ||
| 155 | /** | |
| 156 | * Loads the class with the specified name. If an application has special | |
| 157 | * needs regarding the class loaders to be used, it can hook in here. This | |
| 158 | * implementation delegates to the <code>getClass()</code> method of | |
| 159 | * Commons Lang's | |
| 160 | * <code><a href="http://commons.apache.org/lang/api-release/org/apache/commons/lang/ClassUtils.html"> | |
| 161 | * ClassUtils</a></code>. | |
| 162 | * | |
| 163 | * @param className the name of the class to be loaded | |
| 164 | * @return the corresponding class object | |
| 165 | * @throws ClassNotFoundException if the class cannot be loaded | |
| 166 | */ | |
| 167 | protected Class fetchClass(String className) throws ClassNotFoundException | |
| 168 | { | |
| 169 | 9 | return ClassUtils.getClass(className); |
| 170 | } | |
| 171 | } |