Coverage Report - org.apache.commons.configuration.interpol.ConstantLookup
 
Classes in this File Line Coverage Branch Coverage Complexity
ConstantLookup
100%
31/31
87%
7/8
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}. The {@code lookup()} 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 1210218 2011-12-04 20:57:48Z 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  9
 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  1
     private static Map<String, String> constantCache = new HashMap<String, String>();
 62  
 
 63  
     /** The logger. */
 64  9
     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()} 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  
     @Override
 79  
     public String lookup(String var)
 80  
     {
 81  17
         if (var == null)
 82  
         {
 83  1
             return null;
 84  
         }
 85  
 
 86  
         String result;
 87  16
         synchronized (constantCache)
 88  
         {
 89  16
             result = constantCache.get(var);
 90  16
         }
 91  16
         if (result != null)
 92  
         {
 93  7
             return result;
 94  
         }
 95  
 
 96  9
         int fieldPos = var.lastIndexOf(FIELD_SEPRATOR);
 97  9
         if (fieldPos < 0)
 98  
         {
 99  1
             return null;
 100  
         }
 101  
         try
 102  
         {
 103  8
             Object value = resolveField(var.substring(0, fieldPos), var
 104  
                     .substring(fieldPos + 1));
 105  5
             if (value != null)
 106  
             {
 107  5
                 synchronized (constantCache)
 108  
                 {
 109  
                     // In worst case, the value will be fetched multiple times
 110  
                     // because of this lax synchronisation, but for constant
 111  
                     // values this shouldn't be a problem.
 112  5
                     constantCache.put(var, String.valueOf(value));
 113  5
                 }
 114  5
                 result = value.toString();
 115  
             }
 116  
         }
 117  3
         catch (Exception ex)
 118  
         {
 119  3
             log.warn("Could not obtain value for variable " + var, ex);
 120  5
         }
 121  
 
 122  8
         return result;
 123  
     }
 124  
 
 125  
     /**
 126  
      * Clears the shared cache with the so far resolved constants.
 127  
      */
 128  
     public static void clear()
 129  
     {
 130  8
         synchronized (constantCache)
 131  
         {
 132  8
             constantCache.clear();
 133  8
         }
 134  8
     }
 135  
 
 136  
     /**
 137  
      * Determines the value of the specified constant member field of a class.
 138  
      * This implementation will call {@code fetchClass()} to obtain the
 139  
      * {@code java.lang.Class} object for the target class. Then it will
 140  
      * use reflection to obtain the field's value. For this to work the field
 141  
      * must be accessable.
 142  
      *
 143  
      * @param className the name of the class
 144  
      * @param fieldName the name of the member field of that class to read
 145  
      * @return the field's value
 146  
      * @throws Exception if an error occurs
 147  
      */
 148  
     protected Object resolveField(String className, String fieldName)
 149  
             throws Exception
 150  
     {
 151  8
         Class<?> clazz = fetchClass(className);
 152  7
         Field field = clazz.getField(fieldName);
 153  5
         return field.get(null);
 154  
     }
 155  
 
 156  
     /**
 157  
      * Loads the class with the specified name. If an application has special
 158  
      * needs regarding the class loaders to be used, it can hook in here. This
 159  
      * implementation delegates to the {@code getClass()} method of
 160  
      * Commons Lang's
 161  
      * <code><a href="http://commons.apache.org/lang/api-release/org/apache/commons/lang/ClassUtils.html">
 162  
      * ClassUtils</a></code>.
 163  
      *
 164  
      * @param className the name of the class to be loaded
 165  
      * @return the corresponding class object
 166  
      * @throws ClassNotFoundException if the class cannot be loaded
 167  
      */
 168  
     protected Class<?> fetchClass(String className) throws ClassNotFoundException
 169  
     {
 170  8
         return ClassUtils.getClass(className);
 171  
     }
 172  
 }