1 package org.codehaus.plexus.util;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 import org.codehaus.plexus.util.reflection.Reflector;
20 import org.codehaus.plexus.util.reflection.ReflectorException;
21
22 import java.io.FilterReader;
23 import java.io.IOException;
24 import java.io.PushbackReader;
25 import java.io.Reader;
26 import java.util.Collections;
27 import java.util.HashSet;
28 import java.util.Map;
29 import java.util.Set;
30 import java.util.TreeMap;
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 public class LineOrientedInterpolatingReader
56 extends FilterReader
57 {
58 public static final String DEFAULT_START_DELIM = "${";
59
60 public static final String DEFAULT_END_DELIM = "}";
61
62 public static final String DEFAULT_ESCAPE_SEQ = "\\";
63
64 private static final char CARRIAGE_RETURN_CHAR = '\r';
65
66 private static final char NEWLINE_CHAR = '\n';
67
68 private final PushbackReader pushbackReader;
69
70 private final Map<String, Object> context;
71
72 private final String startDelim;
73
74 private final String endDelim;
75
76 private final String escapeSeq;
77
78 private final int minExpressionSize;
79
80 private final Reflector reflector;
81
82 private int lineIdx = -1;
83
84 private String line;
85
86
87
88
89
90
91
92
93
94
95 public LineOrientedInterpolatingReader( Reader reader, Map<String, ?> context, String startDelim, String endDelim,
96 String escapeSeq )
97 {
98 super( reader );
99
100 this.startDelim = startDelim;
101
102 this.endDelim = endDelim;
103
104 this.escapeSeq = escapeSeq;
105
106
107 this.minExpressionSize = startDelim.length() + endDelim.length() + 1;
108
109 this.context = Collections.unmodifiableMap( context );
110
111 this.reflector = new Reflector();
112
113 if ( reader instanceof PushbackReader )
114 {
115 this.pushbackReader = (PushbackReader) reader;
116 }
117 else
118 {
119 this.pushbackReader = new PushbackReader( reader, 1 );
120 }
121 }
122
123
124
125
126
127
128
129
130
131 public LineOrientedInterpolatingReader( Reader reader, Map<String, ?> context, String startDelim, String endDelim )
132 {
133 this( reader, context, startDelim, endDelim, DEFAULT_ESCAPE_SEQ );
134 }
135
136
137
138
139
140
141
142 public LineOrientedInterpolatingReader( Reader reader, Map<String, ?> context )
143 {
144 this( reader, context, DEFAULT_START_DELIM, DEFAULT_END_DELIM, DEFAULT_ESCAPE_SEQ );
145 }
146
147 @Override
148 public int read()
149 throws IOException
150 {
151 if ( line == null || lineIdx >= line.length() )
152 {
153 readAndInterpolateLine();
154 }
155
156 int next = -1;
157
158 if ( line != null && lineIdx < line.length() )
159 {
160 next = line.charAt( lineIdx++ );
161 }
162
163 return next;
164 }
165
166 @Override
167 public int read( char[] cbuf, int off, int len )
168 throws IOException
169 {
170 int fillCount = 0;
171
172 for ( int i = off; i < off + len; i++ )
173 {
174 int next = read();
175 if ( next > -1 )
176 {
177 cbuf[i] = (char) next;
178 }
179 else
180 {
181 break;
182 }
183
184 fillCount++;
185 }
186
187 if ( fillCount == 0 )
188 {
189 fillCount = -1;
190 }
191
192 return fillCount;
193 }
194
195 @Override
196 public long skip( long n )
197 throws IOException
198 {
199 long skipCount = 0;
200
201 for ( long i = 0; i < n; i++ )
202 {
203 int next = read();
204
205 if ( next < 0 )
206 {
207 break;
208 }
209
210 skipCount++;
211 }
212
213 return skipCount;
214 }
215
216 private void readAndInterpolateLine()
217 throws IOException
218 {
219 String rawLine = readLine();
220
221 if ( rawLine != null )
222 {
223 Set<String> expressions = parseForExpressions( rawLine );
224
225 Map<String, Object> evaluatedExpressions = evaluateExpressions( expressions );
226
227 String interpolated = replaceWithInterpolatedValues( rawLine, evaluatedExpressions );
228
229 if ( interpolated != null && interpolated.length() > 0 )
230 {
231 line = interpolated;
232 lineIdx = 0;
233 }
234 }
235 else
236 {
237 line = null;
238 lineIdx = -1;
239 }
240 }
241
242
243
244
245
246 private String readLine()
247 throws IOException
248 {
249 StringBuilder lineBuffer = new StringBuilder( 40 );
250 int next;
251
252 boolean lastWasCR = false;
253 while ( ( next = pushbackReader.read() ) > -1 )
254 {
255 char c = (char) next;
256
257 if ( c == CARRIAGE_RETURN_CHAR )
258 {
259 lastWasCR = true;
260 lineBuffer.append( c );
261 }
262 else if ( c == NEWLINE_CHAR )
263 {
264 lineBuffer.append( c );
265 break;
266 }
267 else if ( lastWasCR )
268 {
269 pushbackReader.unread( c );
270 break;
271 }
272 else
273 {
274 lineBuffer.append( c );
275 }
276 }
277
278 if ( lineBuffer.length() < 1 )
279 {
280 return null;
281 }
282 else
283 {
284 return lineBuffer.toString();
285 }
286 }
287
288 private String replaceWithInterpolatedValues( String rawLine, Map<String, Object> evaluatedExpressions )
289 {
290 String result = rawLine;
291
292 for ( Object o : evaluatedExpressions.entrySet() )
293 {
294 Map.Entry entry = (Map.Entry) o;
295
296 String expression = (String) entry.getKey();
297
298 String value = String.valueOf( entry.getValue() );
299
300 result = findAndReplaceUnlessEscaped( result, expression, value );
301 }
302
303 return result;
304 }
305
306 private Map<String, Object> evaluateExpressions( Set<String> expressions )
307 {
308 Map<String, Object> evaluated = new TreeMap<String, Object>();
309
310 for ( Object expression : expressions )
311 {
312 String rawExpression = (String) expression;
313
314 String realExpression =
315 rawExpression.substring( startDelim.length(), rawExpression.length() - endDelim.length() );
316
317 String[] parts = realExpression.split( "\\." );
318 if ( parts.length > 0 )
319 {
320 Object value = context.get( parts[0] );
321
322 if ( value != null )
323 {
324 for ( int i = 1; i < parts.length; i++ )
325 {
326 try
327 {
328 value = reflector.getObjectProperty( value, parts[i] );
329
330 if ( value == null )
331 {
332 break;
333 }
334 }
335 catch ( ReflectorException e )
336 {
337
338 e.printStackTrace();
339
340 break;
341 }
342 }
343
344 evaluated.put( rawExpression, value );
345 }
346 }
347 }
348
349 return evaluated;
350 }
351
352 private Set<String> parseForExpressions( String rawLine )
353 {
354 Set<String> expressions = new HashSet<String>();
355
356 if ( rawLine != null )
357 {
358 int placeholder = -1;
359
360 do
361 {
362
363 int start = findDelimiter( rawLine, startDelim, placeholder );
364
365
366 if ( start < 0 )
367 {
368
369 break;
370 }
371
372
373 int end = findDelimiter( rawLine, endDelim, start + 1 );
374
375
376 if ( end < 0 )
377 {
378
379 break;
380 }
381
382
383
384
385 expressions.add( rawLine.substring( start, end + endDelim.length() ) );
386
387
388 placeholder = end + 1;
389 }
390 while ( placeholder < rawLine.length() - minExpressionSize );
391 }
392
393 return expressions;
394 }
395
396 private int findDelimiter( String rawLine, String delimiter, int lastPos )
397 {
398 int placeholder = lastPos;
399
400 int position;
401 do
402 {
403 position = rawLine.indexOf( delimiter, placeholder );
404
405 if ( position < 0 )
406 {
407 break;
408 }
409 else
410 {
411 int escEndIdx = rawLine.indexOf( escapeSeq, placeholder ) + escapeSeq.length();
412
413 if ( escEndIdx > escapeSeq.length() - 1 && escEndIdx == position )
414 {
415 placeholder = position + 1;
416 position = -1;
417 }
418 }
419
420 }
421 while ( position < 0 && placeholder < rawLine.length() - endDelim.length() );
422
423
424
425 return position;
426 }
427
428 private String findAndReplaceUnlessEscaped( String rawLine, String search, String replace )
429 {
430 StringBuilder lineBuffer = new StringBuilder( (int) ( rawLine.length() * 1.5 ) );
431
432 int lastReplacement = -1;
433
434 do
435 {
436 int nextReplacement = rawLine.indexOf( search, lastReplacement + 1 );
437 if ( nextReplacement > -1 )
438 {
439 if ( lastReplacement < 0 )
440 {
441 lastReplacement = 0;
442 }
443
444 lineBuffer.append( rawLine, lastReplacement, nextReplacement );
445
446 int escIdx = rawLine.indexOf( escapeSeq, lastReplacement + 1 );
447 if ( escIdx > -1 && escIdx + escapeSeq.length() == nextReplacement )
448 {
449 lineBuffer.setLength( lineBuffer.length() - escapeSeq.length() );
450 lineBuffer.append( search );
451 }
452 else
453 {
454 lineBuffer.append( replace );
455 }
456
457 lastReplacement = nextReplacement + search.length();
458 }
459 else
460 {
461 break;
462 }
463 }
464 while ( lastReplacement > -1 );
465
466 if ( lastReplacement < rawLine.length() )
467 {
468 lineBuffer.append( rawLine, lastReplacement, rawLine.length() );
469 }
470
471 return lineBuffer.toString();
472 }
473
474 }