001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      https://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.commons.lang3;
018
019import java.util.Objects;
020import java.util.regex.Matcher;
021import java.util.regex.Pattern;
022
023/**
024 * Helpers to process Strings using regular expressions.
025 *
026 * @see java.util.regex.Pattern
027 * @since 3.8
028 */
029public class RegExUtils {
030
031    /**
032     * The pattern to split version strings.
033     */
034    static final Pattern VERSION_SPLIT_PATTERN = Pattern.compile("\\.");
035
036    /**
037     * Compiles the given regular expression into a pattern with the {@link Pattern#DOTALL} flag.
038     *
039     * @param regex The expression to be compiled.
040     * @return The given regular expression compiled into a pattern with the {@link Pattern#DOTALL} flag.
041     * @since 3.13.0
042     */
043    public static Pattern dotAll(final String regex) {
044        return Pattern.compile(regex, Pattern.DOTALL);
045    }
046
047    /**
048     * Compiles the given regular expression into a pattern with the {@link Pattern#DOTALL} flag, then creates a matcher that will match the given text against
049     * this pattern.
050     *
051     * @param regex The expression to be compiled.
052     * @param text  The character sequence to be matched.
053     * @return A new matcher for this pattern.
054     * @since 3.18.0
055     */
056    public static Matcher dotAllMatcher(final String regex, final CharSequence text) {
057        return dotAll(regex).matcher(text);
058    }
059
060    /**
061     * Compiles the given regular expression into a pattern with the {@link Pattern#DOTALL} flag, then creates a matcher that will match the given text against
062     * this pattern.
063     *
064     * @param regex The expression to be compiled.
065     * @param text  The character sequence to be matched.
066     * @return A new matcher for this pattern.
067     * @since 3.13.0
068     * @deprecated Use {@link #dotAllMatcher(String, CharSequence)}.
069     */
070    @Deprecated
071    public static Matcher dotAllMatcher(final String regex, final String text) {
072        return dotAll(regex).matcher(text);
073    }
074
075    /**
076     * Removes each substring of the text String that matches the given regular expression pattern.
077     *
078     * This method is a {@code null} safe equivalent to:
079     * <ul>
080     *  <li>{@code pattern.matcher(text).replaceAll(StringUtils.EMPTY)}</li>
081     * </ul>
082     *
083     * <p>
084     * A {@code null} reference passed to this method is a no-op.
085     * </p>
086     *
087     * <pre>{@code
088     * RegExUtils.removeAll(null, *)      = null
089     * RegExUtils.removeAll("any", (Pattern) null)  = "any"
090     * RegExUtils.removeAll("any", Pattern.compile(""))    = "any"
091     * RegExUtils.removeAll("any", Pattern.compile(".*"))  = ""
092     * RegExUtils.removeAll("any", Pattern.compile(".+"))  = ""
093     * RegExUtils.removeAll("abc", Pattern.compile(".?"))  = ""
094     * RegExUtils.removeAll("A<__>\n<__>B", Pattern.compile("<.*>"))      = "A\nB"
095     * RegExUtils.removeAll("A<__>\n<__>B", Pattern.compile("(?s)<.*>"))  = "AB"
096     * RegExUtils.removeAll("A<__>\n<__>B", Pattern.compile("<.*>", Pattern.DOTALL))  = "AB"
097     * RegExUtils.removeAll("ABCabc123abc", Pattern.compile("[a-z]"))     = "ABC123"
098     * }</pre>
099     *
100     * @param text  text to remove from, may be null.
101     * @param regex  The regular expression to which this string is to be matched.
102     * @return  the text with any removes processed,
103     *              {@code null} if null String input.
104     *
105     * @see #replaceAll(CharSequence, Pattern, String)
106     * @see java.util.regex.Matcher#replaceAll(String)
107     * @see java.util.regex.Pattern
108     * @since 3.18.0
109     */
110    public static String removeAll(final CharSequence text, final Pattern regex) {
111        return replaceAll(text, regex, StringUtils.EMPTY);
112    }
113
114    /**
115     * Removes each substring of the text String that matches the given regular expression pattern.
116     *
117     * This method is a {@code null} safe equivalent to:
118     * <ul>
119     *  <li>{@code pattern.matcher(text).replaceAll(StringUtils.EMPTY)}</li>
120     * </ul>
121     *
122     * <p>
123     * A {@code null} reference passed to this method is a no-op.
124     * </p>
125     *
126     * <pre>{@code
127     * RegExUtils.removeAll(null, *)      = null
128     * RegExUtils.removeAll("any", (Pattern) null)  = "any"
129     * RegExUtils.removeAll("any", Pattern.compile(""))    = "any"
130     * RegExUtils.removeAll("any", Pattern.compile(".*"))  = ""
131     * RegExUtils.removeAll("any", Pattern.compile(".+"))  = ""
132     * RegExUtils.removeAll("abc", Pattern.compile(".?"))  = ""
133     * RegExUtils.removeAll("A<__>\n<__>B", Pattern.compile("<.*>"))      = "A\nB"
134     * RegExUtils.removeAll("A<__>\n<__>B", Pattern.compile("(?s)<.*>"))  = "AB"
135     * RegExUtils.removeAll("A<__>\n<__>B", Pattern.compile("<.*>", Pattern.DOTALL))  = "AB"
136     * RegExUtils.removeAll("ABCabc123abc", Pattern.compile("[a-z]"))     = "ABC123"
137     * }</pre>
138     *
139     * @param text  text to remove from, may be null.
140     * @param regex  The regular expression to which this string is to be matched
141     * @return  the text with any removes processed,
142     *              {@code null} if null String input.
143     *
144     * @see #replaceAll(CharSequence, Pattern, String)
145     * @see java.util.regex.Matcher#replaceAll(String)
146     * @see java.util.regex.Pattern
147     * @deprecated Use {@link #removeAll(CharSequence, Pattern)}.
148     */
149    @Deprecated
150    public static String removeAll(final String text, final Pattern regex) {
151        return replaceAll((CharSequence) text, regex, StringUtils.EMPTY);
152    }
153
154    /**
155     * Removes each substring of the text String that matches the given regular expression.
156     *
157     * This method is a {@code null} safe equivalent to:
158     * <ul>
159     *  <li>{@code text.replaceAll(regex, StringUtils.EMPTY)}</li>
160     *  <li>{@code Pattern.compile(regex).matcher(text).replaceAll(StringUtils.EMPTY)}</li>
161     * </ul>
162     *
163     * <p>
164     * A {@code null} reference passed to this method is a no-op.
165     * </p>
166     *
167     * <p>
168     * Unlike in the {@link #removePattern(CharSequence, String)} method, the {@link Pattern#DOTALL} option
169     * is NOT automatically added.
170     * To use the DOTALL option prepend {@code "(?s)"} to the regex.
171     * DOTALL is also known as single-line mode in Perl.
172     * </p>
173     *
174     * <pre>{@code
175     * RegExUtils.removeAll(null, *)      = null
176     * RegExUtils.removeAll("any", (String) null)  = "any"
177     * RegExUtils.removeAll("any", "")    = "any"
178     * RegExUtils.removeAll("any", ".*")  = ""
179     * RegExUtils.removeAll("any", ".+")  = ""
180     * RegExUtils.removeAll("abc", ".?")  = ""
181     * RegExUtils.removeAll("A<__>\n<__>B", "<.*>")      = "A\nB"
182     * RegExUtils.removeAll("A<__>\n<__>B", "(?s)<.*>")  = "AB"
183     * RegExUtils.removeAll("ABCabc123abc", "[a-z]")     = "ABC123"
184     * }</pre>
185     *
186     * @param text  text to remove from, may be null
187     * @param regex  The regular expression to which this string is to be matched
188     * @return  the text with any removes processed,
189     *              {@code null} if null String input.
190     *
191     * @throws  java.util.regex.PatternSyntaxException
192     *              Thrown if the regular expression's syntax is invalid.
193     *
194     * @see #replaceAll(String, String, String)
195     * @see #removePattern(CharSequence, String)
196     * @see String#replaceAll(String, String)
197     * @see java.util.regex.Pattern
198     * @see java.util.regex.Pattern#DOTALL
199     */
200    public static String removeAll(final String text, final String regex) {
201        return replaceAll(text, regex, StringUtils.EMPTY);
202    }
203
204    /**
205     * Removes the first substring of the text string that matches the given regular expression pattern.
206     *
207     * This method is a {@code null} safe equivalent to:
208     * <ul>
209     *  <li>{@code pattern.matcher(text).replaceFirst(StringUtils.EMPTY)}</li>
210     * </ul>
211     *
212     * <p>
213     * A {@code null} reference passed to this method is a no-op.
214     * </p>
215     *
216     * <pre>{@code
217     * RegExUtils.removeFirst(null, *)      = null
218     * RegExUtils.removeFirst("any", (Pattern) null)  = "any"
219     * RegExUtils.removeFirst("any", Pattern.compile(""))    = "any"
220     * RegExUtils.removeFirst("any", Pattern.compile(".*"))  = ""
221     * RegExUtils.removeFirst("any", Pattern.compile(".+"))  = ""
222     * RegExUtils.removeFirst("abc", Pattern.compile(".?"))  = "bc"
223     * RegExUtils.removeFirst("A<__>\n<__>B", Pattern.compile("<.*>"))      = "A\n<__>B"
224     * RegExUtils.removeFirst("A<__>\n<__>B", Pattern.compile("(?s)<.*>"))  = "AB"
225     * RegExUtils.removeFirst("ABCabc123", Pattern.compile("[a-z]"))          = "ABCbc123"
226     * RegExUtils.removeFirst("ABCabc123abc", Pattern.compile("[a-z]+"))      = "ABC123abc"
227     * }</pre>
228     *
229     * @param text  text to remove from, may be null.
230     * @param regex  The regular expression pattern to which this string is to be matched.
231     * @return  the text with the first replacement processed,
232     *              {@code null} if null String input.
233     *
234     * @see #replaceFirst(String, Pattern, String)
235     * @see java.util.regex.Matcher#replaceFirst(String)
236     * @see java.util.regex.Pattern
237     * @since 3.18.0
238     */
239    public static String removeFirst(final CharSequence text, final Pattern regex) {
240        return replaceFirst(text, regex, StringUtils.EMPTY);
241    }
242
243    /**
244     * Removes the first substring of the text string that matches the given regular expression pattern.
245     *
246     * This method is a {@code null} safe equivalent to:
247     * <ul>
248     *  <li>{@code pattern.matcher(text).replaceFirst(StringUtils.EMPTY)}</li>
249     * </ul>
250     *
251     * <p>
252     * A {@code null} reference passed to this method is a no-op.
253     * </p>
254     *
255     * <pre>{@code
256     * RegExUtils.removeFirst(null, *)      = null
257     * RegExUtils.removeFirst("any", (Pattern) null)  = "any"
258     * RegExUtils.removeFirst("any", Pattern.compile(""))    = "any"
259     * RegExUtils.removeFirst("any", Pattern.compile(".*"))  = ""
260     * RegExUtils.removeFirst("any", Pattern.compile(".+"))  = ""
261     * RegExUtils.removeFirst("abc", Pattern.compile(".?"))  = "bc"
262     * RegExUtils.removeFirst("A<__>\n<__>B", Pattern.compile("<.*>"))      = "A\n<__>B"
263     * RegExUtils.removeFirst("A<__>\n<__>B", Pattern.compile("(?s)<.*>"))  = "AB"
264     * RegExUtils.removeFirst("ABCabc123", Pattern.compile("[a-z]"))          = "ABCbc123"
265     * RegExUtils.removeFirst("ABCabc123abc", Pattern.compile("[a-z]+"))      = "ABC123abc"
266     * }</pre>
267     *
268     * @param text  text to remove from, may be null.
269     * @param regex  The regular expression pattern to which this string is to be matched.
270     * @return  the text with the first replacement processed,
271     *              {@code null} if null String input.
272     *
273     * @see #replaceFirst(String, Pattern, String)
274     * @see java.util.regex.Matcher#replaceFirst(String)
275     * @see java.util.regex.Pattern
276     * @deprecated Use {@link #removeFirst(CharSequence, Pattern)}.
277     */
278    @Deprecated
279    public static String removeFirst(final String text, final Pattern regex) {
280        return replaceFirst(text, regex, StringUtils.EMPTY);
281    }
282
283    /**
284     * Removes the first substring of the text string that matches the given regular expression.
285     *
286     * This method is a {@code null} safe equivalent to:
287     * <ul>
288     *  <li>{@code text.replaceFirst(regex, StringUtils.EMPTY)}</li>
289     *  <li>{@code Pattern.compile(regex).matcher(text).replaceFirst(StringUtils.EMPTY)}</li>
290     * </ul>
291     *
292     * <p>
293     * A {@code null} reference passed to this method is a no-op.
294     * </p>
295     *
296     * <p>
297     * The {@link Pattern#DOTALL} option is NOT automatically added.
298     * To use the DOTALL option prepend {@code "(?s)"} to the regex.
299     * DOTALL is also known as single-line mode in Perl.
300     * </p>
301     *
302     * <pre>{@code
303     * RegExUtils.removeFirst(null, *)      = null
304     * RegExUtils.removeFirst("any", (String) null)  = "any"
305     * RegExUtils.removeFirst("any", "")    = "any"
306     * RegExUtils.removeFirst("any", ".*")  = ""
307     * RegExUtils.removeFirst("any", ".+")  = ""
308     * RegExUtils.removeFirst("abc", ".?")  = "bc"
309     * RegExUtils.removeFirst("A<__>\n<__>B", "<.*>")      = "A\n<__>B"
310     * RegExUtils.removeFirst("A<__>\n<__>B", "(?s)<.*>")  = "AB"
311     * RegExUtils.removeFirst("ABCabc123", "[a-z]")          = "ABCbc123"
312     * RegExUtils.removeFirst("ABCabc123abc", "[a-z]+")      = "ABC123abc"
313     * }</pre>
314     *
315     * @param text  text to remove from, may be null.
316     * @param regex  The regular expression to which this string is to be matched.
317     * @return  the text with the first replacement processed,
318     *              {@code null} if null String input.
319     *
320     * @throws  java.util.regex.PatternSyntaxException
321     *              Thrown if the regular expression's syntax is invalid.
322     *
323     * @see #replaceFirst(String, String, String)
324     * @see String#replaceFirst(String, String)
325     * @see java.util.regex.Pattern
326     * @see java.util.regex.Pattern#DOTALL
327     */
328    public static String removeFirst(final String text, final String regex) {
329        return replaceFirst(text, regex, StringUtils.EMPTY);
330    }
331
332    /**
333     * Removes each substring of the source String that matches the given regular expression using the DOTALL option.
334     *
335     * This call is a {@code null} safe equivalent to:
336     * <ul>
337     * <li>{@code text.replaceAll(&quot;(?s)&quot; + regex, StringUtils.EMPTY)}</li>
338     * <li>{@code Pattern.compile(regex, Pattern.DOTALL).matcher(text).replaceAll(StringUtils.EMPTY)}</li>
339     * </ul>
340     *
341     * <p>
342     * A {@code null} reference passed to this method is a no-op.
343     * </p>
344     *
345     * <pre>{@code
346     * RegExUtils.removePattern(null, *)       = null
347     * RegExUtils.removePattern("any", (String) null)   = "any"
348     * RegExUtils.removePattern("A<__>\n<__>B", "<.*>")  = "AB"
349     * RegExUtils.removePattern("ABCabc123", "[a-z]")    = "ABC123"
350     * }</pre>
351     *
352     * @param text
353     *            the source string.
354     * @param regex
355     *            the regular expression to which this string is to be matched.
356     * @return The resulting {@link String}.
357     * @see #replacePattern(CharSequence, String, String)
358     * @see String#replaceAll(String, String)
359     * @see Pattern#DOTALL
360     * @since 3.18.0
361     */
362    public static String removePattern(final CharSequence text, final String regex) {
363        return replacePattern(text, regex, StringUtils.EMPTY);
364    }
365
366    /**
367     * Removes each substring of the source String that matches the given regular expression using the DOTALL option.
368     *
369     * This call is a {@code null} safe equivalent to:
370     * <ul>
371     * <li>{@code text.replaceAll(&quot;(?s)&quot; + regex, StringUtils.EMPTY)}</li>
372     * <li>{@code Pattern.compile(regex, Pattern.DOTALL).matcher(text).replaceAll(StringUtils.EMPTY)}</li>
373     * </ul>
374     *
375     * <p>
376     * A {@code null} reference passed to this method is a no-op.
377     * </p>
378     *
379     * <pre>{@code
380     * RegExUtils.removePattern(null, *)       = null
381     * RegExUtils.removePattern("any", (String) null)   = "any"
382     * RegExUtils.removePattern("A<__>\n<__>B", "<.*>")  = "AB"
383     * RegExUtils.removePattern("ABCabc123", "[a-z]")    = "ABC123"
384     * }</pre>
385     *
386     * @param text
387     *            the source string.
388     * @param regex
389     *            the regular expression to which this string is to be matched.
390     * @return The resulting {@link String}.
391     * @see #replacePattern(CharSequence, String, String)
392     * @see String#replaceAll(String, String)
393     * @see Pattern#DOTALL
394     * @deprecated Use {@link #removePattern(CharSequence, String)}.
395     */
396    @Deprecated
397    public static String removePattern(final String text, final String regex) {
398        return replacePattern((CharSequence) text, regex, StringUtils.EMPTY);
399    }
400
401    /**
402     * Replaces each substring of the text String that matches the given regular expression pattern with the given replacement.
403     *
404     * This method is a {@code null} safe equivalent to:
405     * <ul>
406     *  <li>{@code pattern.matcher(text).replaceAll(replacement)}</li>
407     * </ul>
408     *
409     * <p>
410     * A {@code null} reference passed to this method is a no-op.
411     * </p>
412     *
413     * <pre>{@code
414     * RegExUtils.replaceAll(null, *, *)       = null
415     * RegExUtils.replaceAll("any", (Pattern) null, *)   = "any"
416     * RegExUtils.replaceAll("any", *, null)   = "any"
417     * RegExUtils.replaceAll("", Pattern.compile(""), "zzz")    = "zzz"
418     * RegExUtils.replaceAll("", Pattern.compile(".*"), "zzz")  = "zzz"
419     * RegExUtils.replaceAll("", Pattern.compile(".+"), "zzz")  = ""
420     * RegExUtils.replaceAll("abc", Pattern.compile(""), "ZZ")  = "ZZaZZbZZcZZ"
421     * RegExUtils.replaceAll("<__>\n<__>", Pattern.compile("<.*>"), "z")                 = "z\nz"
422     * RegExUtils.replaceAll("<__>\n<__>", Pattern.compile("<.*>", Pattern.DOTALL), "z") = "z"
423     * RegExUtils.replaceAll("<__>\n<__>", Pattern.compile("(?s)<.*>"), "z")             = "z"
424     * RegExUtils.replaceAll("ABCabc123", Pattern.compile("[a-z]"), "_")       = "ABC___123"
425     * RegExUtils.replaceAll("ABCabc123", Pattern.compile("[^A-Z0-9]+"), "_")  = "ABC_123"
426     * RegExUtils.replaceAll("ABCabc123", Pattern.compile("[^A-Z0-9]+"), "")   = "ABC123"
427     * RegExUtils.replaceAll("Lorem ipsum  dolor   sit", Pattern.compile("( +)([a-z]+)"), "_$2")  = "Lorem_ipsum_dolor_sit"
428     * }</pre>
429     *
430     * @param text  text to search and replace in, may be null.
431     * @param regex  The regular expression pattern to which this string is to be matched.
432     * @param replacement  The string to be substituted for each match.
433     * @return  the text with any replacements processed,
434     *              {@code null} if null String input.
435     * @see java.util.regex.Matcher#replaceAll(String)
436     * @see java.util.regex.Pattern
437     */
438    public static String replaceAll(final CharSequence text, final Pattern regex, final String replacement) {
439        if (ObjectUtils.anyNull(text, regex, replacement)) {
440            return toStringOrNull(text);
441        }
442        return regex.matcher(text).replaceAll(replacement);
443    }
444
445    /**
446     * Replaces each substring of the text String that matches the given regular expression pattern with the given replacement.
447     *
448     * This method is a {@code null} safe equivalent to:
449     * <ul>
450     *  <li>{@code pattern.matcher(text).replaceAll(replacement)}</li>
451     * </ul>
452     *
453     * <p>
454     * A {@code null} reference passed to this method is a no-op.
455     * </p>
456     *
457     * <pre>{@code
458     * RegExUtils.replaceAll(null, *, *)       = null
459     * RegExUtils.replaceAll("any", (Pattern) null, *)   = "any"
460     * RegExUtils.replaceAll("any", *, null)   = "any"
461     * RegExUtils.replaceAll("", Pattern.compile(""), "zzz")    = "zzz"
462     * RegExUtils.replaceAll("", Pattern.compile(".*"), "zzz")  = "zzz"
463     * RegExUtils.replaceAll("", Pattern.compile(".+"), "zzz")  = ""
464     * RegExUtils.replaceAll("abc", Pattern.compile(""), "ZZ")  = "ZZaZZbZZcZZ"
465     * RegExUtils.replaceAll("<__>\n<__>", Pattern.compile("<.*>"), "z")                 = "z\nz"
466     * RegExUtils.replaceAll("<__>\n<__>", Pattern.compile("<.*>", Pattern.DOTALL), "z") = "z"
467     * RegExUtils.replaceAll("<__>\n<__>", Pattern.compile("(?s)<.*>"), "z")             = "z"
468     * RegExUtils.replaceAll("ABCabc123", Pattern.compile("[a-z]"), "_")       = "ABC___123"
469     * RegExUtils.replaceAll("ABCabc123", Pattern.compile("[^A-Z0-9]+"), "_")  = "ABC_123"
470     * RegExUtils.replaceAll("ABCabc123", Pattern.compile("[^A-Z0-9]+"), "")   = "ABC123"
471     * RegExUtils.replaceAll("Lorem ipsum  dolor   sit", Pattern.compile("( +)([a-z]+)"), "_$2")  = "Lorem_ipsum_dolor_sit"
472     * }</pre>
473     *
474     * @param text  text to search and replace in, may be null.
475     * @param regex  The regular expression pattern to which this string is to be matched.
476     * @param replacement  The string to be substituted for each match.
477     * @return  the text with any replacements processed,
478     *              {@code null} if null String input.
479     * @see java.util.regex.Matcher#replaceAll(String)
480     * @see java.util.regex.Pattern
481     * @deprecated Use {@link #replaceAll(CharSequence, Pattern, String)}.
482     */
483    @Deprecated
484    public static String replaceAll(final String text, final Pattern regex, final String replacement) {
485        return replaceAll((CharSequence) text, regex, replacement);
486    }
487
488    /**
489     * Replaces each substring of the text String that matches the given regular expression
490     * with the given replacement.
491     *
492     * This method is a {@code null} safe equivalent to:
493     * <ul>
494     *  <li>{@code text.replaceAll(regex, replacement)}</li>
495     *  <li>{@code Pattern.compile(regex).matcher(text).replaceAll(replacement)}</li>
496     * </ul>
497     *
498     * <p>
499     * A {@code null} reference passed to this method is a no-op.
500     * </p>
501     *
502     * <p>
503     * Unlike in the {@link #replacePattern(CharSequence, String, String)} method, the {@link Pattern#DOTALL} option
504     * is NOT automatically added.
505     * To use the DOTALL option prepend {@code "(?s)"} to the regex.
506     * DOTALL is also known as single-line mode in Perl.
507     * </p>
508     *
509     * <pre>{@code
510     * RegExUtils.replaceAll(null, *, *)       = null
511     * RegExUtils.replaceAll("any", (String) null, *)   = "any"
512     * RegExUtils.replaceAll("any", *, null)   = "any"
513     * RegExUtils.replaceAll("", "", "zzz")    = "zzz"
514     * RegExUtils.replaceAll("", ".*", "zzz")  = "zzz"
515     * RegExUtils.replaceAll("", ".+", "zzz")  = ""
516     * RegExUtils.replaceAll("abc", "", "ZZ")  = "ZZaZZbZZcZZ"
517     * RegExUtils.replaceAll("<__>\n<__>", "<.*>", "z")      = "z\nz"
518     * RegExUtils.replaceAll("<__>\n<__>", "(?s)<.*>", "z")  = "z"
519     * RegExUtils.replaceAll("ABCabc123", "[a-z]", "_")       = "ABC___123"
520     * RegExUtils.replaceAll("ABCabc123", "[^A-Z0-9]+", "_")  = "ABC_123"
521     * RegExUtils.replaceAll("ABCabc123", "[^A-Z0-9]+", "")   = "ABC123"
522     * RegExUtils.replaceAll("Lorem ipsum  dolor   sit", "( +)([a-z]+)", "_$2")  = "Lorem_ipsum_dolor_sit"
523     * }</pre>
524     *
525     * @param text  text to search and replace in, may be null.
526     * @param regex  The regular expression to which this string is to be matched.
527     * @param replacement  The string to be substituted for each match.
528     * @return  the text with any replacements processed,
529     *              {@code null} if null String input.
530     * @throws  java.util.regex.PatternSyntaxException
531     *              Thrown if the regular expression's syntax is invalid.
532     * @see #replacePattern(String, String, String)
533     * @see String#replaceAll(String, String)
534     * @see java.util.regex.Pattern
535     * @see java.util.regex.Pattern#DOTALL
536     */
537    public static String replaceAll(final String text, final String regex, final String replacement) {
538        if (ObjectUtils.anyNull(text, regex, replacement)) {
539            return text;
540        }
541        return text.replaceAll(regex, replacement);
542    }
543
544    /**
545     * Replaces the first substring of the text string that matches the given regular expression pattern
546     * with the given replacement.
547     *
548     * This method is a {@code null} safe equivalent to:
549     * <ul>
550     *  <li>{@code pattern.matcher(text).replaceFirst(replacement)}</li>
551     * </ul>
552     *
553     * <p>
554     * A {@code null} reference passed to this method is a no-op.
555     * </p>
556     *
557     * <pre>{@code
558     * RegExUtils.replaceFirst(null, *, *)       = null
559     * RegExUtils.replaceFirst("any", (Pattern) null, *)   = "any"
560     * RegExUtils.replaceFirst("any", *, null)   = "any"
561     * RegExUtils.replaceFirst("", Pattern.compile(""), "zzz")    = "zzz"
562     * RegExUtils.replaceFirst("", Pattern.compile(".*"), "zzz")  = "zzz"
563     * RegExUtils.replaceFirst("", Pattern.compile(".+"), "zzz")  = ""
564     * RegExUtils.replaceFirst("abc", Pattern.compile(""), "ZZ")  = "ZZabc"
565     * RegExUtils.replaceFirst("<__>\n<__>", Pattern.compile("<.*>"), "z")      = "z\n<__>"
566     * RegExUtils.replaceFirst("<__>\n<__>", Pattern.compile("(?s)<.*>"), "z")  = "z"
567     * RegExUtils.replaceFirst("ABCabc123", Pattern.compile("[a-z]"), "_")          = "ABC_bc123"
568     * RegExUtils.replaceFirst("ABCabc123abc", Pattern.compile("[^A-Z0-9]+"), "_")  = "ABC_123abc"
569     * RegExUtils.replaceFirst("ABCabc123abc", Pattern.compile("[^A-Z0-9]+"), "")   = "ABC123abc"
570     * RegExUtils.replaceFirst("Lorem ipsum  dolor   sit", Pattern.compile("( +)([a-z]+)"), "_$2")  = "Lorem_ipsum  dolor   sit"
571     * }</pre>
572     *
573     * @param text  text to search and replace in, may be null.
574     * @param regex  The regular expression pattern to which this string is to be matched.
575     * @param replacement  The string to be substituted for the first match
576     * @return  the text with the first replacement processed,
577     *              {@code null} if null String input.
578     * @see java.util.regex.Matcher#replaceFirst(String)
579     * @see java.util.regex.Pattern
580     * @since 3.18.0
581     */
582    public static String replaceFirst(final CharSequence text, final Pattern regex, final String replacement) {
583        if (text == null || regex == null || replacement == null) {
584            return toStringOrNull(text);
585        }
586        return regex.matcher(text).replaceFirst(replacement);
587    }
588
589    /**
590     * Replaces the first substring of the text string that matches the given regular expression pattern
591     * with the given replacement.
592     *
593     * This method is a {@code null} safe equivalent to:
594     * <ul>
595     *  <li>{@code pattern.matcher(text).replaceFirst(replacement)}</li>
596     * </ul>
597     *
598     * <p>
599     * A {@code null} reference passed to this method is a no-op.
600     * </p>
601     *
602     * <pre>{@code
603     * RegExUtils.replaceFirst(null, *, *)       = null
604     * RegExUtils.replaceFirst("any", (Pattern) null, *)   = "any"
605     * RegExUtils.replaceFirst("any", *, null)   = "any"
606     * RegExUtils.replaceFirst("", Pattern.compile(""), "zzz")    = "zzz"
607     * RegExUtils.replaceFirst("", Pattern.compile(".*"), "zzz")  = "zzz"
608     * RegExUtils.replaceFirst("", Pattern.compile(".+"), "zzz")  = ""
609     * RegExUtils.replaceFirst("abc", Pattern.compile(""), "ZZ")  = "ZZabc"
610     * RegExUtils.replaceFirst("<__>\n<__>", Pattern.compile("<.*>"), "z")      = "z\n<__>"
611     * RegExUtils.replaceFirst("<__>\n<__>", Pattern.compile("(?s)<.*>"), "z")  = "z"
612     * RegExUtils.replaceFirst("ABCabc123", Pattern.compile("[a-z]"), "_")          = "ABC_bc123"
613     * RegExUtils.replaceFirst("ABCabc123abc", Pattern.compile("[^A-Z0-9]+"), "_")  = "ABC_123abc"
614     * RegExUtils.replaceFirst("ABCabc123abc", Pattern.compile("[^A-Z0-9]+"), "")   = "ABC123abc"
615     * RegExUtils.replaceFirst("Lorem ipsum  dolor   sit", Pattern.compile("( +)([a-z]+)"), "_$2")  = "Lorem_ipsum  dolor   sit"
616     * }</pre>
617     *
618     * @param text  text to search and replace in, may be null.
619     * @param regex  The regular expression pattern to which this string is to be matched.
620     * @param replacement  The string to be substituted for the first match.
621     * @return  the text with the first replacement processed,
622     *              {@code null} if null String input.
623     * @see java.util.regex.Matcher#replaceFirst(String)
624     * @see java.util.regex.Pattern
625     * @deprecated Use {@link #replaceFirst(CharSequence, Pattern, String)}.
626     */
627    @Deprecated
628    public static String replaceFirst(final String text, final Pattern regex, final String replacement) {
629        return replaceFirst((CharSequence) text, regex, replacement);
630    }
631
632    /**
633     * Replaces the first substring of the text string that matches the given regular expression
634     * with the given replacement.
635     *
636     * This method is a {@code null} safe equivalent to:
637     * <ul>
638     *  <li>{@code text.replaceFirst(regex, replacement)}</li>
639     *  <li>{@code Pattern.compile(regex).matcher(text).replaceFirst(replacement)}</li>
640     * </ul>
641     *
642     * <p>
643     * A {@code null} reference passed to this method is a no-op.
644     * </p>
645     *
646     * <p>
647     * The {@link Pattern#DOTALL} option is NOT automatically added.
648     * To use the DOTALL option prepend {@code "(?s)"} to the regex.
649     * DOTALL is also known as single-line mode in Perl.
650     * </p>
651     *
652     * <pre>{@code
653     * RegExUtils.replaceFirst(null, *, *)       = null
654     * RegExUtils.replaceFirst("any", (String) null, *)   = "any"
655     * RegExUtils.replaceFirst("any", *, null)   = "any"
656     * RegExUtils.replaceFirst("", "", "zzz")    = "zzz"
657     * RegExUtils.replaceFirst("", ".*", "zzz")  = "zzz"
658     * RegExUtils.replaceFirst("", ".+", "zzz")  = ""
659     * RegExUtils.replaceFirst("abc", "", "ZZ")  = "ZZabc"
660     * RegExUtils.replaceFirst("<__>\n<__>", "<.*>", "z")      = "z\n<__>"
661     * RegExUtils.replaceFirst("<__>\n<__>", "(?s)<.*>", "z")  = "z"
662     * RegExUtils.replaceFirst("ABCabc123", "[a-z]", "_")          = "ABC_bc123"
663     * RegExUtils.replaceFirst("ABCabc123abc", "[^A-Z0-9]+", "_")  = "ABC_123abc"
664     * RegExUtils.replaceFirst("ABCabc123abc", "[^A-Z0-9]+", "")   = "ABC123abc"
665     * RegExUtils.replaceFirst("Lorem ipsum  dolor   sit", "( +)([a-z]+)", "_$2")  = "Lorem_ipsum  dolor   sit"
666     * }</pre>
667     *
668     * @param text  text to search and replace in, may be null.
669     * @param regex  The regular expression to which this string is to be matched.
670     * @param replacement  The string to be substituted for the first match.
671     * @return  the text with the first replacement processed,
672     *              {@code null} if null String input.
673     * @throws  java.util.regex.PatternSyntaxException
674     *              Thrown if the regular expression's syntax is invalid.
675     * @see String#replaceFirst(String, String)
676     * @see java.util.regex.Pattern
677     * @see java.util.regex.Pattern#DOTALL
678     */
679    public static String replaceFirst(final String text, final String regex, final String replacement) {
680        if (text == null || regex == null || replacement == null) {
681            return text;
682        }
683        return text.replaceFirst(regex, replacement);
684    }
685
686    /**
687     * Replaces each substring of the source String that matches the given regular expression with the given
688     * replacement using the {@link Pattern#DOTALL} option. DOTALL is also known as single-line mode in Perl.
689     *
690     * This call is a {@code null} safe equivalent to:
691     * <ul>
692     * <li>{@code text.replaceAll(&quot;(?s)&quot; + regex, replacement)}</li>
693     * <li>{@code Pattern.compile(regex, Pattern.DOTALL).matcher(text).replaceAll(replacement)}</li>
694     * </ul>
695     *
696     * <p>
697     * A {@code null} reference passed to this method is a no-op.
698     * </p>
699     *
700     * <pre>{@code
701     * RegExUtils.replacePattern(null, *, *)       = null
702     * RegExUtils.replacePattern("any", (String) null, *)   = "any"
703     * RegExUtils.replacePattern("any", *, null)   = "any"
704     * RegExUtils.replacePattern("", "", "zzz")    = "zzz"
705     * RegExUtils.replacePattern("", ".*", "zzz")  = "zzz"
706     * RegExUtils.replacePattern("", ".+", "zzz")  = ""
707     * RegExUtils.replacePattern("<__>\n<__>", "<.*>", "z")       = "z"
708     * RegExUtils.replacePattern("ABCabc123", "[a-z]", "_")       = "ABC___123"
709     * RegExUtils.replacePattern("ABCabc123", "[^A-Z0-9]+", "_")  = "ABC_123"
710     * RegExUtils.replacePattern("ABCabc123", "[^A-Z0-9]+", "")   = "ABC123"
711     * RegExUtils.replacePattern("Lorem ipsum  dolor   sit", "( +)([a-z]+)", "_$2")  = "Lorem_ipsum_dolor_sit"
712     * }</pre>
713     *
714     * @param text
715     *            the source string.
716     * @param regex
717     *            the regular expression to which this string is to be matched.
718     * @param replacement
719     *            the string to be substituted for each match.
720     * @return The resulting {@link String}.
721     * @see #replaceAll(String, String, String)
722     * @see String#replaceAll(String, String)
723     * @see Pattern#DOTALL
724     * @since 3.18.0
725     */
726    public static String replacePattern(final CharSequence text, final String regex, final String replacement) {
727        if (ObjectUtils.anyNull(text, regex, replacement)) {
728            return toStringOrNull(text);
729        }
730        return dotAllMatcher(regex, text).replaceAll(replacement);
731    }
732
733    /**
734     * Replaces each substring of the source String that matches the given regular expression with the given
735     * replacement using the {@link Pattern#DOTALL} option. DOTALL is also known as single-line mode in Perl.
736     *
737     * This call is a {@code null} safe equivalent to:
738     * <ul>
739     * <li>{@code text.replaceAll(&quot;(?s)&quot; + regex, replacement)}</li>
740     * <li>{@code Pattern.compile(regex, Pattern.DOTALL).matcher(text).replaceAll(replacement)}</li>
741     * </ul>
742     *
743     * <p>
744     * A {@code null} reference passed to this method is a no-op.
745     * </p>
746     *
747     * <pre>{@code
748     * RegExUtils.replacePattern(null, *, *)       = null
749     * RegExUtils.replacePattern("any", (String) null, *)   = "any"
750     * RegExUtils.replacePattern("any", *, null)   = "any"
751     * RegExUtils.replacePattern("", "", "zzz")    = "zzz"
752     * RegExUtils.replacePattern("", ".*", "zzz")  = "zzz"
753     * RegExUtils.replacePattern("", ".+", "zzz")  = ""
754     * RegExUtils.replacePattern("<__>\n<__>", "<.*>", "z")       = "z"
755     * RegExUtils.replacePattern("ABCabc123", "[a-z]", "_")       = "ABC___123"
756     * RegExUtils.replacePattern("ABCabc123", "[^A-Z0-9]+", "_")  = "ABC_123"
757     * RegExUtils.replacePattern("ABCabc123", "[^A-Z0-9]+", "")   = "ABC123"
758     * RegExUtils.replacePattern("Lorem ipsum  dolor   sit", "( +)([a-z]+)", "_$2")  = "Lorem_ipsum_dolor_sit"
759     * }</pre>
760     *
761     * @param text
762     *            the source string.
763     * @param regex
764     *            the regular expression to which this string is to be matched.
765     * @param replacement
766     *            the string to be substituted for each match.
767     * @return The resulting {@link String}.
768     * @see #replaceAll(String, String, String)
769     * @see String#replaceAll(String, String)
770     * @see Pattern#DOTALL
771     * @deprecated Use {@link #replacePattern(CharSequence, String, String)}.
772     */
773    @Deprecated
774    public static String replacePattern(final String text, final String regex, final String replacement) {
775        return replacePattern((CharSequence) text, regex, replacement);
776    }
777
778    private static String toStringOrNull(final CharSequence text) {
779        return Objects.toString(text, null);
780    }
781
782    /**
783     * Make private in 4.0.
784     *
785     * @deprecated TODO Make private in 4.0.
786     */
787    @Deprecated
788    public RegExUtils() {
789        // empty
790    }
791}