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>A {@code null} reference passed to this method is a no-op.</p>
084     *
085     * <pre>{@code
086     * StringUtils.removeAll(null, *)      = null
087     * StringUtils.removeAll("any", (Pattern) null)  = "any"
088     * StringUtils.removeAll("any", Pattern.compile(""))    = "any"
089     * StringUtils.removeAll("any", Pattern.compile(".*"))  = ""
090     * StringUtils.removeAll("any", Pattern.compile(".+"))  = ""
091     * StringUtils.removeAll("abc", Pattern.compile(".?"))  = ""
092     * StringUtils.removeAll("A<__>\n<__>B", Pattern.compile("<.*>"))      = "A\nB"
093     * StringUtils.removeAll("A<__>\n<__>B", Pattern.compile("(?s)<.*>"))  = "AB"
094     * StringUtils.removeAll("A<__>\n<__>B", Pattern.compile("<.*>", Pattern.DOTALL))  = "AB"
095     * StringUtils.removeAll("ABCabc123abc", Pattern.compile("[a-z]"))     = "ABC123"
096     * }</pre>
097     *
098     * @param text  text to remove from, may be null.
099     * @param regex  the regular expression to which this string is to be matched.
100     * @return  the text with any removes processed,
101     *              {@code null} if null String input.
102     *
103     * @see #replaceAll(CharSequence, Pattern, String)
104     * @see java.util.regex.Matcher#replaceAll(String)
105     * @see java.util.regex.Pattern
106     * @since 3.18.0
107     */
108    public static String removeAll(final CharSequence text, final Pattern regex) {
109        return replaceAll(text, regex, StringUtils.EMPTY);
110    }
111
112    /**
113     * Removes each substring of the text String that matches the given regular expression pattern.
114     *
115     * This method is a {@code null} safe equivalent to:
116     * <ul>
117     *  <li>{@code pattern.matcher(text).replaceAll(StringUtils.EMPTY)}</li>
118     * </ul>
119     *
120     * <p>A {@code null} reference passed to this method is a no-op.</p>
121     *
122     * <pre>{@code
123     * StringUtils.removeAll(null, *)      = null
124     * StringUtils.removeAll("any", (Pattern) null)  = "any"
125     * StringUtils.removeAll("any", Pattern.compile(""))    = "any"
126     * StringUtils.removeAll("any", Pattern.compile(".*"))  = ""
127     * StringUtils.removeAll("any", Pattern.compile(".+"))  = ""
128     * StringUtils.removeAll("abc", Pattern.compile(".?"))  = ""
129     * StringUtils.removeAll("A<__>\n<__>B", Pattern.compile("<.*>"))      = "A\nB"
130     * StringUtils.removeAll("A<__>\n<__>B", Pattern.compile("(?s)<.*>"))  = "AB"
131     * StringUtils.removeAll("A<__>\n<__>B", Pattern.compile("<.*>", Pattern.DOTALL))  = "AB"
132     * StringUtils.removeAll("ABCabc123abc", Pattern.compile("[a-z]"))     = "ABC123"
133     * }</pre>
134     *
135     * @param text  text to remove from, may be null.
136     * @param regex  the regular expression to which this string is to be matched
137     * @return  the text with any removes processed,
138     *              {@code null} if null String input.
139     *
140     * @see #replaceAll(CharSequence, Pattern, String)
141     * @see java.util.regex.Matcher#replaceAll(String)
142     * @see java.util.regex.Pattern
143     * @deprecated Use {@link #removeAll(CharSequence, Pattern)}.
144     */
145    @Deprecated
146    public static String removeAll(final String text, final Pattern regex) {
147        return replaceAll((CharSequence) text, regex, StringUtils.EMPTY);
148    }
149
150    /**
151     * Removes each substring of the text String that matches the given regular expression.
152     *
153     * This method is a {@code null} safe equivalent to:
154     * <ul>
155     *  <li>{@code text.replaceAll(regex, StringUtils.EMPTY)}</li>
156     *  <li>{@code Pattern.compile(regex).matcher(text).replaceAll(StringUtils.EMPTY)}</li>
157     * </ul>
158     *
159     * <p>A {@code null} reference passed to this method is a no-op.</p>
160     *
161     * <p>Unlike in the {@link #removePattern(CharSequence, String)} method, the {@link Pattern#DOTALL} option
162     * is NOT automatically added.
163     * To use the DOTALL option prepend {@code "(?s)"} to the regex.
164     * DOTALL is also known as single-line mode in Perl.</p>
165     *
166     * <pre>{@code
167     * StringUtils.removeAll(null, *)      = null
168     * StringUtils.removeAll("any", (String) null)  = "any"
169     * StringUtils.removeAll("any", "")    = "any"
170     * StringUtils.removeAll("any", ".*")  = ""
171     * StringUtils.removeAll("any", ".+")  = ""
172     * StringUtils.removeAll("abc", ".?")  = ""
173     * StringUtils.removeAll("A<__>\n<__>B", "<.*>")      = "A\nB"
174     * StringUtils.removeAll("A<__>\n<__>B", "(?s)<.*>")  = "AB"
175     * StringUtils.removeAll("ABCabc123abc", "[a-z]")     = "ABC123"
176     * }</pre>
177     *
178     * @param text  text to remove from, may be null
179     * @param regex  the regular expression to which this string is to be matched
180     * @return  the text with any removes processed,
181     *              {@code null} if null String input.
182     *
183     * @throws  java.util.regex.PatternSyntaxException
184     *              if the regular expression's syntax is invalid.
185     *
186     * @see #replaceAll(String, String, String)
187     * @see #removePattern(CharSequence, String)
188     * @see String#replaceAll(String, String)
189     * @see java.util.regex.Pattern
190     * @see java.util.regex.Pattern#DOTALL
191     */
192    public static String removeAll(final String text, final String regex) {
193        return replaceAll(text, regex, StringUtils.EMPTY);
194    }
195
196    /**
197     * Removes the first substring of the text string that matches the given regular expression pattern.
198     *
199     * This method is a {@code null} safe equivalent to:
200     * <ul>
201     *  <li>{@code pattern.matcher(text).replaceFirst(StringUtils.EMPTY)}</li>
202     * </ul>
203     *
204     * <p>A {@code null} reference passed to this method is a no-op.</p>
205     *
206     * <pre>{@code
207     * StringUtils.removeFirst(null, *)      = null
208     * StringUtils.removeFirst("any", (Pattern) null)  = "any"
209     * StringUtils.removeFirst("any", Pattern.compile(""))    = "any"
210     * StringUtils.removeFirst("any", Pattern.compile(".*"))  = ""
211     * StringUtils.removeFirst("any", Pattern.compile(".+"))  = ""
212     * StringUtils.removeFirst("abc", Pattern.compile(".?"))  = "bc"
213     * StringUtils.removeFirst("A<__>\n<__>B", Pattern.compile("<.*>"))      = "A\n<__>B"
214     * StringUtils.removeFirst("A<__>\n<__>B", Pattern.compile("(?s)<.*>"))  = "AB"
215     * StringUtils.removeFirst("ABCabc123", Pattern.compile("[a-z]"))          = "ABCbc123"
216     * StringUtils.removeFirst("ABCabc123abc", Pattern.compile("[a-z]+"))      = "ABC123abc"
217     * }</pre>
218     *
219     * @param text  text to remove from, may be null.
220     * @param regex  the regular expression pattern to which this string is to be matched.
221     * @return  the text with the first replacement processed,
222     *              {@code null} if null String input.
223     *
224     * @see #replaceFirst(String, Pattern, String)
225     * @see java.util.regex.Matcher#replaceFirst(String)
226     * @see java.util.regex.Pattern
227     * @since 3.18.0
228     */
229    public static String removeFirst(final CharSequence text, final Pattern regex) {
230        return replaceFirst(text, regex, StringUtils.EMPTY);
231    }
232
233    /**
234     * Removes the first substring of the text string that matches the given regular expression pattern.
235     *
236     * This method is a {@code null} safe equivalent to:
237     * <ul>
238     *  <li>{@code pattern.matcher(text).replaceFirst(StringUtils.EMPTY)}</li>
239     * </ul>
240     *
241     * <p>A {@code null} reference passed to this method is a no-op.</p>
242     *
243     * <pre>{@code
244     * StringUtils.removeFirst(null, *)      = null
245     * StringUtils.removeFirst("any", (Pattern) null)  = "any"
246     * StringUtils.removeFirst("any", Pattern.compile(""))    = "any"
247     * StringUtils.removeFirst("any", Pattern.compile(".*"))  = ""
248     * StringUtils.removeFirst("any", Pattern.compile(".+"))  = ""
249     * StringUtils.removeFirst("abc", Pattern.compile(".?"))  = "bc"
250     * StringUtils.removeFirst("A<__>\n<__>B", Pattern.compile("<.*>"))      = "A\n<__>B"
251     * StringUtils.removeFirst("A<__>\n<__>B", Pattern.compile("(?s)<.*>"))  = "AB"
252     * StringUtils.removeFirst("ABCabc123", Pattern.compile("[a-z]"))          = "ABCbc123"
253     * StringUtils.removeFirst("ABCabc123abc", Pattern.compile("[a-z]+"))      = "ABC123abc"
254     * }</pre>
255     *
256     * @param text  text to remove from, may be null.
257     * @param regex  the regular expression pattern to which this string is to be matched.
258     * @return  the text with the first replacement processed,
259     *              {@code null} if null String input.
260     *
261     * @see #replaceFirst(String, Pattern, String)
262     * @see java.util.regex.Matcher#replaceFirst(String)
263     * @see java.util.regex.Pattern
264     * @deprecated Use {@link #removeFirst(CharSequence, Pattern)}.
265     */
266    @Deprecated
267    public static String removeFirst(final String text, final Pattern regex) {
268        return replaceFirst(text, regex, StringUtils.EMPTY);
269    }
270
271    /**
272     * Removes the first substring of the text string that matches the given regular expression.
273     *
274     * This method is a {@code null} safe equivalent to:
275     * <ul>
276     *  <li>{@code text.replaceFirst(regex, StringUtils.EMPTY)}</li>
277     *  <li>{@code Pattern.compile(regex).matcher(text).replaceFirst(StringUtils.EMPTY)}</li>
278     * </ul>
279     *
280     * <p>A {@code null} reference passed to this method is a no-op.</p>
281     *
282     * <p>The {@link Pattern#DOTALL} option is NOT automatically added.
283     * To use the DOTALL option prepend {@code "(?s)"} to the regex.
284     * DOTALL is also known as single-line mode in Perl.</p>
285     *
286     * <pre>{@code
287     * StringUtils.removeFirst(null, *)      = null
288     * StringUtils.removeFirst("any", (String) null)  = "any"
289     * StringUtils.removeFirst("any", "")    = "any"
290     * StringUtils.removeFirst("any", ".*")  = ""
291     * StringUtils.removeFirst("any", ".+")  = ""
292     * StringUtils.removeFirst("abc", ".?")  = "bc"
293     * StringUtils.removeFirst("A<__>\n<__>B", "<.*>")      = "A\n<__>B"
294     * StringUtils.removeFirst("A<__>\n<__>B", "(?s)<.*>")  = "AB"
295     * StringUtils.removeFirst("ABCabc123", "[a-z]")          = "ABCbc123"
296     * StringUtils.removeFirst("ABCabc123abc", "[a-z]+")      = "ABC123abc"
297     * }</pre>
298     *
299     * @param text  text to remove from, may be null.
300     * @param regex  the regular expression to which this string is to be matched.
301     * @return  the text with the first replacement processed,
302     *              {@code null} if null String input.
303     *
304     * @throws  java.util.regex.PatternSyntaxException
305     *              if the regular expression's syntax is invalid.
306     *
307     * @see #replaceFirst(String, String, String)
308     * @see String#replaceFirst(String, String)
309     * @see java.util.regex.Pattern
310     * @see java.util.regex.Pattern#DOTALL
311     */
312    public static String removeFirst(final String text, final String regex) {
313        return replaceFirst(text, regex, StringUtils.EMPTY);
314    }
315
316    /**
317     * Removes each substring of the source String that matches the given regular expression using the DOTALL option.
318     *
319     * This call is a {@code null} safe equivalent to:
320     * <ul>
321     * <li>{@code text.replaceAll(&quot;(?s)&quot; + regex, StringUtils.EMPTY)}</li>
322     * <li>{@code Pattern.compile(regex, Pattern.DOTALL).matcher(text).replaceAll(StringUtils.EMPTY)}</li>
323     * </ul>
324     *
325     * <p>A {@code null} reference passed to this method is a no-op.</p>
326     *
327     * <pre>{@code
328     * StringUtils.removePattern(null, *)       = null
329     * StringUtils.removePattern("any", (String) null)   = "any"
330     * StringUtils.removePattern("A<__>\n<__>B", "<.*>")  = "AB"
331     * StringUtils.removePattern("ABCabc123", "[a-z]")    = "ABC123"
332     * }</pre>
333     *
334     * @param text
335     *            the source string.
336     * @param regex
337     *            the regular expression to which this string is to be matched.
338     * @return The resulting {@link String}.
339     * @see #replacePattern(CharSequence, String, String)
340     * @see String#replaceAll(String, String)
341     * @see Pattern#DOTALL
342     * @since 3.18.0
343     */
344    public static String removePattern(final CharSequence text, final String regex) {
345        return replacePattern(text, regex, StringUtils.EMPTY);
346    }
347
348    /**
349     * Removes each substring of the source String that matches the given regular expression using the DOTALL option.
350     *
351     * This call is a {@code null} safe equivalent to:
352     * <ul>
353     * <li>{@code text.replaceAll(&quot;(?s)&quot; + regex, StringUtils.EMPTY)}</li>
354     * <li>{@code Pattern.compile(regex, Pattern.DOTALL).matcher(text).replaceAll(StringUtils.EMPTY)}</li>
355     * </ul>
356     *
357     * <p>A {@code null} reference passed to this method is a no-op.</p>
358     *
359     * <pre>{@code
360     * StringUtils.removePattern(null, *)       = null
361     * StringUtils.removePattern("any", (String) null)   = "any"
362     * StringUtils.removePattern("A<__>\n<__>B", "<.*>")  = "AB"
363     * StringUtils.removePattern("ABCabc123", "[a-z]")    = "ABC123"
364     * }</pre>
365     *
366     * @param text
367     *            the source string.
368     * @param regex
369     *            the regular expression to which this string is to be matched.
370     * @return The resulting {@link String}.
371     * @see #replacePattern(CharSequence, String, String)
372     * @see String#replaceAll(String, String)
373     * @see Pattern#DOTALL
374     * @deprecated Use {@link #removePattern(CharSequence, String)}.
375     */
376    @Deprecated
377    public static String removePattern(final String text, final String regex) {
378        return replacePattern((CharSequence) text, regex, StringUtils.EMPTY);
379    }
380
381    /**
382     * Replaces each substring of the text String that matches the given regular expression pattern with the given replacement.
383     *
384     * This method is a {@code null} safe equivalent to:
385     * <ul>
386     *  <li>{@code pattern.matcher(text).replaceAll(replacement)}</li>
387     * </ul>
388     *
389     * <p>A {@code null} reference passed to this method is a no-op.</p>
390     *
391     * <pre>{@code
392     * StringUtils.replaceAll(null, *, *)       = null
393     * StringUtils.replaceAll("any", (Pattern) null, *)   = "any"
394     * StringUtils.replaceAll("any", *, null)   = "any"
395     * StringUtils.replaceAll("", Pattern.compile(""), "zzz")    = "zzz"
396     * StringUtils.replaceAll("", Pattern.compile(".*"), "zzz")  = "zzz"
397     * StringUtils.replaceAll("", Pattern.compile(".+"), "zzz")  = ""
398     * StringUtils.replaceAll("abc", Pattern.compile(""), "ZZ")  = "ZZaZZbZZcZZ"
399     * StringUtils.replaceAll("<__>\n<__>", Pattern.compile("<.*>"), "z")                 = "z\nz"
400     * StringUtils.replaceAll("<__>\n<__>", Pattern.compile("<.*>", Pattern.DOTALL), "z") = "z"
401     * StringUtils.replaceAll("<__>\n<__>", Pattern.compile("(?s)<.*>"), "z")             = "z"
402     * StringUtils.replaceAll("ABCabc123", Pattern.compile("[a-z]"), "_")       = "ABC___123"
403     * StringUtils.replaceAll("ABCabc123", Pattern.compile("[^A-Z0-9]+"), "_")  = "ABC_123"
404     * StringUtils.replaceAll("ABCabc123", Pattern.compile("[^A-Z0-9]+"), "")   = "ABC123"
405     * StringUtils.replaceAll("Lorem ipsum  dolor   sit", Pattern.compile("( +)([a-z]+)"), "_$2")  = "Lorem_ipsum_dolor_sit"
406     * }</pre>
407     *
408     * @param text  text to search and replace in, may be null.
409     * @param regex  the regular expression pattern to which this string is to be matched.
410     * @param replacement  the string to be substituted for each match.
411     * @return  the text with any replacements processed,
412     *              {@code null} if null String input.
413     * @see java.util.regex.Matcher#replaceAll(String)
414     * @see java.util.regex.Pattern
415     */
416    public static String replaceAll(final CharSequence text, final Pattern regex, final String replacement) {
417        if (ObjectUtils.anyNull(text, regex, replacement)) {
418            return toStringOrNull(text);
419        }
420        return regex.matcher(text).replaceAll(replacement);
421    }
422
423    /**
424     * Replaces each substring of the text String that matches the given regular expression pattern with the given replacement.
425     *
426     * This method is a {@code null} safe equivalent to:
427     * <ul>
428     *  <li>{@code pattern.matcher(text).replaceAll(replacement)}</li>
429     * </ul>
430     *
431     * <p>A {@code null} reference passed to this method is a no-op.</p>
432     *
433     * <pre>{@code
434     * StringUtils.replaceAll(null, *, *)       = null
435     * StringUtils.replaceAll("any", (Pattern) null, *)   = "any"
436     * StringUtils.replaceAll("any", *, null)   = "any"
437     * StringUtils.replaceAll("", Pattern.compile(""), "zzz")    = "zzz"
438     * StringUtils.replaceAll("", Pattern.compile(".*"), "zzz")  = "zzz"
439     * StringUtils.replaceAll("", Pattern.compile(".+"), "zzz")  = ""
440     * StringUtils.replaceAll("abc", Pattern.compile(""), "ZZ")  = "ZZaZZbZZcZZ"
441     * StringUtils.replaceAll("<__>\n<__>", Pattern.compile("<.*>"), "z")                 = "z\nz"
442     * StringUtils.replaceAll("<__>\n<__>", Pattern.compile("<.*>", Pattern.DOTALL), "z") = "z"
443     * StringUtils.replaceAll("<__>\n<__>", Pattern.compile("(?s)<.*>"), "z")             = "z"
444     * StringUtils.replaceAll("ABCabc123", Pattern.compile("[a-z]"), "_")       = "ABC___123"
445     * StringUtils.replaceAll("ABCabc123", Pattern.compile("[^A-Z0-9]+"), "_")  = "ABC_123"
446     * StringUtils.replaceAll("ABCabc123", Pattern.compile("[^A-Z0-9]+"), "")   = "ABC123"
447     * StringUtils.replaceAll("Lorem ipsum  dolor   sit", Pattern.compile("( +)([a-z]+)"), "_$2")  = "Lorem_ipsum_dolor_sit"
448     * }</pre>
449     *
450     * @param text  text to search and replace in, may be null.
451     * @param regex  the regular expression pattern to which this string is to be matched.
452     * @param replacement  the string to be substituted for each match.
453     * @return  the text with any replacements processed,
454     *              {@code null} if null String input.
455     * @see java.util.regex.Matcher#replaceAll(String)
456     * @see java.util.regex.Pattern
457     * @deprecated Use {@link #replaceAll(CharSequence, Pattern, String)}.
458     */
459    @Deprecated
460    public static String replaceAll(final String text, final Pattern regex, final String replacement) {
461        return replaceAll((CharSequence) text, regex, replacement);
462    }
463
464    /**
465     * Replaces each substring of the text String that matches the given regular expression
466     * with the given replacement.
467     *
468     * This method is a {@code null} safe equivalent to:
469     * <ul>
470     *  <li>{@code text.replaceAll(regex, replacement)}</li>
471     *  <li>{@code Pattern.compile(regex).matcher(text).replaceAll(replacement)}</li>
472     * </ul>
473     *
474     * <p>A {@code null} reference passed to this method is a no-op.</p>
475     *
476     * <p>Unlike in the {@link #replacePattern(CharSequence, String, String)} method, the {@link Pattern#DOTALL} option
477     * is NOT automatically added.
478     * To use the DOTALL option prepend {@code "(?s)"} to the regex.
479     * DOTALL is also known as single-line mode in Perl.</p>
480     *
481     * <pre>{@code
482     * StringUtils.replaceAll(null, *, *)       = null
483     * StringUtils.replaceAll("any", (String) null, *)   = "any"
484     * StringUtils.replaceAll("any", *, null)   = "any"
485     * StringUtils.replaceAll("", "", "zzz")    = "zzz"
486     * StringUtils.replaceAll("", ".*", "zzz")  = "zzz"
487     * StringUtils.replaceAll("", ".+", "zzz")  = ""
488     * StringUtils.replaceAll("abc", "", "ZZ")  = "ZZaZZbZZcZZ"
489     * StringUtils.replaceAll("<__>\n<__>", "<.*>", "z")      = "z\nz"
490     * StringUtils.replaceAll("<__>\n<__>", "(?s)<.*>", "z")  = "z"
491     * StringUtils.replaceAll("ABCabc123", "[a-z]", "_")       = "ABC___123"
492     * StringUtils.replaceAll("ABCabc123", "[^A-Z0-9]+", "_")  = "ABC_123"
493     * StringUtils.replaceAll("ABCabc123", "[^A-Z0-9]+", "")   = "ABC123"
494     * StringUtils.replaceAll("Lorem ipsum  dolor   sit", "( +)([a-z]+)", "_$2")  = "Lorem_ipsum_dolor_sit"
495     * }</pre>
496     *
497     * @param text  text to search and replace in, may be null.
498     * @param regex  the regular expression to which this string is to be matched.
499     * @param replacement  the string to be substituted for each match.
500     * @return  the text with any replacements processed,
501     *              {@code null} if null String input.
502     * @throws  java.util.regex.PatternSyntaxException
503     *              if the regular expression's syntax is invalid.
504     * @see #replacePattern(String, String, String)
505     * @see String#replaceAll(String, String)
506     * @see java.util.regex.Pattern
507     * @see java.util.regex.Pattern#DOTALL
508     */
509    public static String replaceAll(final String text, final String regex, final String replacement) {
510        if (ObjectUtils.anyNull(text, regex, replacement)) {
511            return text;
512        }
513        return text.replaceAll(regex, replacement);
514    }
515
516    /**
517     * Replaces the first substring of the text string that matches the given regular expression pattern
518     * with the given replacement.
519     *
520     * This method is a {@code null} safe equivalent to:
521     * <ul>
522     *  <li>{@code pattern.matcher(text).replaceFirst(replacement)}</li>
523     * </ul>
524     *
525     * <p>A {@code null} reference passed to this method is a no-op.</p>
526     *
527     * <pre>{@code
528     * StringUtils.replaceFirst(null, *, *)       = null
529     * StringUtils.replaceFirst("any", (Pattern) null, *)   = "any"
530     * StringUtils.replaceFirst("any", *, null)   = "any"
531     * StringUtils.replaceFirst("", Pattern.compile(""), "zzz")    = "zzz"
532     * StringUtils.replaceFirst("", Pattern.compile(".*"), "zzz")  = "zzz"
533     * StringUtils.replaceFirst("", Pattern.compile(".+"), "zzz")  = ""
534     * StringUtils.replaceFirst("abc", Pattern.compile(""), "ZZ")  = "ZZabc"
535     * StringUtils.replaceFirst("<__>\n<__>", Pattern.compile("<.*>"), "z")      = "z\n<__>"
536     * StringUtils.replaceFirst("<__>\n<__>", Pattern.compile("(?s)<.*>"), "z")  = "z"
537     * StringUtils.replaceFirst("ABCabc123", Pattern.compile("[a-z]"), "_")          = "ABC_bc123"
538     * StringUtils.replaceFirst("ABCabc123abc", Pattern.compile("[^A-Z0-9]+"), "_")  = "ABC_123abc"
539     * StringUtils.replaceFirst("ABCabc123abc", Pattern.compile("[^A-Z0-9]+"), "")   = "ABC123abc"
540     * StringUtils.replaceFirst("Lorem ipsum  dolor   sit", Pattern.compile("( +)([a-z]+)"), "_$2")  = "Lorem_ipsum  dolor   sit"
541     * }</pre>
542     *
543     * @param text  text to search and replace in, may be null.
544     * @param regex  the regular expression pattern to which this string is to be matched.
545     * @param replacement  the string to be substituted for the first match
546     * @return  the text with the first replacement processed,
547     *              {@code null} if null String input.
548     * @see java.util.regex.Matcher#replaceFirst(String)
549     * @see java.util.regex.Pattern
550     * @since 3.18.0
551     */
552    public static String replaceFirst(final CharSequence text, final Pattern regex, final String replacement) {
553        if (text == null || regex == null || replacement == null) {
554            return toStringOrNull(text);
555        }
556        return regex.matcher(text).replaceFirst(replacement);
557    }
558
559    /**
560     * Replaces the first substring of the text string that matches the given regular expression pattern
561     * with the given replacement.
562     *
563     * This method is a {@code null} safe equivalent to:
564     * <ul>
565     *  <li>{@code pattern.matcher(text).replaceFirst(replacement)}</li>
566     * </ul>
567     *
568     * <p>A {@code null} reference passed to this method is a no-op.</p>
569     *
570     * <pre>{@code
571     * StringUtils.replaceFirst(null, *, *)       = null
572     * StringUtils.replaceFirst("any", (Pattern) null, *)   = "any"
573     * StringUtils.replaceFirst("any", *, null)   = "any"
574     * StringUtils.replaceFirst("", Pattern.compile(""), "zzz")    = "zzz"
575     * StringUtils.replaceFirst("", Pattern.compile(".*"), "zzz")  = "zzz"
576     * StringUtils.replaceFirst("", Pattern.compile(".+"), "zzz")  = ""
577     * StringUtils.replaceFirst("abc", Pattern.compile(""), "ZZ")  = "ZZabc"
578     * StringUtils.replaceFirst("<__>\n<__>", Pattern.compile("<.*>"), "z")      = "z\n<__>"
579     * StringUtils.replaceFirst("<__>\n<__>", Pattern.compile("(?s)<.*>"), "z")  = "z"
580     * StringUtils.replaceFirst("ABCabc123", Pattern.compile("[a-z]"), "_")          = "ABC_bc123"
581     * StringUtils.replaceFirst("ABCabc123abc", Pattern.compile("[^A-Z0-9]+"), "_")  = "ABC_123abc"
582     * StringUtils.replaceFirst("ABCabc123abc", Pattern.compile("[^A-Z0-9]+"), "")   = "ABC123abc"
583     * StringUtils.replaceFirst("Lorem ipsum  dolor   sit", Pattern.compile("( +)([a-z]+)"), "_$2")  = "Lorem_ipsum  dolor   sit"
584     * }</pre>
585     *
586     * @param text  text to search and replace in, may be null.
587     * @param regex  the regular expression pattern to which this string is to be matched.
588     * @param replacement  the string to be substituted for the first match.
589     * @return  the text with the first replacement processed,
590     *              {@code null} if null String input.
591     * @see java.util.regex.Matcher#replaceFirst(String)
592     * @see java.util.regex.Pattern
593     * @deprecated Use {@link #replaceFirst(CharSequence, Pattern, String)}.
594     */
595    @Deprecated
596    public static String replaceFirst(final String text, final Pattern regex, final String replacement) {
597        return replaceFirst((CharSequence) text, regex, replacement);
598    }
599
600    /**
601     * Replaces the first substring of the text string that matches the given regular expression
602     * with the given replacement.
603     *
604     * This method is a {@code null} safe equivalent to:
605     * <ul>
606     *  <li>{@code text.replaceFirst(regex, replacement)}</li>
607     *  <li>{@code Pattern.compile(regex).matcher(text).replaceFirst(replacement)}</li>
608     * </ul>
609     *
610     * <p>A {@code null} reference passed to this method is a no-op.</p>
611     *
612     * <p>The {@link Pattern#DOTALL} option is NOT automatically added.
613     * To use the DOTALL option prepend {@code "(?s)"} to the regex.
614     * DOTALL is also known as single-line mode in Perl.</p>
615     *
616     * <pre>{@code
617     * StringUtils.replaceFirst(null, *, *)       = null
618     * StringUtils.replaceFirst("any", (String) null, *)   = "any"
619     * StringUtils.replaceFirst("any", *, null)   = "any"
620     * StringUtils.replaceFirst("", "", "zzz")    = "zzz"
621     * StringUtils.replaceFirst("", ".*", "zzz")  = "zzz"
622     * StringUtils.replaceFirst("", ".+", "zzz")  = ""
623     * StringUtils.replaceFirst("abc", "", "ZZ")  = "ZZabc"
624     * StringUtils.replaceFirst("<__>\n<__>", "<.*>", "z")      = "z\n<__>"
625     * StringUtils.replaceFirst("<__>\n<__>", "(?s)<.*>", "z")  = "z"
626     * StringUtils.replaceFirst("ABCabc123", "[a-z]", "_")          = "ABC_bc123"
627     * StringUtils.replaceFirst("ABCabc123abc", "[^A-Z0-9]+", "_")  = "ABC_123abc"
628     * StringUtils.replaceFirst("ABCabc123abc", "[^A-Z0-9]+", "")   = "ABC123abc"
629     * StringUtils.replaceFirst("Lorem ipsum  dolor   sit", "( +)([a-z]+)", "_$2")  = "Lorem_ipsum  dolor   sit"
630     * }</pre>
631     *
632     * @param text  text to search and replace in, may be null.
633     * @param regex  the regular expression to which this string is to be matched.
634     * @param replacement  the string to be substituted for the first match.
635     * @return  the text with the first replacement processed,
636     *              {@code null} if null String input.
637     * @throws  java.util.regex.PatternSyntaxException
638     *              if the regular expression's syntax is invalid.
639     * @see String#replaceFirst(String, String)
640     * @see java.util.regex.Pattern
641     * @see java.util.regex.Pattern#DOTALL
642     */
643    public static String replaceFirst(final String text, final String regex, final String replacement) {
644        if (text == null || regex == null || replacement == null) {
645            return text;
646        }
647        return text.replaceFirst(regex, replacement);
648    }
649
650    /**
651     * Replaces each substring of the source String that matches the given regular expression with the given
652     * replacement using the {@link Pattern#DOTALL} option. DOTALL is also known as single-line mode in Perl.
653     *
654     * This call is a {@code null} safe equivalent to:
655     * <ul>
656     * <li>{@code text.replaceAll(&quot;(?s)&quot; + regex, replacement)}</li>
657     * <li>{@code Pattern.compile(regex, Pattern.DOTALL).matcher(text).replaceAll(replacement)}</li>
658     * </ul>
659     *
660     * <p>A {@code null} reference passed to this method is a no-op.</p>
661     *
662     * <pre>{@code
663     * StringUtils.replacePattern(null, *, *)       = null
664     * StringUtils.replacePattern("any", (String) null, *)   = "any"
665     * StringUtils.replacePattern("any", *, null)   = "any"
666     * StringUtils.replacePattern("", "", "zzz")    = "zzz"
667     * StringUtils.replacePattern("", ".*", "zzz")  = "zzz"
668     * StringUtils.replacePattern("", ".+", "zzz")  = ""
669     * StringUtils.replacePattern("<__>\n<__>", "<.*>", "z")       = "z"
670     * StringUtils.replacePattern("ABCabc123", "[a-z]", "_")       = "ABC___123"
671     * StringUtils.replacePattern("ABCabc123", "[^A-Z0-9]+", "_")  = "ABC_123"
672     * StringUtils.replacePattern("ABCabc123", "[^A-Z0-9]+", "")   = "ABC123"
673     * StringUtils.replacePattern("Lorem ipsum  dolor   sit", "( +)([a-z]+)", "_$2")  = "Lorem_ipsum_dolor_sit"
674     * }</pre>
675     *
676     * @param text
677     *            the source string.
678     * @param regex
679     *            the regular expression to which this string is to be matched.
680     * @param replacement
681     *            the string to be substituted for each match.
682     * @return The resulting {@link String}.
683     * @see #replaceAll(String, String, String)
684     * @see String#replaceAll(String, String)
685     * @see Pattern#DOTALL
686     * @since 3.18.0
687     */
688    public static String replacePattern(final CharSequence text, final String regex, final String replacement) {
689        if (ObjectUtils.anyNull(text, regex, replacement)) {
690            return toStringOrNull(text);
691        }
692        return dotAllMatcher(regex, text).replaceAll(replacement);
693    }
694
695    /**
696     * Replaces each substring of the source String that matches the given regular expression with the given
697     * replacement using the {@link Pattern#DOTALL} option. DOTALL is also known as single-line mode in Perl.
698     *
699     * This call is a {@code null} safe equivalent to:
700     * <ul>
701     * <li>{@code text.replaceAll(&quot;(?s)&quot; + regex, replacement)}</li>
702     * <li>{@code Pattern.compile(regex, Pattern.DOTALL).matcher(text).replaceAll(replacement)}</li>
703     * </ul>
704     *
705     * <p>A {@code null} reference passed to this method is a no-op.</p>
706     *
707     * <pre>{@code
708     * StringUtils.replacePattern(null, *, *)       = null
709     * StringUtils.replacePattern("any", (String) null, *)   = "any"
710     * StringUtils.replacePattern("any", *, null)   = "any"
711     * StringUtils.replacePattern("", "", "zzz")    = "zzz"
712     * StringUtils.replacePattern("", ".*", "zzz")  = "zzz"
713     * StringUtils.replacePattern("", ".+", "zzz")  = ""
714     * StringUtils.replacePattern("<__>\n<__>", "<.*>", "z")       = "z"
715     * StringUtils.replacePattern("ABCabc123", "[a-z]", "_")       = "ABC___123"
716     * StringUtils.replacePattern("ABCabc123", "[^A-Z0-9]+", "_")  = "ABC_123"
717     * StringUtils.replacePattern("ABCabc123", "[^A-Z0-9]+", "")   = "ABC123"
718     * StringUtils.replacePattern("Lorem ipsum  dolor   sit", "( +)([a-z]+)", "_$2")  = "Lorem_ipsum_dolor_sit"
719     * }</pre>
720     *
721     * @param text
722     *            the source string.
723     * @param regex
724     *            the regular expression to which this string is to be matched.
725     * @param replacement
726     *            the string to be substituted for each match.
727     * @return The resulting {@link String}.
728     * @see #replaceAll(String, String, String)
729     * @see String#replaceAll(String, String)
730     * @see Pattern#DOTALL
731     * @deprecated Use {@link #replacePattern(CharSequence, String, String)}.
732     */
733    @Deprecated
734    public static String replacePattern(final String text, final String regex, final String replacement) {
735        return replacePattern((CharSequence) text, regex, replacement);
736    }
737
738    private static String toStringOrNull(final CharSequence text) {
739        return Objects.toString(text, null);
740    }
741
742    /**
743     * Make private in 4.0.
744     *
745     * @deprecated TODO Make private in 4.0.
746     */
747    @Deprecated
748    public RegExUtils() {
749        // empty
750    }
751}