All Downloads are FREE. Search and download functionalities are using the official Maven repository.

checkers.regex.RegexUtil Maven / Gradle / Ivy

Go to download

Annotations ("type qualifiers") from the checker framework, backported so they can be used in pre-JDK8 applications

There is a newer version: 1.7.5
Show newest version
package checkers.regex;

import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;

import checkers.nullness.quals.*;
import checkers.regex.quals.*;

// This class should be kept in sync with plume.RegexUtil .

/**
 * Utility methods for regular expressions, most notably for testing whether
 * a string is a regular expression.
 * 

* * For an example of intended use, see section Testing * whether a string is a regular expression in the Checker Framework * manual. *

* * Runtime Dependency: * Using this class introduces a runtime dependency. * This means that you need to distribute (or link to) the Checker * Framework, along with your binaries. * To eliminate this dependency, you can simply copy this class into your * own project. */ public class RegexUtil { /** * A checked version of {@link PatternSyntaxException}. *

* This exception is useful when an illegal regex is detected but the * contextual information to report a helpful error message is not available * at the current depth in the call stack. By using a checked * PatternSyntaxException the error must be handled up the call stack where * a better error message can be reported. *

* * Typical usage is: *

   * void myMethod(...) throws CheckedPatternSyntaxException {
   *   ...
   *   if (! isRegex(myString)) {
   *     throw new CheckedPatternSyntaxException(...);
   *   }
   *   ... Pattern.compile(myString) ...
   * 
* * Simply calling Pattern.compile would have a similar effect, * in that PatternSyntaxException would be thrown at run time if * myString is not a regular expression. There are two problems * with such an approach. First, a client of myMethod might * forget to handle the exception, since PatternSyntaxException * is not checked. Also, the Regex Checker would issue a warning about * the call to Pattern.compile that might throw an exception. * The above usage pattern avoids both problems. * * @see PatternSyntaxException */ public static class CheckedPatternSyntaxException extends Exception { private static final long serialVersionUID = 6266881831979001480L; private final PatternSyntaxException pse; /** * Constructs a new CheckedPatternSyntaxException equivalent to the * given {@link PatternSyntaxException}. *

* Consider calling this constructor with the result of * {@link RegexUtil#regexError}. */ public CheckedPatternSyntaxException(PatternSyntaxException pse) { this.pse = pse; } /** * Constructs a new CheckedPatternSyntaxException. * * @param desc A description of the error * @param regex The erroneous pattern * @param index The approximate index in the pattern of the error, * or {@code -1} if the index is not known */ public CheckedPatternSyntaxException(String desc, String regex, int index) { this(new PatternSyntaxException(desc, regex, index)); } /** * Retrieves the description of the error. * * @return The description of the error */ public String getDescription() { return pse.getDescription(); } /** * Retrieves the error index. * * @return The approximate index in the pattern of the error, or {@code -1} * if the index is not known */ public int getIndex() { return pse.getIndex(); } /** * Returns a multi-line string containing the description of the syntax * error and its index, the erroneous regular-expression pattern, and a * visual indication of the error index within the pattern. * * @return The full detail message */ @Override public String getMessage() { return pse.getMessage(); } /** * Retrieves the erroneous regular-expression pattern. * * @return The erroneous pattern */ public String getPattern() { return pse.getPattern(); } } private RegexUtil() { throw new AssertionError("Class RegexUtil shouldn't be instantiated"); } /** * Returns true if the argument is a syntactically valid regular * expression. */ public static boolean isRegex(String s) { return isRegex(s, 0); } /** * Returns true if the argument is a syntactically valid regular * expression with at least the given number of groups. */ @SuppressWarnings("regex") // RegexUtil @Pure public static boolean isRegex(String s, int groups) { Pattern p; try { p = Pattern.compile(s); } catch (PatternSyntaxException e) { return false; } return getGroupCount(p) >= groups; } /** * Returns true if the argument is a syntactically valid regular * expression. */ @SuppressWarnings("regex") // RegexUtil @Pure public static boolean isRegex(char c) { return isRegex(Character.toString(c)); } /** * Returns null if the argument is a syntactically valid regular * expression. Otherwise returns a string describing why the argument is * not a regex. */ @SuppressWarnings("regex") // RegexUtil @Pure public static @Nullable String regexError(String s) { return regexError(s, 0); } /** * Returns null if the argument is a syntactically valid regular * expression with at least the given number of groups. Otherwise returns * a string describing why the argument is not a regex. */ @SuppressWarnings("regex") // RegexUtil @Pure public static @Nullable String regexError(String s, int groups) { try { Pattern p = Pattern.compile(s); int actualGroups = getGroupCount(p); if (actualGroups < groups) { return regexErrorMessage(s, groups, actualGroups); } } catch (PatternSyntaxException e) { return e.getMessage(); } return null; } /** * Returns null if the argument is a syntactically valid regular * expression. Otherwise returns a PatternSyntaxException describing * why the argument is not a regex. */ @SuppressWarnings("regex") // RegexUtil @Pure public static @Nullable PatternSyntaxException regexException(String s) { return regexException(s, 0); } /** * Returns null if the argument is a syntactically valid regular * expression with at least the given number of groups. Otherwise returns a * PatternSyntaxException describing why the argument is not a regex. */ @SuppressWarnings("regex") // RegexUtil @Pure public static @Nullable PatternSyntaxException regexException(String s, int groups) { try { Pattern p = Pattern.compile(s); int actualGroups = getGroupCount(p); if (actualGroups < groups) { return new PatternSyntaxException(regexErrorMessage(s, groups, actualGroups), s, -1); } } catch (PatternSyntaxException pse) { return pse; } return null; } /** * Returns the argument as a {@code @Regex String} if it is a regex, * otherwise throws an error. The purpose of this method is to suppress Regex * Checker warnings. Once the the Regex Checker supports flow-sensitivity, it * should be very rarely needed. */ public static @Regex String asRegex(String s) { return asRegex(s, 0); } /** * Returns the argument as a {@code @Regex(groups) String} if it is a regex * with at least the given number of groups, otherwise throws an error. The * purpose of this method is to suppress Regex Checker warnings. Once the the * Regex Checker supports flow-sensitivity, it should be very rarely needed. */ @SuppressWarnings("regex") // RegexUtil @Pure public static @Regex String asRegex(String s, int groups) { try { Pattern p = Pattern.compile(s); int actualGroups = getGroupCount(p); if (actualGroups < groups) { throw new Error(regexErrorMessage(s, groups, actualGroups)); } return s; } catch (PatternSyntaxException e) { throw new Error(e); } } /** * Generates an error message for s when expectedGroups are needed, but s * only has actualGroups. */ private static String regexErrorMessage(String s, int expectedGroups, int actualGroups) { return "regex \"" + s + "\" has " + actualGroups + " groups, but " + expectedGroups + " groups are needed."; } /** * Returns the count of groups in the argument. */ private static int getGroupCount(Pattern p) { return p.matcher("").groupCount(); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy