You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by gg...@apache.org on 2019/08/04 21:41:47 UTC

[commons-text] 02/02: [TEXT-171] StringLookupFactory.addDefaultStringLookups(Map) does not convert keys to lower case.

This is an automated email from the ASF dual-hosted git repository.

ggregory pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/commons-text.git

commit bc1b2049f171050385678905a4359287052168d1
Author: Gary Gregory <ga...@gmail.com>
AuthorDate: Sun Aug 4 17:41:41 2019 -0400

    [TEXT-171] StringLookupFactory.addDefaultStringLookups(Map) does not
    convert keys to lower case.
    
    Also, better Javadoc.
---
 src/changes/changes.xml                            |   1 +
 .../org/apache/commons/text/StringSubstitutor.java | 515 +++++++++------------
 .../text/lookup/InterpolatorStringLookup.java      |   8 +-
 .../commons/text/lookup/StringLookupFactory.java   |  29 +-
 src/site/xdoc/userguide.xml                        |  26 ++
 .../org/apache/commons/text/StrLookupTest.java     |   2 +-
 ...ubstitutorWithInterpolatorStringLookupTest.java |  39 +-
 .../lookup/ResourceBundleStringLookupTest.java     |   2 +-
 .../text/lookup/StringLookupFactoryTest.java       |  34 +-
 .../commons/text/lookup/UrlStringLookupTest.java   |   3 +-
 .../example}/testResourceBundleLookup.properties   |   1 +
 11 files changed, 325 insertions(+), 335 deletions(-)

diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index 8d1527e..7f41562 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -50,6 +50,7 @@ The <action> type attribute can be add,update,fix,remove.
     <action issue="TEXT-168" type="fix" dev="ggregory" due-to="luksan47">(doc) Fixed wrong value for Jaro-Winkler example #117</action>
     <action issue="TEXT-169" type="add" dev="ggregory" due-to="Gary Gregory">Add helper factory method org.apache.commons.text.StringSubstitutor.createInterpolator().</action>
     <action issue="TEXT-170" type="add" dev="ggregory" due-to="Gary Gregory">Add String lookup for host names and IP addresses (DnsStringLookup).</action>
+    <action issue="TEXT-171" type="fix" dev="ggregory" due-to="Gary Gregory">StringLookupFactory.addDefaultStringLookups(Map) does not convert keys to lower case.</action>
     <action                  type="update" dev="ggregory" due-to="Gary Gregory">Expand Javadoc for StringSubstitutor and friends.</action>
     
   </release>
diff --git a/src/main/java/org/apache/commons/text/StringSubstitutor.java b/src/main/java/org/apache/commons/text/StringSubstitutor.java
index 99b9c7a..07f8507 100644
--- a/src/main/java/org/apache/commons/text/StringSubstitutor.java
+++ b/src/main/java/org/apache/commons/text/StringSubstitutor.java
@@ -34,22 +34,27 @@ import org.apache.commons.text.matcher.StringMatcherFactory;
  * <p>
  * This class takes a piece of text and substitutes all the variables within it. The default definition of a variable is
  * <code>${variableName}</code>. The prefix and suffix can be changed via constructors and set methods.
+ * </p>
  * <p>
  * Variable values are typically resolved from a map, but could also be resolved from system properties, or by supplying
  * a custom variable resolver.
  * <p>
  * The simplest example is to use this class to replace Java System properties. For example:
+ * </p>
  *
  * <pre>
  * StringSubstitutor
  *         .replaceSystemProperties("You are running with java.version = ${java.version} and os.name = ${os.name}.");
  * </pre>
+ *
+ * <h1>Using a Custom Map</h1>
  * <p>
  * Typical usage of this class follows the following pattern: First an instance is created and initialized with the map
  * that contains the values for the available variables. If a prefix and/or suffix for variables should be used other
  * than the default ones, the appropriate settings can be performed. After that the {@code replace()} method can be
  * called passing in the source text for interpolation. In the returned text all variable references (as long as their
  * values are known) will be resolved. The following example demonstrates this:
+ * </p>
  *
  * <pre>
  * Map valuesMap = HashMap();
@@ -60,18 +65,23 @@ import org.apache.commons.text.matcher.StringMatcherFactory;
  * String resolvedString = sub.replace(templateString);
  * </pre>
  *
+ * <p>
  * yielding:
+ * </p>
  *
  * <pre>
  *      The quick brown fox jumped over the lazy dog.
  * </pre>
+ *
+ * <h1>Providing Default Values</h1>
  * <p>
- * Also, this class allows to set a default value for unresolved variables. The default value for a variable can be
- * appended to the variable name after the variable default value delimiter. The default value of the variable default
- * value delimiter is ':-', as in bash and other *nix shells, as those are arguably where the default ${} delimiter set
+ * This class lets you set a default value for unresolved variables. The default value for a variable can be appended to
+ * the variable name after the variable default value delimiter. The default value of the variable default value
+ * delimiter is ':-', as in bash and other *nix shells, as those are arguably where the default ${} delimiter set
  * originated. The variable default value delimiter can be manually set by calling
  * {@link #setValueDelimiterMatcher(StringMatcher)}, {@link #setValueDelimiter(char)} or
  * {@link #setValueDelimiter(String)}. The following shows an example with variable default value settings:
+ * </p>
  *
  * <pre>
  * Map valuesMap = HashMap();
@@ -82,57 +92,106 @@ import org.apache.commons.text.matcher.StringMatcherFactory;
  * String resolvedString = sub.replace(templateString);
  * </pre>
  *
+ * <p>
  * yielding:
+ * </p>
  *
  * <pre>
  *      The quick brown fox jumped over the lazy dog. 1234567890.
  * </pre>
+ *
+ * <p>
+ * {@code StringSubstitutor} supports throwing exceptions for unresolved variables, you enable this by setting calling
+ * {@link #setEnableUndefinedVariableException(boolean)} with {@code true}.
+ * </p>
+ *
+ * <h1>Reusing Instances</h1>
  * <p>
  * In addition to this usage pattern there are some static convenience methods that cover the most common use cases.
  * These methods can be used without the need of manually creating an instance. However if multiple replace operations
  * are to be performed, creating and reusing an instance of this class will be more efficient.
+ * </p>
+ * <p>
+ * This class is <b>not</b> thread safe.
+ * </p>
+ *
+ * <h1>Using Interpolation</h1>
+ * <p>
+ * The default interpolator let's you use string lookups like:
+ * </p>
+ *
+ * <pre>
+final StringSubstitutor interpolator = StringSubstitutor.createInterpolator();
+interpolator.setEnableSubstitutionInVariables(true); // Allows for nested $'s.
+final String text = interpolator.replace(
+    "Base64 Decoder:        ${base64Decoder:SGVsbG9Xb3JsZCE=}\n" +
+    "Base64 Encoder:        ${base64Encoder:HelloWorld!}\n" +
+    "Java Constant:         ${const:java.awt.event.KeyEvent.VK_ESCAPE}\n" +
+    "Date:                  ${date:yyyy-MM-dd}\n" +
+    "DNS:                   ${dns:address|apache.org}\n" +
+    "Environment Variable:  ${env:USERNAME}\n" +
+    "File Content:          ${file:UTF-8:src/test/resources/document.properties}\n" +
+    "Java:                  ${java:version}\n" +
+    "Localhost:             ${localhost:canonical-name}\n" +
+    "Properties File:       ${properties:src/test/resources/document.properties::mykey}\n" +
+    "Resource Bundle:       ${resourceBundle:org.example.testResourceBundleLookup:mykey}\n" +
+    "Script:                ${script:javascript:3 + 4}\n" +
+    "System Property:       ${sys:user.dir}\n" +
+    "URL Decoder:           ${urlDecoder:Hello%20World%21}\n" +
+    "URL Encoder:           ${urlEncoder:Hello World!}\n" +
+    "URL Content (HTTP):    ${url:UTF-8:http://www.apache.org}\n" +
+    "URL Content (HTTPS):   ${url:UTF-8:https://www.apache.org}\n" +
+    "URL Content (File):    ${url:UTF-8:file:///${sys:user.dir}/src/test/resources/document.properties}\n" +
+    "XML XPath:             ${xml:src/test/resources/document.xml:/root/path/to/node}\n"
+);
+ * </pre>
+ * <p>
+ * For documentation of each lookup, see {@link StringLookupFactory}.
+ * </p>
+ *
+ * <h1>Using Recursive Variable Replacement</h1>
  * <p>
  * Variable replacement works in a recursive way. Thus, if a variable value contains a variable then that variable will
  * also be replaced. Cyclic replacements are detected and will cause an exception to be thrown.
+ * </p>
  * <p>
  * Sometimes the interpolation's result must contain a variable prefix. As an example take the following source text:
+ * </p>
  *
  * <pre>
  *   The variable ${${name}} must be used.
  * </pre>
  *
+ * <p>
  * Here only the variable's name referred to in the text should be replaced resulting in the text (assuming that the
  * value of the {@code name} variable is {@code x}):
+ * </p>
  *
  * <pre>
  *   The variable ${x} must be used.
  * </pre>
  *
+ * <p>
  * To achieve this effect there are two possibilities: Either set a different prefix and suffix for variables which do
  * not conflict with the result text you want to produce. The other possibility is to use the escape character, by
  * default '$'. If this character is placed before a variable reference, this reference is ignored and won't be
  * replaced. For example:
+ * </p>
  *
  * <pre>
  *   The variable $${${name}} must be used.
  * </pre>
  * <p>
  * In some complex scenarios you might even want to perform substitution in the names of variables, for instance
+ * </p>
  *
  * <pre>
  * ${jre-${java.specification.version}}
  * </pre>
  *
  * <p>
- * {@code StringSubstitutor} supports this recursive substitution in variable names, but it has to be enabled
- * explicitly by calling {@link #setEnableSubstitutionInVariables(boolean)} with {@code true}.
- * </p>
- * <p>
- * {@code StringSubstitutor} supports throwing exceptions for unresolved variables, you enable this by setting
- * calling {@link #setEnableUndefinedVariableException(boolean)} with {@code true}.
- * </p>
- * <p>
- * This class is <b>not</b> thread safe.
+ * {@code StringSubstitutor} supports this recursive substitution in variable names, but it has to be enabled explicitly
+ * by calling {@link #setEnableSubstitutionInVariables(boolean)} with {@code true}.
  * </p>
  *
  * @since 1.3
@@ -178,8 +237,8 @@ public class StringSubstitutor {
     /**
      * Constant for the default value delimiter of a variable.
      */
-    public static final StringMatcher DEFAULT_VALUE_DELIMITER =
-            StringMatcherFactory.INSTANCE.stringMatcher(DEFAULT_VAR_DEFAULT);
+    public static final StringMatcher DEFAULT_VALUE_DELIMITER = StringMatcherFactory.INSTANCE
+            .stringMatcher(DEFAULT_VAR_DEFAULT);
 
     /**
      * Creates a new instance using the interpolator string lookup
@@ -187,10 +246,9 @@ public class StringSubstitutor {
      * <p>
      * This StringSubstitutor lets you perform substituions like:
      * </p>
+     *
      * <pre>
-     * StringSubstitutor.createInterpolator().replace(
-     *  "OS name: ${sys:os.name}, " +
-     *  "3 + 4 = ${script:javascript:3 + 4}");
+     * StringSubstitutor.createInterpolator().replace("OS name: ${sys:os.name}, " + "3 + 4 = ${script:javascript:3 + 4}");
      * </pre>
      *
      * @return a new instance using the interpolator string lookup.
@@ -204,15 +262,11 @@ public class StringSubstitutor {
     /**
      * Replaces all the occurrences of variables in the given source object with their matching values from the map.
      *
-     * @param <V>
-     *            the type of the values in the map
-     * @param source
-     *            the source text containing the variables to substitute, null returns null
-     * @param valueMap
-     *            the map with the values, may be null
+     * @param <V>      the type of the values in the map
+     * @param source   the source text containing the variables to substitute, null returns null
+     * @param valueMap the map with the values, may be null
      * @return The result of the replace operation
-     * @throws IllegalArgumentException
-     *             if a variable is not found and enableUndefinedVariableException is true
+     * @throws IllegalArgumentException if a variable is not found and enableUndefinedVariableException is true
      */
     public static <V> String replace(final Object source, final Map<String, V> valueMap) {
         return new StringSubstitutor(valueMap).replace(source);
@@ -222,21 +276,14 @@ public class StringSubstitutor {
      * Replaces all the occurrences of variables in the given source object with their matching values from the map.
      * This method allows to specify a custom variable prefix and suffix
      *
-     * @param <V>
-     *            the type of the values in the map
-     * @param source
-     *            the source text containing the variables to substitute, null returns null
-     * @param valueMap
-     *            the map with the values, may be null
-     * @param prefix
-     *            the prefix of variables, not null
-     * @param suffix
-     *            the suffix of variables, not null
+     * @param <V>      the type of the values in the map
+     * @param source   the source text containing the variables to substitute, null returns null
+     * @param valueMap the map with the values, may be null
+     * @param prefix   the prefix of variables, not null
+     * @param suffix   the suffix of variables, not null
      * @return The result of the replace operation
-     * @throws IllegalArgumentException
-     *             if the prefix or suffix is null
-     * @throws IllegalArgumentException
-     *             if a variable is not found and enableUndefinedVariableException is true
+     * @throws IllegalArgumentException if the prefix or suffix is null
+     * @throws IllegalArgumentException if a variable is not found and enableUndefinedVariableException is true
      */
     public static <V> String replace(final Object source, final Map<String, V> valueMap, final String prefix,
             final String suffix) {
@@ -247,13 +294,10 @@ public class StringSubstitutor {
      * Replaces all the occurrences of variables in the given source object with their matching values from the
      * properties.
      *
-     * @param source
-     *            the source text containing the variables to substitute, null returns null
-     * @param valueProperties
-     *            the properties with values, may be null
+     * @param source          the source text containing the variables to substitute, null returns null
+     * @param valueProperties the properties with values, may be null
      * @return The result of the replace operation
-     * @throws IllegalArgumentException
-     *             if a variable is not found and enableUndefinedVariableException is true
+     * @throws IllegalArgumentException if a variable is not found and enableUndefinedVariableException is true
      */
     public static String replace(final Object source, final Properties valueProperties) {
         if (valueProperties == null) {
@@ -273,11 +317,9 @@ public class StringSubstitutor {
      * 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
+     * @param source the source text containing the variables to substitute, null returns null
      * @return The result of the replace operation
-     * @throws IllegalArgumentException
-     *             if a variable is not found and enableUndefinedVariableException is true
+     * @throws IllegalArgumentException if a variable is not found and enableUndefinedVariableException is true
      */
     public static String replaceSystemProperties(final Object source) {
         return new StringSubstitutor(StringLookupFactory.INSTANCE.systemPropertyStringLookup()).replace(source);
@@ -340,10 +382,8 @@ public class StringSubstitutor {
      * 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
+     * @param <V>      the type of the values in the map
+     * @param valueMap the map with the variables' values, may be null
      */
     public <V> StringSubstitutor(final Map<String, V> valueMap) {
         this(StringLookupFactory.INSTANCE.mapStringLookup(valueMap), DEFAULT_PREFIX, DEFAULT_SUFFIX, DEFAULT_ESCAPE);
@@ -352,16 +392,11 @@ public class StringSubstitutor {
     /**
      * 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
+     * @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> StringSubstitutor(final Map<String, V> valueMap, final String prefix, final String suffix) {
         this(StringLookupFactory.INSTANCE.mapStringLookup(valueMap), prefix, suffix, DEFAULT_ESCAPE);
@@ -370,18 +405,12 @@ public class StringSubstitutor {
     /**
      * 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
-     * @throws IllegalArgumentException
-     *             if the prefix or suffix is null
+     * @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
+     * @throws IllegalArgumentException if the prefix or suffix is null
      */
     public <V> StringSubstitutor(final Map<String, V> valueMap, final String prefix, final String suffix,
             final char escape) {
@@ -391,20 +420,13 @@ public class StringSubstitutor {
     /**
      * 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
+     * @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
      */
     public <V> StringSubstitutor(final Map<String, V> valueMap, final String prefix, final String suffix,
             final char escape, final String valueDelimiter) {
@@ -414,8 +436,7 @@ public class StringSubstitutor {
     /**
      * Creates a new instance and initializes it.
      *
-     * @param variableResolver
-     *            the variable resolver, may be null
+     * @param variableResolver the variable resolver, may be null
      */
     public StringSubstitutor(final StringLookup variableResolver) {
         this(variableResolver, DEFAULT_PREFIX, DEFAULT_SUFFIX, DEFAULT_ESCAPE);
@@ -424,16 +445,11 @@ public class StringSubstitutor {
     /**
      * Creates a new instance and initializes it.
      *
-     * @param variableResolver
-     *            the variable resolver, may be null
-     * @param prefix
-     *            the prefix for variables, not null
-     * @param suffix
-     *            the suffix for variables, not null
-     * @param escape
-     *            the escape character
-     * @throws IllegalArgumentException
-     *             if the prefix or suffix is null
+     * @param variableResolver the variable resolver, may be null
+     * @param prefix           the prefix for variables, not null
+     * @param suffix           the suffix for variables, not null
+     * @param escape           the escape character
+     * @throws IllegalArgumentException if the prefix or suffix is null
      */
     public StringSubstitutor(final StringLookup variableResolver, final String prefix, final String suffix,
             final char escape) {
@@ -447,18 +463,12 @@ public class StringSubstitutor {
     /**
      * Creates a new instance and initializes it.
      *
-     * @param variableResolver
-     *            the variable resolver, 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 string, may be null
-     * @throws IllegalArgumentException
-     *             if the prefix or suffix is null
+     * @param variableResolver the variable resolver, 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 string, may be null
+     * @throws IllegalArgumentException if the prefix or suffix is null
      */
     public StringSubstitutor(final StringLookup variableResolver, final String prefix, final String suffix,
             final char escape, final String valueDelimiter) {
@@ -472,16 +482,11 @@ public class StringSubstitutor {
     /**
      * Creates a new instance and initializes it.
      *
-     * @param variableResolver
-     *            the variable resolver, may be null
-     * @param prefixMatcher
-     *            the prefix for variables, not null
-     * @param suffixMatcher
-     *            the suffix for variables, not null
-     * @param escape
-     *            the escape character
-     * @throws IllegalArgumentException
-     *             if the prefix or suffix is null
+     * @param variableResolver the variable resolver, may be null
+     * @param prefixMatcher    the prefix for variables, not null
+     * @param suffixMatcher    the suffix for variables, not null
+     * @param escape           the escape character
+     * @throws IllegalArgumentException if the prefix or suffix is null
      */
     public StringSubstitutor(final StringLookup variableResolver, final StringMatcher prefixMatcher,
             final StringMatcher suffixMatcher, final char escape) {
@@ -491,18 +496,12 @@ public class StringSubstitutor {
     /**
      * Creates a new instance and initializes it.
      *
-     * @param variableResolver
-     *            the variable resolver, may be null
-     * @param prefixMatcher
-     *            the prefix for variables, not null
-     * @param suffixMatcher
-     *            the suffix for variables, not null
-     * @param escape
-     *            the escape character
-     * @param valueDelimiterMatcher
-     *            the variable default value delimiter matcher, may be null
-     * @throws IllegalArgumentException
-     *             if the prefix or suffix is null
+     * @param variableResolver      the variable resolver, may be null
+     * @param prefixMatcher         the prefix for variables, not null
+     * @param suffixMatcher         the suffix for variables, not null
+     * @param escape                the escape character
+     * @param valueDelimiterMatcher the variable default value delimiter matcher, may be null
+     * @throws IllegalArgumentException if the prefix or suffix is null
      */
     public StringSubstitutor(final StringLookup variableResolver, final StringMatcher prefixMatcher,
             final StringMatcher suffixMatcher, final char escape, final StringMatcher valueDelimiterMatcher) {
@@ -516,10 +515,8 @@ public class StringSubstitutor {
     /**
      * Checks if the specified variable is already in the stack (list) of variables.
      *
-     * @param varName
-     *            the variable name to check
-     * @param priorVariables
-     *            the list of prior variables
+     * @param varName        the variable name to check
+     * @param priorVariables the list of prior variables
      */
     private void checkCyclicSubstitution(final String varName, final List<String> priorVariables) {
         if (!priorVariables.contains(varName)) {
@@ -637,8 +634,7 @@ public class StringSubstitutor {
     }
 
     /**
-     * Returns a flag whether exception can be thrown upon undefined
-     * variable.
+     * Returns a flag whether exception can be thrown upon undefined variable.
      *
      * @return The fail on undefined variable flag
      */
@@ -660,11 +656,9 @@ public class StringSubstitutor {
      * Replaces all the occurrences of variables with their matching values from the resolver using the given source
      * array as a template. The array is not altered by this method.
      *
-     * @param source
-     *            the character array to replace in, not altered, null returns null
+     * @param source the character array to replace in, not altered, null returns null
      * @return The result of the replace operation
-     * @throws IllegalArgumentException
-     *             if variable is not found when its allowed to throw exception
+     * @throws IllegalArgumentException if variable is not found when its allowed to throw exception
      */
     public String replace(final char[] source) {
         if (source == null) {
@@ -682,15 +676,11 @@ public class StringSubstitutor {
      * Only the specified portion of the array will be processed. The rest of the array is not processed, and is not
      * returned.
      *
-     * @param source
-     *            the character array to replace in, not altered, null returns null
-     * @param offset
-     *            the start offset within the array, must be valid
-     * @param length
-     *            the length within the array to be processed, must be valid
+     * @param source the character array to replace in, not altered, null returns null
+     * @param offset the start offset within the array, must be valid
+     * @param length the length within the array to be processed, must be valid
      * @return The result of the replace operation
-     * @throws IllegalArgumentException
-     *             if variable is not found when its allowed to throw exception
+     * @throws IllegalArgumentException if variable is not found when its allowed to throw exception
      */
     public String replace(final char[] source, final int offset, final int length) {
         if (source == null) {
@@ -705,11 +695,9 @@ public class StringSubstitutor {
      * Replaces all the occurrences of variables with their matching values from the resolver using the given source as
      * a template. The source is not altered by this method.
      *
-     * @param source
-     *            the buffer to use as a template, not changed, null returns null
+     * @param source the buffer to use as a template, not changed, null returns null
      * @return The result of the replace operation
-     * @throws IllegalArgumentException
-     *             if variable is not found when its allowed to throw exception
+     * @throws IllegalArgumentException if variable is not found when its allowed to throw exception
      */
     public String replace(final CharSequence source) {
         if (source == null) {
@@ -725,15 +713,11 @@ public class StringSubstitutor {
      * Only the specified portion of the buffer will be processed. The rest of the buffer is not processed, and is not
      * returned.
      *
-     * @param source
-     *            the buffer to use as a template, not changed, null returns null
-     * @param offset
-     *            the start offset within the array, must be valid
-     * @param length
-     *            the length within the array to be processed, must be valid
+     * @param source the buffer to use as a template, not changed, null returns null
+     * @param offset the start offset within the array, must be valid
+     * @param length the length within the array to be processed, must be valid
      * @return The result of the replace operation
-     * @throws IllegalArgumentException
-     *             if variable is not found when its allowed to throw exception
+     * @throws IllegalArgumentException if variable is not found when its allowed to throw exception
      */
     public String replace(final CharSequence source, final int offset, final int length) {
         if (source == null) {
@@ -749,11 +733,9 @@ public class StringSubstitutor {
      * Replaces all the occurrences of variables in the given source object with their matching values from the
      * resolver. The input source object is converted to a string using <code>toString</code> and is not altered.
      *
-     * @param source
-     *            the source to replace in, null returns null
+     * @param source the source to replace in, null returns null
      * @return The result of the replace operation
-     * @throws IllegalArgumentException
-     *             if a variable is not found and enableUndefinedVariableException is true
+     * @throws IllegalArgumentException if a variable is not found and enableUndefinedVariableException is true
      */
     public String replace(final Object source) {
         if (source == null) {
@@ -769,11 +751,9 @@ public class StringSubstitutor {
      * Replaces all the occurrences of variables with their matching values from the resolver using the given source
      * string as a template.
      *
-     * @param source
-     *            the string to replace in, null returns null
+     * @param source the string to replace in, null returns null
      * @return The result of the replace operation
-     * @throws IllegalArgumentException
-     *             if variable is not found when its allowed to throw exception
+     * @throws IllegalArgumentException if variable is not found when its allowed to throw exception
      */
     public String replace(final String source) {
         if (source == null) {
@@ -793,15 +773,11 @@ public class StringSubstitutor {
      * Only the specified portion of the string will be processed. The rest of the string is not processed, and is not
      * returned.
      *
-     * @param source
-     *            the string to replace in, null returns null
-     * @param offset
-     *            the start offset within the array, must be valid
-     * @param length
-     *            the length within the array to be processed, must be valid
+     * @param source the string to replace in, null returns null
+     * @param offset the start offset within the array, must be valid
+     * @param length the length within the array to be processed, must be valid
      * @return The result of the replace operation
-     * @throws IllegalArgumentException
-     *             if variable is not found when its allowed to throw exception
+     * @throws IllegalArgumentException if variable is not found when its allowed to throw exception
      */
     public String replace(final String source, final int offset, final int length) {
         if (source == null) {
@@ -819,11 +795,9 @@ public class StringSubstitutor {
      * Replaces all the occurrences of variables with their matching values from the resolver using the given source
      * buffer as a template. The buffer is not altered by this method.
      *
-     * @param source
-     *            the buffer to use as a template, not changed, null returns null
+     * @param source the buffer to use as a template, not changed, null returns null
      * @return The result of the replace operation
-      * @throws IllegalArgumentException
-     *             if variable is not found when its allowed to throw exception
+     * @throws IllegalArgumentException if variable is not found when its allowed to throw exception
      */
     public String replace(final StringBuffer source) {
         if (source == null) {
@@ -841,15 +815,11 @@ public class StringSubstitutor {
      * Only the specified portion of the buffer will be processed. The rest of the buffer is not processed, and is not
      * returned.
      *
-     * @param source
-     *            the buffer to use as a template, not changed, null returns null
-     * @param offset
-     *            the start offset within the array, must be valid
-     * @param length
-     *            the length within the array to be processed, must be valid
+     * @param source the buffer to use as a template, not changed, null returns null
+     * @param offset the start offset within the array, must be valid
+     * @param length the length within the array to be processed, must be valid
      * @return The result of the replace operation
-     * @throws IllegalArgumentException
-     *             if variable is not found when its allowed to throw exception
+     * @throws IllegalArgumentException if variable is not found when its allowed to throw exception
      */
     public String replace(final StringBuffer source, final int offset, final int length) {
         if (source == null) {
@@ -865,11 +835,9 @@ public class StringSubstitutor {
      * Replaces all the occurrences of variables with their matching values from the resolver using the given source
      * builder as a template. The builder is not altered by this method.
      *
-     * @param source
-     *            the builder to use as a template, not changed, null returns null
+     * @param source the builder to use as a template, not changed, null returns null
      * @return The result of the replace operation
-     * @throws IllegalArgumentException
-     *             if variable is not found when its allowed to throw exception
+     * @throws IllegalArgumentException if variable is not found when its allowed to throw exception
      */
     public String replace(final TextStringBuilder source) {
         if (source == null) {
@@ -887,15 +855,11 @@ public class StringSubstitutor {
      * Only the specified portion of the builder will be processed. The rest of the builder is not processed, and is not
      * returned.
      *
-     * @param source
-     *            the builder to use as a template, not changed, null returns null
-     * @param offset
-     *            the start offset within the array, must be valid
-     * @param length
-     *            the length within the array to be processed, must be valid
+     * @param source the builder to use as a template, not changed, null returns null
+     * @param offset the start offset within the array, must be valid
+     * @param length the length within the array to be processed, must be valid
      * @return The result of the replace operation
-      * @throws IllegalArgumentException
-     *             if variable is not found when its allowed to throw exception
+     * @throws IllegalArgumentException if variable is not found when its allowed to throw exception
      */
     public String replace(final TextStringBuilder source, final int offset, final int length) {
         if (source == null) {
@@ -911,8 +875,7 @@ public class StringSubstitutor {
      * Replaces all the occurrences of variables within the given source buffer with their matching values from the
      * resolver. The buffer is updated with the result.
      *
-     * @param source
-     *            the buffer to replace in, updated, null returns zero
+     * @param source the buffer to replace in, updated, null returns zero
      * @return true if altered
      */
     public boolean replaceIn(final StringBuffer source) {
@@ -929,15 +892,11 @@ public class StringSubstitutor {
      * Only the specified portion of the buffer will be processed. The rest of the buffer is not processed, but it is
      * not deleted.
      *
-     * @param source
-     *            the buffer to replace in, updated, null returns zero
-     * @param offset
-     *            the start offset within the array, must be valid
-     * @param length
-     *            the length within the buffer to be processed, must be valid
+     * @param source the buffer to replace in, updated, null returns zero
+     * @param offset the start offset within the array, must be valid
+     * @param length the length within the buffer to be processed, must be valid
      * @return true if altered
-     * @throws IllegalArgumentException
-     *             if variable is not found when its allowed to throw exception
+     * @throws IllegalArgumentException if variable is not found when its allowed to throw exception
      */
     public boolean replaceIn(final StringBuffer source, final int offset, final int length) {
         if (source == null) {
@@ -956,8 +915,7 @@ public class StringSubstitutor {
      * Replaces all the occurrences of variables within the given source buffer with their matching values from the
      * resolver. The buffer is updated with the result.
      *
-     * @param source
-     *            the buffer to replace in, updated, null returns zero
+     * @param source the buffer to replace in, updated, null returns zero
      * @return true if altered
      */
     public boolean replaceIn(final StringBuilder source) {
@@ -974,15 +932,11 @@ public class StringSubstitutor {
      * Only the specified portion of the buffer will be processed. The rest of the buffer is not processed, but it is
      * not deleted.
      *
-     * @param source
-     *            the buffer to replace in, updated, null returns zero
-     * @param offset
-     *            the start offset within the array, must be valid
-     * @param length
-     *            the length within the buffer to be processed, must be valid
+     * @param source the buffer to replace in, updated, null returns zero
+     * @param offset the start offset within the array, must be valid
+     * @param length the length within the buffer to be processed, must be valid
      * @return true if altered
-     * @throws IllegalArgumentException
-     *             if variable is not found when its allowed to throw exception
+     * @throws IllegalArgumentException if variable is not found when its allowed to throw exception
      */
     public boolean replaceIn(final StringBuilder source, final int offset, final int length) {
         if (source == null) {
@@ -1001,11 +955,9 @@ public class StringSubstitutor {
      * Replaces all the occurrences of variables within the given source builder with their matching values from the
      * resolver.
      *
-     * @param source
-     *            the builder to replace in, updated, null returns zero
+     * @param source the builder to replace in, updated, null returns zero
      * @return true if altered
-     * @throws IllegalArgumentException
-     *             if variable is not found when its allowed to throw exception
+     * @throws IllegalArgumentException if variable is not found when its allowed to throw exception
      */
     public boolean replaceIn(final TextStringBuilder source) {
         if (source == null) {
@@ -1021,15 +973,11 @@ public class StringSubstitutor {
      * Only the specified portion of the builder will be processed. The rest of the builder is not processed, but it is
      * not deleted.
      *
-     * @param source
-     *            the builder to replace in, null returns zero
-     * @param offset
-     *            the start offset within the array, must be valid
-     * @param length
-     *            the length within the builder to be processed, must be valid
+     * @param source the builder to replace in, null returns zero
+     * @param offset the start offset within the array, must be valid
+     * @param length the length within the builder to be processed, must be valid
      * @return true if altered
-     * @throws IllegalArgumentException
-     *             if variable is not found when its allowed to throw exception
+     * @throws IllegalArgumentException if variable is not found when its allowed to throw exception
      */
     public boolean replaceIn(final TextStringBuilder source, final int offset, final int length) {
         if (source == null) {
@@ -1048,14 +996,10 @@ public class StringSubstitutor {
      * passed the variable's name and must return the corresponding value. This implementation uses the
      * {@link #getStringLookup()} 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
+     * @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 TextStringBuilder buf, final int startPos,
@@ -1070,8 +1014,7 @@ public class StringSubstitutor {
     /**
      * Sets a flag whether substitution is done in variable values (recursive).
      *
-     * @param disableSubstitutionInValues
-     *            true if substitution in variable value are disabled
+     * @param disableSubstitutionInValues true if substitution in variable value are disabled
      * @return this, to enable chaining
      */
     public StringSubstitutor setDisableSubstitutionInValues(final boolean disableSubstitutionInValues) {
@@ -1084,8 +1027,7 @@ public class StringSubstitutor {
      * contain other variables which are processed first before the original variable is evaluated, e.g.
      * <code>${jre-${java.version}}</code>. The default value is <b>false</b>.
      *
-     * @param enableSubstitutionInVariables
-     *            the new value of the flag
+     * @param enableSubstitutionInVariables the new value of the flag
      * @return this, to enable chaining
      */
     public StringSubstitutor setEnableSubstitutionInVariables(final boolean enableSubstitutionInVariables) {
@@ -1096,8 +1038,7 @@ public class StringSubstitutor {
     /**
      * Sets a flag whether exception should be thrown if any variable is undefined.
      *
-     * @param failOnUndefinedVariable
-     *            true if exception should be thrown on undefined variable
+     * @param failOnUndefinedVariable true if exception should be thrown on undefined variable
      * @return this, to enable chaining
      */
     public StringSubstitutor setEnableUndefinedVariableException(final boolean failOnUndefinedVariable) {
@@ -1109,8 +1050,7 @@ public class StringSubstitutor {
      * Sets the escape character. If this character is placed before a variable reference in the source text, this
      * variable will be ignored.
      *
-     * @param escapeCharacter
-     *            the escape character (0 for disabling escaping)
+     * @param escapeCharacter the escape character (0 for disabling escaping)
      * @return this, to enable chaining
      */
     public StringSubstitutor setEscapeChar(final char escapeCharacter) {
@@ -1125,8 +1065,7 @@ public class StringSubstitutor {
      * (e.g. <code>$${this-is-escaped}</code> becomes <code>${this-is-escaped}</code>). The default value is
      * <b>false</b>
      *
-     * @param preserveEscapes
-     *            true if escapes are to be preserved
+     * @param preserveEscapes true if escapes are to be preserved
      * @return this, to enable chaining
      */
     public StringSubstitutor setPreserveEscapes(final boolean preserveEscapes) {
@@ -1140,8 +1079,7 @@ public class StringSubstitutor {
      * The variable default value delimiter is the character or characters that delimite the variable name and the
      * variable default value. This method allows a single character variable default value delimiter to be easily set.
      *
-     * @param valueDelimiter
-     *            the variable default value delimiter character to use
+     * @param valueDelimiter the variable default value delimiter character to use
      * @return this, to enable chaining
      */
     public StringSubstitutor setValueDelimiter(final char valueDelimiter) {
@@ -1157,8 +1095,7 @@ public class StringSubstitutor {
      * If the <code>valueDelimiter</code> is null or empty string, then the variable default value resolution becomes
      * disabled.
      *
-     * @param valueDelimiter
-     *            the variable default value delimiter string to use, may be null or empty
+     * @param valueDelimiter the variable default value delimiter string to use, may be null or empty
      * @return this, to enable chaining
      */
     public StringSubstitutor setValueDelimiter(final String valueDelimiter) {
@@ -1178,8 +1115,7 @@ public class StringSubstitutor {
      * <p>
      * If the <code>valueDelimiterMatcher</code> is null, then the variable default value resolution becomes disabled.
      *
-     * @param valueDelimiterMatcher
-     *            variable default value delimiter matcher to use, may be null
+     * @param valueDelimiterMatcher variable default value delimiter matcher to use, may be null
      * @return this, to enable chaining
      */
     public StringSubstitutor setValueDelimiterMatcher(final StringMatcher valueDelimiterMatcher) {
@@ -1193,8 +1129,7 @@ public class StringSubstitutor {
      * The variable prefix is the character or characters that identify the start of a variable. This method allows a
      * single character prefix to be easily set.
      *
-     * @param prefix
-     *            the prefix character to use
+     * @param prefix the prefix character to use
      * @return this, to enable chaining
      */
     public StringSubstitutor setVariablePrefix(final char prefix) {
@@ -1207,11 +1142,9 @@ public class StringSubstitutor {
      * The variable prefix is the character or characters that identify the start of a variable. This method allows a
      * string prefix to be easily set.
      *
-     * @param prefix
-     *            the prefix for variables, not null
+     * @param prefix the prefix for variables, not null
      * @return this, to enable chaining
-     * @throws IllegalArgumentException
-     *             if the prefix is null
+     * @throws IllegalArgumentException if the prefix is null
      */
     public StringSubstitutor setVariablePrefix(final String prefix) {
         Validate.isTrue(prefix != null, "Variable prefix must not be null!");
@@ -1224,11 +1157,9 @@ public class StringSubstitutor {
      * The variable prefix is the character or characters that identify the start of a variable. This prefix is
      * expressed in terms of a matcher allowing advanced prefix matches.
      *
-     * @param prefixMatcher
-     *            the prefix matcher to use, null ignored
+     * @param prefixMatcher the prefix matcher to use, null ignored
      * @return this, to enable chaining
-     * @throws IllegalArgumentException
-     *             if the prefix matcher is null
+     * @throws IllegalArgumentException if the prefix matcher is null
      */
     public StringSubstitutor setVariablePrefixMatcher(final StringMatcher prefixMatcher) {
         Validate.isTrue(prefixMatcher != null, "Variable prefix matcher must not be null!");
@@ -1239,8 +1170,7 @@ public class StringSubstitutor {
     /**
      * Sets the VariableResolver that is used to lookup variables.
      *
-     * @param variableResolver
-     *            the VariableResolver
+     * @param variableResolver the VariableResolver
      * @return this, to enable chaining
      */
     public StringSubstitutor setVariableResolver(final StringLookup variableResolver) {
@@ -1254,8 +1184,7 @@ public class StringSubstitutor {
      * The variable suffix is the character or characters that identify the end of a variable. This method allows a
      * single character suffix to be easily set.
      *
-     * @param suffix
-     *            the suffix character to use
+     * @param suffix the suffix character to use
      * @return this, to enable chaining
      */
     public StringSubstitutor setVariableSuffix(final char suffix) {
@@ -1268,11 +1197,9 @@ public class StringSubstitutor {
      * The variable suffix is the character or characters that identify the end of a variable. This method allows a
      * string suffix to be easily set.
      *
-     * @param suffix
-     *            the suffix for variables, not null
+     * @param suffix the suffix for variables, not null
      * @return this, to enable chaining
-     * @throws IllegalArgumentException
-     *             if the suffix is null
+     * @throws IllegalArgumentException if the suffix is null
      */
     public StringSubstitutor setVariableSuffix(final String suffix) {
         Validate.isTrue(suffix != null, "Variable suffix must not be null!");
@@ -1285,11 +1212,9 @@ public class StringSubstitutor {
      * The variable suffix is the character or characters that identify the end of a variable. This suffix is expressed
      * in terms of a matcher allowing advanced suffix matches.
      *
-     * @param suffixMatcher
-     *            the suffix matcher to use, null ignored
+     * @param suffixMatcher the suffix matcher to use, null ignored
      * @return this, to enable chaining
-     * @throws IllegalArgumentException
-     *             if the suffix matcher is null
+     * @throws IllegalArgumentException if the suffix matcher is null
      */
     public StringSubstitutor setVariableSuffixMatcher(final StringMatcher suffixMatcher) {
         Validate.isTrue(suffixMatcher != null, "Variable suffix matcher must not be null!");
@@ -1307,12 +1232,9 @@ public class StringSubstitutor {
      * Writers of subclasses can override this method if they need access to the substitution process at the start or
      * end.
      *
-     * @param buf
-     *            the string builder to substitute into, not null
-     * @param offset
-     *            the start offset within the builder, must be valid
-     * @param length
-     *            the length within the builder to be processed, must be valid
+     * @param buf    the string builder to substitute into, not null
+     * @param offset the start offset within the builder, must be valid
+     * @param length the length within the builder to be processed, must be valid
      * @return true if altered
      */
     protected boolean substitute(final TextStringBuilder buf, final int offset, final int length) {
@@ -1323,18 +1245,13 @@ public class StringSubstitutor {
      * Recursive handler for multiple levels of interpolation. This is the main interpolation method, which resolves the
      * values of all variable references contained in the passed in text.
      *
-     * @param buf
-     *            the string builder to substitute into, not null
-     * @param offset
-     *            the start offset within the builder, must be valid
-     * @param length
-     *            the length within the builder to be processed, must be valid
-     * @param priorVariables
-     *            the stack keeping track of the replaced variables, may be null
+     * @param buf            the string builder to substitute into, not null
+     * @param offset         the start offset within the builder, must be valid
+     * @param length         the length within the builder to be processed, must be valid
+     * @param priorVariables the stack keeping track of the replaced variables, may be null
      * @return The length change that occurs, unless priorVariables is null when the int represents a boolean flag as to
      *         whether any change occurred.
-     * @throws IllegalArgumentException
-     *             if variable is not found when its allowed to throw exception
+     * @throws IllegalArgumentException if variable is not found when its allowed to throw exception
      */
     private int substitute(final TextStringBuilder buf, final int offset, final int length,
             List<String> priorVariables) {
diff --git a/src/main/java/org/apache/commons/text/lookup/InterpolatorStringLookup.java b/src/main/java/org/apache/commons/text/lookup/InterpolatorStringLookup.java
index ce6d63c..3b44447 100644
--- a/src/main/java/org/apache/commons/text/lookup/InterpolatorStringLookup.java
+++ b/src/main/java/org/apache/commons/text/lookup/InterpolatorStringLookup.java
@@ -96,7 +96,7 @@ class InterpolatorStringLookup extends AbstractStringLookup {
         this.defaultStringLookup = defaultStringLookup;
         this.stringLookupMap = new HashMap<>(stringLookupMap.size());
         for (final Entry<String, StringLookup> entry : stringLookupMap.entrySet()) {
-            this.stringLookupMap.put(entry.getKey().toLowerCase(Locale.ROOT), entry.getValue());
+            this.stringLookupMap.put(toKey(entry.getKey()), entry.getValue());
         }
         if (addDefaultLookups) {
             StringLookupFactory.INSTANCE.addDefaultStringLookups(this.stringLookupMap);
@@ -112,6 +112,10 @@ class InterpolatorStringLookup extends AbstractStringLookup {
         return stringLookupMap;
     }
 
+    static String toKey(final String key) {
+        return key.toLowerCase(Locale.ROOT);
+    }
+
     /**
      * Resolves the specified variable. This implementation will try to extract a variable prefix from the given
      * variable name (the first colon (':') is used as prefix separator). It then passes the name of the variable with
@@ -130,7 +134,7 @@ class InterpolatorStringLookup extends AbstractStringLookup {
 
         final int prefixPos = var.indexOf(PREFIX_SEPARATOR);
         if (prefixPos >= 0) {
-            final String prefix = var.substring(0, prefixPos).toLowerCase(Locale.ROOT);
+            final String prefix = toKey(var.substring(0, prefixPos));
             final String name = var.substring(prefixPos + 1);
             final StringLookup lookup = stringLookupMap.get(prefix);
             String value = null;
diff --git a/src/main/java/org/apache/commons/text/lookup/StringLookupFactory.java b/src/main/java/org/apache/commons/text/lookup/StringLookupFactory.java
index 443f808..ebda233 100644
--- a/src/main/java/org/apache/commons/text/lookup/StringLookupFactory.java
+++ b/src/main/java/org/apache/commons/text/lookup/StringLookupFactory.java
@@ -293,7 +293,7 @@ public final class StringLookupFactory {
             // "base64" is deprecated in favor of KEY_BASE64_DECODER.
             stringLookupMap.put("base64", Base64DecoderStringLookup.INSTANCE);
             for (final DefaultStringLookup stringLookup : DefaultStringLookup.values()) {
-                stringLookupMap.put(stringLookup.getKey(), stringLookup.getStringLookup());
+                stringLookupMap.put(InterpolatorStringLookup.toKey(stringLookup.getKey()), stringLookup.getStringLookup());
             }
         }
     }
@@ -698,10 +698,13 @@ public final class StringLookupFactory {
     /**
      * Returns the PropertiesStringLookup singleton instance.
      * <p>
-     * Looks up the value for the key in the format "DocumentPath:MyKey".
+     * Looks up the value for the key in the format "DocumentPath::MyKey".
      * </p>
      * <p>
-     * For example: "com/domain/document.properties:MyKey".
+     * Note the use of "::" instead of ":" to allow for "C:" drive letters in paths.
+     * </p>
+     * <p>
+     * For example: "com/domain/document.properties::MyKey".
      * </p>
      *
      * <p>
@@ -709,18 +712,18 @@ public final class StringLookupFactory {
      * </p>
      *
      * <pre>
-     * StringLookupFactory.INSTANCE.propertiesStringLookup().lookup("com/domain/document.properties:MyKey");
+     * StringLookupFactory.INSTANCE.propertiesStringLookup().lookup("com/domain/document.properties::MyKey");
      * </pre>
      * <p>
      * Using a {@link StringSubstitutor}:
      * </p>
      *
      * <pre>
-     * StringSubstitutor.createInterpolator().replace("... ${properties:com/domain/document.properties:MyKey} ..."));
+     * StringSubstitutor.createInterpolator().replace("... ${properties:com/domain/document.properties::MyKey} ..."));
      * </pre>
      * <p>
-     * The above examples convert {@code "com/domain/document.properties:MyKey"} to the key value in the properties file
-     * at the path "com/domain/document.properties".
+     * The above examples convert {@code "com/domain/document.properties::MyKey"} to the key value in the properties
+     * file at the path "com/domain/document.properties".
      * </p>
      *
      * @return The PropertiesStringLookup singleton instance.
@@ -832,17 +835,17 @@ public final class StringLookupFactory {
      * </p>
      *
      * <pre>
-     * StringLookupFactory.INSTANCE.systemPropertyStringLookup().lookup("USER");
+     * StringLookupFactory.INSTANCE.systemPropertyStringLookup().lookup("os.name");
      * </pre>
      * <p>
      * Using a {@link StringSubstitutor}:
      * </p>
      *
      * <pre>
-     * StringSubstitutor.createInterpolator().replace("... ${env:USER} ..."));
+     * StringSubstitutor.createInterpolator().replace("... ${sys:os.name} ..."));
      * </pre>
      * <p>
-     * The above examples convert {@code "USER"} to the current Linux user.
+     * The above examples convert {@code "os.name"} to the operating system name.
      * </p>
      *
      * @return The SystemPropertyStringLookup singleton instance.
@@ -934,17 +937,17 @@ public final class StringLookupFactory {
      * </p>
      *
      * <pre>
-     * StringLookupFactory.INSTANCE.urlStringLookup().lookup("UTF-8:http://www.google.com");
+     * StringLookupFactory.INSTANCE.urlStringLookup().lookup("UTF-8:https://www.apache.org");
      * </pre>
      * <p>
      * Using a {@link StringSubstitutor}:
      * </p>
      *
      * <pre>
-     * StringSubstitutor.createInterpolator().replace("... ${url:UTF-8:http://www.google.com} ..."));
+     * StringSubstitutor.createInterpolator().replace("... ${url:UTF-8:https://www.apache.org} ..."));
      * </pre>
      * <p>
-     * The above examples convert {@code "UTF-8:http://www.google.com"} to the contents of that page.
+     * The above examples convert {@code "UTF-8:https://www.apache.org"} to the contents of that page.
      * </p>
      *
      * @return The UrlStringLookup singleton instance.
diff --git a/src/site/xdoc/userguide.xml b/src/site/xdoc/userguide.xml
index 9784950..56438cd 100644
--- a/src/site/xdoc/userguide.xml
+++ b/src/site/xdoc/userguide.xml
@@ -181,7 +181,33 @@ limitations under the License.
         where you can select which lookup are used from 
         <a href="http://commons.apache.org/proper/commons-text/apidocs/org/apache/commons/text/lookup/StringLookupFactory.html">StringLookupFactory</a>.</p>
       <p>
+        The SS lets you build complex strings:
       </p>
+      <code>
+final StringSubstitutor interpolator = StringSubstitutor.createInterpolator();
+interpolator.setEnableSubstitutionInVariables(true); // Allows for nested $'s.
+final String text = interpolator.replace(
+    "Base64 Decoder:        ${base64Decoder:SGVsbG9Xb3JsZCE=}\n" +
+    "Base64 Encoder:        ${base64Encoder:HelloWorld!}\n" +
+    "Java Constant:         ${const:java.awt.event.KeyEvent.VK_ESCAPE}\n" +
+    "Date:                  ${date:yyyy-MM-dd}\n" +
+    "DNS:                   ${dns:address|apache.org}\n" +
+    "Environment Variable:  ${env:USERNAME}\n" +
+    "File Content:          ${file:UTF-8:src/test/resources/document.properties}\n" +
+    "Java:                  ${java:version}\n" +
+    "Localhost:             ${localhost:canonical-name}\n" +
+    "Properties File:       ${properties:src/test/resources/document.properties::mykey}\n" +
+    "Resource Bundle:       ${resourceBundle:org.example.testResourceBundleLookup:mykey}\n" +
+    "Script:                ${script:javascript:3 + 4}\n" +
+    "System Property:       ${sys:user.dir}\n" +
+    "URL Decoder:           ${urlDecoder:Hello%20World%21}\n" +
+    "URL Encoder:           ${urlEncoder:Hello World!}\n" +
+    "URL Content (HTTP):    ${url:UTF-8:http://www.apache.org}\n" +
+    "URL Content (HTTPS):   ${url:UTF-8:https://www.apache.org}\n" +
+    "URL Content (File):    ${url:UTF-8:file:///${sys:user.dir}/src/test/resources/document.properties}\n" +
+    "XML XPath:             ${xml:src/test/resources/document.xml:/root/path/to/node}\n"
+);
+      </code>
     </section>
 
     <section name="text.similarity">
diff --git a/src/test/java/org/apache/commons/text/StrLookupTest.java b/src/test/java/org/apache/commons/text/StrLookupTest.java
index 876bfc4..1ebd0be 100644
--- a/src/test/java/org/apache/commons/text/StrLookupTest.java
+++ b/src/test/java/org/apache/commons/text/StrLookupTest.java
@@ -106,7 +106,7 @@ public class StrLookupTest  {
 
     @Test
     public void testResourceBundleLookup() {
-        final ResourceBundle map = ResourceBundle.getBundle("testResourceBundleLookup");
+        final ResourceBundle map = ResourceBundle.getBundle("org.example.testResourceBundleLookup");
         assertEquals("value", StrLookup.resourceBundleLookup(map).lookup("key"));
         assertEquals("2", StrLookup.resourceBundleLookup(map).lookup("number"));
         assertNull(StrLookup.resourceBundleLookup(map).lookup(null));
diff --git a/src/test/java/org/apache/commons/text/StringSubstitutorWithInterpolatorStringLookupTest.java b/src/test/java/org/apache/commons/text/StringSubstitutorWithInterpolatorStringLookupTest.java
index feb73eb..8fac019 100644
--- a/src/test/java/org/apache/commons/text/StringSubstitutorWithInterpolatorStringLookupTest.java
+++ b/src/test/java/org/apache/commons/text/StringSubstitutorWithInterpolatorStringLookupTest.java
@@ -59,9 +59,46 @@ public class StringSubstitutorWithInterpolatorStringLookupTest {
     }
 
     @Test
+    public void testDefaultInterpolator() {
+        // Used to cut and paste into the docs.
+        // @formatter:off
+        final StringSubstitutor interpolator = StringSubstitutor.createInterpolator();
+        interpolator.setEnableSubstitutionInVariables(true); // Allows for nested $'s.
+        final String text = interpolator.replace(
+                "Base64 Decoder:        ${base64Decoder:SGVsbG9Xb3JsZCE=}\n" +
+                "Base64 Encoder:        ${base64Encoder:HelloWorld!}\n" +
+                "Java Constant:         ${const:java.awt.event.KeyEvent.VK_ESCAPE}\n" +
+                "Date:                  ${date:yyyy-MM-dd}\n" +
+                "DNS:                   ${dns:address|apache.org}\n" +
+                "Environment Variable:  ${env:USERNAME}\n" +
+                "File Content:          ${file:UTF-8:src/test/resources/document.properties}\n" +
+                "Java:                  ${java:version}\n" +
+                "Localhost:             ${localhost:canonical-name}\n" +
+                "Properties File:       ${properties:src/test/resources/document.properties::mykey}\n" +
+                "Resource Bundle:       ${resourceBundle:org.example.testResourceBundleLookup:mykey}\n" +
+                "Script:                ${script:javascript:3 + 4}\n" +
+                "System Property:       ${sys:user.dir}\n" +
+                "URL Decoder:           ${urlDecoder:Hello%20World%21}\n" +
+                "URL Encoder:           ${urlEncoder:Hello World!}\n" +
+                "URL Content (HTTP):    ${url:UTF-8:http://www.apache.org}\n" +
+                "URL Content (HTTPS):   ${url:UTF-8:https://www.apache.org}\n" +
+                "URL Content (File):    ${url:UTF-8:file:///${sys:user.dir}/src/test/resources/document.properties}\n" +
+                "XML XPath:             ${xml:src/test/resources/document.xml:/root/path/to/node}\n"
+        );
+        // @formatter:on
+        Assertions.assertNotNull(text);
+        // TEXT-171:
+        Assertions.assertFalse(text.contains("${base64Decoder:SGVsbG9Xb3JsZCE=}"));
+        Assertions.assertFalse(text.contains("${base64Encoder:HelloWorld!}"));
+        Assertions.assertFalse(text.contains("${urlDecoder:Hello%20World%21}"));
+        Assertions.assertFalse(text.contains("${urlEncoder:Hello World!}"));
+        Assertions.assertFalse(text.contains("${resourceBundle:org.example.testResourceBundleLookup:mykey}"));
+        // System.out.println(text);
+    }
+    @Test
     public void testDefaultValueForMissingKeyInResourceBundle() {
         final StringLookup interpolatorStringLookup = StringLookupFactory.INSTANCE.interpolatorStringLookup(
-            StringLookupFactory.INSTANCE.resourceBundleStringLookup("testResourceBundleLookup"));
+            StringLookupFactory.INSTANCE.resourceBundleStringLookup("org.example.testResourceBundleLookup"));
         assertEquals("${missingKey:-defaultValue}", interpolatorStringLookup.lookup("keyWithMissingKey"));
         final StringSubstitutor stringSubstitutor = new StringSubstitutor(interpolatorStringLookup);
         // The following would throw a MissingResourceException before TEXT-165.
diff --git a/src/test/java/org/apache/commons/text/lookup/ResourceBundleStringLookupTest.java b/src/test/java/org/apache/commons/text/lookup/ResourceBundleStringLookupTest.java
index c6dbd13..3977fbc 100644
--- a/src/test/java/org/apache/commons/text/lookup/ResourceBundleStringLookupTest.java
+++ b/src/test/java/org/apache/commons/text/lookup/ResourceBundleStringLookupTest.java
@@ -30,7 +30,7 @@ import org.junit.jupiter.api.Test;
  */
 public class ResourceBundleStringLookupTest {
 
-    private static final String TEST_RESOURCE_BUNDLE = "testResourceBundleLookup";
+    private static final String TEST_RESOURCE_BUNDLE = "org.example.testResourceBundleLookup";
 
     @Test
     public void testAny() {
diff --git a/src/test/java/org/apache/commons/text/lookup/StringLookupFactoryTest.java b/src/test/java/org/apache/commons/text/lookup/StringLookupFactoryTest.java
index e7731e7..527959c 100644
--- a/src/test/java/org/apache/commons/text/lookup/StringLookupFactoryTest.java
+++ b/src/test/java/org/apache/commons/text/lookup/StringLookupFactoryTest.java
@@ -30,23 +30,23 @@ import org.junit.jupiter.api.Test;
 public class StringLookupFactoryTest {
 
     public static void assertDefaultKeys(final Map<String, StringLookup> stringLookupMap) {
-        assertTrue(stringLookupMap.containsKey(StringLookupFactory.KEY_BASE64_DECODER));
-        assertTrue(stringLookupMap.containsKey(StringLookupFactory.KEY_BASE64_ENCODER));
-        assertTrue(stringLookupMap.containsKey(StringLookupFactory.KEY_CONST));
-        assertTrue(stringLookupMap.containsKey(StringLookupFactory.KEY_DATE));
-        assertTrue(stringLookupMap.containsKey(StringLookupFactory.KEY_DNS));
-        assertTrue(stringLookupMap.containsKey(StringLookupFactory.KEY_ENV));
-        assertTrue(stringLookupMap.containsKey(StringLookupFactory.KEY_FILE));
-        assertTrue(stringLookupMap.containsKey(StringLookupFactory.KEY_JAVA));
-        assertTrue(stringLookupMap.containsKey(StringLookupFactory.KEY_LOCALHOST));
-        assertTrue(stringLookupMap.containsKey(StringLookupFactory.KEY_PROPERTIES));
-        assertTrue(stringLookupMap.containsKey(StringLookupFactory.KEY_RESOURCE_BUNDLE));
-        assertTrue(stringLookupMap.containsKey(StringLookupFactory.KEY_SCRIPT));
-        assertTrue(stringLookupMap.containsKey(StringLookupFactory.KEY_SYS));
-        assertTrue(stringLookupMap.containsKey(StringLookupFactory.KEY_URL));
-        assertTrue(stringLookupMap.containsKey(StringLookupFactory.KEY_URL_DECODER));
-        assertTrue(stringLookupMap.containsKey(StringLookupFactory.KEY_URL_ENCODER));
-        assertTrue(stringLookupMap.containsKey(StringLookupFactory.KEY_XML));
+        assertTrue(stringLookupMap.containsKey(InterpolatorStringLookup.toKey(StringLookupFactory.KEY_BASE64_DECODER)));
+        assertTrue(stringLookupMap.containsKey(InterpolatorStringLookup.toKey(StringLookupFactory.KEY_BASE64_ENCODER)));
+        assertTrue(stringLookupMap.containsKey(InterpolatorStringLookup.toKey(StringLookupFactory.KEY_CONST)));
+        assertTrue(stringLookupMap.containsKey(InterpolatorStringLookup.toKey(StringLookupFactory.KEY_DATE)));
+        assertTrue(stringLookupMap.containsKey(InterpolatorStringLookup.toKey(StringLookupFactory.KEY_DNS)));
+        assertTrue(stringLookupMap.containsKey(InterpolatorStringLookup.toKey(StringLookupFactory.KEY_ENV)));
+        assertTrue(stringLookupMap.containsKey(InterpolatorStringLookup.toKey(StringLookupFactory.KEY_FILE)));
+        assertTrue(stringLookupMap.containsKey(InterpolatorStringLookup.toKey(StringLookupFactory.KEY_JAVA)));
+        assertTrue(stringLookupMap.containsKey(InterpolatorStringLookup.toKey(StringLookupFactory.KEY_LOCALHOST)));
+        assertTrue(stringLookupMap.containsKey(InterpolatorStringLookup.toKey(StringLookupFactory.KEY_PROPERTIES)));
+        assertTrue(stringLookupMap.containsKey(InterpolatorStringLookup.toKey(StringLookupFactory.KEY_RESOURCE_BUNDLE)));
+        assertTrue(stringLookupMap.containsKey(InterpolatorStringLookup.toKey(StringLookupFactory.KEY_SCRIPT)));
+        assertTrue(stringLookupMap.containsKey(InterpolatorStringLookup.toKey(StringLookupFactory.KEY_SYS)));
+        assertTrue(stringLookupMap.containsKey(InterpolatorStringLookup.toKey(StringLookupFactory.KEY_URL)));
+        assertTrue(stringLookupMap.containsKey(InterpolatorStringLookup.toKey(StringLookupFactory.KEY_URL_DECODER)));
+        assertTrue(stringLookupMap.containsKey(InterpolatorStringLookup.toKey(StringLookupFactory.KEY_URL_ENCODER)));
+        assertTrue(stringLookupMap.containsKey(InterpolatorStringLookup.toKey(StringLookupFactory.KEY_XML)));
     }
 
     @Test
diff --git a/src/test/java/org/apache/commons/text/lookup/UrlStringLookupTest.java b/src/test/java/org/apache/commons/text/lookup/UrlStringLookupTest.java
index 13be021..cf7444c 100644
--- a/src/test/java/org/apache/commons/text/lookup/UrlStringLookupTest.java
+++ b/src/test/java/org/apache/commons/text/lookup/UrlStringLookupTest.java
@@ -59,7 +59,8 @@ public class UrlStringLookupTest {
 
     @Test
     public void testHttpScheme() throws Exception {
-        Assertions.assertNotNull(UrlStringLookup.INSTANCE.lookup("UTF-8:http://www.google.com"));
+        Assertions.assertNotNull(UrlStringLookup.INSTANCE.lookup("UTF-8:https://www.apache.org"));
+        Assertions.assertNotNull(UrlStringLookup.INSTANCE.lookup("UTF-8:https://www.google.com"));
     }
 
     @Test
diff --git a/src/test/resources/testResourceBundleLookup.properties b/src/test/resources/org/example/testResourceBundleLookup.properties
similarity index 97%
rename from src/test/resources/testResourceBundleLookup.properties
rename to src/test/resources/org/example/testResourceBundleLookup.properties
index ffc5e13..822001e 100644
--- a/src/test/resources/testResourceBundleLookup.properties
+++ b/src/test/resources/org/example/testResourceBundleLookup.properties
@@ -16,3 +16,4 @@
 key = value
 number = 2
 keyWithMissingKey = ${missingKey:-defaultValue}
+mykey = Hello World!