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 }