1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55 package org.codehaus.plexus.interpolation.multi;
56
57 import java.io.FilterReader;
58 import java.io.IOException;
59 import java.io.Reader;
60 import java.util.LinkedHashSet;
61
62 import org.codehaus.plexus.interpolation.InterpolationException;
63 import org.codehaus.plexus.interpolation.Interpolator;
64 import org.codehaus.plexus.interpolation.RecursionInterceptor;
65 import org.codehaus.plexus.interpolation.SimpleRecursionInterceptor;
66
67
68
69
70
71
72
73 public class MultiDelimiterInterpolatorFilterReader extends FilterReader {
74
75
76 private Interpolator interpolator;
77
78
79
80
81 private RecursionInterceptor recursionInterceptor;
82
83
84 private String replaceData = null;
85
86
87 private int replaceIndex = -1;
88
89
90 private int previousIndex = -1;
91
92
93 public static final String DEFAULT_BEGIN_TOKEN = "${";
94
95
96 public static final String DEFAULT_END_TOKEN = "}";
97
98
99 private boolean interpolateWithPrefixPattern = true;
100
101 private String escapeString;
102
103 private boolean useEscape = false;
104
105
106 private boolean preserveEscapeString = false;
107
108 private LinkedHashSet<DelimiterSpecification> delimiters = new LinkedHashSet<DelimiterSpecification>();
109
110 private DelimiterSpecification currentSpec;
111
112 private String beginToken;
113
114 private String originalBeginToken;
115
116 private String endToken;
117
118
119
120
121
122
123 public MultiDelimiterInterpolatorFilterReader(Reader in, Interpolator interpolator) {
124 this(in, interpolator, new SimpleRecursionInterceptor());
125 }
126
127
128
129
130
131
132
133 public MultiDelimiterInterpolatorFilterReader(Reader in, Interpolator interpolator, RecursionInterceptor ri) {
134 super(in);
135
136 this.interpolator = interpolator;
137
138
139 this.interpolator.setCacheAnswers(true);
140
141 recursionInterceptor = ri;
142
143 delimiters.add(DelimiterSpecification.DEFAULT_SPEC);
144 }
145
146 public MultiDelimiterInterpolatorFilterReader addDelimiterSpec(String delimiterSpec) {
147 if (delimiterSpec == null) {
148 return this;
149 }
150 delimiters.add(DelimiterSpecification.parse(delimiterSpec));
151 return this;
152 }
153
154 public boolean removeDelimiterSpec(String delimiterSpec) {
155 if (delimiterSpec == null) {
156 return false;
157 }
158 return delimiters.remove(DelimiterSpecification.parse(delimiterSpec));
159 }
160
161 public MultiDelimiterInterpolatorFilterReader setDelimiterSpecs(LinkedHashSet<String> specs) {
162 delimiters.clear();
163 for (String spec : specs) {
164 if (spec == null) {
165 continue;
166 }
167 delimiters.add(DelimiterSpecification.parse(spec));
168 }
169
170 return this;
171 }
172
173
174
175
176
177
178
179
180
181
182 public long skip(long n) throws IOException {
183 if (n < 0L) {
184 throw new IllegalArgumentException("skip value is negative");
185 }
186
187 for (long i = 0; i < n; i++) {
188 if (read() == -1) {
189 return i;
190 }
191 }
192 return n;
193 }
194
195
196
197
198
199
200
201
202
203
204
205 public int read(char cbuf[], int off, int len) throws IOException {
206 for (int i = 0; i < len; i++) {
207 int ch = read();
208 if (ch == -1) {
209 if (i == 0) {
210 return -1;
211 } else {
212 return i;
213 }
214 }
215 cbuf[off + i] = (char) ch;
216 }
217 return len;
218 }
219
220
221
222
223
224
225
226 public int read() throws IOException {
227 if (replaceIndex != -1 && replaceIndex < replaceData.length()) {
228 int ch = replaceData.charAt(replaceIndex++);
229 if (replaceIndex >= replaceData.length()) {
230 replaceIndex = -1;
231 }
232 return ch;
233 }
234
235 int ch = -1;
236 if (previousIndex != -1 && previousIndex < this.endToken.length()) {
237 ch = this.endToken.charAt(previousIndex++);
238 } else {
239 ch = in.read();
240 }
241
242 boolean inEscape = false;
243
244 if ((inEscape = (useEscape && ch == escapeString.charAt(0))) || reselectDelimiterSpec(ch)) {
245 StringBuilder key = new StringBuilder();
246
247 key.append((char) ch);
248
249
250 boolean atEnd = false;
251
252 if (inEscape) {
253 for (int i = 0; i < escapeString.length() - 1; i++) {
254 ch = in.read();
255 if (ch == -1) {
256 atEnd = true;
257 break;
258 }
259
260 key.append((char) ch);
261 }
262
263 if (!atEnd) {
264 ch = in.read();
265 if (!reselectDelimiterSpec(ch)) {
266 replaceData = key.toString();
267 replaceIndex = 1;
268 return replaceData.charAt(0);
269 } else {
270 key.append((char) ch);
271 }
272 }
273 }
274
275 int beginTokenMatchPos = 1;
276 do {
277 if (atEnd) {
278
279 break;
280 }
281
282 if (previousIndex != -1 && previousIndex < this.endToken.length()) {
283 ch = this.endToken.charAt(previousIndex++);
284 } else {
285 ch = in.read();
286 }
287 if (ch != -1) {
288 key.append((char) ch);
289 if ((beginTokenMatchPos < this.originalBeginToken.length())
290 && (ch != this.originalBeginToken.charAt(beginTokenMatchPos))) {
291 ch = -1;
292 break;
293 }
294 } else {
295 break;
296 }
297
298 beginTokenMatchPos++;
299 } while (ch != this.endToken.charAt(0));
300
301
302 if (ch != -1 && this.endToken.length() > 1) {
303 int endTokenMatchPos = 1;
304
305 do {
306 if (previousIndex != -1 && previousIndex < this.endToken.length()) {
307 ch = this.endToken.charAt(previousIndex++);
308 } else {
309 ch = in.read();
310 }
311
312 if (ch != -1) {
313 key.append((char) ch);
314
315 if (ch != this.endToken.charAt(endTokenMatchPos++)) {
316 ch = -1;
317 break;
318 }
319
320 } else {
321 break;
322 }
323 } while (endTokenMatchPos < this.endToken.length());
324 }
325
326
327
328
329 if (ch == -1) {
330 replaceData = key.toString();
331 replaceIndex = 1;
332 return replaceData.charAt(0);
333 }
334
335 String value = null;
336 try {
337 boolean escapeFound = false;
338 if (useEscape) {
339 if (key.toString().startsWith(beginToken)) {
340 String keyStr = key.toString();
341 if (!preserveEscapeString) {
342 value = keyStr.substring(escapeString.length(), keyStr.length());
343 } else {
344 value = keyStr;
345 }
346 escapeFound = true;
347 }
348 }
349 if (!escapeFound) {
350 if (interpolateWithPrefixPattern) {
351 value = interpolator.interpolate(key.toString(), "", recursionInterceptor);
352 } else {
353 value = interpolator.interpolate(key.toString(), recursionInterceptor);
354 }
355 }
356 } catch (InterpolationException e) {
357 IllegalArgumentException error = new IllegalArgumentException(e.getMessage());
358 error.initCause(e);
359
360 throw error;
361 }
362
363 if (value != null) {
364 if (value.length() != 0) {
365 replaceData = value;
366 replaceIndex = 0;
367 }
368 return read();
369 } else {
370 previousIndex = 0;
371 replaceData = key.substring(0, key.length() - this.endToken.length());
372 replaceIndex = 0;
373 return this.beginToken.charAt(0);
374 }
375 }
376
377 return ch;
378 }
379
380 private boolean reselectDelimiterSpec(int ch) {
381 for (DelimiterSpecification spec : delimiters) {
382 if (ch == spec.getBegin().charAt(0)) {
383 currentSpec = spec;
384 originalBeginToken = currentSpec.getBegin();
385 beginToken = useEscape ? escapeString + originalBeginToken : originalBeginToken;
386 endToken = currentSpec.getEnd();
387
388 return true;
389 }
390 }
391
392 return false;
393 }
394
395 public boolean isInterpolateWithPrefixPattern() {
396 return interpolateWithPrefixPattern;
397 }
398
399 public void setInterpolateWithPrefixPattern(boolean interpolateWithPrefixPattern) {
400 this.interpolateWithPrefixPattern = interpolateWithPrefixPattern;
401 }
402
403 public String getEscapeString() {
404 return escapeString;
405 }
406
407 public void setEscapeString(String escapeString) {
408
409 if (escapeString != null && escapeString.length() >= 1) {
410 this.escapeString = escapeString;
411 this.useEscape = escapeString != null && escapeString.length() >= 1;
412 }
413 }
414
415 public boolean isPreserveEscapeString() {
416 return preserveEscapeString;
417 }
418
419 public void setPreserveEscapeString(boolean preserveEscapeString) {
420 this.preserveEscapeString = preserveEscapeString;
421 }
422
423 public RecursionInterceptor getRecursionInterceptor() {
424 return recursionInterceptor;
425 }
426
427 public MultiDelimiterInterpolatorFilterReader setRecursionInterceptor(RecursionInterceptor recursionInterceptor) {
428 this.recursionInterceptor = recursionInterceptor;
429 return this;
430 }
431 }