Coverage Report - org.kuali.spring.util.PropertiesResolver
 
Classes in this File Line Coverage Branch Coverage Complexity
PropertiesResolver
72%
31/43
50%
7/14
1.647
PropertiesResolver$ResolvePropertyContext
52%
12/23
N/A
1.647
 
 1  
 package org.kuali.spring.util;
 2  
 
 3  
 import java.util.ArrayList;
 4  
 import java.util.Collections;
 5  
 import java.util.List;
 6  
 import java.util.Properties;
 7  
 
 8  
 import org.slf4j.Logger;
 9  
 import org.slf4j.LoggerFactory;
 10  
 import org.springframework.util.ObjectUtils;
 11  
 
 12  8
 public class PropertiesResolver extends PlaceholderStringResolver {
 13  1
     private static final Logger LOGGER = LoggerFactory.getLogger(PropertiesResolver.class);
 14  
     public static final boolean DEFAULT_IS_SORT = true;
 15  8
     boolean sort = DEFAULT_IS_SORT;
 16  
 
 17  8
     protected class ResolvePropertyContext {
 18  
         public ResolvePropertyContext(List<String> keys, String key, Properties properties,
 19  441
                 Properties resolvedProperties, ValueRetriever retriever) {
 20  441
             super();
 21  441
             this.keys = keys;
 22  441
             this.key = key;
 23  441
             this.properties = properties;
 24  441
             this.resolvedProperties = resolvedProperties;
 25  441
             this.retriever = retriever;
 26  441
         }
 27  
 
 28  
         List<String> keys;
 29  
         String key;
 30  
         Properties properties;
 31  
         Properties resolvedProperties;
 32  
         ValueRetriever retriever;
 33  
 
 34  
         public List<String> getKeys() {
 35  0
             return keys;
 36  
         }
 37  
 
 38  
         public void setKeys(List<String> keys) {
 39  0
             this.keys = keys;
 40  0
         }
 41  
 
 42  
         public String getKey() {
 43  882
             return key;
 44  
         }
 45  
 
 46  
         public void setKey(String key) {
 47  0
             this.key = key;
 48  0
         }
 49  
 
 50  
         public Properties getProperties() {
 51  882
             return properties;
 52  
         }
 53  
 
 54  
         public void setProperties(Properties properties) {
 55  0
             this.properties = properties;
 56  0
         }
 57  
 
 58  
         public Properties getResolvedProperties() {
 59  441
             return resolvedProperties;
 60  
         }
 61  
 
 62  
         public void setResolvedProperties(Properties resolvedProperties) {
 63  0
             this.resolvedProperties = resolvedProperties;
 64  0
         }
 65  
 
 66  
         public ValueRetriever getRetriever() {
 67  882
             return retriever;
 68  
         }
 69  
 
 70  
         public void setRetriever(ValueRetriever retriever) {
 71  0
             this.retriever = retriever;
 72  0
         }
 73  
     }
 74  
 
 75  
     /**
 76  
      * Resolve any placeholders in the Properties object passed in
 77  
      */
 78  
     public Properties resolve(Properties properties) {
 79  4
         return resolve(properties, new PropertiesRetriever(properties));
 80  
     }
 81  
 
 82  
     /**
 83  
      * Resolve any placeholders in the Properties object passed in, using the ValueRetriever passed in to locate
 84  
      * replacement values for placeholder text
 85  
      */
 86  
     public Properties resolve(Properties properties, ValueRetriever retriever) {
 87  
         // Storage for resolved properties
 88  4
         Properties resolvedProperties = new Properties();
 89  
         // Store the keys in a list so we can sort them if needed
 90  4
         List<String> keys = new ArrayList<String>(properties.stringPropertyNames());
 91  4
         if (isSort()) {
 92  
             // Resolve them in sorted order
 93  4
             Collections.sort(keys);
 94  
         }
 95  
         // Iterate through the keys, resolving both keys and values as we go
 96  4
         for (String key : keys) {
 97  441
             resolveProperty(new ResolvePropertyContext(keys, key, properties, resolvedProperties, retriever));
 98  
         }
 99  4
         return resolvedProperties;
 100  
     }
 101  
 
 102  
     protected void resolveProperty(ResolvePropertyContext ctx) {
 103  441
         String key = ctx.getKey();
 104  441
         Properties properties = ctx.getProperties();
 105  
         // First resolve the key
 106  441
         String resolvedKey = resolve(key, ctx.getRetriever());
 107  
 
 108  
         // Emit an info level message when a key is resolved to a new value
 109  441
         if (!key.equals(resolvedKey)) {
 110  0
             LOGGER.info("Resolved key [{}]->[{}]", key, resolvedKey);
 111  
         }
 112  
 
 113  
         // Obtain a value for the key
 114  441
         String value = properties.getProperty(key);
 115  
         // Resolve any placeholders in the value
 116  441
         String resolvedValue = resolve(value, ctx.getRetriever());
 117  
 
 118  
         // Check for duplicates
 119  441
         duplicatePropertyCheck(ctx, resolvedKey, resolvedValue);
 120  
 
 121  
         // Emit an info level message when a value gets resolved to something new
 122  441
         if (!value.equals(resolvedValue)) {
 123  1
             LOGGER.info("Resolved value for '" + resolvedKey + "'  [{}]->[{}]",
 124  
                     plogger.getLogValue(resolvedKey, value), plogger.getLogValue(resolvedKey, resolvedValue));
 125  
         }
 126  441
         LOGGER.debug("Adding resolved property {}=[{}]", resolvedKey, plogger.getLogValue(resolvedKey, resolvedValue));
 127  
         // Store the resolved key/value pair
 128  441
         ctx.getResolvedProperties().setProperty(resolvedKey, resolvedValue);
 129  441
     }
 130  
 
 131  
     /**
 132  
      * A duplicate property can occur when resolving placeholders inside a key.
 133  
      * 
 134  
      * For example, given a properties file with the following entries:<br>
 135  
      * <code>
 136  
      * cat=lion
 137  
      * lion.speed=fast
 138  
      * ${cat}.speed=slow
 139  
      * </code>
 140  
      * 
 141  
      * The key ${cat}.speed will resolve to 'lion.speed' which duplicates the original entry in the properties file
 142  
      */
 143  
     protected void duplicatePropertyCheck(ResolvePropertyContext ctx, String resolvedKey, String resolvedValue) {
 144  441
         String rawKey = ctx.getKey();
 145  441
         Properties properties = ctx.getProperties();
 146  
         // The keys are the same, can't be a duplicate property, nothing to do
 147  441
         if (rawKey.equals(resolvedKey)) {
 148  441
             return;
 149  
         }
 150  
 
 151  
         // The keys are not the same, but resolvedKey is a new key, nothing to do
 152  0
         if (!ctx.getKeys().contains(resolvedKey)) {
 153  0
             return;
 154  
         }
 155  
 
 156  
         // If we get this far, the keys are not the same, and resolved key points to a property that already exists
 157  
 
 158  
         // Extract the existing value
 159  0
         String existingValue = properties.getProperty(resolvedKey);
 160  
 
 161  
         // Check the values
 162  0
         if (ObjectUtils.nullSafeEquals(existingValue, resolvedValue)) {
 163  
             // The values are the same, just emit a warning and proceed
 164  0
             LOGGER.warn("Duplicate property detected for '" + resolvedKey + "'.  '" + rawKey + "' resolved to '"
 165  
                     + resolvedKey + "' which already has a value. Both values are the same: [{}]",
 166  
                     plogger.getLogValue(resolvedKey, existingValue));
 167  0
             return;
 168  
         } else {
 169  
             // Otherwise we have an issue
 170  0
             StringBuilder sb = new StringBuilder();
 171  0
             sb.append("Duplicate property value detected for '" + resolvedKey + "'.");
 172  0
             sb.append("  The key '" + rawKey + "' resolved to '" + resolvedKey
 173  
                     + "' and there is a value for that key already present.");
 174  0
             sb.append("  Existing value:[" + existingValue + "]  New Value:[" + resolvedValue + "]");
 175  0
             throw new IllegalArgumentException(sb.toString());
 176  
         }
 177  
     }
 178  
 
 179  
     public boolean isSort() {
 180  5
         return sort;
 181  
     }
 182  
 
 183  
     public void setSort(boolean sort) {
 184  1
         this.sort = sort;
 185  1
     }
 186  
 
 187  
 }