diff --git a/core-java-modules/core-java-regex/pom.xml b/core-java-modules/core-java-regex/pom.xml
index 129b6039cd..1c55177d70 100644
--- a/core-java-modules/core-java-regex/pom.xml
+++ b/core-java-modules/core-java-regex/pom.xml
@@ -25,6 +25,12 @@
jmh-generator-annprocess
${jmh-core.version}
+
+ org.assertj
+ assertj-core
+ 3.15.0
+ test
+
diff --git a/core-java-modules/core-java-regex/src/main/java/com/baeldung/replacetokens/ReplacingTokens.java b/core-java-modules/core-java-regex/src/main/java/com/baeldung/replacetokens/ReplacingTokens.java
new file mode 100644
index 0000000000..a4774ad572
--- /dev/null
+++ b/core-java-modules/core-java-regex/src/main/java/com/baeldung/replacetokens/ReplacingTokens.java
@@ -0,0 +1,63 @@
+package com.baeldung.replacetokens;
+
+import java.util.function.Function;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class ReplacingTokens {
+ public static final Pattern TITLE_CASE_PATTERN = Pattern.compile("(?<=^|[^A-Za-z])([A-Z][a-z]*)(?=[^A-Za-z]|$)");
+
+ /**
+ * Iterate over the title case tokens in the input and replace them with lowercase
+ * @param original the original string
+ * @return a string with words replaced with their lowercase equivalents
+ */
+ public static String replaceTitleCaseWithLowerCase(String original) {
+ int lastIndex = 0;
+ StringBuilder output = new StringBuilder();
+ Matcher matcher = TITLE_CASE_PATTERN.matcher(original);
+ while (matcher.find()) {
+ output.append(original, lastIndex, matcher.start())
+ .append(convert(matcher.group(1)));
+
+ lastIndex = matcher.end();
+ }
+ if (lastIndex < original.length()) {
+ output.append(original, lastIndex, original.length());
+ }
+ return output.toString();
+ }
+
+ /**
+ * Convert a token found into its desired lowercase
+ * @param token the token to convert
+ * @return the converted token
+ */
+ private static String convert(String token) {
+ return token.toLowerCase();
+ }
+
+ /**
+ * Replace all the tokens in an input using the algorithm provided for each
+ * @param original original string
+ * @param tokenPattern the pattern to match with
+ * @param converter the conversion to apply
+ * @return the substituted string
+ */
+ public static String replaceTokens(String original, Pattern tokenPattern,
+ Function converter) {
+ int lastIndex = 0;
+ StringBuilder output = new StringBuilder();
+ Matcher matcher = tokenPattern.matcher(original);
+ while (matcher.find()) {
+ output.append(original, lastIndex, matcher.start())
+ .append(converter.apply(matcher));
+
+ lastIndex = matcher.end();
+ }
+ if (lastIndex < original.length()) {
+ output.append(original, lastIndex, original.length());
+ }
+ return output.toString();
+ }
+}
diff --git a/core-java-modules/core-java-regex/src/test/java/com/baeldung/replacetokens/ReplacingTokensUnitTest.java b/core-java-modules/core-java-regex/src/test/java/com/baeldung/replacetokens/ReplacingTokensUnitTest.java
new file mode 100644
index 0000000000..b42a17f88e
--- /dev/null
+++ b/core-java-modules/core-java-regex/src/test/java/com/baeldung/replacetokens/ReplacingTokensUnitTest.java
@@ -0,0 +1,79 @@
+package com.baeldung.replacetokens;
+
+import org.junit.Test;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import static com.baeldung.replacetokens.ReplacingTokens.TITLE_CASE_PATTERN;
+import static com.baeldung.replacetokens.ReplacingTokens.replaceTokens;
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class ReplacingTokensUnitTest {
+ private static final String EXAMPLE_INPUT = "First 3 Capital Words! then 10 TLAs, I Found";
+ private static final String EXAMPLE_INPUT_PROCESSED = "first 3 capital words! then 10 TLAs, i found";
+
+ @Test
+ public void whenFindMatches_thenTitleWordsFound() {
+ Matcher matcher = TITLE_CASE_PATTERN.matcher(EXAMPLE_INPUT);
+ List matches = new ArrayList<>();
+ while (matcher.find()) {
+ matches.add(matcher.group(1));
+ }
+
+ assertThat(matches)
+ .containsExactly("First", "Capital", "Words", "I", "Found");
+ }
+
+ @Test
+ public void exploreMatches() {
+ Matcher matcher = TITLE_CASE_PATTERN.matcher(EXAMPLE_INPUT);
+ while (matcher.find()) {
+ System.out.println("Match: " + matcher.group(0));
+ System.out.println("Start: " + matcher.start());
+ System.out.println("End: " + matcher.end());
+ }
+ }
+
+ @Test
+ public void whenReplaceTokensWithLowerCase() {
+ assertThat(ReplacingTokens.replaceTitleCaseWithLowerCase(EXAMPLE_INPUT))
+ .isEqualTo(EXAMPLE_INPUT_PROCESSED);
+ }
+
+ @Test
+ public void whenReplaceTokensWithLowerCaseUsingGeneralPurpose() {
+ assertThat(replaceTokens("First 3 Capital Words! then 10 TLAs, I Found",
+ TITLE_CASE_PATTERN,
+ match -> match.group(1).toLowerCase()))
+ .isEqualTo("first 3 capital words! then 10 TLAs, i found");
+ }
+
+ @Test
+ public void escapeRegexCharacters() {
+ Pattern regexCharacters = Pattern.compile("[<(\\[{\\\\^\\-=$!|\\]})?*+.>]");
+
+ assertThat(replaceTokens("A regex character like [",
+ regexCharacters,
+ match -> "\\" + match.group()))
+ .isEqualTo("A regex character like \\[");
+ }
+
+ @Test
+ public void replacePlaceholders() {
+ Pattern placeholderPattern = Pattern.compile("\\$\\{(?[A-Za-z0-9-_]+)}");
+
+ Map placeholderValues = new HashMap<>();
+ placeholderValues.put("name", "Bill");
+ placeholderValues.put("company", "Baeldung");
+
+ assertThat(replaceTokens("Hi ${name} at ${company}",
+ placeholderPattern,
+ match -> placeholderValues.get(match.group("placeholder"))))
+ .isEqualTo("Hi Bill at Baeldung");
+ }
+}