1   
2   
3   
4   
5   
6   
7   
8   
9   
10  
11  
12  
13  
14  
15  
16  package org.kuali.ole.utility;
17  
18  import java.util.HashMap;
19  import java.util.HashSet;
20  import java.util.Map;
21  import java.util.Properties;
22  import java.util.Set;
23  
24  import org.slf4j.Logger;
25  import org.slf4j.LoggerFactory;
26  import org.springframework.util.Assert;
27  import org.springframework.util.StringUtils;
28  
29  
30  
31  
32  
33  
34  
35  
36  
37  
38  
39  public class PropertyPlaceholderHelper {
40  
41      private static final Logger logger = LoggerFactory.getLogger(PropertyPlaceholderHelper.class);
42  
43      private static final Map<String, String> wellKnownSimplePrefixes = new HashMap<String, String>(4);
44  
45      static {
46          wellKnownSimplePrefixes.put("}", "{");
47          wellKnownSimplePrefixes.put("]", "[");
48          wellKnownSimplePrefixes.put(")", "(");
49      }
50  
51      private final String placeholderPrefix;
52  
53      private final String placeholderSuffix;
54  
55      private final String simplePrefix;
56  
57      private final String valueSeparator;
58  
59      private final boolean ignoreUnresolvablePlaceholders;
60  
61      
62  
63  
64  
65  
66  
67      public PropertyPlaceholderHelper(String placeholderPrefix, String placeholderSuffix) {
68          this(placeholderPrefix, placeholderSuffix, null, true);
69      }
70  
71      
72  
73  
74  
75  
76  
77  
78  
79  
80      public PropertyPlaceholderHelper(String placeholderPrefix, String placeholderSuffix, String valueSeparator, boolean ignoreUnresolvablePlaceholders) {
81  
82          Assert.notNull(placeholderPrefix, "placeholderPrefix must not be null");
83          Assert.notNull(placeholderSuffix, "placeholderSuffix must not be null");
84          this.placeholderPrefix = placeholderPrefix;
85          this.placeholderSuffix = placeholderSuffix;
86          String simplePrefixForSuffix = wellKnownSimplePrefixes.get(this.placeholderSuffix);
87          if (simplePrefixForSuffix != null && this.placeholderPrefix.endsWith(simplePrefixForSuffix)) {
88              this.simplePrefix = simplePrefixForSuffix;
89          } else {
90              this.simplePrefix = this.placeholderPrefix;
91          }
92          this.valueSeparator = valueSeparator;
93          this.ignoreUnresolvablePlaceholders = ignoreUnresolvablePlaceholders;
94      }
95  
96      
97  
98  
99  
100 
101 
102 
103     public String replacePlaceholders(String value, final Properties properties) {
104         Assert.notNull(properties, "Argument 'properties' must not be null.");
105         return replacePlaceholders(value, new PlaceholderResolver() {
106             @Override
107             public String resolvePlaceholder(String placeholderName) {
108                 return properties.getProperty(placeholderName);
109             }
110         });
111     }
112 
113     
114 
115 
116 
117 
118 
119 
120     public String replacePlaceholders(String value, PlaceholderResolver placeholderResolver) {
121         Assert.notNull(value, "Argument 'value' must not be null.");
122         return parseStringValue(value, placeholderResolver, new HashSet<String>());
123     }
124 
125     protected String parseStringValue(String strVal, PlaceholderResolver placeholderResolver, Set<String> visitedPlaceholders) {
126 
127         StringBuilder buf = new StringBuilder(strVal);
128 
129         int startIndex = strVal.indexOf(this.placeholderPrefix);
130         while (startIndex != -1) {
131             int endIndex = findPlaceholderEndIndex(buf, startIndex);
132             if (endIndex != -1) {
133                 String placeholder = buf.substring(startIndex + this.placeholderPrefix.length(), endIndex);
134                 if (!visitedPlaceholders.add(placeholder)) {
135                     throw new IllegalArgumentException("Circular placeholder reference '" + placeholder + "' in property definitions");
136                 }
137                 
138                 placeholder = parseStringValue(placeholder, placeholderResolver, visitedPlaceholders);
139 
140                 
141                 String propVal = placeholderResolver.resolvePlaceholder(placeholder);
142                 if (propVal == null && this.valueSeparator != null) {
143                     int separatorIndex = placeholder.indexOf(this.valueSeparator);
144                     if (separatorIndex != -1) {
145                         String actualPlaceholder = placeholder.substring(0, separatorIndex);
146                         String defaultValue = placeholder.substring(separatorIndex + this.valueSeparator.length());
147                         propVal = placeholderResolver.resolvePlaceholder(actualPlaceholder);
148                         if (propVal == null) {
149                             propVal = defaultValue;
150                         }
151                     }
152                 }
153                 if (propVal != null) {
154                     
155                     
156                     propVal = parseStringValue(propVal, placeholderResolver, visitedPlaceholders);
157                     buf.replace(startIndex, endIndex + this.placeholderSuffix.length(), propVal);
158                     if (logger.isTraceEnabled()) {
159                         logger.trace("Resolved placeholder '" + placeholder + "'");
160                     }
161                     startIndex = buf.indexOf(this.placeholderPrefix, startIndex + propVal.length());
162                 } else if (this.ignoreUnresolvablePlaceholders) {
163                     
164                     startIndex = buf.indexOf(this.placeholderPrefix, endIndex + this.placeholderSuffix.length());
165                 } else {
166                     throw new IllegalArgumentException("Could not resolve placeholder '" + placeholder + "'");
167                 }
168 
169                 visitedPlaceholders.remove(placeholder);
170             } else {
171                 startIndex = -1;
172             }
173         }
174 
175         return buf.toString();
176     }
177 
178     private int findPlaceholderEndIndex(CharSequence buf, int startIndex) {
179         int index = startIndex + this.placeholderPrefix.length();
180         int withinNestedPlaceholder = 0;
181         while (index < buf.length()) {
182             if (StringUtils.substringMatch(buf, index, this.placeholderSuffix)) {
183                 if (withinNestedPlaceholder > 0) {
184                     withinNestedPlaceholder--;
185                     index = index + this.placeholderSuffix.length();
186                 } else {
187                     return index;
188                 }
189             } else if (StringUtils.substringMatch(buf, index, this.simplePrefix)) {
190                 withinNestedPlaceholder++;
191                 index = index + this.simplePrefix.length();
192             } else {
193                 index++;
194             }
195         }
196         return -1;
197     }
198 
199     
200 
201 
202 
203 
204     public static interface PlaceholderResolver {
205 
206         
207 
208 
209 
210 
211 
212         String resolvePlaceholder(String placeholderName);
213     }
214 
215 }