1 package org.codehaus.plexus.interpolation.multi;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 import java.util.ArrayList;
20 import java.util.HashMap;
21 import java.util.HashSet;
22 import java.util.LinkedHashSet;
23 import java.util.List;
24 import java.util.Map;
25 import java.util.Set;
26
27 import org.codehaus.plexus.interpolation.InterpolationCycleException;
28 import org.codehaus.plexus.interpolation.InterpolationException;
29 import org.codehaus.plexus.interpolation.InterpolationPostProcessor;
30 import org.codehaus.plexus.interpolation.Interpolator;
31 import org.codehaus.plexus.interpolation.RecursionInterceptor;
32 import org.codehaus.plexus.interpolation.SimpleRecursionInterceptor;
33 import org.codehaus.plexus.interpolation.ValueSource;
34
35 public class MultiDelimiterStringSearchInterpolator implements Interpolator {
36
37 private static final int MAX_TRIES = 10;
38
39 private Map existingAnswers = new HashMap();
40
41 private List<ValueSource> valueSources = new ArrayList<ValueSource>();
42
43 private List postProcessors = new ArrayList();
44
45 private boolean cacheAnswers = false;
46
47 private LinkedHashSet<DelimiterSpecification> delimiters = new LinkedHashSet<DelimiterSpecification>();
48
49 private String escapeString;
50
51 public MultiDelimiterStringSearchInterpolator() {
52 delimiters.add(DelimiterSpecification.DEFAULT_SPEC);
53 }
54
55 public MultiDelimiterStringSearchInterpolator addDelimiterSpec(String delimiterSpec) {
56 if (delimiterSpec == null) {
57 return this;
58 }
59 delimiters.add(DelimiterSpecification.parse(delimiterSpec));
60 return this;
61 }
62
63 public boolean removeDelimiterSpec(String delimiterSpec) {
64 if (delimiterSpec == null) {
65 return false;
66 }
67 return delimiters.remove(DelimiterSpecification.parse(delimiterSpec));
68 }
69
70 public MultiDelimiterStringSearchInterpolator withValueSource(ValueSource vs) {
71 addValueSource(vs);
72 return this;
73 }
74
75 public MultiDelimiterStringSearchInterpolator withPostProcessor(InterpolationPostProcessor postProcessor) {
76 addPostProcessor(postProcessor);
77 return this;
78 }
79
80
81
82
83 public void addValueSource(ValueSource valueSource) {
84 valueSources.add(valueSource);
85 }
86
87
88
89
90 public void removeValuesSource(ValueSource valueSource) {
91 valueSources.remove(valueSource);
92 }
93
94
95
96
97 public void addPostProcessor(InterpolationPostProcessor postProcessor) {
98 postProcessors.add(postProcessor);
99 }
100
101
102
103
104 public void removePostProcessor(InterpolationPostProcessor postProcessor) {
105 postProcessors.remove(postProcessor);
106 }
107
108 public String interpolate(String input, String thisPrefixPattern) throws InterpolationException {
109 return interpolate(input, new SimpleRecursionInterceptor());
110 }
111
112 public String interpolate(String input, String thisPrefixPattern, RecursionInterceptor recursionInterceptor)
113 throws InterpolationException {
114 return interpolate(input, recursionInterceptor);
115 }
116
117 public String interpolate(String input) throws InterpolationException {
118 return interpolate(input, new SimpleRecursionInterceptor());
119 }
120
121
122
123
124
125
126 public String interpolate(String input, RecursionInterceptor recursionInterceptor) throws InterpolationException {
127 try {
128 return interpolate(input, recursionInterceptor, new HashSet());
129 } finally {
130 if (!cacheAnswers) {
131 existingAnswers.clear();
132 }
133 }
134 }
135
136 private String interpolate(String input, RecursionInterceptor recursionInterceptor, Set<String> unresolvable)
137 throws InterpolationException {
138 if (input == null) {
139
140 return "";
141 }
142 StringBuilder result = new StringBuilder(input.length() * 2);
143
144 String lastResult = input;
145 int tries = 0;
146 do {
147 tries++;
148 if (result.length() > 0) {
149 lastResult = result.toString();
150 result.setLength(0);
151 }
152
153 int startIdx = -1;
154 int endIdx = -1;
155
156 DelimiterSpecification selectedSpec = null;
157 while ((selectedSpec = select(input, endIdx)) != null) {
158 String startExpr = selectedSpec.getBegin();
159 String endExpr = selectedSpec.getEnd();
160
161 startIdx = selectedSpec.getNextStartIndex();
162 result.append(input, endIdx + 1, startIdx);
163
164 endIdx = input.indexOf(endExpr, startIdx + 1);
165 if (endIdx < 0) {
166 break;
167 }
168
169 String wholeExpr = input.substring(startIdx, endIdx + endExpr.length());
170 String realExpr = wholeExpr.substring(startExpr.length(), wholeExpr.length() - endExpr.length());
171
172 if (startIdx >= 0 && escapeString != null && escapeString.length() > 0) {
173 int startEscapeIdx = (startIdx == 0) ? 0 : startIdx - escapeString.length();
174 if (startEscapeIdx >= 0) {
175 String escape = input.substring(startEscapeIdx, startIdx);
176 if (escape != null && escapeString.equals(escape)) {
177 result.append(wholeExpr);
178 if (startEscapeIdx > 0) {
179 --startEscapeIdx;
180 }
181 result.replace(startEscapeIdx, startEscapeIdx + escapeString.length(), "");
182 continue;
183 }
184 }
185 }
186
187 boolean resolved = false;
188 if (!unresolvable.contains(wholeExpr)) {
189 if (realExpr.startsWith(".")) {
190 realExpr = realExpr.substring(1);
191 }
192
193 if (recursionInterceptor.hasRecursiveExpression(realExpr)) {
194 throw new InterpolationCycleException(recursionInterceptor, realExpr, wholeExpr);
195 }
196
197 recursionInterceptor.expressionResolutionStarted(realExpr);
198
199 Object value = existingAnswers.get(realExpr);
200 Object bestAnswer = null;
201 for (ValueSource vs : valueSources) {
202 if (value != null) break;
203
204 value = vs.getValue(realExpr);
205
206 if (value != null && value.toString().contains(wholeExpr)) {
207 bestAnswer = value;
208 value = null;
209 }
210 }
211
212
213
214
215 if (value == null && bestAnswer != null) {
216 throw new InterpolationCycleException(recursionInterceptor, realExpr, wholeExpr);
217 }
218
219 if (value != null) {
220 value = interpolate(String.valueOf(value), recursionInterceptor, unresolvable);
221
222 if (postProcessors != null && !postProcessors.isEmpty()) {
223 for (Object postProcessor1 : postProcessors) {
224 InterpolationPostProcessor postProcessor = (InterpolationPostProcessor) postProcessor1;
225 Object newVal = postProcessor.execute(realExpr, value);
226 if (newVal != null) {
227 value = newVal;
228 break;
229 }
230 }
231 }
232
233
234
235
236
237 result.append(String.valueOf(value));
238 resolved = true;
239 } else {
240 unresolvable.add(wholeExpr);
241 }
242
243 recursionInterceptor.expressionResolutionFinished(realExpr);
244 }
245
246 if (!resolved) {
247 result.append(wholeExpr);
248 }
249
250 if (endIdx > -1) {
251 endIdx += endExpr.length() - 1;
252 }
253 }
254
255 if (endIdx == -1 && startIdx > -1) {
256 result.append(input, startIdx, input.length());
257 } else if (endIdx < input.length()) {
258 result.append(input, endIdx + 1, input.length());
259 }
260 } while (!lastResult.equals(result.toString()) && tries < MAX_TRIES);
261
262 return result.toString();
263 }
264
265 private DelimiterSpecification select(String input, int lastEndIdx) {
266 DelimiterSpecification selected = null;
267
268 for (DelimiterSpecification spec : delimiters) {
269 spec.clearNextStart();
270
271 if (selected == null) {
272 int idx = input.indexOf(spec.getBegin(), lastEndIdx + 1);
273 if (idx > -1) {
274 spec.setNextStartIndex(idx);
275 selected = spec;
276 }
277 }
278 }
279
280 return selected;
281 }
282
283
284
285
286
287
288
289
290 public List getFeedback() {
291 List messages = new ArrayList();
292 for (ValueSource vs : valueSources) {
293 List feedback = vs.getFeedback();
294 if (feedback != null && !feedback.isEmpty()) {
295 messages.addAll(feedback);
296 }
297 }
298
299 return messages;
300 }
301
302
303
304
305 public void clearFeedback() {
306 for (ValueSource vs : valueSources) {
307 vs.clearFeedback();
308 }
309 }
310
311 public boolean isCacheAnswers() {
312 return cacheAnswers;
313 }
314
315 public void setCacheAnswers(boolean cacheAnswers) {
316 this.cacheAnswers = cacheAnswers;
317 }
318
319 public void clearAnswers() {
320 existingAnswers.clear();
321 }
322
323 public String getEscapeString() {
324 return escapeString;
325 }
326
327 public void setEscapeString(String escapeString) {
328 this.escapeString = escapeString;
329 }
330
331 public MultiDelimiterStringSearchInterpolator escapeString(String escapeString) {
332 this.escapeString = escapeString;
333 return this;
334 }
335
336 public MultiDelimiterStringSearchInterpolator setDelimiterSpecs(LinkedHashSet<String> specs) {
337 delimiters.clear();
338 for (String spec : specs) {
339 if (spec == null) {
340 continue;
341 }
342 delimiters.add(DelimiterSpecification.parse(spec));
343 }
344
345 return this;
346 }
347 }