|
17 | 17 | package org.springframework.util;
|
18 | 18 |
|
19 | 19 | import java.util.Properties;
|
| 20 | +import java.util.Set; |
| 21 | +import java.util.HashSet; |
20 | 22 |
|
21 | 23 | /**
|
22 | 24 | * Utility class for working with Strings that have placeholder values in them. A placeholder takes the form
|
@@ -63,29 +65,75 @@ public String resolvePlaceholder(String placeholderName) {
|
63 | 65 | * @return the supplied value with placeholders replaced inline.
|
64 | 66 | */
|
65 | 67 | public static String replacePlaceholders(String value, PlaceholderResolver placeholderResolver) {
|
66 |
| - StringBuilder result = new StringBuilder(value); |
| 68 | + return parseStringValue(value, placeholderResolver, new HashSet<String>()); |
| 69 | + } |
| 70 | + |
| 71 | + protected static String parseStringValue(String strVal, PlaceholderResolver placeholderResolver, Set<String> visitedPlaceholders) { |
| 72 | + StringBuilder buf = new StringBuilder(strVal); |
67 | 73 |
|
68 |
| - int startIndex = result.indexOf(PLACEHOLDER_PREFIX); |
| 74 | + int startIndex = strVal.indexOf(PLACEHOLDER_PREFIX); |
69 | 75 | while (startIndex != -1) {
|
70 |
| - int endIndex = result.indexOf(PLACEHOLDER_SUFFIX, startIndex + PLACEHOLDER_PREFIX.length()); |
| 76 | + int endIndex = findPlaceholderEndIndex(buf, startIndex); |
71 | 77 | if (endIndex != -1) {
|
72 |
| - String placeholder = result.substring(startIndex + PLACEHOLDER_PREFIX.length(), endIndex); |
73 |
| - int nextIndex = endIndex + PLACEHOLDER_SUFFIX.length(); |
| 78 | + String placeholder = buf.substring(startIndex + PLACEHOLDER_PREFIX.length(), endIndex); |
| 79 | + if (!visitedPlaceholders.add(placeholder)) { |
| 80 | + throw new IllegalArgumentException( |
| 81 | + "Circular placeholder reference '" + placeholder + "' in property definitions"); |
| 82 | + } |
| 83 | + // Recursive invocation, parsing placeholders contained in the placeholder key. |
| 84 | + placeholder = parseStringValue(placeholder, placeholderResolver, visitedPlaceholders); |
74 | 85 |
|
| 86 | + // Now obtain the value for the fully resolved key... |
75 | 87 | String propVal = placeholderResolver.resolvePlaceholder(placeholder);
|
76 | 88 | if (propVal != null) {
|
77 |
| - result.replace(startIndex, endIndex + PLACEHOLDER_SUFFIX.length(), propVal); |
78 |
| - nextIndex = startIndex + propVal.length(); |
| 89 | + // Recursive invocation, parsing placeholders contained in the |
| 90 | + // previously resolved placeholder value. |
| 91 | + propVal = parseStringValue(propVal, placeholderResolver, visitedPlaceholders); |
| 92 | + buf.replace(startIndex, endIndex + PLACEHOLDER_SUFFIX.length(), propVal); |
| 93 | + |
| 94 | + //if (logger.isTraceEnabled()) { |
| 95 | + // logger.trace("Resolved placeholder '" + placeholder + "'"); |
| 96 | + //} |
| 97 | + |
| 98 | + startIndex = buf.indexOf(PLACEHOLDER_PREFIX, startIndex + propVal.length()); |
| 99 | + } |
| 100 | + else { |
| 101 | + // Proceed with unprocessed value. |
| 102 | + startIndex = buf.indexOf(PLACEHOLDER_PREFIX, endIndex + PLACEHOLDER_SUFFIX.length()); |
79 | 103 | }
|
80 | 104 |
|
81 |
| - startIndex = result.indexOf(PLACEHOLDER_PREFIX, nextIndex); |
| 105 | + visitedPlaceholders.remove(placeholder); |
82 | 106 | }
|
83 | 107 | else {
|
84 | 108 | startIndex = -1;
|
85 | 109 | }
|
86 | 110 | }
|
87 | 111 |
|
88 |
| - return result.toString(); |
| 112 | + return buf.toString(); |
| 113 | + } |
| 114 | + |
| 115 | + private static int findPlaceholderEndIndex(CharSequence buf, int startIndex) { |
| 116 | + int index = startIndex + PLACEHOLDER_PREFIX.length(); |
| 117 | + int withinNestedPlaceholder = 0; |
| 118 | + while (index < buf.length()) { |
| 119 | + if (StringUtils.substringMatch(buf, index, PLACEHOLDER_SUFFIX)) { |
| 120 | + if (withinNestedPlaceholder > 0) { |
| 121 | + withinNestedPlaceholder--; |
| 122 | + index = index + PLACEHOLDER_PREFIX.length() - 1; |
| 123 | + } |
| 124 | + else { |
| 125 | + return index; |
| 126 | + } |
| 127 | + } |
| 128 | + else if (StringUtils.substringMatch(buf, index, PLACEHOLDER_PREFIX)) { |
| 129 | + withinNestedPlaceholder++; |
| 130 | + index = index + PLACEHOLDER_PREFIX.length(); |
| 131 | + } |
| 132 | + else { |
| 133 | + index++; |
| 134 | + } |
| 135 | + } |
| 136 | + return -1; |
89 | 137 | }
|
90 | 138 |
|
91 | 139 | /**
|
|
0 commit comments