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, startExpr, endExpr);
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 }