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     *
414     * @see java.util.regex.Matcher#replaceAll(String)
415     * @see java.util.regex.Pattern
416     */
417    public static String replaceAll(final CharSequence text, final Pattern regex, final String replacement) {
418        if (ObjectUtils.anyNull(text, regex, replacement)) {
419            return toStringOrNull(text);
420        }
421        return regex.matcher(text).replaceAll(replacement);
422    }
423
424    /**
425     * Replaces each substring of the text String that matches the given regular expression pattern with the given replacement.
426     *
427     * This method is a {@code null} safe equivalent to:
428     * <ul>
429     *  <li>{@code pattern.matcher(text).replaceAll(replacement)}</li>
430     * </ul>
431     *
432     * <p>A {@code null} reference passed to this method is a no-op.</p>
433     *
434     * <pre>{@code
435     * StringUtils.replaceAll(null, *, *)       = null
436     * StringUtils.replaceAll("any", (Pattern) null, *)   = "any"
437     * StringUtils.replaceAll("any", *, null)   = "any"
438     * StringUtils.replaceAll("", Pattern.compile(""), "zzz")    = "zzz"
439     * StringUtils.replaceAll("", Pattern.compile(".*"), "zzz")  = "zzz"
440     * StringUtils.replaceAll("", Pattern.compile(".+"), "zzz")  = ""
441     * StringUtils.replaceAll("abc", Pattern.compile(""), "ZZ")  = "ZZaZZbZZcZZ"
442     * StringUtils.replaceAll("<__>\n<__>", Pattern.compile("<.*>"), "z")                 = "z\nz"
443     * StringUtils.replaceAll("<__>\n<__>", Pattern.compile("<.*>", Pattern.DOTALL), "z") = "z"
444     * StringUtils.replaceAll("<__>\n<__>", Pattern.compile("(?s)<.*>"), "z")             = "z"
445     * StringUtils.replaceAll("ABCabc123", Pattern.compile("[a-z]"), "_")       = "ABC___123"
446     * StringUtils.replaceAll("ABCabc123", Pattern.compile("[^A-Z0-9]+"), "_")  = "ABC_123"
447     * StringUtils.replaceAll("ABCabc123", Pattern.compile("[^A-Z0-9]+"), "")   = "ABC123"
448     * StringUtils.replaceAll("Lorem ipsum  dolor   sit", Pattern.compile("( +)([a-z]+)"), "_$2")  = "Lorem_ipsum_dolor_sit"
449     * }</pre>
450     *
451     * @param text  text to search and replace in, may be null
452     * @param regex  the regular expression pattern to which this string is to be matched
453     * @param replacement  the string to be substituted for each match
454     * @return  the text with any replacements processed,
455     *              {@code null} if null String input
456     *
457     * @see java.util.regex.Matcher#replaceAll(String)
458     * @see java.util.regex.Pattern
459     * @deprecated Use {@link #replaceAll(CharSequence, Pattern, String)}.
460     */
461    @Deprecated
462    public static String replaceAll(final String text, final Pattern regex, final String replacement) {
463        return replaceAll((CharSequence) text, regex, replacement);
464    }
465
466    /**
467     * Replaces each substring of the text String that matches the given regular expression
468     * with the given replacement.
469     *
470     * This method is a {@code null} safe equivalent to:
471     * <ul>
472     *  <li>{@code text.replaceAll(regex, replacement)}</li>
473     *  <li>{@code Pattern.compile(regex).matcher(text).replaceAll(replacement)}</li>
474     * </ul>
475     *
476     * <p>A {@code null} reference passed to this method is a no-op.</p>
477     *
478     * <p>Unlike in the {@link #replacePattern(CharSequence, String, String)} method, the {@link Pattern#DOTALL} option
479     * is NOT automatically added.
480     * To use the DOTALL option prepend {@code "(?s)"} to the regex.
481     * DOTALL is also known as single-line mode in Perl.</p>
482     *
483     * <pre>{@code
484     * StringUtils.replaceAll(null, *, *)       = null
485     * StringUtils.replaceAll("any", (String) null, *)   = "any"
486     * StringUtils.replaceAll("any", *, null)   = "any"
487     * StringUtils.replaceAll("", "", "zzz")    = "zzz"
488     * StringUtils.replaceAll("", ".*", "zzz")  = "zzz"
489     * StringUtils.replaceAll("", ".+", "zzz")  = ""
490     * StringUtils.replaceAll("abc", "", "ZZ")  = "ZZaZZbZZcZZ"
491     * StringUtils.replaceAll("<__>\n<__>", "<.*>", "z")      = "z\nz"
492     * StringUtils.replaceAll("<__>\n<__>", "(?s)<.*>", "z")  = "z"
493     * StringUtils.replaceAll("ABCabc123", "[a-z]", "_")       = "ABC___123"
494     * StringUtils.replaceAll("ABCabc123", "[^A-Z0-9]+", "_")  = "ABC_123"
495     * StringUtils.replaceAll("ABCabc123", "[^A-Z0-9]+", "")   = "ABC123"
496     * StringUtils.replaceAll("Lorem ipsum  dolor   sit", "( +)([a-z]+)", "_$2")  = "Lorem_ipsum_dolor_sit"
497     * }</pre>
498     *
499     * @param text  text to search and replace in, may be null
500     * @param regex  the regular expression to which this string is to be matched
501     * @param replacement  the string to be substituted for each match
502     * @return  the text with any replacements processed,
503     *              {@code null} if null String input
504     *
505     * @throws  java.util.regex.PatternSyntaxException
506     *              if the regular expression's syntax is invalid
507     *
508     * @see #replacePattern(String, String, String)
509     * @see String#replaceAll(String, String)
510     * @see java.util.regex.Pattern
511     * @see java.util.regex.Pattern#DOTALL
512     */
513    public static String replaceAll(final String text, final String regex, final String replacement) {
514        if (ObjectUtils.anyNull(text, regex, replacement)) {
515            return text;
516        }
517        return text.replaceAll(regex, replacement);
518    }
519
520    /**
521     * Replaces the first substring of the text string that matches the given regular expression pattern
522     * with the given replacement.
523     *
524     * This method is a {@code null} safe equivalent to:
525     * <ul>
526     *  <li>{@code pattern.matcher(text).replaceFirst(replacement)}</li>
527     * </ul>
528     *
529     * <p>A {@code null} reference passed to this method is a no-op.</p>
530     *
531     * <pre>{@code
532     * StringUtils.replaceFirst(null, *, *)       = null
533     * StringUtils.replaceFirst("any", (Pattern) null, *)   = "any"
534     * StringUtils.replaceFirst("any", *, null)   = "any"
535     * StringUtils.replaceFirst("", Pattern.compile(""), "zzz")    = "zzz"
536     * StringUtils.replaceFirst("", Pattern.compile(".*"), "zzz")  = "zzz"
537     * StringUtils.replaceFirst("", Pattern.compile(".+"), "zzz")  = ""
538     * StringUtils.replaceFirst("abc", Pattern.compile(""), "ZZ")  = "ZZabc"
539     * StringUtils.replaceFirst("<__>\n<__>", Pattern.compile("<.*>"), "z")      = "z\n<__>"
540     * StringUtils.replaceFirst("<__>\n<__>", Pattern.compile("(?s)<.*>"), "z")  = "z"
541     * StringUtils.replaceFirst("ABCabc123", Pattern.compile("[a-z]"), "_")          = "ABC_bc123"
542     * StringUtils.replaceFirst("ABCabc123abc", Pattern.compile("[^A-Z0-9]+"), "_")  = "ABC_123abc"
543     * StringUtils.replaceFirst("ABCabc123abc", Pattern.compile("[^A-Z0-9]+"), "")   = "ABC123abc"
544     * StringUtils.replaceFirst("Lorem ipsum  dolor   sit", Pattern.compile("( +)([a-z]+)"), "_$2")  = "Lorem_ipsum  dolor   sit"
545     * }</pre>
546     *
547     * @param text  text to search and replace in, may be null
548     * @param regex  the regular expression pattern to which this string is to be matched
549     * @param replacement  the string to be substituted for the first match
550     * @return  the text with the first replacement processed,
551     *              {@code null} if null String input
552     *
553     * @see java.util.regex.Matcher#replaceFirst(String)
554     * @see java.util.regex.Pattern
555     * @since 3.18.0
556     */
557    public static String replaceFirst(final CharSequence text, final Pattern regex, final String replacement) {
558        if (text == null || regex == null || replacement == null) {
559            return toStringOrNull(text);
560        }
561        return regex.matcher(text).replaceFirst(replacement);
562    }
563
564    /**
565     * Replaces the first substring of the text string that matches the given regular expression pattern
566     * with the given replacement.
567     *
568     * This method is a {@code null} safe equivalent to:
569     * <ul>
570     *  <li>{@code pattern.matcher(text).replaceFirst(replacement)}</li>
571     * </ul>
572     *
573     * <p>A {@code null} reference passed to this method is a no-op.</p>
574     *
575     * <pre>{@code
576     * StringUtils.replaceFirst(null, *, *)       = null
577     * StringUtils.replaceFirst("any", (Pattern) null, *)   = "any"
578     * StringUtils.replaceFirst("any", *, null)   = "any"
579     * StringUtils.replaceFirst("", Pattern.compile(""), "zzz")    = "zzz"
580     * StringUtils.replaceFirst("", Pattern.compile(".*"), "zzz")  = "zzz"
581     * StringUtils.replaceFirst("", Pattern.compile(".+"), "zzz")  = ""
582     * StringUtils.replaceFirst("abc", Pattern.compile(""), "ZZ")  = "ZZabc"
583     * StringUtils.replaceFirst("<__>\n<__>", Pattern.compile("<.*>"), "z")      = "z\n<__>"
584     * StringUtils.replaceFirst("<__>\n<__>", Pattern.compile("(?s)<.*>"), "z")  = "z"
585     * StringUtils.replaceFirst("ABCabc123", Pattern.compile("[a-z]"), "_")          = "ABC_bc123"
586     * StringUtils.replaceFirst("ABCabc123abc", Pattern.compile("[^A-Z0-9]+"), "_")  = "ABC_123abc"
587     * StringUtils.replaceFirst("ABCabc123abc", Pattern.compile("[^A-Z0-9]+"), "")   = "ABC123abc"
588     * StringUtils.replaceFirst("Lorem ipsum  dolor   sit", Pattern.compile("( +)([a-z]+)"), "_$2")  = "Lorem_ipsum  dolor   sit"
589     * }</pre>
590     *
591     * @param text  text to search and replace in, may be null
592     * @param regex  the regular expression pattern to which this string is to be matched
593     * @param replacement  the string to be substituted for the first match
594     * @return  the text with the first replacement processed,
595     *              {@code null} if null String input
596     *
597     * @see java.util.regex.Matcher#replaceFirst(String)
598     * @see java.util.regex.Pattern
599     * @deprecated Use {@link #replaceFirst(CharSequence, Pattern, String)}.
600     */
601    @Deprecated
602    public static String replaceFirst(final String text, final Pattern regex, final String replacement) {
603        return replaceFirst((CharSequence) text, regex, replacement);
604    }
605
606    /**
607     * Replaces the first substring of the text string that matches the given regular expression
608     * with the given replacement.
609     *
610     * This method is a {@code null} safe equivalent to:
611     * <ul>
612     *  <li>{@code text.replaceFirst(regex, replacement)}</li>
613     *  <li>{@code Pattern.compile(regex).matcher(text).replaceFirst(replacement)}</li>
614     * </ul>
615     *
616     * <p>A {@code null} reference passed to this method is a no-op.</p>
617     *
618     * <p>The {@link Pattern#DOTALL} option is NOT automatically added.
619     * To use the DOTALL option prepend {@code "(?s)"} to the regex.
620     * DOTALL is also known as single-line mode in Perl.</p>
621     *
622     * <pre>{@code
623     * StringUtils.replaceFirst(null, *, *)       = null
624     * StringUtils.replaceFirst("any", (String) null, *)   = "any"
625     * StringUtils.replaceFirst("any", *, null)   = "any"
626     * StringUtils.replaceFirst("", "", "zzz")    = "zzz"
627     * StringUtils.replaceFirst("", ".*", "zzz")  = "zzz"
628     * StringUtils.replaceFirst("", ".+", "zzz")  = ""
629     * StringUtils.replaceFirst("abc", "", "ZZ")  = "ZZabc"
630     * StringUtils.replaceFirst("<__>\n<__>", "<.*>", "z")      = "z\n<__>"
631     * StringUtils.replaceFirst("<__>\n<__>", "(?s)<.*>", "z")  = "z"
632     * StringUtils.replaceFirst("ABCabc123", "[a-z]", "_")          = "ABC_bc123"
633     * StringUtils.replaceFirst("ABCabc123abc", "[^A-Z0-9]+", "_")  = "ABC_123abc"
634     * StringUtils.replaceFirst("ABCabc123abc", "[^A-Z0-9]+", "")   = "ABC123abc"
635     * StringUtils.replaceFirst("Lorem ipsum  dolor   sit", "( +)([a-z]+)", "_$2")  = "Lorem_ipsum  dolor   sit"
636     * }</pre>
637     *
638     * @param text  text to search and replace in, may be null
639     * @param regex  the regular expression to which this string is to be matched
640     * @param replacement  the string to be substituted for the first match
641     * @return  the text with the first replacement processed,
642     *              {@code null} if null String input
643     *
644     * @throws  java.util.regex.PatternSyntaxException
645     *              if the regular expression's syntax is invalid
646     *
647     * @see String#replaceFirst(String, String)
648     * @see java.util.regex.Pattern
649     * @see java.util.regex.Pattern#DOTALL
650     */
651    public static String replaceFirst(final String text, final String regex, final String replacement) {
652        if (text == null || regex == null || replacement == null) {
653            return text;
654        }
655        return text.replaceFirst(regex, replacement);
656    }
657
658    /**
659     * Replaces each substring of the source String that matches the given regular expression with the given
660     * replacement using the {@link Pattern#DOTALL} option. DOTALL is also known as single-line mode in Perl.
661     *
662     * This call is a {@code null} safe equivalent to:
663     * <ul>
664     * <li>{@code text.replaceAll(&quot;(?s)&quot; + regex, replacement)}</li>
665     * <li>{@code Pattern.compile(regex, Pattern.DOTALL).matcher(text).replaceAll(replacement)}</li>
666     * </ul>
667     *
668     * <p>A {@code null} reference passed to this method is a no-op.</p>
669     *
670     * <pre>{@code
671     * StringUtils.replacePattern(null, *, *)       = null
672     * StringUtils.replacePattern("any", (String) null, *)   = "any"
673     * StringUtils.replacePattern("any", *, null)   = "any"
674     * StringUtils.replacePattern("", "", "zzz")    = "zzz"
675     * StringUtils.replacePattern("", ".*", "zzz")  = "zzz"
676     * StringUtils.replacePattern("", ".+", "zzz")  = ""
677     * StringUtils.replacePattern("<__>\n<__>", "<.*>", "z")       = "z"
678     * StringUtils.replacePattern("ABCabc123", "[a-z]", "_")       = "ABC___123"
679     * StringUtils.replacePattern("ABCabc123", "[^A-Z0-9]+", "_")  = "ABC_123"
680     * StringUtils.replacePattern("ABCabc123", "[^A-Z0-9]+", "")   = "ABC123"
681     * StringUtils.replacePattern("Lorem ipsum  dolor   sit", "( +)([a-z]+)", "_$2")  = "Lorem_ipsum_dolor_sit"
682     * }</pre>
683     *
684     * @param text
685     *            the source string
686     * @param regex
687     *            the regular expression to which this string is to be matched
688     * @param replacement
689     *            the string to be substituted for each match
690     * @return The resulting {@link String}
691     * @see #replaceAll(String, String, String)
692     * @see String#replaceAll(String, String)
693     * @see Pattern#DOTALL
694     * @since 3.18.0
695     */
696    public static String replacePattern(final CharSequence text, final String regex, final String replacement) {
697        if (ObjectUtils.anyNull(text, regex, replacement)) {
698            return toStringOrNull(text);
699        }
700        return dotAllMatcher(regex, text).replaceAll(replacement);
701    }
702
703    /**
704     * Replaces each substring of the source String that matches the given regular expression with the given
705     * replacement using the {@link Pattern#DOTALL} option. DOTALL is also known as single-line mode in Perl.
706     *
707     * This call is a {@code null} safe equivalent to:
708     * <ul>
709     * <li>{@code text.replaceAll(&quot;(?s)&quot; + regex, replacement)}</li>
710     * <li>{@code Pattern.compile(regex, Pattern.DOTALL).matcher(text).replaceAll(replacement)}</li>
711     * </ul>
712     *
713     * <p>A {@code null} reference passed to this method is a no-op.</p>
714     *
715     * <pre>{@code
716     * StringUtils.replacePattern(null, *, *)       = null
717     * StringUtils.replacePattern("any", (String) null, *)   = "any"
718     * StringUtils.replacePattern("any", *, null)   = "any"
719     * StringUtils.replacePattern("", "", "zzz")    = "zzz"
720     * StringUtils.replacePattern("", ".*", "zzz")  = "zzz"
721     * StringUtils.replacePattern("", ".+", "zzz")  = ""
722     * StringUtils.replacePattern("<__>\n<__>", "<.*>", "z")       = "z"
723     * StringUtils.replacePattern("ABCabc123", "[a-z]", "_")       = "ABC___123"
724     * StringUtils.replacePattern("ABCabc123", "[^A-Z0-9]+", "_")  = "ABC_123"
725     * StringUtils.replacePattern("ABCabc123", "[^A-Z0-9]+", "")   = "ABC123"
726     * StringUtils.replacePattern("Lorem ipsum  dolor   sit", "( +)([a-z]+)", "_$2")  = "Lorem_ipsum_dolor_sit"
727     * }</pre>
728     *
729     * @param text
730     *            the source string
731     * @param regex
732     *            the regular expression to which this string is to be matched
733     * @param replacement
734     *            the string to be substituted for each match
735     * @return The resulting {@link String}
736     * @see #replaceAll(String, String, String)
737     * @see String#replaceAll(String, String)
738     * @see Pattern#DOTALL
739     * @deprecated Use {@link #replacePattern(CharSequence, String, String)}.
740     */
741    @Deprecated
742    public static String replacePattern(final String text, final String regex, final String replacement) {
743        return replacePattern((CharSequence) text, regex, replacement);
744    }
745
746    private static String toStringOrNull(final CharSequence text) {
747        return Objects.toString(text, null);
748    }
749
750    /**
751     * Make private in 4.0.
752     *
753     * @deprecated TODO Make private in 4.0.
754     */
755    @Deprecated
756    public RegExUtils() {
757        // empty
758    }
759}