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