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 2018/02/12 17:22:08 UTC

[2/5] [text] [TEXT-114] Add a replacement for StrSubstitutor based on interfaces: StringSubstitutor.

http://git-wip-us.apache.org/repos/asf/commons-text/blob/4b67dd51/src/test/java/org/apache/commons/text/StrSubstitutorTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/commons/text/StrSubstitutorTest.java b/src/test/java/org/apache/commons/text/StrSubstitutorTest.java
index 4ab52ed..cbd95c3 100644
--- a/src/test/java/org/apache/commons/text/StrSubstitutorTest.java
+++ b/src/test/java/org/apache/commons/text/StrSubstitutorTest.java
@@ -1,876 +1,832 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.commons.text;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertSame;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
-
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Properties;
-
-import org.apache.commons.lang3.mutable.MutableObject;
-import org.apache.commons.text.lookup.StringLookup;
-import org.apache.commons.text.lookup.StringLookupFactory;
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-
-/**
- * Test class for {@link StrSubstitutor}.
- */
-public class StrSubstitutorTest {
-
-    private Map<String, String> values;
-
-    @Before
-    public void setUp() throws Exception {
-        values = new HashMap<>();
-        values.put("animal", "quick brown fox");
-        values.put("target", "lazy dog");
-    }
-
-    @After
-    public void tearDown() throws Exception {
-        values = null;
-    }
-
-    //-----------------------------------------------------------------------
-    /**
-     * Tests simple key replace.
-     */
-    @Test
-    public void testReplaceSimple() {
-        doTestReplace("The quick brown fox jumps over the lazy dog.", "The ${animal} jumps over the ${target}.", true);
-    }
-
-    /**
-     * Tests simple key replace.
-     */
-    @Test
-    public void testReplaceSolo() {
-        doTestReplace("quick brown fox", "${animal}", false);
-    }
-
-    /**
-     * Tests replace with no variables.
-     */
-    @Test
-    public void testReplaceNoVariables() {
-        doTestNoReplace("The balloon arrived.");
-    }
-
-    /**
-     * Tests replace with null.
-     */
-    @Test
-    public void testReplaceNull() {
-        doTestNoReplace(null);
-    }
-
-    /**
-     * Tests replace with null.
-     */
-    @Test
-    public void testReplaceEmpty() {
-        doTestNoReplace("");
-    }
-
-    /**
-     * Tests key replace changing map after initialization (not recommended).
-     */
-    @Test
-    public void testReplaceChangedMap() {
-        final StrSubstitutor sub = new StrSubstitutor(values);
-        values.put("target", "moon");
-        assertEquals("The quick brown fox jumps over the moon.",
-                sub.replace("The ${animal} jumps over the ${target}."));
-    }
-
-    /**
-     * Tests unknown key replace.
-     */
-    @Test
-    public void testReplaceUnknownKey() {
-        doTestReplace("The ${person} jumps over the lazy dog.", "The ${person} jumps over the ${target}.", true);
-        doTestReplace("The ${person} jumps over the lazy dog. 1234567890.",
-                "The ${person} jumps over the ${target}. ${undefined.number:-1234567890}.", true);
-    }
-
-    /**
-     * Tests adjacent keys.
-     */
-    @Test
-    public void testReplaceAdjacentAtStart() {
-        values.put("code", "GBP");
-        values.put("amount", "12.50");
-        final StrSubstitutor sub = new StrSubstitutor(values);
-        assertEquals("GBP12.50 charged", sub.replace("${code}${amount} charged"));
-    }
-
-    /**
-     * Tests adjacent keys.
-     */
-    @Test
-    public void testReplaceAdjacentAtEnd() {
-        values.put("code", "GBP");
-        values.put("amount", "12.50");
-        final StrSubstitutor sub = new StrSubstitutor(values);
-        assertEquals("Amount is GBP12.50", sub.replace("Amount is ${code}${amount}"));
-    }
-
-    /**
-     * Tests simple recursive replace.
-     */
-    @Test
-    public void testReplaceRecursive() {
-        values.put("animal", "${critter}");
-        values.put("target", "${pet}");
-        values.put("pet", "${petCharacteristic} dog");
-        values.put("petCharacteristic", "lazy");
-        values.put("critter", "${critterSpeed} ${critterColor} ${critterType}");
-        values.put("critterSpeed", "quick");
-        values.put("critterColor", "brown");
-        values.put("critterType", "fox");
-        doTestReplace("The quick brown fox jumps over the lazy dog.", "The ${animal} jumps over the ${target}.", true);
-
-        values.put("pet", "${petCharacteristicUnknown:-lazy} dog");
-        doTestReplace("The quick brown fox jumps over the lazy dog.", "The ${animal} jumps over the ${target}.", true);
-    }
-
-    @Test
-    public void testDisableSubstitutionInValues() {
-        final StrSubstitutor sub = new StrSubstitutor(values);
-        sub.setDisableSubstitutionInValues(true);
-        values.put("animal", "${critter}");
-        values.put("target", "${pet}");
-        values.put("pet", "${petCharacteristic} dog");
-        values.put("petCharacteristic", "lazy");
-        values.put("critter", "${critterSpeed} ${critterColor} ${critterType}");
-        values.put("critterSpeed", "quick");
-        values.put("critterColor", "brown");
-        values.put("critterType", "fox");
-        doTestReplace(sub, "The ${critter} jumps over the ${pet}.", "The ${animal} jumps over the ${target}.", true);
-    }
-
-    /**
-     * Tests escaping.
-     */
-    @Test
-    public void testReplaceEscaping() {
-        doTestReplace("The ${animal} jumps over the lazy dog.", "The $${animal} jumps over the ${target}.", true);
-    }
-
-    /**
-     * Tests escaping.
-     */
-    @Test
-    public void testReplaceSoloEscaping() {
-        doTestReplace("${animal}", "$${animal}", false);
-    }
-
-    /**
-     * Tests complex escaping.
-     */
-    @Test
-    public void testReplaceComplexEscaping() {
-        doTestReplace("The ${quick brown fox} jumps over the lazy dog.",
-                "The $${${animal}} jumps over the ${target}.", true);
-        doTestReplace("The ${quick brown fox} jumps over the lazy dog. ${1234567890}.",
-                "The $${${animal}} jumps over the ${target}. $${${undefined.number:-1234567890}}.", true);
-    }
-
-    /**
-     * Tests when no prefix or suffix.
-     */
-    @Test
-    public void testReplaceNoPrefixNoSuffix() {
-        doTestReplace("The animal jumps over the lazy dog.", "The animal jumps over the ${target}.", true);
-    }
-
-    /**
-     * Tests when no incomplete prefix.
-     */
-    @Test
-    public void testReplaceIncompletePrefix() {
-        doTestReplace("The {animal} jumps over the lazy dog.", "The {animal} jumps over the ${target}.", true);
-    }
-
-    /**
-     * Tests when prefix but no suffix.
-     */
-    @Test
-    public void testReplacePrefixNoSuffix() {
-        doTestReplace("The ${animal jumps over the ${target} lazy dog.",
-                "The ${animal jumps over the ${target} ${target}.", true);
-    }
-
-    /**
-     * Tests when suffix but no prefix.
-     */
-    @Test
-    public void testReplaceNoPrefixSuffix() {
-        doTestReplace("The animal} jumps over the lazy dog.", "The animal} jumps over the ${target}.", true);
-    }
-
-    /**
-     * Tests when no variable name.
-     */
-    @Test
-    public void testReplaceEmptyKeys() {
-        doTestReplace("The ${} jumps over the lazy dog.", "The ${} jumps over the ${target}.", true);
-        doTestReplace("The animal jumps over the lazy dog.", "The ${:-animal} jumps over the ${target}.", true);
-    }
-
-    /**
-     * Tests replace creates output same as input.
-     */
-    @Test
-    public void testReplaceToIdentical() {
-        values.put("animal", "$${${thing}}");
-        values.put("thing", "animal");
-        doTestReplace("The ${animal} jumps.", "The ${animal} jumps.", true);
-    }
-
-    /**
-     * Tests a cyclic replace operation.
-     * The cycle should be detected and cause an exception to be thrown.
-     */
-    @Test
-    public void testCyclicReplacement() {
-        final Map<String, String> map = new HashMap<>();
-        map.put("animal", "${critter}");
-        map.put("target", "${pet}");
-        map.put("pet", "${petCharacteristic} dog");
-        map.put("petCharacteristic", "lazy");
-        map.put("critter", "${critterSpeed} ${critterColor} ${critterType}");
-        map.put("critterSpeed", "quick");
-        map.put("critterColor", "brown");
-        map.put("critterType", "${animal}");
-        StrSubstitutor sub = new StrSubstitutor(map);
-        try {
-            sub.replace("The ${animal} jumps over the ${target}.");
-            fail("Cyclic replacement was not detected!");
-        } catch (final IllegalStateException ex) {
-            // expected
-        }
-
-        // also check even when default value is set.
-        map.put("critterType", "${animal:-fox}");
-        sub = new StrSubstitutor(map);
-        try {
-            sub.replace("The ${animal} jumps over the ${target}.");
-            fail("Cyclic replacement was not detected!");
-        } catch (final IllegalStateException ex) {
-            // expected
-        }
-    }
-
-    /**
-     * Tests interpolation with weird boundary patterns.
-     */
-    @Test
-    public void testReplaceWeirdPattens() {
-        doTestNoReplace("");
-        doTestNoReplace("${}");
-        doTestNoReplace("${ }");
-        doTestNoReplace("${\t}");
-        doTestNoReplace("${\n}");
-        doTestNoReplace("${\b}");
-        doTestNoReplace("${");
-        doTestNoReplace("$}");
-        doTestNoReplace("}");
-        doTestNoReplace("${}$");
-        doTestNoReplace("${${");
-        doTestNoReplace("${${}}");
-        doTestNoReplace("${$${}}");
-        doTestNoReplace("${$$${}}");
-        doTestNoReplace("${$$${$}}");
-        doTestNoReplace("${${}}");
-        doTestNoReplace("${${ }}");
-    }
-
-    /**
-     * Tests simple key replace.
-     */
-    @Test
-    public void testReplacePartialString_noReplace() {
-        final StrSubstitutor sub = new StrSubstitutor();
-        assertEquals("${animal} jumps", sub.replace("The ${animal} jumps over the ${target}.", 4, 15));
-    }
-
-    /**
-     * Tests whether a variable can be replaced in a variable name.
-     */
-    @Test
-    public void testReplaceInVariable() {
-        values.put("animal.1", "fox");
-        values.put("animal.2", "mouse");
-        values.put("species", "2");
-        final StrSubstitutor sub = new StrSubstitutor(values);
-        sub.setEnableSubstitutionInVariables(true);
-        assertEquals(
-                "Wrong result (1)",
-                "The mouse jumps over the lazy dog.",
-                sub.replace("The ${animal.${species}} jumps over the ${target}."));
-        values.put("species", "1");
-        assertEquals(
-                "Wrong result (2)",
-                "The fox jumps over the lazy dog.",
-                sub.replace("The ${animal.${species}} jumps over the ${target}."));
-        assertEquals(
-                "Wrong result (3)",
-                "The fox jumps over the lazy dog.",
-                sub.replace("The ${unknown.animal.${unknown.species:-1}:-fox} "
-                        + "jumps over the ${unknow.target:-lazy dog}."));
-    }
-
-    /**
-     * Tests whether substitution in variable names is disabled per default.
-     */
-    @Test
-    public void testReplaceInVariableDisabled() {
-        values.put("animal.1", "fox");
-        values.put("animal.2", "mouse");
-        values.put("species", "2");
-        final StrSubstitutor sub = new StrSubstitutor(values);
-        assertEquals(
-                "Wrong result (1)",
-                "The ${animal.${species}} jumps over the lazy dog.",
-                sub.replace("The ${animal.${species}} jumps over the ${target}."));
-        assertEquals(
-                "Wrong result (2)",
-                "The ${animal.${species:-1}} jumps over the lazy dog.",
-                sub.replace("The ${animal.${species:-1}} jumps over the ${target}."));
-    }
-
-    /**
-     * Tests complex and recursive substitution in variable names.
-     */
-    @Test
-    public void testReplaceInVariableRecursive() {
-        values.put("animal.2", "brown fox");
-        values.put("animal.1", "white mouse");
-        values.put("color", "white");
-        values.put("species.white", "1");
-        values.put("species.brown", "2");
-        final StrSubstitutor sub = new StrSubstitutor(values);
-        sub.setEnableSubstitutionInVariables(true);
-        assertEquals(
-                "Wrong result (1)",
-                "The white mouse jumps over the lazy dog.",
-                sub.replace("The ${animal.${species.${color}}} jumps over the ${target}."));
-        assertEquals(
-                "Wrong result (2)",
-                "The brown fox jumps over the lazy dog.",
-                sub.replace("The ${animal.${species.${unknownColor:-brown}}} jumps over the ${target}."));
-    }
-
-    @Test
-    public void testDefaultValueDelimiters() {
-        final Map<String, String> map = new HashMap<>();
-        map.put("animal", "fox");
-        map.put("target", "dog");
-
-        StrSubstitutor sub = new StrSubstitutor(map, "${", "}", '$');
-        assertEquals("The fox jumps over the lazy dog. 1234567890.",
-                sub.replace("The ${animal} jumps over the lazy ${target}. ${undefined.number:-1234567890}."));
-
-        sub = new StrSubstitutor(map, "${", "}", '$', "?:");
-        assertEquals("The fox jumps over the lazy dog. 1234567890.",
-                sub.replace("The ${animal} jumps over the lazy ${target}. ${undefined.number?:1234567890}."));
-
-        sub = new StrSubstitutor(map, "${", "}", '$', "||");
-        assertEquals("The fox jumps over the lazy dog. 1234567890.",
-                sub.replace("The ${animal} jumps over the lazy ${target}. ${undefined.number||1234567890}."));
-
-        sub = new StrSubstitutor(map, "${", "}", '$', "!");
-        assertEquals("The fox jumps over the lazy dog. 1234567890.",
-                sub.replace("The ${animal} jumps over the lazy ${target}. ${undefined.number!1234567890}."));
-
-        sub = new StrSubstitutor(map, "${", "}", '$', "");
-        sub.setValueDelimiterMatcher(null);
-        assertEquals("The fox jumps over the lazy dog. ${undefined.number!1234567890}.",
-                sub.replace("The ${animal} jumps over the lazy ${target}. ${undefined.number!1234567890}."));
-
-        sub = new StrSubstitutor(map, "${", "}", '$');
-        sub.setValueDelimiterMatcher(null);
-        assertEquals("The fox jumps over the lazy dog. ${undefined.number!1234567890}.",
-                sub.replace("The ${animal} jumps over the lazy ${target}. ${undefined.number!1234567890}."));
-    }
-
-    //-----------------------------------------------------------------------
-    /**
-     * Tests protected.
-     */
-    @Test
-    public void testResolveVariable() {
-        final StrBuilder builder = new StrBuilder("Hi ${name}!");
-        final Map<String, String> map = new HashMap<>();
-        map.put("name", "commons");
-        final StrSubstitutor sub = new StrSubstitutor(map) {
-            @Override
-            protected String resolveVariable(final String variableName, final StrBuilder buf, final int startPos,
-                    final int endPos) {
-                assertEquals("name", variableName);
-                assertSame(builder, buf);
-                assertEquals(3, startPos);
-                assertEquals(10, endPos);
-                return "jakarta";
-            }
-        };
-        sub.replaceIn(builder);
-        assertEquals("Hi jakarta!", builder.toString());
-    }
-
-    //-----------------------------------------------------------------------
-    /**
-     * Tests constructor.
-     */
-    @Test
-    public void testConstructorNoArgs() {
-        final StrSubstitutor sub = new StrSubstitutor();
-        assertEquals("Hi ${name}", sub.replace("Hi ${name}"));
-    }
-
-    /**
-     * Tests constructor.
-     */
-    @Test
-    public void testConstructorMapPrefixSuffix() {
-        final Map<String, String> map = new HashMap<>();
-        map.put("name", "commons");
-        final StrSubstitutor sub = new StrSubstitutor(map, "<", ">");
-        assertEquals("Hi < commons", sub.replace("Hi $< <name>"));
-    }
-
-    /**
-     * Tests constructor.
-     */
-    @Test
-    public void testConstructorMapFull() {
-        final Map<String, String> map = new HashMap<>();
-        map.put("name", "commons");
-        StrSubstitutor sub = new StrSubstitutor(map, "<", ">", '!');
-        assertEquals("Hi < commons", sub.replace("Hi !< <name>"));
-        sub = new StrSubstitutor(map, "<", ">", '!', "||");
-        assertEquals("Hi < commons", sub.replace("Hi !< <name2||commons>"));
-    }
-
-    //-----------------------------------------------------------------------
-    /**
-     * Tests get set.
-     */
-    @Test
-    public void testGetSetEscape() {
-        final StrSubstitutor sub = new StrSubstitutor();
-        assertEquals('$', sub.getEscapeChar());
-        sub.setEscapeChar('<');
-        assertEquals('<', sub.getEscapeChar());
-    }
-
-    /**
-     * Tests get set.
-     */
-    @Test
-    public void testGetSetPrefix() {
-        final StrSubstitutor sub = new StrSubstitutor();
-        assertTrue(sub.getVariablePrefixMatcher() instanceof StrMatcher.StringMatcher);
-        sub.setVariablePrefix('<');
-        assertTrue(sub.getVariablePrefixMatcher() instanceof StrMatcher.CharMatcher);
-
-        sub.setVariablePrefix("<<");
-        assertTrue(sub.getVariablePrefixMatcher() instanceof StrMatcher.StringMatcher);
-        try {
-            sub.setVariablePrefix((String) null);
-            fail();
-        } catch (final IllegalArgumentException ex) {
-            // expected
-        }
-        assertTrue(sub.getVariablePrefixMatcher() instanceof StrMatcher.StringMatcher);
-
-        final StrMatcher matcher = StrMatcher.commaMatcher();
-        sub.setVariablePrefixMatcher(matcher);
-        assertSame(matcher, sub.getVariablePrefixMatcher());
-        try {
-            sub.setVariablePrefixMatcher((StrMatcher) null);
-            fail();
-        } catch (final IllegalArgumentException ex) {
-            // expected
-        }
-        assertSame(matcher, sub.getVariablePrefixMatcher());
-    }
-
-    /**
-     * Tests get set.
-     */
-    @Test
-    public void testGetSetSuffix() {
-        final StrSubstitutor sub = new StrSubstitutor();
-        assertTrue(sub.getVariableSuffixMatcher() instanceof StrMatcher.StringMatcher);
-        sub.setVariableSuffix('<');
-        assertTrue(sub.getVariableSuffixMatcher() instanceof StrMatcher.CharMatcher);
-
-        sub.setVariableSuffix("<<");
-        assertTrue(sub.getVariableSuffixMatcher() instanceof StrMatcher.StringMatcher);
-        try {
-            sub.setVariableSuffix((String) null);
-            fail();
-        } catch (final IllegalArgumentException ex) {
-            // expected
-        }
-        assertTrue(sub.getVariableSuffixMatcher() instanceof StrMatcher.StringMatcher);
-
-        final StrMatcher matcher = StrMatcher.commaMatcher();
-        sub.setVariableSuffixMatcher(matcher);
-        assertSame(matcher, sub.getVariableSuffixMatcher());
-        try {
-            sub.setVariableSuffixMatcher((StrMatcher) null);
-            fail();
-        } catch (final IllegalArgumentException ex) {
-            // expected
-        }
-        assertSame(matcher, sub.getVariableSuffixMatcher());
-    }
-
-    /**
-     * Tests get set.
-     */
-    @Test
-    public void testGetSetValueDelimiter() {
-        final StrSubstitutor sub = new StrSubstitutor();
-        assertTrue(sub.getValueDelimiterMatcher() instanceof StrMatcher.StringMatcher);
-        sub.setValueDelimiter(':');
-        assertTrue(sub.getValueDelimiterMatcher() instanceof StrMatcher.CharMatcher);
-
-        sub.setValueDelimiter("||");
-        assertTrue(sub.getValueDelimiterMatcher() instanceof StrMatcher.StringMatcher);
-        sub.setValueDelimiter((String) null);
-        assertNull(sub.getValueDelimiterMatcher());
-
-        final StrMatcher matcher = StrMatcher.commaMatcher();
-        sub.setValueDelimiterMatcher(matcher);
-        assertSame(matcher, sub.getValueDelimiterMatcher());
-        sub.setValueDelimiterMatcher((StrMatcher) null);
-        assertNull(sub.getValueDelimiterMatcher());
-    }
-
-    //-----------------------------------------------------------------------
-    /**
-     * Tests static.
-     */
-    @Test
-    public void testStaticReplace() {
-        final Map<String, String> map = new HashMap<>();
-        map.put("name", "commons");
-        assertEquals("Hi commons!", StrSubstitutor.replace("Hi ${name}!", map));
-    }
-
-    /**
-     * Tests static.
-     */
-    @Test
-    public void testStaticReplacePrefixSuffix() {
-        final Map<String, String> map = new HashMap<>();
-        map.put("name", "commons");
-        assertEquals("Hi commons!", StrSubstitutor.replace("Hi <name>!", map, "<", ">"));
-    }
-
-    /**
-     * Tests interpolation with system properties.
-     */
-    @Test
-    public void testStaticReplaceSystemProperties() {
-        final StrBuilder buf = new StrBuilder();
-        buf.append("Hi ").append(System.getProperty("user.name"));
-        buf.append(", you are working with ");
-        buf.append(System.getProperty("os.name"));
-        buf.append(", your home directory is ");
-        buf.append(System.getProperty("user.home")).append('.');
-        assertEquals(buf.toString(), StrSubstitutor.replaceSystemProperties("Hi ${user.name}, you are "
-            + "working with ${os.name}, your home "
-            + "directory is ${user.home}."));
-    }
-
-    /**
-     * Test for LANG-1055: StrSubstitutor.replaceSystemProperties does not work consistently
-     */
-    @Test
-    public void testLANG1055() {
-        System.setProperty("test_key", "test_value");
-
-        final String expected = StrSubstitutor.replace("test_key=${test_key}", System.getProperties());
-        final String actual = StrSubstitutor.replaceSystemProperties("test_key=${test_key}");
-        assertEquals(expected, actual);
-    }
-
-    /**
-     * Test the replace of a properties object
-     */
-    @Test
-    public void testSubstituteDefaultProperties() {
-        final String org = "${doesnotwork}";
-        System.setProperty("doesnotwork", "It works!");
-
-        // create a new Properties object with the System.getProperties as default
-        final Properties props = new Properties(System.getProperties());
-
-        assertEquals("It works!", StrSubstitutor.replace(org, props));
-    }
-
-    @Test
-    public void testSamePrefixAndSuffix() {
-        final Map<String, String> map = new HashMap<>();
-        map.put("greeting", "Hello");
-        map.put(" there ", "XXX");
-        map.put("name", "commons");
-        assertEquals("Hi commons!", StrSubstitutor.replace("Hi @name@!", map, "@", "@"));
-        assertEquals("Hello there commons!", StrSubstitutor.replace("@greeting@ there @name@!", map, "@", "@"));
-    }
-
-    @Test
-    public void testSubstitutePreserveEscape() {
-        final String org = "${not-escaped} $${escaped}";
-        final Map<String, String> map = new HashMap<>();
-        map.put("not-escaped", "value");
-
-        final StrSubstitutor sub = new StrSubstitutor(map, "${", "}", '$');
-        assertFalse(sub.isPreserveEscapes());
-        assertEquals("value ${escaped}", sub.replace(org));
-
-        sub.setPreserveEscapes(true);
-        assertTrue(sub.isPreserveEscapes());
-        assertEquals("value $${escaped}", sub.replace(org));
-    }
-
-    private void doTestReplace(final String expectedResult, final String replaceTemplate, final boolean substring) {
-        final StrSubstitutor sub = new StrSubstitutor(values);
-        doTestReplace(sub, expectedResult, replaceTemplate, substring);
-    }
-
-    //-----------------------------------------------------------------------
-    private void doTestReplace(final StrSubstitutor sub, final String expectedResult, final String replaceTemplate,
-            final boolean substring) {
-        final String expectedShortResult = expectedResult.substring(1, expectedResult.length() - 1);
-
-        // replace using String
-        assertEquals(expectedResult, sub.replace(replaceTemplate));
-        if (substring) {
-            assertEquals(expectedShortResult, sub.replace(replaceTemplate, 1, replaceTemplate.length() - 2));
-        }
-
-        // replace using char[]
-        final char[] chars = replaceTemplate.toCharArray();
-        assertEquals(expectedResult, sub.replace(chars));
-        if (substring) {
-            assertEquals(expectedShortResult, sub.replace(chars, 1, chars.length - 2));
-        }
-
-        // replace using StringBuffer
-        StringBuffer buf = new StringBuffer(replaceTemplate);
-        assertEquals(expectedResult, sub.replace(buf));
-        if (substring) {
-            assertEquals(expectedShortResult, sub.replace(buf, 1, buf.length() - 2));
-        }
-
-        // replace using StringBuilder
-        StringBuilder builder = new StringBuilder(replaceTemplate);
-        assertEquals(expectedResult, sub.replace(builder));
-        if (substring) {
-            assertEquals(expectedShortResult, sub.replace(builder, 1, builder.length() - 2));
-        }
-
-        // replace using StrBuilder
-        StrBuilder bld = new StrBuilder(replaceTemplate);
-        assertEquals(expectedResult, sub.replace(bld));
-        if (substring) {
-            assertEquals(expectedShortResult, sub.replace(bld, 1, bld.length() - 2));
-        }
-
-        // replace using object
-        final MutableObject<String> obj = new MutableObject<>(replaceTemplate);  // toString returns template
-        assertEquals(expectedResult, sub.replace(obj));
-
-        // replace in StringBuffer
-        buf = new StringBuffer(replaceTemplate);
-        assertTrue(sub.replaceIn(buf));
-        assertEquals(expectedResult, buf.toString());
-        if (substring) {
-            buf = new StringBuffer(replaceTemplate);
-            assertTrue(sub.replaceIn(buf, 1, buf.length() - 2));
-            assertEquals(expectedResult, buf.toString());  // expect full result as remainder is untouched
-        }
-
-        // replace in StringBuilder
-        builder = new StringBuilder(replaceTemplate);
-        assertTrue(sub.replaceIn(builder));
-        assertEquals(expectedResult, builder.toString());
-        if (substring) {
-            builder = new StringBuilder(replaceTemplate);
-            assertTrue(sub.replaceIn(builder, 1, builder.length() - 2));
-            assertEquals(expectedResult, builder.toString());  // expect full result as remainder is untouched
-        }
-
-        // replace in StrBuilder
-        bld = new StrBuilder(replaceTemplate);
-        assertTrue(sub.replaceIn(bld));
-        assertEquals(expectedResult, bld.toString());
-        if (substring) {
-            bld = new StrBuilder(replaceTemplate);
-            assertTrue(sub.replaceIn(bld, 1, bld.length() - 2));
-            assertEquals(expectedResult, bld.toString());  // expect full result as remainder is untouched
-        }
-    }
-
-    private void doTestNoReplace(final String replaceTemplate) {
-        final StrSubstitutor sub = new StrSubstitutor(values);
-
-        if (replaceTemplate == null) {
-            assertNull(sub.replace((String) null));
-            assertNull(sub.replace((String) null, 0, 100));
-            assertNull(sub.replace((char[]) null));
-            assertNull(sub.replace((char[]) null, 0, 100));
-            assertNull(sub.replace((StringBuffer) null));
-            assertNull(sub.replace((StringBuffer) null, 0, 100));
-            assertNull(sub.replace((StrBuilder) null));
-            assertNull(sub.replace((StrBuilder) null, 0, 100));
-            assertNull(sub.replace((Object) null));
-            assertFalse(sub.replaceIn((StringBuffer) null));
-            assertFalse(sub.replaceIn((StringBuffer) null, 0, 100));
-            assertFalse(sub.replaceIn((StrBuilder) null));
-            assertFalse(sub.replaceIn((StrBuilder) null, 0, 100));
-        } else {
-            assertEquals(replaceTemplate, sub.replace(replaceTemplate));
-            final StrBuilder bld = new StrBuilder(replaceTemplate);
-            assertFalse(sub.replaceIn(bld));
-            assertEquals(replaceTemplate, bld.toString());
-        }
-    }
-
-    @Test
-    public void testReplaceInTakingTwoAndThreeIntsReturningFalse() {
-        final Map<String, Object> hashMap = new HashMap<>();
-        final StringLookup mapStringLookup = StringLookupFactory.INSTANCE.mapStringLookup(hashMap);
-        final StrMatcher strMatcher = StrMatcher.tabMatcher();
-        final StrSubstitutor strSubstitutor =
-                new StrSubstitutor(mapStringLookup, strMatcher, strMatcher, 'b', strMatcher);
-
-        assertFalse(strSubstitutor.replaceIn((StringBuilder) null, 1315, (-1369)));
-        assertEquals('b', strSubstitutor.getEscapeChar());
-        assertFalse(strSubstitutor.isPreserveEscapes());
-    }
-
-    @Test
-    public void testReplaceInTakingTwoAndThreeIntsReturningFalse_deprecated() {
-        final Map<String, Object> hashMap = new HashMap<>();
-        final StrLookup.MapStrLookup<Object> mapStrLookup = new StrLookup.MapStrLookup<>(hashMap);
-        final StrMatcher strMatcher = StrMatcher.tabMatcher();
-        final StrSubstitutor strSubstitutor =
-                new StrSubstitutor(mapStrLookup, strMatcher, strMatcher, 'b', strMatcher);
-
-        assertFalse(strSubstitutor.replaceIn((StringBuilder) null, 1315, (-1369)));
-        assertEquals('b', strSubstitutor.getEscapeChar());
-        assertFalse(strSubstitutor.isPreserveEscapes());
-    }
-
-    @Test
-    public void testReplaceInTakingStringBuilderWithNonNull() {
-        final StringLookup strLookup = StringLookupFactory.INSTANCE.systemPropertyStringLookup();
-        final StrSubstitutor strSubstitutor = new StrSubstitutor(strLookup, "b<H", "b<H", '\'');
-        final StringBuilder stringBuilder = new StringBuilder((CharSequence) "b<H");
-
-        assertEquals('\'', strSubstitutor.getEscapeChar());
-        assertFalse(strSubstitutor.replaceIn(stringBuilder));
-    }
-
-    @Test
-    public void testReplaceInTakingStringBuilderWithNonNull_deprecated() {
-        final StrLookup<String> strLookup = StrLookup.systemPropertiesLookup();
-        final StrSubstitutor strSubstitutor = new StrSubstitutor(strLookup, "b<H", "b<H", '\'');
-        final StringBuilder stringBuilder = new StringBuilder((CharSequence) "b<H");
-
-        assertEquals('\'', strSubstitutor.getEscapeChar());
-        assertFalse(strSubstitutor.replaceIn(stringBuilder));
-    }
-
-    @Test
-    public void testReplaceInTakingStringBufferWithNonNull() {
-        final StrSubstitutor strSubstitutor =
-                new StrSubstitutor(new HashMap<String, String>(), "WV@i#y?N*[", "WV@i#y?N*[", '*');
-
-        assertFalse(strSubstitutor.isPreserveEscapes());
-        assertFalse(strSubstitutor.replaceIn(new StringBuffer("WV@i#y?N*[")));
-        assertEquals('*', strSubstitutor.getEscapeChar());
-    }
-
-    @Test
-    public void testCreatesStrSubstitutorTakingStrLookupAndCallsReplaceTakingTwoAndThreeInts() {
-        final Map<String, CharacterPredicates> map = new HashMap<>();
-        final StringLookup mapStringLookup = StringLookupFactory.INSTANCE.mapStringLookup(map);
-        final StrSubstitutor strSubstitutor = new StrSubstitutor(mapStringLookup);
-
-        assertNull(strSubstitutor.replace((CharSequence) null, 0, 0));
-        assertEquals('$', strSubstitutor.getEscapeChar());
-    }
-
-    @Test
-    public void testCreatesStrSubstitutorTakingStrLookupAndCallsReplaceTakingTwoAndThreeInts_deprecated() {
-        final Map<String, CharacterPredicates> map = new HashMap<>();
-        final StrLookup.MapStrLookup<CharacterPredicates> mapStrLookup = new StrLookup.MapStrLookup<>(map);
-        final StrSubstitutor strSubstitutor = new StrSubstitutor(mapStrLookup);
-
-        assertNull(strSubstitutor.replace((CharSequence) null, 0, 0));
-        assertEquals('$', strSubstitutor.getEscapeChar());
-    }
-
-    @Test
-    public void testReplaceTakingCharSequenceReturningNull() {
-        final StrSubstitutor strSubstitutor = new StrSubstitutor((StringLookup) null);
-
-        assertNull(strSubstitutor.replace((CharSequence) null));
-        assertFalse(strSubstitutor.isPreserveEscapes());
-        assertEquals('$', strSubstitutor.getEscapeChar());
-    }
-
-    @Test
-    public void testReplaceTakingCharSequenceReturningNull_deprecated() {
-        final StrSubstitutor strSubstitutor = new StrSubstitutor((StrLookup<?>) null);
-
-        assertNull(strSubstitutor.replace((CharSequence) null));
-        assertFalse(strSubstitutor.isPreserveEscapes());
-        assertEquals('$', strSubstitutor.getEscapeChar());
-    }
-
-    @Test(expected = NullPointerException.class)
-    public void testReplaceTakingThreeArgumentsThrowsNullPointerException() {
-        StrSubstitutor.replace(null, (Properties) null);
-    }
-
-    @Test
-    public void testReplaceInTakingStringBuilderWithNull() {
-        final Map<String, Object> map = new HashMap<>();
-        final StrSubstitutor strSubstitutor = new StrSubstitutor(map, "", "", 'T', "K+<'f");
-
-        assertFalse(strSubstitutor.replaceIn((StringBuilder) null));
-    }
-
-}
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.text;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Properties;
+
+import org.apache.commons.lang3.mutable.MutableObject;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Test class for {@link StrSubstitutor}.
+ */
+public class StrSubstitutorTest {
+
+    private Map<String, String> values;
+
+    @Before
+    public void setUp() throws Exception {
+        values = new HashMap<>();
+        values.put("animal", "quick brown fox");
+        values.put("target", "lazy dog");
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        values = null;
+    }
+
+    //-----------------------------------------------------------------------
+    /**
+     * Tests simple key replace.
+     */
+    @Test
+    public void testReplaceSimple() {
+        doTestReplace("The quick brown fox jumps over the lazy dog.", "The ${animal} jumps over the ${target}.", true);
+    }
+
+    /**
+     * Tests simple key replace.
+     */
+    @Test
+    public void testReplaceSolo() {
+        doTestReplace("quick brown fox", "${animal}", false);
+    }
+
+    /**
+     * Tests replace with no variables.
+     */
+    @Test
+    public void testReplaceNoVariables() {
+        doTestNoReplace("The balloon arrived.");
+    }
+
+    /**
+     * Tests replace with null.
+     */
+    @Test
+    public void testReplaceNull() {
+        doTestNoReplace(null);
+    }
+
+    /**
+     * Tests replace with null.
+     */
+    @Test
+    public void testReplaceEmpty() {
+        doTestNoReplace("");
+    }
+
+    /**
+     * Tests key replace changing map after initialization (not recommended).
+     */
+    @Test
+    public void testReplaceChangedMap() {
+        final StrSubstitutor sub = new StrSubstitutor(values);
+        values.put("target", "moon");
+        assertEquals("The quick brown fox jumps over the moon.",
+                sub.replace("The ${animal} jumps over the ${target}."));
+    }
+
+    /**
+     * Tests unknown key replace.
+     */
+    @Test
+    public void testReplaceUnknownKey() {
+        doTestReplace("The ${person} jumps over the lazy dog.", "The ${person} jumps over the ${target}.", true);
+        doTestReplace("The ${person} jumps over the lazy dog. 1234567890.",
+                "The ${person} jumps over the ${target}. ${undefined.number:-1234567890}.", true);
+    }
+
+    /**
+     * Tests adjacent keys.
+     */
+    @Test
+    public void testReplaceAdjacentAtStart() {
+        values.put("code", "GBP");
+        values.put("amount", "12.50");
+        final StrSubstitutor sub = new StrSubstitutor(values);
+        assertEquals("GBP12.50 charged", sub.replace("${code}${amount} charged"));
+    }
+
+    /**
+     * Tests adjacent keys.
+     */
+    @Test
+    public void testReplaceAdjacentAtEnd() {
+        values.put("code", "GBP");
+        values.put("amount", "12.50");
+        final StrSubstitutor sub = new StrSubstitutor(values);
+        assertEquals("Amount is GBP12.50", sub.replace("Amount is ${code}${amount}"));
+    }
+
+    /**
+     * Tests simple recursive replace.
+     */
+    @Test
+    public void testReplaceRecursive() {
+        values.put("animal", "${critter}");
+        values.put("target", "${pet}");
+        values.put("pet", "${petCharacteristic} dog");
+        values.put("petCharacteristic", "lazy");
+        values.put("critter", "${critterSpeed} ${critterColor} ${critterType}");
+        values.put("critterSpeed", "quick");
+        values.put("critterColor", "brown");
+        values.put("critterType", "fox");
+        doTestReplace("The quick brown fox jumps over the lazy dog.", "The ${animal} jumps over the ${target}.", true);
+
+        values.put("pet", "${petCharacteristicUnknown:-lazy} dog");
+        doTestReplace("The quick brown fox jumps over the lazy dog.", "The ${animal} jumps over the ${target}.", true);
+    }
+
+    @Test
+    public void testDisableSubstitutionInValues() {
+        final StrSubstitutor sub = new StrSubstitutor(values);
+        sub.setDisableSubstitutionInValues(true);
+        values.put("animal", "${critter}");
+        values.put("target", "${pet}");
+        values.put("pet", "${petCharacteristic} dog");
+        values.put("petCharacteristic", "lazy");
+        values.put("critter", "${critterSpeed} ${critterColor} ${critterType}");
+        values.put("critterSpeed", "quick");
+        values.put("critterColor", "brown");
+        values.put("critterType", "fox");
+        doTestReplace(sub, "The ${critter} jumps over the ${pet}.", "The ${animal} jumps over the ${target}.", true);
+    }
+
+    /**
+     * Tests escaping.
+     */
+    @Test
+    public void testReplaceEscaping() {
+        doTestReplace("The ${animal} jumps over the lazy dog.", "The $${animal} jumps over the ${target}.", true);
+    }
+
+    /**
+     * Tests escaping.
+     */
+    @Test
+    public void testReplaceSoloEscaping() {
+        doTestReplace("${animal}", "$${animal}", false);
+    }
+
+    /**
+     * Tests complex escaping.
+     */
+    @Test
+    public void testReplaceComplexEscaping() {
+        doTestReplace("The ${quick brown fox} jumps over the lazy dog.",
+                "The $${${animal}} jumps over the ${target}.", true);
+        doTestReplace("The ${quick brown fox} jumps over the lazy dog. ${1234567890}.",
+                "The $${${animal}} jumps over the ${target}. $${${undefined.number:-1234567890}}.", true);
+    }
+
+    /**
+     * Tests when no prefix or suffix.
+     */
+    @Test
+    public void testReplaceNoPrefixNoSuffix() {
+        doTestReplace("The animal jumps over the lazy dog.", "The animal jumps over the ${target}.", true);
+    }
+
+    /**
+     * Tests when no incomplete prefix.
+     */
+    @Test
+    public void testReplaceIncompletePrefix() {
+        doTestReplace("The {animal} jumps over the lazy dog.", "The {animal} jumps over the ${target}.", true);
+    }
+
+    /**
+     * Tests when prefix but no suffix.
+     */
+    @Test
+    public void testReplacePrefixNoSuffix() {
+        doTestReplace("The ${animal jumps over the ${target} lazy dog.",
+                "The ${animal jumps over the ${target} ${target}.", true);
+    }
+
+    /**
+     * Tests when suffix but no prefix.
+     */
+    @Test
+    public void testReplaceNoPrefixSuffix() {
+        doTestReplace("The animal} jumps over the lazy dog.", "The animal} jumps over the ${target}.", true);
+    }
+
+    /**
+     * Tests when no variable name.
+     */
+    @Test
+    public void testReplaceEmptyKeys() {
+        doTestReplace("The ${} jumps over the lazy dog.", "The ${} jumps over the ${target}.", true);
+        doTestReplace("The animal jumps over the lazy dog.", "The ${:-animal} jumps over the ${target}.", true);
+    }
+
+    /**
+     * Tests replace creates output same as input.
+     */
+    @Test
+    public void testReplaceToIdentical() {
+        values.put("animal", "$${${thing}}");
+        values.put("thing", "animal");
+        doTestReplace("The ${animal} jumps.", "The ${animal} jumps.", true);
+    }
+
+    /**
+     * Tests a cyclic replace operation.
+     * The cycle should be detected and cause an exception to be thrown.
+     */
+    @Test
+    public void testCyclicReplacement() {
+        final Map<String, String> map = new HashMap<>();
+        map.put("animal", "${critter}");
+        map.put("target", "${pet}");
+        map.put("pet", "${petCharacteristic} dog");
+        map.put("petCharacteristic", "lazy");
+        map.put("critter", "${critterSpeed} ${critterColor} ${critterType}");
+        map.put("critterSpeed", "quick");
+        map.put("critterColor", "brown");
+        map.put("critterType", "${animal}");
+        StrSubstitutor sub = new StrSubstitutor(map);
+        try {
+            sub.replace("The ${animal} jumps over the ${target}.");
+            fail("Cyclic replacement was not detected!");
+        } catch (final IllegalStateException ex) {
+            // expected
+        }
+
+        // also check even when default value is set.
+        map.put("critterType", "${animal:-fox}");
+        sub = new StrSubstitutor(map);
+        try {
+            sub.replace("The ${animal} jumps over the ${target}.");
+            fail("Cyclic replacement was not detected!");
+        } catch (final IllegalStateException ex) {
+            // expected
+        }
+    }
+
+    /**
+     * Tests interpolation with weird boundary patterns.
+     */
+    @Test
+    public void testReplaceWeirdPattens() {
+        doTestNoReplace("");
+        doTestNoReplace("${}");
+        doTestNoReplace("${ }");
+        doTestNoReplace("${\t}");
+        doTestNoReplace("${\n}");
+        doTestNoReplace("${\b}");
+        doTestNoReplace("${");
+        doTestNoReplace("$}");
+        doTestNoReplace("}");
+        doTestNoReplace("${}$");
+        doTestNoReplace("${${");
+        doTestNoReplace("${${}}");
+        doTestNoReplace("${$${}}");
+        doTestNoReplace("${$$${}}");
+        doTestNoReplace("${$$${$}}");
+        doTestNoReplace("${${}}");
+        doTestNoReplace("${${ }}");
+    }
+
+    /**
+     * Tests simple key replace.
+     */
+    @Test
+    public void testReplacePartialString_noReplace() {
+        final StrSubstitutor sub = new StrSubstitutor();
+        assertEquals("${animal} jumps", sub.replace("The ${animal} jumps over the ${target}.", 4, 15));
+    }
+
+    /**
+     * Tests whether a variable can be replaced in a variable name.
+     */
+    @Test
+    public void testReplaceInVariable() {
+        values.put("animal.1", "fox");
+        values.put("animal.2", "mouse");
+        values.put("species", "2");
+        final StrSubstitutor sub = new StrSubstitutor(values);
+        sub.setEnableSubstitutionInVariables(true);
+        assertEquals(
+                "Wrong result (1)",
+                "The mouse jumps over the lazy dog.",
+                sub.replace("The ${animal.${species}} jumps over the ${target}."));
+        values.put("species", "1");
+        assertEquals(
+                "Wrong result (2)",
+                "The fox jumps over the lazy dog.",
+                sub.replace("The ${animal.${species}} jumps over the ${target}."));
+        assertEquals(
+                "Wrong result (3)",
+                "The fox jumps over the lazy dog.",
+                sub.replace("The ${unknown.animal.${unknown.species:-1}:-fox} "
+                        + "jumps over the ${unknow.target:-lazy dog}."));
+    }
+
+    /**
+     * Tests whether substitution in variable names is disabled per default.
+     */
+    @Test
+    public void testReplaceInVariableDisabled() {
+        values.put("animal.1", "fox");
+        values.put("animal.2", "mouse");
+        values.put("species", "2");
+        final StrSubstitutor sub = new StrSubstitutor(values);
+        assertEquals(
+                "Wrong result (1)",
+                "The ${animal.${species}} jumps over the lazy dog.",
+                sub.replace("The ${animal.${species}} jumps over the ${target}."));
+        assertEquals(
+                "Wrong result (2)",
+                "The ${animal.${species:-1}} jumps over the lazy dog.",
+                sub.replace("The ${animal.${species:-1}} jumps over the ${target}."));
+    }
+
+    /**
+     * Tests complex and recursive substitution in variable names.
+     */
+    @Test
+    public void testReplaceInVariableRecursive() {
+        values.put("animal.2", "brown fox");
+        values.put("animal.1", "white mouse");
+        values.put("color", "white");
+        values.put("species.white", "1");
+        values.put("species.brown", "2");
+        final StrSubstitutor sub = new StrSubstitutor(values);
+        sub.setEnableSubstitutionInVariables(true);
+        assertEquals(
+                "Wrong result (1)",
+                "The white mouse jumps over the lazy dog.",
+                sub.replace("The ${animal.${species.${color}}} jumps over the ${target}."));
+        assertEquals(
+                "Wrong result (2)",
+                "The brown fox jumps over the lazy dog.",
+                sub.replace("The ${animal.${species.${unknownColor:-brown}}} jumps over the ${target}."));
+    }
+
+    @Test
+    public void testDefaultValueDelimiters() {
+        final Map<String, String> map = new HashMap<>();
+        map.put("animal", "fox");
+        map.put("target", "dog");
+
+        StrSubstitutor sub = new StrSubstitutor(map, "${", "}", '$');
+        assertEquals("The fox jumps over the lazy dog. 1234567890.",
+                sub.replace("The ${animal} jumps over the lazy ${target}. ${undefined.number:-1234567890}."));
+
+        sub = new StrSubstitutor(map, "${", "}", '$', "?:");
+        assertEquals("The fox jumps over the lazy dog. 1234567890.",
+                sub.replace("The ${animal} jumps over the lazy ${target}. ${undefined.number?:1234567890}."));
+
+        sub = new StrSubstitutor(map, "${", "}", '$', "||");
+        assertEquals("The fox jumps over the lazy dog. 1234567890.",
+                sub.replace("The ${animal} jumps over the lazy ${target}. ${undefined.number||1234567890}."));
+
+        sub = new StrSubstitutor(map, "${", "}", '$', "!");
+        assertEquals("The fox jumps over the lazy dog. 1234567890.",
+                sub.replace("The ${animal} jumps over the lazy ${target}. ${undefined.number!1234567890}."));
+
+        sub = new StrSubstitutor(map, "${", "}", '$', "");
+        sub.setValueDelimiterMatcher(null);
+        assertEquals("The fox jumps over the lazy dog. ${undefined.number!1234567890}.",
+                sub.replace("The ${animal} jumps over the lazy ${target}. ${undefined.number!1234567890}."));
+
+        sub = new StrSubstitutor(map, "${", "}", '$');
+        sub.setValueDelimiterMatcher(null);
+        assertEquals("The fox jumps over the lazy dog. ${undefined.number!1234567890}.",
+                sub.replace("The ${animal} jumps over the lazy ${target}. ${undefined.number!1234567890}."));
+    }
+
+    //-----------------------------------------------------------------------
+    /**
+     * Tests protected.
+     */
+    @Test
+    public void testResolveVariable() {
+        final StrBuilder builder = new StrBuilder("Hi ${name}!");
+        final Map<String, String> map = new HashMap<>();
+        map.put("name", "commons");
+        final StrSubstitutor sub = new StrSubstitutor(map) {
+            @Override
+            protected String resolveVariable(final String variableName, final StrBuilder buf, final int startPos,
+                    final int endPos) {
+                assertEquals("name", variableName);
+                assertSame(builder, buf);
+                assertEquals(3, startPos);
+                assertEquals(10, endPos);
+                return "jakarta";
+            }
+        };
+        sub.replaceIn(builder);
+        assertEquals("Hi jakarta!", builder.toString());
+    }
+
+    //-----------------------------------------------------------------------
+    /**
+     * Tests constructor.
+     */
+    @Test
+    public void testConstructorNoArgs() {
+        final StrSubstitutor sub = new StrSubstitutor();
+        assertEquals("Hi ${name}", sub.replace("Hi ${name}"));
+    }
+
+    /**
+     * Tests constructor.
+     */
+    @Test
+    public void testConstructorMapPrefixSuffix() {
+        final Map<String, String> map = new HashMap<>();
+        map.put("name", "commons");
+        final StrSubstitutor sub = new StrSubstitutor(map, "<", ">");
+        assertEquals("Hi < commons", sub.replace("Hi $< <name>"));
+    }
+
+    /**
+     * Tests constructor.
+     */
+    @Test
+    public void testConstructorMapFull() {
+        final Map<String, String> map = new HashMap<>();
+        map.put("name", "commons");
+        StrSubstitutor sub = new StrSubstitutor(map, "<", ">", '!');
+        assertEquals("Hi < commons", sub.replace("Hi !< <name>"));
+        sub = new StrSubstitutor(map, "<", ">", '!', "||");
+        assertEquals("Hi < commons", sub.replace("Hi !< <name2||commons>"));
+    }
+
+    //-----------------------------------------------------------------------
+    /**
+     * Tests get set.
+     */
+    @Test
+    public void testGetSetEscape() {
+        final StrSubstitutor sub = new StrSubstitutor();
+        assertEquals('$', sub.getEscapeChar());
+        sub.setEscapeChar('<');
+        assertEquals('<', sub.getEscapeChar());
+    }
+
+    /**
+     * Tests get set.
+     */
+    @Test
+    public void testGetSetPrefix() {
+        final StrSubstitutor sub = new StrSubstitutor();
+        assertTrue(sub.getVariablePrefixMatcher() instanceof StrMatcher.StringMatcher);
+        sub.setVariablePrefix('<');
+        assertTrue(sub.getVariablePrefixMatcher() instanceof StrMatcher.CharMatcher);
+
+        sub.setVariablePrefix("<<");
+        assertTrue(sub.getVariablePrefixMatcher() instanceof StrMatcher.StringMatcher);
+        try {
+            sub.setVariablePrefix((String) null);
+            fail();
+        } catch (final IllegalArgumentException ex) {
+            // expected
+        }
+        assertTrue(sub.getVariablePrefixMatcher() instanceof StrMatcher.StringMatcher);
+
+        final StrMatcher matcher = StrMatcher.commaMatcher();
+        sub.setVariablePrefixMatcher(matcher);
+        assertSame(matcher, sub.getVariablePrefixMatcher());
+        try {
+            sub.setVariablePrefixMatcher((StrMatcher) null);
+            fail();
+        } catch (final IllegalArgumentException ex) {
+            // expected
+        }
+        assertSame(matcher, sub.getVariablePrefixMatcher());
+    }
+
+    /**
+     * Tests get set.
+     */
+    @Test
+    public void testGetSetSuffix() {
+        final StrSubstitutor sub = new StrSubstitutor();
+        assertTrue(sub.getVariableSuffixMatcher() instanceof StrMatcher.StringMatcher);
+        sub.setVariableSuffix('<');
+        assertTrue(sub.getVariableSuffixMatcher() instanceof StrMatcher.CharMatcher);
+
+        sub.setVariableSuffix("<<");
+        assertTrue(sub.getVariableSuffixMatcher() instanceof StrMatcher.StringMatcher);
+        try {
+            sub.setVariableSuffix((String) null);
+            fail();
+        } catch (final IllegalArgumentException ex) {
+            // expected
+        }
+        assertTrue(sub.getVariableSuffixMatcher() instanceof StrMatcher.StringMatcher);
+
+        final StrMatcher matcher = StrMatcher.commaMatcher();
+        sub.setVariableSuffixMatcher(matcher);
+        assertSame(matcher, sub.getVariableSuffixMatcher());
+        try {
+            sub.setVariableSuffixMatcher((StrMatcher) null);
+            fail();
+        } catch (final IllegalArgumentException ex) {
+            // expected
+        }
+        assertSame(matcher, sub.getVariableSuffixMatcher());
+    }
+
+    /**
+     * Tests get set.
+     */
+    @Test
+    public void testGetSetValueDelimiter() {
+        final StrSubstitutor sub = new StrSubstitutor();
+        assertTrue(sub.getValueDelimiterMatcher() instanceof StrMatcher.StringMatcher);
+        sub.setValueDelimiter(':');
+        assertTrue(sub.getValueDelimiterMatcher() instanceof StrMatcher.CharMatcher);
+
+        sub.setValueDelimiter("||");
+        assertTrue(sub.getValueDelimiterMatcher() instanceof StrMatcher.StringMatcher);
+        sub.setValueDelimiter((String) null);
+        assertNull(sub.getValueDelimiterMatcher());
+
+        final StrMatcher matcher = StrMatcher.commaMatcher();
+        sub.setValueDelimiterMatcher(matcher);
+        assertSame(matcher, sub.getValueDelimiterMatcher());
+        sub.setValueDelimiterMatcher((StrMatcher) null);
+        assertNull(sub.getValueDelimiterMatcher());
+    }
+
+    //-----------------------------------------------------------------------
+    /**
+     * Tests static.
+     */
+    @Test
+    public void testStaticReplace() {
+        final Map<String, String> map = new HashMap<>();
+        map.put("name", "commons");
+        assertEquals("Hi commons!", StrSubstitutor.replace("Hi ${name}!", map));
+    }
+
+    /**
+     * Tests static.
+     */
+    @Test
+    public void testStaticReplacePrefixSuffix() {
+        final Map<String, String> map = new HashMap<>();
+        map.put("name", "commons");
+        assertEquals("Hi commons!", StrSubstitutor.replace("Hi <name>!", map, "<", ">"));
+    }
+
+    /**
+     * Tests interpolation with system properties.
+     */
+    @Test
+    public void testStaticReplaceSystemProperties() {
+        final StrBuilder buf = new StrBuilder();
+        buf.append("Hi ").append(System.getProperty("user.name"));
+        buf.append(", you are working with ");
+        buf.append(System.getProperty("os.name"));
+        buf.append(", your home directory is ");
+        buf.append(System.getProperty("user.home")).append('.');
+        assertEquals(buf.toString(), StrSubstitutor.replaceSystemProperties("Hi ${user.name}, you are "
+            + "working with ${os.name}, your home "
+            + "directory is ${user.home}."));
+    }
+
+    /**
+     * Test for LANG-1055: StrSubstitutor.replaceSystemProperties does not work consistently
+     */
+    @Test
+    public void testLANG1055() {
+        System.setProperty("test_key", "test_value");
+
+        final String expected = StrSubstitutor.replace("test_key=${test_key}", System.getProperties());
+        final String actual = StrSubstitutor.replaceSystemProperties("test_key=${test_key}");
+        assertEquals(expected, actual);
+    }
+
+    /**
+     * Test the replace of a properties object
+     */
+    @Test
+    public void testSubstituteDefaultProperties() {
+        final String org = "${doesnotwork}";
+        System.setProperty("doesnotwork", "It works!");
+
+        // create a new Properties object with the System.getProperties as default
+        final Properties props = new Properties(System.getProperties());
+
+        assertEquals("It works!", StrSubstitutor.replace(org, props));
+    }
+
+    @Test
+    public void testSamePrefixAndSuffix() {
+        final Map<String, String> map = new HashMap<>();
+        map.put("greeting", "Hello");
+        map.put(" there ", "XXX");
+        map.put("name", "commons");
+        assertEquals("Hi commons!", StrSubstitutor.replace("Hi @name@!", map, "@", "@"));
+        assertEquals("Hello there commons!", StrSubstitutor.replace("@greeting@ there @name@!", map, "@", "@"));
+    }
+
+    @Test
+    public void testSubstitutePreserveEscape() {
+        final String org = "${not-escaped} $${escaped}";
+        final Map<String, String> map = new HashMap<>();
+        map.put("not-escaped", "value");
+
+        final StrSubstitutor sub = new StrSubstitutor(map, "${", "}", '$');
+        assertFalse(sub.isPreserveEscapes());
+        assertEquals("value ${escaped}", sub.replace(org));
+
+        sub.setPreserveEscapes(true);
+        assertTrue(sub.isPreserveEscapes());
+        assertEquals("value $${escaped}", sub.replace(org));
+    }
+
+    private void doTestReplace(final String expectedResult, final String replaceTemplate, final boolean substring) {
+        final StrSubstitutor sub = new StrSubstitutor(values);
+        doTestReplace(sub, expectedResult, replaceTemplate, substring);
+    }
+
+    //-----------------------------------------------------------------------
+    private void doTestReplace(final StrSubstitutor sub, final String expectedResult, final String replaceTemplate,
+            final boolean substring) {
+        final String expectedShortResult = expectedResult.substring(1, expectedResult.length() - 1);
+
+        // replace using String
+        assertEquals(expectedResult, sub.replace(replaceTemplate));
+        if (substring) {
+            assertEquals(expectedShortResult, sub.replace(replaceTemplate, 1, replaceTemplate.length() - 2));
+        }
+
+        // replace using char[]
+        final char[] chars = replaceTemplate.toCharArray();
+        assertEquals(expectedResult, sub.replace(chars));
+        if (substring) {
+            assertEquals(expectedShortResult, sub.replace(chars, 1, chars.length - 2));
+        }
+
+        // replace using StringBuffer
+        StringBuffer buf = new StringBuffer(replaceTemplate);
+        assertEquals(expectedResult, sub.replace(buf));
+        if (substring) {
+            assertEquals(expectedShortResult, sub.replace(buf, 1, buf.length() - 2));
+        }
+
+        // replace using StringBuilder
+        StringBuilder builder = new StringBuilder(replaceTemplate);
+        assertEquals(expectedResult, sub.replace(builder));
+        if (substring) {
+            assertEquals(expectedShortResult, sub.replace(builder, 1, builder.length() - 2));
+        }
+
+        // replace using StrBuilder
+        StrBuilder bld = new StrBuilder(replaceTemplate);
+        assertEquals(expectedResult, sub.replace(bld));
+        if (substring) {
+            assertEquals(expectedShortResult, sub.replace(bld, 1, bld.length() - 2));
+        }
+
+        // replace using object
+        final MutableObject<String> obj = new MutableObject<>(replaceTemplate);  // toString returns template
+        assertEquals(expectedResult, sub.replace(obj));
+
+        // replace in StringBuffer
+        buf = new StringBuffer(replaceTemplate);
+        assertTrue(sub.replaceIn(buf));
+        assertEquals(expectedResult, buf.toString());
+        if (substring) {
+            buf = new StringBuffer(replaceTemplate);
+            assertTrue(sub.replaceIn(buf, 1, buf.length() - 2));
+            assertEquals(expectedResult, buf.toString());  // expect full result as remainder is untouched
+        }
+
+        // replace in StringBuilder
+        builder = new StringBuilder(replaceTemplate);
+        assertTrue(sub.replaceIn(builder));
+        assertEquals(expectedResult, builder.toString());
+        if (substring) {
+            builder = new StringBuilder(replaceTemplate);
+            assertTrue(sub.replaceIn(builder, 1, builder.length() - 2));
+            assertEquals(expectedResult, builder.toString());  // expect full result as remainder is untouched
+        }
+
+        // replace in StrBuilder
+        bld = new StrBuilder(replaceTemplate);
+        assertTrue(sub.replaceIn(bld));
+        assertEquals(expectedResult, bld.toString());
+        if (substring) {
+            bld = new StrBuilder(replaceTemplate);
+            assertTrue(sub.replaceIn(bld, 1, bld.length() - 2));
+            assertEquals(expectedResult, bld.toString());  // expect full result as remainder is untouched
+        }
+    }
+
+    private void doTestNoReplace(final String replaceTemplate) {
+        final StrSubstitutor sub = new StrSubstitutor(values);
+
+        if (replaceTemplate == null) {
+            assertNull(sub.replace((String) null));
+            assertNull(sub.replace((String) null, 0, 100));
+            assertNull(sub.replace((char[]) null));
+            assertNull(sub.replace((char[]) null, 0, 100));
+            assertNull(sub.replace((StringBuffer) null));
+            assertNull(sub.replace((StringBuffer) null, 0, 100));
+            assertNull(sub.replace((StrBuilder) null));
+            assertNull(sub.replace((StrBuilder) null, 0, 100));
+            assertNull(sub.replace((Object) null));
+            assertFalse(sub.replaceIn((StringBuffer) null));
+            assertFalse(sub.replaceIn((StringBuffer) null, 0, 100));
+            assertFalse(sub.replaceIn((StrBuilder) null));
+            assertFalse(sub.replaceIn((StrBuilder) null, 0, 100));
+        } else {
+            assertEquals(replaceTemplate, sub.replace(replaceTemplate));
+            final StrBuilder bld = new StrBuilder(replaceTemplate);
+            assertFalse(sub.replaceIn(bld));
+            assertEquals(replaceTemplate, bld.toString());
+        }
+    }
+
+    @Test
+    public void testReplaceInTakingTwoAndThreeIntsReturningFalse() {
+        final Map<String, Object> hashMap = new HashMap<>();
+        final StrLookup.MapStrLookup<Object> strLookupMapStrLookup = new StrLookup.MapStrLookup<>(hashMap);
+        final StrMatcher strMatcher = StrMatcher.tabMatcher();
+        final StrSubstitutor strSubstitutor =
+                new StrSubstitutor(strLookupMapStrLookup, strMatcher, strMatcher, 'b', strMatcher);
+
+        assertFalse(strSubstitutor.replaceIn((StringBuilder) null, 1315, (-1369)));
+        assertEquals('b', strSubstitutor.getEscapeChar());
+        assertFalse(strSubstitutor.isPreserveEscapes());
+    }
+
+    @Test
+    public void testReplaceInTakingStringBuilderWithNonNull() {
+        final StrLookup<String> strLookup = StrLookup.systemPropertiesLookup();
+        final StrSubstitutor strSubstitutor = new StrSubstitutor(strLookup, "b<H", "b<H", '\'');
+        final StringBuilder stringBuilder = new StringBuilder((CharSequence) "b<H");
+
+        assertEquals('\'', strSubstitutor.getEscapeChar());
+        assertFalse(strSubstitutor.replaceIn(stringBuilder));
+    }
+
+    @Test
+    public void testReplaceInTakingStringBufferWithNonNull() {
+        final StrSubstitutor strSubstitutor =
+                new StrSubstitutor(new HashMap<String, String>(), "WV@i#y?N*[", "WV@i#y?N*[", '*');
+
+        assertFalse(strSubstitutor.isPreserveEscapes());
+        assertFalse(strSubstitutor.replaceIn(new StringBuffer("WV@i#y?N*[")));
+        assertEquals('*', strSubstitutor.getEscapeChar());
+    }
+
+    @Test
+    public void testCreatesStrSubstitutorTakingStrLookupAndCallsReplaceTakingTwoAndThreeInts() {
+        final Map<String, CharacterPredicates> map = new HashMap<>();
+        final StrLookup.MapStrLookup<CharacterPredicates> strLookupMapStrLookup = new StrLookup.MapStrLookup<>(map);
+        final StrSubstitutor strSubstitutor = new StrSubstitutor(strLookupMapStrLookup);
+
+        assertNull(strSubstitutor.replace((CharSequence) null, 0, 0));
+        assertEquals('$', strSubstitutor.getEscapeChar());
+    }
+
+    @Test
+    public void testReplaceTakingCharSequenceReturningNull() {
+        final StrSubstitutor strSubstitutor = new StrSubstitutor((StrLookup<?>) null);
+
+        assertNull(strSubstitutor.replace((CharSequence) null));
+        assertFalse(strSubstitutor.isPreserveEscapes());
+        assertEquals('$', strSubstitutor.getEscapeChar());
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void testReplaceTakingThreeArgumentsThrowsNullPointerException() {
+        StrSubstitutor.replace(null, (Properties) null);
+    }
+
+    @Test
+    public void testReplaceInTakingStringBuilderWithNull() {
+        final Map<String, Object> map = new HashMap<>();
+        final StrSubstitutor strSubstitutor = new StrSubstitutor(map, "", "", 'T', "K+<'f");
+
+        assertFalse(strSubstitutor.replaceIn((StringBuilder) null));
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/commons-text/blob/4b67dd51/src/test/java/org/apache/commons/text/StrSubstitutorWithInterpolatorStringLookupTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/commons/text/StrSubstitutorWithInterpolatorStringLookupTest.java b/src/test/java/org/apache/commons/text/StrSubstitutorWithInterpolatorStringLookupTest.java
deleted file mode 100644
index d50d3b7..0000000
--- a/src/test/java/org/apache/commons/text/StrSubstitutorWithInterpolatorStringLookupTest.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.commons.text;
-
-import java.util.HashMap;
-import java.util.Map;
-
-import org.apache.commons.text.lookup.StringLookupFactory;
-import org.junit.Assert;
-import org.junit.Test;
-
-public class StrSubstitutorWithInterpolatorStringLookupTest {
-
-    @Test
-    public void testMapAndSystemProperty() {
-        final String key = "key";
-        final String value = "value";
-        final Map<String, String> map = new HashMap<>();
-        map.put(key, value);
-        final StrSubstitutor strSubst = new StrSubstitutor(StringLookupFactory.INSTANCE.interpolatorStringLookup(map));
-        final String spKey = "user.name";
-        Assert.assertEquals(System.getProperty(spKey), strSubst.replace("${sys:" + spKey + "}"));
-        Assert.assertEquals(value, strSubst.replace("${" + key + "}"));
-    }
-
-    @Test
-    public void testSystemProperty() {
-        final StrSubstitutor strSubst = new StrSubstitutor(StringLookupFactory.INSTANCE.interpolatorStringLookup());
-        final String spKey = "user.name";
-        Assert.assertEquals(System.getProperty(spKey), strSubst.replace("${sys:" + spKey + "}"));
-    }
-}