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