View Javadoc
1   package org.codehaus.plexus.interpolation;
2   
3   import java.util.ArrayList;
4   import java.util.Collections;
5   import java.util.HashMap;
6   import java.util.List;
7   import java.util.Map;
8   import java.util.Properties;
9   
10  import org.codehaus.plexus.interpolation.os.OperatingSystemUtils;
11  import org.junit.jupiter.api.BeforeEach;
12  import org.junit.jupiter.api.Test;
13  
14  import static org.junit.jupiter.api.Assertions.assertEquals;
15  import static org.junit.jupiter.api.Assertions.fail;
16  
17  public class StringSearchInterpolatorTest {
18  
19      @BeforeEach
20      void setUp() {
21          EnvarBasedValueSource.resetStatics();
22      }
23  
24      @Test
25      void longDelimitersInContext() throws Exception {
26          String src = "This is a <expression>test.label</expression> for long delimiters in context.";
27          String result = "This is a test for long delimiters in context.";
28  
29          Properties p = new Properties();
30          p.setProperty("test.label", "test");
31  
32          StringSearchInterpolator interpolator = new StringSearchInterpolator("<expression>", "</expression>");
33          interpolator.addValueSource(new PropertiesBasedValueSource(p));
34  
35          assertEquals(result, interpolator.interpolate(src));
36      }
37  
38      @Test
39      void longDelimitersWithNoStartContext() throws Exception {
40          String src = "<expression>test.label</expression> for long delimiters in context.";
41          String result = "test for long delimiters in context.";
42  
43          Properties p = new Properties();
44          p.setProperty("test.label", "test");
45  
46          StringSearchInterpolator interpolator = new StringSearchInterpolator("<expression>", "</expression>");
47          interpolator.addValueSource(new PropertiesBasedValueSource(p));
48  
49          assertEquals(result, interpolator.interpolate(src));
50      }
51  
52      @Test
53      void longDelimitersWithNoEndContext() throws Exception {
54          String src = "This is a <expression>test.label</expression>";
55          String result = "This is a test";
56  
57          Properties p = new Properties();
58          p.setProperty("test.label", "test");
59  
60          StringSearchInterpolator interpolator = new StringSearchInterpolator("<expression>", "</expression>");
61          interpolator.addValueSource(new PropertiesBasedValueSource(p));
62  
63          assertEquals(result, interpolator.interpolate(src));
64      }
65  
66      @Test
67      void longDelimitersWithNoContext() throws Exception {
68          String src = "<expression>test.label</expression>";
69          String result = "test";
70  
71          Properties p = new Properties();
72          p.setProperty("test.label", "test");
73  
74          StringSearchInterpolator interpolator = new StringSearchInterpolator("<expression>", "</expression>");
75          interpolator.addValueSource(new PropertiesBasedValueSource(p));
76  
77          assertEquals(result, interpolator.interpolate(src));
78      }
79  
80      @Test
81      void longDelimitersPassedToValueSource() throws Exception {
82          String src = "<expression>test</expression>";
83  
84          StringSearchInterpolator interpolator = new StringSearchInterpolator("<expression>", "</expression>");
85          interpolator.addValueSource(new AbstractValueSource(false) {
86  
87              @Override
88              public Object getValue(String expression, String expressionStartDelimiter, String expressionEndDelimiter) {
89                  assertEquals("<expression>", expressionStartDelimiter);
90                  assertEquals("</expression>", expressionEndDelimiter);
91                  return expression;
92              }
93  
94              @Override
95              public Object getValue(String expression) {
96                  fail("This method is not supposed to be called");
97                  return null;
98              }
99          });
100 
101         assertEquals("test", interpolator.interpolate(src));
102     }
103 
104     @Test
105     void simpleSubstitution() throws Exception {
106         Properties p = new Properties();
107         p.setProperty("key", "value");
108 
109         StringSearchInterpolator interpolator = new StringSearchInterpolator();
110         interpolator.addValueSource(new PropertiesBasedValueSource(p));
111 
112         assertEquals("This is a test value.", interpolator.interpolate("This is a test ${key}."));
113     }
114 
115     @Test
116     void simpleSubstitutionTwoExpressions() throws Exception {
117         Properties p = new Properties();
118         p.setProperty("key", "value");
119         p.setProperty("key2", "value2");
120 
121         StringSearchInterpolator interpolator = new StringSearchInterpolator();
122         interpolator.addValueSource(new PropertiesBasedValueSource(p));
123 
124         assertEquals("value-value2", interpolator.interpolate("${key}-${key2}"));
125     }
126 
127     @Test
128     void brokenExpressionLeaveItAlone() throws Exception {
129         Properties p = new Properties();
130         p.setProperty("key", "value");
131 
132         StringSearchInterpolator interpolator = new StringSearchInterpolator();
133         interpolator.addValueSource(new PropertiesBasedValueSource(p));
134 
135         assertEquals("This is a test ${key.", interpolator.interpolate("This is a test ${key."));
136     }
137 
138     @Test
139     void shouldFailOnExpressionCycle() {
140         Properties props = new Properties();
141         props.setProperty("key1", "${key2}");
142         props.setProperty("key2", "${key1}");
143 
144         StringSearchInterpolator rbi = new StringSearchInterpolator();
145         rbi.addValueSource(new PropertiesBasedValueSource(props));
146 
147         try {
148             rbi.interpolate("${key1}", new SimpleRecursionInterceptor());
149 
150             fail("Should detect expression cycle and fail.");
151         } catch (InterpolationException e) {
152             // expected
153         }
154     }
155 
156     @Test
157     void shouldResolveByUsingObjectListMap() throws Exception {
158         StringSearchInterpolator rbi = new StringSearchInterpolator();
159         rbi.addValueSource(new ObjectBasedValueSource(this));
160         String result =
161                 rbi.interpolate("this is a ${var} ${list[1].name} ${anArray[2].name} ${map(Key with spaces).name}");
162 
163         assertEquals("this is a testVar testIndexedWithList testIndexedWithArray testMap", result);
164     }
165 
166     @Test
167     void shouldResolveByContextValue() throws Exception {
168         StringSearchInterpolator rbi = new StringSearchInterpolator();
169 
170         Map<String, String> context = new HashMap<>();
171         context.put("var", "testVar");
172 
173         rbi.addValueSource(new MapBasedValueSource(context));
174 
175         String result = rbi.interpolate("this is a ${var}");
176 
177         assertEquals("this is a testVar", result);
178     }
179 
180     @Test
181     void shouldResolveByEnvar() throws Exception {
182         OperatingSystemUtils.setEnvVarSource(() -> {
183             HashMap<String, String> map = new HashMap<>();
184             map.put("SOME_ENV", "variable");
185             map.put("OTHER_ENV", "other variable");
186             return map;
187         });
188 
189         StringSearchInterpolator rbi = new StringSearchInterpolator();
190 
191         rbi.addValueSource(new EnvarBasedValueSource(false));
192 
193         String result = rbi.interpolate("this is a ${env.SOME_ENV} ${env.OTHER_ENV}");
194 
195         assertEquals("this is a variable other variable", result);
196     }
197 
198     @Test
199     void usePostProcessorDoesNotChangeValue() throws Exception {
200         StringSearchInterpolator rbi = new StringSearchInterpolator();
201 
202         Map<String, String> context = new HashMap<>();
203         context.put("test.var", "testVar");
204 
205         rbi.addValueSource(new MapBasedValueSource(context));
206 
207         rbi.addPostProcessor((expression, value) -> null);
208 
209         String result = rbi.interpolate("this is a ${test.var}");
210 
211         assertEquals("this is a testVar", result);
212     }
213 
214     @Test
215     void usePostProcessorChangesValue() throws Exception {
216 
217         StringSearchInterpolator rbi = new StringSearchInterpolator();
218 
219         Map<String, String> context = new HashMap<>();
220         context.put("test.var", "testVar");
221 
222         rbi.addValueSource(new MapBasedValueSource(context));
223 
224         rbi.addPostProcessor((expression, value) -> value + "2");
225 
226         String result = rbi.interpolate("this is a ${test.var}");
227 
228         assertEquals("this is a testVar2", result);
229     }
230 
231     @Test
232     void simpleSubstitutionWithDefinedExpr() throws Exception {
233         Properties p = new Properties();
234         p.setProperty("key", "value");
235 
236         StringSearchInterpolator interpolator = new StringSearchInterpolator("@{", "}");
237         interpolator.addValueSource(new PropertiesBasedValueSource(p));
238 
239         assertEquals("This is a test value.", interpolator.interpolate("This is a test @{key}."));
240     }
241 
242     @Test
243     void escape() throws Exception {
244         Properties p = new Properties();
245         p.setProperty("key", "value");
246 
247         StringSearchInterpolator interpolator = new StringSearchInterpolator("@{", "}");
248         interpolator.setEscapeString("\\");
249         interpolator.addValueSource(new PropertiesBasedValueSource(p));
250 
251         String result = interpolator.interpolate("This is a test \\@{key}.");
252 
253         assertEquals("This is a test @{key}.", result);
254     }
255 
256     @Test
257     void escapeWithLongEscapeStr() throws Exception {
258         Properties p = new Properties();
259         p.setProperty("key", "value");
260 
261         StringSearchInterpolator interpolator = new StringSearchInterpolator("@{", "}");
262         interpolator.setEscapeString("$$");
263         interpolator.addValueSource(new PropertiesBasedValueSource(p));
264 
265         String result = interpolator.interpolate("This is a test $$@{key}.");
266 
267         assertEquals("This is a test @{key}.", result);
268     }
269 
270     @Test
271     void escapeWithLongEscapeStrAtStart() throws Exception {
272         Properties p = new Properties();
273         p.setProperty("key", "value");
274 
275         StringSearchInterpolator interpolator = new StringSearchInterpolator("@{", "}");
276         interpolator.setEscapeString("$$");
277         interpolator.addValueSource(new PropertiesBasedValueSource(p));
278 
279         String result = interpolator.interpolate("$$@{key} This is a test.");
280 
281         assertEquals("@{key} This is a test.", result);
282     }
283 
284     @Test
285     void notEscapeWithLongEscapeStrAtStart() throws Exception {
286         Properties p = new Properties();
287         p.setProperty("key", "value");
288 
289         StringSearchInterpolator interpolator = new StringSearchInterpolator("@{", "}");
290         interpolator.setEscapeString("$$");
291         interpolator.addValueSource(new PropertiesBasedValueSource(p));
292 
293         String result = interpolator.interpolate("@{key} This is a test.");
294 
295         assertEquals("value This is a test.", result);
296     }
297 
298     @Test
299     void escapeNotFailWithNullEscapeStr() throws Exception {
300         Properties p = new Properties();
301         p.setProperty("key", "value");
302 
303         StringSearchInterpolator interpolator = new StringSearchInterpolator("@{", "}");
304         interpolator.setEscapeString(null);
305         interpolator.addValueSource(new PropertiesBasedValueSource(p));
306 
307         String result = interpolator.interpolate("This is a test @{key}.");
308 
309         assertEquals("This is a test value.", result);
310     }
311 
312     @Test
313     void onlyEscapeExprAtStart() throws Exception {
314         Properties p = new Properties();
315         p.setProperty("key", "value");
316 
317         StringSearchInterpolator interpolator = new StringSearchInterpolator("@{", "}");
318         interpolator.setEscapeString("\\");
319         interpolator.addValueSource(new PropertiesBasedValueSource(p));
320 
321         String result = interpolator.interpolate("\\@{key} This is a test.");
322 
323         assertEquals("@{key} This is a test.", result);
324     }
325 
326     @Test
327     void notEscapeExprAtStart() throws Exception {
328         Properties p = new Properties();
329         p.setProperty("key", "value");
330 
331         StringSearchInterpolator interpolator = new StringSearchInterpolator("@{", "}");
332         interpolator.setEscapeString("\\");
333         interpolator.addValueSource(new PropertiesBasedValueSource(p));
334 
335         String result = interpolator.interpolate("@{key} This is a test.");
336 
337         assertEquals("value This is a test.", result);
338     }
339 
340     @Test
341     void escapeExprAtStart() throws Exception {
342         Properties p = new Properties();
343         p.setProperty("key", "value");
344 
345         StringSearchInterpolator interpolator = new StringSearchInterpolator("@", "@");
346         interpolator.setEscapeString("\\");
347         interpolator.addValueSource(new PropertiesBasedValueSource(p));
348 
349         String result = interpolator.interpolate("\\@key@ This is a test @key@.");
350 
351         assertEquals("@key@ This is a test value.", result);
352     }
353 
354     @Test
355     void npeFree() throws Exception {
356         Properties p = new Properties();
357         p.setProperty("key", "value");
358 
359         StringSearchInterpolator interpolator = new StringSearchInterpolator("@{", "}");
360         interpolator.setEscapeString("\\");
361         interpolator.addValueSource(new PropertiesBasedValueSource(p));
362 
363         String result = interpolator.interpolate(null);
364 
365         assertEquals("", result);
366     }
367 
368     @Test
369     void interruptedInterpolate() throws Exception {
370         Interpolator interpolator = new StringSearchInterpolator();
371         RecursionInterceptor recursionInterceptor = new SimpleRecursionInterceptor();
372         final boolean[] error = new boolean[] {false};
373         interpolator.addValueSource(new ValueSource() {
374             public Object getValue(String expression) {
375                 if (expression.equals("key")) {
376                     if (error[0]) {
377                         throw new IllegalStateException("broken");
378                     }
379                     return "val";
380                 } else {
381                     return null;
382                 }
383             }
384 
385             public List getFeedback() {
386                 return Collections.EMPTY_LIST;
387             }
388 
389             public void clearFeedback() {}
390         });
391         assertEquals("-val-", interpolator.interpolate("-${key}-", recursionInterceptor), "control case");
392         error[0] = true;
393         try {
394             interpolator.interpolate("-${key}-", recursionInterceptor);
395             fail("should have thrown exception");
396         } catch (IllegalStateException x) {
397             // right
398         }
399         error[0] = false;
400         assertEquals(
401                 "-val-",
402                 interpolator.interpolate("-${key}-", recursionInterceptor),
403                 "should not believe there is a cycle here");
404     }
405 
406     @Test
407     void cacheAnswersTrue() throws Exception {
408         Properties p = new Properties();
409         p.setProperty("key", "value");
410 
411         class CountingStringSearchInterpolator extends StringSearchInterpolator {
412             private int existingCallCount;
413 
414             @Override
415             protected Object getExistingAnswer(String key) {
416                 Object value = super.getExistingAnswer(key);
417                 if (value != null) {
418                     ++existingCallCount;
419                 }
420                 return value;
421             }
422 
423             public int getExistingCallCount() {
424                 return existingCallCount;
425             }
426         }
427 
428         CountingStringSearchInterpolator interpolator = new CountingStringSearchInterpolator();
429         interpolator.setCacheAnswers(true);
430         interpolator.addValueSource(new PropertiesBasedValueSource(p));
431 
432         String result = interpolator.interpolate("${key}-${key}-${key}-${key}");
433 
434         assertEquals("value-value-value-value", result);
435         // first value is interpolated and saved, then the 3 next answers came from existing answer Map
436         assertEquals(3, interpolator.getExistingCallCount());
437 
438         // answers are preserved between calls:
439         result = interpolator.interpolate("${key}-${key}-${key}-${key}");
440         assertEquals("value-value-value-value", result);
441         // 3 from the first call to interpolate(), plus 4 from second call
442         assertEquals(3 + 4, interpolator.getExistingCallCount());
443     }
444 
445     @Test
446     void cacheAnswersFalse() throws Exception {
447         Properties p = new Properties();
448         p.setProperty("key", "value");
449 
450         class CountingStringSearchInterpolator extends StringSearchInterpolator {
451             private int existingCallCount;
452 
453             @Override
454             protected Object getExistingAnswer(String key) {
455                 Object value = super.getExistingAnswer(key);
456                 if (value != null) {
457                     ++existingCallCount;
458                 }
459                 return value;
460             }
461 
462             public int getExistingCallCount() {
463                 return existingCallCount;
464             }
465         }
466 
467         CountingStringSearchInterpolator interpolator = new CountingStringSearchInterpolator();
468         interpolator.addValueSource(new PropertiesBasedValueSource(p));
469 
470         String result = interpolator.interpolate("${key}-${key}-${key}-${key}");
471 
472         assertEquals("value-value-value-value", result);
473         // all values are interpolated each time
474         assertEquals(0, interpolator.getExistingCallCount());
475     }
476 
477     public String getVar() {
478         return "testVar";
479     }
480 
481     public Person[] getAnArray() {
482         Person[] array = new Person[3];
483         array[0] = new Person("Gabriel");
484         array[1] = new Person("Daniela");
485         array[2] = new Person("testIndexedWithArray");
486         return array;
487     }
488 
489     public List<Person> getList() {
490         List<Person> list = new ArrayList<>();
491         list.add(new Person("Gabriel"));
492         list.add(new Person("testIndexedWithList"));
493         list.add(new Person("Daniela"));
494         return list;
495     }
496 
497     public Map<String, Person> getMap() {
498         Map<String, Person> map = new HashMap<>();
499         map.put("Key with spaces", new Person("testMap"));
500         return map;
501     }
502 
503     public static class Person {
504         private final String name;
505 
506         public Person(String name) {
507             this.name = name;
508         }
509 
510         public String getName() {
511             return name;
512         }
513     }
514 }