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