001 /*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements. See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License. You may obtain a copy of the License at
008 *
009 * http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017 package org.apache.commons.lang.text;
018
019 import java.util.ArrayList;
020 import java.util.Enumeration;
021 import java.util.HashMap;
022 import java.util.List;
023 import java.util.Map;
024 import java.util.Properties;
025
026 /**
027 * Substitutes variables within a string by values.
028 * <p>
029 * This class takes a piece of text and substitutes all the variables within it.
030 * The default definition of a variable is <code>${variableName}</code>.
031 * The prefix and suffix can be changed via constructors and set methods.
032 * <p>
033 * Variable values are typically resolved from a map, but could also be resolved
034 * from system properties, or by supplying a custom variable resolver.
035 * <p>
036 * The simplest example is to use this class to replace Java System properties. For example:
037 * <pre>
038 * StrSubstitutor.replaceSystemProperties(
039 * "You are running with java.version = ${java.version} and os.name = ${os.name}.");
040 * </pre>
041 * <p>
042 * Typical usage of this class follows the following pattern: First an instance is created
043 * and initialized with the map that contains the values for the available variables.
044 * If a prefix and/or suffix for variables should be used other than the default ones,
045 * the appropriate settings can be performed. After that the <code>replace()</code>
046 * method can be called passing in the source text for interpolation. In the returned
047 * text all variable references (as long as their values are known) will be resolved.
048 * The following example demonstrates this:
049 * <pre>
050 * Map valuesMap = HashMap();
051 * valuesMap.put("animal", "quick brown fox");
052 * valuesMap.put("target", "lazy dog");
053 * String templateString = "The ${animal} jumped over the ${target}.";
054 * StrSubstitutor sub = new StrSubstitutor(valuesMap);
055 * String resolvedString = sub.replace(templateString);
056 * </pre>
057 * yielding:
058 * <pre>
059 * The quick brown fox jumped over the lazy dog.
060 * </pre>
061 * <p>
062 * In addition to this usage pattern there are some static convenience methods that
063 * cover the most common use cases. These methods can be used without the need of
064 * manually creating an instance. However if multiple replace operations are to be
065 * performed, creating and reusing an instance of this class will be more efficient.
066 * <p>
067 * Variable replacement works in a recursive way. Thus, if a variable value contains
068 * a variable then that variable will also be replaced. Cyclic replacements are
069 * detected and will cause an exception to be thrown.
070 * <p>
071 * Sometimes the interpolation's result must contain a variable prefix. As an example
072 * take the following source text:
073 * <pre>
074 * The variable ${${name}} must be used.
075 * </pre>
076 * Here only the variable's name referred to in the text should be replaced resulting
077 * in the text (assuming that the value of the <code>name</code> variable is <code>x</code>):
078 * <pre>
079 * The variable ${x} must be used.
080 * </pre>
081 * To achieve this effect there are two possibilities: Either set a different prefix
082 * and suffix for variables which do not conflict with the result text you want to
083 * produce. The other possibility is to use the escape character, by default '$'.
084 * If this character is placed before a variable reference, this reference is ignored
085 * and won't be replaced. For example:
086 * <pre>
087 * The variable $${${name}} must be used.
088 * </pre>
089 * <p>
090 * In some complex scenarios you might even want to perform substitution in the
091 * names of variables, for instance
092 * <pre>
093 * ${jre-${java.specification.version}}
094 * </pre>
095 * <code>StrSubstitutor</code> supports this recursive substitution in variable
096 * names, but it has to be enabled explicitly by setting the
097 * {@link #setEnableSubstitutionInVariables(boolean) enableSubstitutionInVariables}
098 * property to <b>true</b>.
099 *
100 * @author Apache Software Foundation
101 * @author Oliver Heger
102 * @version $Id: StrSubstitutor.java 1057354 2011-01-10 20:48:47Z niallp $
103 * @since 2.2
104 */
105 public class StrSubstitutor {
106
107 /**
108 * Constant for the default escape character.
109 */
110 public static final char DEFAULT_ESCAPE = '$';
111 /**
112 * Constant for the default variable prefix.
113 */
114 public static final StrMatcher DEFAULT_PREFIX = StrMatcher.stringMatcher("${");
115 /**
116 * Constant for the default variable suffix.
117 */
118 public static final StrMatcher DEFAULT_SUFFIX = StrMatcher.stringMatcher("}");
119
120 /**
121 * Stores the escape character.
122 */
123 private char escapeChar;
124 /**
125 * Stores the variable prefix.
126 */
127 private StrMatcher prefixMatcher;
128 /**
129 * Stores the variable suffix.
130 */
131 private StrMatcher suffixMatcher;
132 /**
133 * Variable resolution is delegated to an implementor of VariableResolver.
134 */
135 private StrLookup variableResolver;
136 /**
137 * The flag whether substitution in variable names is enabled.
138 */
139 private boolean enableSubstitutionInVariables;
140
141 //-----------------------------------------------------------------------
142 /**
143 * Replaces all the occurrences of variables in the given source object with
144 * their matching values from the map.
145 *
146 * @param source the source text containing the variables to substitute, null returns null
147 * @param valueMap the map with the values, may be null
148 * @return the result of the replace operation
149 */
150 public static String replace(Object source, Map valueMap) {
151 return new StrSubstitutor(valueMap).replace(source);
152 }
153
154 /**
155 * Replaces all the occurrences of variables in the given source object with
156 * their matching values from the map. This method allows to specifiy a
157 * custom variable prefix and suffix
158 *
159 * @param source the source text containing the variables to substitute, null returns null
160 * @param valueMap the map with the values, may be null
161 * @param prefix the prefix of variables, not null
162 * @param suffix the suffix of variables, not null
163 * @return the result of the replace operation
164 * @throws IllegalArgumentException if the prefix or suffix is null
165 */
166 public static String replace(Object source, Map valueMap, String prefix, String suffix) {
167 return new StrSubstitutor(valueMap, prefix, suffix).replace(source);
168 }
169
170 /**
171 * Replaces all the occurrences of variables in the given source object with their matching
172 * values from the properties.
173 *
174 * @param source the source text containing the variables to substitute, null returns null
175 * @param valueProperties the properties with values, may be null
176 * @return the result of the replace operation
177 * @since 2.6
178 */
179 public static String replace(Object source, Properties valueProperties)
180 {
181 if (valueProperties == null) {
182 return source.toString();
183 }
184 Map valueMap = new HashMap();
185 Enumeration propNames = valueProperties.propertyNames();
186 while (propNames.hasMoreElements())
187 {
188 String propName = (String)propNames.nextElement();
189 String propValue = valueProperties.getProperty(propName);
190 valueMap.put(propName, propValue);
191 }
192 return StrSubstitutor.replace(source, valueMap);
193 }
194
195 /**
196 * Replaces all the occurrences of variables in the given source object with
197 * their matching values from the system properties.
198 *
199 * @param source the source text containing the variables to substitute, null returns null
200 * @return the result of the replace operation
201 */
202 public static String replaceSystemProperties(Object source) {
203 return new StrSubstitutor(StrLookup.systemPropertiesLookup()).replace(source);
204 }
205
206 //-----------------------------------------------------------------------
207 /**
208 * Creates a new instance with defaults for variable prefix and suffix
209 * and the escaping character.
210 */
211 public StrSubstitutor() {
212 this((StrLookup) null, DEFAULT_PREFIX, DEFAULT_SUFFIX, DEFAULT_ESCAPE);
213 }
214
215 /**
216 * Creates a new instance and initializes it. Uses defaults for variable
217 * prefix and suffix and the escaping character.
218 *
219 * @param valueMap the map with the variables' values, may be null
220 */
221 public StrSubstitutor(Map valueMap) {
222 this(StrLookup.mapLookup(valueMap), DEFAULT_PREFIX, DEFAULT_SUFFIX, DEFAULT_ESCAPE);
223 }
224
225 /**
226 * Creates a new instance and initializes it. Uses a default escaping character.
227 *
228 * @param valueMap the map with the variables' values, may be null
229 * @param prefix the prefix for variables, not null
230 * @param suffix the suffix for variables, not null
231 * @throws IllegalArgumentException if the prefix or suffix is null
232 */
233 public StrSubstitutor(Map valueMap, String prefix, String suffix) {
234 this(StrLookup.mapLookup(valueMap), prefix, suffix, DEFAULT_ESCAPE);
235 }
236
237 /**
238 * Creates a new instance and initializes it.
239 *
240 * @param valueMap the map with the variables' values, may be null
241 * @param prefix the prefix for variables, not null
242 * @param suffix the suffix for variables, not null
243 * @param escape the escape character
244 * @throws IllegalArgumentException if the prefix or suffix is null
245 */
246 public StrSubstitutor(Map valueMap, String prefix, String suffix, char escape) {
247 this(StrLookup.mapLookup(valueMap), prefix, suffix, escape);
248 }
249
250 /**
251 * Creates a new instance and initializes it.
252 *
253 * @param variableResolver the variable resolver, may be null
254 */
255 public StrSubstitutor(StrLookup variableResolver) {
256 this(variableResolver, DEFAULT_PREFIX, DEFAULT_SUFFIX, DEFAULT_ESCAPE);
257 }
258
259 /**
260 * Creates a new instance and initializes it.
261 *
262 * @param variableResolver the variable resolver, may be null
263 * @param prefix the prefix for variables, not null
264 * @param suffix the suffix for variables, not null
265 * @param escape the escape character
266 * @throws IllegalArgumentException if the prefix or suffix is null
267 */
268 public StrSubstitutor(StrLookup variableResolver, String prefix, String suffix, char escape) {
269 this.setVariableResolver(variableResolver);
270 this.setVariablePrefix(prefix);
271 this.setVariableSuffix(suffix);
272 this.setEscapeChar(escape);
273 }
274
275 /**
276 * Creates a new instance and initializes it.
277 *
278 * @param variableResolver the variable resolver, may be null
279 * @param prefixMatcher the prefix for variables, not null
280 * @param suffixMatcher the suffix for variables, not null
281 * @param escape the escape character
282 * @throws IllegalArgumentException if the prefix or suffix is null
283 */
284 public StrSubstitutor(
285 StrLookup variableResolver, StrMatcher prefixMatcher, StrMatcher suffixMatcher, char escape) {
286 this.setVariableResolver(variableResolver);
287 this.setVariablePrefixMatcher(prefixMatcher);
288 this.setVariableSuffixMatcher(suffixMatcher);
289 this.setEscapeChar(escape);
290 }
291
292 //-----------------------------------------------------------------------
293 /**
294 * Replaces all the occurrences of variables with their matching values
295 * from the resolver using the given source string as a template.
296 *
297 * @param source the string to replace in, null returns null
298 * @return the result of the replace operation
299 */
300 public String replace(String source) {
301 if (source == null) {
302 return null;
303 }
304 StrBuilder buf = new StrBuilder(source);
305 if (substitute(buf, 0, source.length()) == false) {
306 return source;
307 }
308 return buf.toString();
309 }
310
311 /**
312 * Replaces all the occurrences of variables with their matching values
313 * from the resolver using the given source string as a template.
314 * <p>
315 * Only the specified portion of the string will be processed.
316 * The rest of the string is not processed, and is not returned.
317 *
318 * @param source the string to replace in, null returns null
319 * @param offset the start offset within the array, must be valid
320 * @param length the length within the array to be processed, must be valid
321 * @return the result of the replace operation
322 */
323 public String replace(String source, int offset, int length) {
324 if (source == null) {
325 return null;
326 }
327 StrBuilder buf = new StrBuilder(length).append(source, offset, length);
328 if (substitute(buf, 0, length) == false) {
329 return source.substring(offset, offset + length);
330 }
331 return buf.toString();
332 }
333
334 //-----------------------------------------------------------------------
335 /**
336 * Replaces all the occurrences of variables with their matching values
337 * from the resolver using the given source array as a template.
338 * The array is not altered by this method.
339 *
340 * @param source the character array to replace in, not altered, null returns null
341 * @return the result of the replace operation
342 */
343 public String replace(char[] source) {
344 if (source == null) {
345 return null;
346 }
347 StrBuilder buf = new StrBuilder(source.length).append(source);
348 substitute(buf, 0, source.length);
349 return buf.toString();
350 }
351
352 /**
353 * Replaces all the occurrences of variables with their matching values
354 * from the resolver using the given source array as a template.
355 * The array is not altered by this method.
356 * <p>
357 * Only the specified portion of the array will be processed.
358 * The rest of the array is not processed, and is not returned.
359 *
360 * @param source the character array to replace in, not altered, null returns null
361 * @param offset the start offset within the array, must be valid
362 * @param length the length within the array to be processed, must be valid
363 * @return the result of the replace operation
364 */
365 public String replace(char[] source, int offset, int length) {
366 if (source == null) {
367 return null;
368 }
369 StrBuilder buf = new StrBuilder(length).append(source, offset, length);
370 substitute(buf, 0, length);
371 return buf.toString();
372 }
373
374 //-----------------------------------------------------------------------
375 /**
376 * Replaces all the occurrences of variables with their matching values
377 * from the resolver using the given source buffer as a template.
378 * The buffer is not altered by this method.
379 *
380 * @param source the buffer to use as a template, not changed, null returns null
381 * @return the result of the replace operation
382 */
383 public String replace(StringBuffer source) {
384 if (source == null) {
385 return null;
386 }
387 StrBuilder buf = new StrBuilder(source.length()).append(source);
388 substitute(buf, 0, buf.length());
389 return buf.toString();
390 }
391
392 /**
393 * Replaces all the occurrences of variables with their matching values
394 * from the resolver using the given source buffer as a template.
395 * The buffer is not altered by this method.
396 * <p>
397 * Only the specified portion of the buffer will be processed.
398 * The rest of the buffer is not processed, and is not returned.
399 *
400 * @param source the buffer to use as a template, not changed, null returns null
401 * @param offset the start offset within the array, must be valid
402 * @param length the length within the array to be processed, must be valid
403 * @return the result of the replace operation
404 */
405 public String replace(StringBuffer source, int offset, int length) {
406 if (source == null) {
407 return null;
408 }
409 StrBuilder buf = new StrBuilder(length).append(source, offset, length);
410 substitute(buf, 0, length);
411 return buf.toString();
412 }
413
414 //-----------------------------------------------------------------------
415 /**
416 * Replaces all the occurrences of variables with their matching values
417 * from the resolver using the given source builder as a template.
418 * The builder is not altered by this method.
419 *
420 * @param source the builder to use as a template, not changed, null returns null
421 * @return the result of the replace operation
422 */
423 public String replace(StrBuilder source) {
424 if (source == null) {
425 return null;
426 }
427 StrBuilder buf = new StrBuilder(source.length()).append(source);
428 substitute(buf, 0, buf.length());
429 return buf.toString();
430 }
431
432 /**
433 * Replaces all the occurrences of variables with their matching values
434 * from the resolver using the given source builder as a template.
435 * The builder is not altered by this method.
436 * <p>
437 * Only the specified portion of the builder will be processed.
438 * The rest of the builder is not processed, and is not returned.
439 *
440 * @param source the builder to use as a template, not changed, null returns null
441 * @param offset the start offset within the array, must be valid
442 * @param length the length within the array to be processed, must be valid
443 * @return the result of the replace operation
444 */
445 public String replace(StrBuilder source, int offset, int length) {
446 if (source == null) {
447 return null;
448 }
449 StrBuilder buf = new StrBuilder(length).append(source, offset, length);
450 substitute(buf, 0, length);
451 return buf.toString();
452 }
453
454 //-----------------------------------------------------------------------
455 /**
456 * Replaces all the occurrences of variables in the given source object with
457 * their matching values from the resolver. The input source object is
458 * converted to a string using <code>toString</code> and is not altered.
459 *
460 * @param source the source to replace in, null returns null
461 * @return the result of the replace operation
462 */
463 public String replace(Object source) {
464 if (source == null) {
465 return null;
466 }
467 StrBuilder buf = new StrBuilder().append(source);
468 substitute(buf, 0, buf.length());
469 return buf.toString();
470 }
471
472 //-----------------------------------------------------------------------
473 /**
474 * Replaces all the occurrences of variables within the given source buffer
475 * with their matching values from the resolver.
476 * The buffer is updated with the result.
477 *
478 * @param source the buffer to replace in, updated, null returns zero
479 * @return true if altered
480 */
481 public boolean replaceIn(StringBuffer source) {
482 if (source == null) {
483 return false;
484 }
485 return replaceIn(source, 0, source.length());
486 }
487
488 /**
489 * Replaces all the occurrences of variables within the given source buffer
490 * with their matching values from the resolver.
491 * The buffer is updated with the result.
492 * <p>
493 * Only the specified portion of the buffer will be processed.
494 * The rest of the buffer is not processed, but it is not deleted.
495 *
496 * @param source the buffer to replace in, updated, null returns zero
497 * @param offset the start offset within the array, must be valid
498 * @param length the length within the buffer to be processed, must be valid
499 * @return true if altered
500 */
501 public boolean replaceIn(StringBuffer source, int offset, int length) {
502 if (source == null) {
503 return false;
504 }
505 StrBuilder buf = new StrBuilder(length).append(source, offset, length);
506 if (substitute(buf, 0, length) == false) {
507 return false;
508 }
509 source.replace(offset, offset + length, buf.toString());
510 return true;
511 }
512
513 //-----------------------------------------------------------------------
514 /**
515 * Replaces all the occurrences of variables within the given source
516 * builder with their matching values from the resolver.
517 *
518 * @param source the builder to replace in, updated, null returns zero
519 * @return true if altered
520 */
521 public boolean replaceIn(StrBuilder source) {
522 if (source == null) {
523 return false;
524 }
525 return substitute(source, 0, source.length());
526 }
527
528 /**
529 * Replaces all the occurrences of variables within the given source
530 * builder with their matching values from the resolver.
531 * <p>
532 * Only the specified portion of the builder will be processed.
533 * The rest of the builder is not processed, but it is not deleted.
534 *
535 * @param source the builder to replace in, null returns zero
536 * @param offset the start offset within the array, must be valid
537 * @param length the length within the builder to be processed, must be valid
538 * @return true if altered
539 */
540 public boolean replaceIn(StrBuilder source, int offset, int length) {
541 if (source == null) {
542 return false;
543 }
544 return substitute(source, offset, length);
545 }
546
547 //-----------------------------------------------------------------------
548 /**
549 * Internal method that substitutes the variables.
550 * <p>
551 * Most users of this class do not need to call this method. This method will
552 * be called automatically by another (public) method.
553 * <p>
554 * Writers of subclasses can override this method if they need access to
555 * the substitution process at the start or end.
556 *
557 * @param buf the string builder to substitute into, not null
558 * @param offset the start offset within the builder, must be valid
559 * @param length the length within the builder to be processed, must be valid
560 * @return true if altered
561 */
562 protected boolean substitute(StrBuilder buf, int offset, int length) {
563 return substitute(buf, offset, length, null) > 0;
564 }
565
566 /**
567 * Recursive handler for multiple levels of interpolation. This is the main
568 * interpolation method, which resolves the values of all variable references
569 * contained in the passed in text.
570 *
571 * @param buf the string builder to substitute into, not null
572 * @param offset the start offset within the builder, must be valid
573 * @param length the length within the builder to be processed, must be valid
574 * @param priorVariables the stack keeping track of the replaced variables, may be null
575 * @return the length change that occurs, unless priorVariables is null when the int
576 * represents a boolean flag as to whether any change occurred.
577 */
578 private int substitute(StrBuilder buf, int offset, int length, List priorVariables) {
579 StrMatcher prefixMatcher = getVariablePrefixMatcher();
580 StrMatcher suffixMatcher = getVariableSuffixMatcher();
581 char escape = getEscapeChar();
582
583 boolean top = (priorVariables == null);
584 boolean altered = false;
585 int lengthChange = 0;
586 char[] chars = buf.buffer;
587 int bufEnd = offset + length;
588 int pos = offset;
589 while (pos < bufEnd) {
590 int startMatchLen = prefixMatcher.isMatch(chars, pos, offset,
591 bufEnd);
592 if (startMatchLen == 0) {
593 pos++;
594 } else {
595 // found variable start marker
596 if (pos > offset && chars[pos - 1] == escape) {
597 // escaped
598 buf.deleteCharAt(pos - 1);
599 chars = buf.buffer; // in case buffer was altered
600 lengthChange--;
601 altered = true;
602 bufEnd--;
603 } else {
604 // find suffix
605 int startPos = pos;
606 pos += startMatchLen;
607 int endMatchLen = 0;
608 int nestedVarCount = 0;
609 while (pos < bufEnd) {
610 if (isEnableSubstitutionInVariables()
611 && (endMatchLen = prefixMatcher.isMatch(chars,
612 pos, offset, bufEnd)) != 0) {
613 // found a nested variable start
614 nestedVarCount++;
615 pos += endMatchLen;
616 continue;
617 }
618
619 endMatchLen = suffixMatcher.isMatch(chars, pos, offset,
620 bufEnd);
621 if (endMatchLen == 0) {
622 pos++;
623 } else {
624 // found variable end marker
625 if (nestedVarCount == 0) {
626 String varName = new String(chars, startPos
627 + startMatchLen, pos - startPos
628 - startMatchLen);
629 if (isEnableSubstitutionInVariables()) {
630 StrBuilder bufName = new StrBuilder(varName);
631 substitute(bufName, 0, bufName.length());
632 varName = bufName.toString();
633 }
634 pos += endMatchLen;
635 int endPos = pos;
636
637 // on the first call initialize priorVariables
638 if (priorVariables == null) {
639 priorVariables = new ArrayList();
640 priorVariables.add(new String(chars,
641 offset, length));
642 }
643
644 // handle cyclic substitution
645 checkCyclicSubstitution(varName, priorVariables);
646 priorVariables.add(varName);
647
648 // resolve the variable
649 String varValue = resolveVariable(varName, buf,
650 startPos, endPos);
651 if (varValue != null) {
652 // recursive replace
653 int varLen = varValue.length();
654 buf.replace(startPos, endPos, varValue);
655 altered = true;
656 int change = substitute(buf, startPos,
657 varLen, priorVariables);
658 change = change
659 + (varLen - (endPos - startPos));
660 pos += change;
661 bufEnd += change;
662 lengthChange += change;
663 chars = buf.buffer; // in case buffer was
664 // altered
665 }
666
667 // remove variable from the cyclic stack
668 priorVariables
669 .remove(priorVariables.size() - 1);
670 break;
671 } else {
672 nestedVarCount--;
673 pos += endMatchLen;
674 }
675 }
676 }
677 }
678 }
679 }
680 if (top) {
681 return (altered ? 1 : 0);
682 }
683 return lengthChange;
684 }
685
686 /**
687 * Checks if the specified variable is already in the stack (list) of variables.
688 *
689 * @param varName the variable name to check
690 * @param priorVariables the list of prior variables
691 */
692 private void checkCyclicSubstitution(String varName, List priorVariables) {
693 if (priorVariables.contains(varName) == false) {
694 return;
695 }
696 StrBuilder buf = new StrBuilder(256);
697 buf.append("Infinite loop in property interpolation of ");
698 buf.append(priorVariables.remove(0));
699 buf.append(": ");
700 buf.appendWithSeparators(priorVariables, "->");
701 throw new IllegalStateException(buf.toString());
702 }
703
704 /**
705 * Internal method that resolves the value of a variable.
706 * <p>
707 * Most users of this class do not need to call this method. This method is
708 * called automatically by the substitution process.
709 * <p>
710 * Writers of subclasses can override this method if they need to alter
711 * how each substitution occurs. The method is passed the variable's name
712 * and must return the corresponding value. This implementation uses the
713 * {@link #getVariableResolver()} with the variable's name as the key.
714 *
715 * @param variableName the name of the variable, not null
716 * @param buf the buffer where the substitution is occurring, not null
717 * @param startPos the start position of the variable including the prefix, valid
718 * @param endPos the end position of the variable including the suffix, valid
719 * @return the variable's value or <b>null</b> if the variable is unknown
720 */
721 protected String resolveVariable(String variableName, StrBuilder buf, int startPos, int endPos) {
722 StrLookup resolver = getVariableResolver();
723 if (resolver == null) {
724 return null;
725 }
726 return resolver.lookup(variableName);
727 }
728
729 // Escape
730 //-----------------------------------------------------------------------
731 /**
732 * Returns the escape character.
733 *
734 * @return the character used for escaping variable references
735 */
736 public char getEscapeChar() {
737 return this.escapeChar;
738 }
739
740 /**
741 * Sets the escape character.
742 * If this character is placed before a variable reference in the source
743 * text, this variable will be ignored.
744 *
745 * @param escapeCharacter the escape character (0 for disabling escaping)
746 */
747 public void setEscapeChar(char escapeCharacter) {
748 this.escapeChar = escapeCharacter;
749 }
750
751 // Prefix
752 //-----------------------------------------------------------------------
753 /**
754 * Gets the variable prefix matcher currently in use.
755 * <p>
756 * The variable prefix is the characer or characters that identify the
757 * start of a variable. This prefix is expressed in terms of a matcher
758 * allowing advanced prefix matches.
759 *
760 * @return the prefix matcher in use
761 */
762 public StrMatcher getVariablePrefixMatcher() {
763 return prefixMatcher;
764 }
765
766 /**
767 * Sets the variable prefix matcher currently in use.
768 * <p>
769 * The variable prefix is the characer or characters that identify the
770 * start of a variable. This prefix is expressed in terms of a matcher
771 * allowing advanced prefix matches.
772 *
773 * @param prefixMatcher the prefix matcher to use, null ignored
774 * @return this, to enable chaining
775 * @throws IllegalArgumentException if the prefix matcher is null
776 */
777 public StrSubstitutor setVariablePrefixMatcher(StrMatcher prefixMatcher) {
778 if (prefixMatcher == null) {
779 throw new IllegalArgumentException("Variable prefix matcher must not be null!");
780 }
781 this.prefixMatcher = prefixMatcher;
782 return this;
783 }
784
785 /**
786 * Sets the variable prefix to use.
787 * <p>
788 * The variable prefix is the character or characters that identify the
789 * start of a variable. This method allows a single character prefix to
790 * be easily set.
791 *
792 * @param prefix the prefix character to use
793 * @return this, to enable chaining
794 */
795 public StrSubstitutor setVariablePrefix(char prefix) {
796 return setVariablePrefixMatcher(StrMatcher.charMatcher(prefix));
797 }
798
799 /**
800 * Sets the variable prefix to use.
801 * <p>
802 * The variable prefix is the characer or characters that identify the
803 * start of a variable. This method allows a string prefix to be easily set.
804 *
805 * @param prefix the prefix for variables, not null
806 * @return this, to enable chaining
807 * @throws IllegalArgumentException if the prefix is null
808 */
809 public StrSubstitutor setVariablePrefix(String prefix) {
810 if (prefix == null) {
811 throw new IllegalArgumentException("Variable prefix must not be null!");
812 }
813 return setVariablePrefixMatcher(StrMatcher.stringMatcher(prefix));
814 }
815
816 // Suffix
817 //-----------------------------------------------------------------------
818 /**
819 * Gets the variable suffix matcher currently in use.
820 * <p>
821 * The variable suffix is the characer or characters that identify the
822 * end of a variable. This suffix is expressed in terms of a matcher
823 * allowing advanced suffix matches.
824 *
825 * @return the suffix matcher in use
826 */
827 public StrMatcher getVariableSuffixMatcher() {
828 return suffixMatcher;
829 }
830
831 /**
832 * Sets the variable suffix matcher currently in use.
833 * <p>
834 * The variable suffix is the characer or characters that identify the
835 * end of a variable. This suffix is expressed in terms of a matcher
836 * allowing advanced suffix matches.
837 *
838 * @param suffixMatcher the suffix matcher to use, null ignored
839 * @return this, to enable chaining
840 * @throws IllegalArgumentException if the suffix matcher is null
841 */
842 public StrSubstitutor setVariableSuffixMatcher(StrMatcher suffixMatcher) {
843 if (suffixMatcher == null) {
844 throw new IllegalArgumentException("Variable suffix matcher must not be null!");
845 }
846 this.suffixMatcher = suffixMatcher;
847 return this;
848 }
849
850 /**
851 * Sets the variable suffix to use.
852 * <p>
853 * The variable suffix is the characer or characters that identify the
854 * end of a variable. This method allows a single character suffix to
855 * be easily set.
856 *
857 * @param suffix the suffix character to use
858 * @return this, to enable chaining
859 */
860 public StrSubstitutor setVariableSuffix(char suffix) {
861 return setVariableSuffixMatcher(StrMatcher.charMatcher(suffix));
862 }
863
864 /**
865 * Sets the variable suffix to use.
866 * <p>
867 * The variable suffix is the character or characters that identify the
868 * end of a variable. This method allows a string suffix to be easily set.
869 *
870 * @param suffix the suffix for variables, not null
871 * @return this, to enable chaining
872 * @throws IllegalArgumentException if the suffix is null
873 */
874 public StrSubstitutor setVariableSuffix(String suffix) {
875 if (suffix == null) {
876 throw new IllegalArgumentException("Variable suffix must not be null!");
877 }
878 return setVariableSuffixMatcher(StrMatcher.stringMatcher(suffix));
879 }
880
881 // Resolver
882 //-----------------------------------------------------------------------
883 /**
884 * Gets the VariableResolver that is used to lookup variables.
885 *
886 * @return the VariableResolver
887 */
888 public StrLookup getVariableResolver() {
889 return this.variableResolver;
890 }
891
892 /**
893 * Sets the VariableResolver that is used to lookup variables.
894 *
895 * @param variableResolver the VariableResolver
896 */
897 public void setVariableResolver(StrLookup variableResolver) {
898 this.variableResolver = variableResolver;
899 }
900
901 // Substitution support in variable names
902 //-----------------------------------------------------------------------
903 /**
904 * Returns a flag whether substitution is done in variable names.
905 *
906 * @return the substitution in variable names flag
907 * @since 2.6
908 */
909 public boolean isEnableSubstitutionInVariables() {
910 return enableSubstitutionInVariables;
911 }
912
913 /**
914 * Sets a flag whether substitution is done in variable names. If set to
915 * <b>true</b>, the names of variables can contain other variables which are
916 * processed first before the original variable is evaluated, e.g.
917 * <code>${jre-${java.version}}</code>. The default value is <b>false</b>.
918 *
919 * @param enableSubstitutionInVariables the new value of the flag
920 * @since 2.6
921 */
922 public void setEnableSubstitutionInVariables(
923 boolean enableSubstitutionInVariables) {
924 this.enableSubstitutionInVariables = enableSubstitutionInVariables;
925 }
926 }