/**
  * Internal method that resolves the value of a variable.
  *
  * <p>Most users of this class do not need to call this method. This method is called
  * automatically by the substitution process.
  *
  * <p>Writers of subclasses can override this method if they need to alter how each substitution
  * occurs. The method is passed the variable's name and must return the corresponding value. This
  * implementation uses the {@link #getVariableResolver()} with the variable's name as the key.
  *
  * @param variableName the name of the variable, not null
  * @param buf the buffer where the substitution is occurring, not null
  * @param startPos the start position of the variable including the prefix, valid
  * @param endPos the end position of the variable including the suffix, valid
  * @return the variable's value or <b>null</b> if the variable is unknown
  */
 protected String resolveVariable(
     final String variableName, final StrBuilder buf, final int startPos, final int endPos) {
   final StrLookup<?> resolver = getVariableResolver();
   if (resolver == null) {
     return null;
   }
   return resolver.lookup(variableName);
 }
 /**
  * Creates a new instance and initializes it.
  *
  * @param <V> the type of the values in the map
  * @param valueMap the map with the variables' values, may be null
  * @param prefix the prefix for variables, not null
  * @param suffix the suffix for variables, not null
  * @param escape the escape character
  * @param valueDelimiter the variable default value delimiter, may be null
  * @throws IllegalArgumentException if the prefix or suffix is null
  * @since 3.2
  */
 public <V> StrSubstitutor(
     final Map<String, V> valueMap,
     final String prefix,
     final String suffix,
     final char escape,
     final String valueDelimiter) {
   this(StrLookup.mapLookup(valueMap), prefix, suffix, escape, valueDelimiter);
 }
 /**
  * Creates a new instance and initializes it. Uses defaults for variable prefix and suffix and the
  * escaping character.
  *
  * @param <V> the type of the values in the map
  * @param valueMap the map with the variables' values, may be null
  */
 public <V> StrSubstitutor(final Map<String, V> valueMap) {
   this(StrLookup.mapLookup(valueMap), DEFAULT_PREFIX, DEFAULT_SUFFIX, DEFAULT_ESCAPE);
 }
 /**
  * Creates a new instance and initializes it. Uses a default escaping character.
  *
  * @param <V> the type of the values in the map
  * @param valueMap the map with the variables' values, may be null
  * @param prefix the prefix for variables, not null
  * @param suffix the suffix for variables, not null
  * @throws IllegalArgumentException if the prefix or suffix is null
  */
 public <V> StrSubstitutor(
     final Map<String, V> valueMap, final String prefix, final String suffix) {
   this(StrLookup.mapLookup(valueMap), prefix, suffix, DEFAULT_ESCAPE);
 }
 /**
  * Replaces all the occurrences of variables in the given source object with their matching values
  * from the system properties.
  *
  * @param source the source text containing the variables to substitute, null returns null
  * @return the result of the replace operation
  */
 public static String replaceSystemProperties(final Object source) {
   return new StrSubstitutor(StrLookup.systemPropertiesLookup()).replace(source);
 }