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