2 * Copyright (c) 2018 Pantheon Technologies, s.r.o. All rights reserved.
4 * This program and the accompanying materials are made available under the
5 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6 * and is available at http://www.eclipse.org/legal/epl-v10.html
8 package org.opendaylight.yangtools.yang.binding;
10 import static com.google.common.base.Preconditions.checkArgument;
12 import com.google.common.annotations.Beta;
13 import java.util.regex.Pattern;
16 public final class RegexPatterns {
17 private static final String NEGATED_PATTERN_PREFIX = "^(?!";
18 private static final String NEGATED_PATTERN_SUFFIX = ").*$";
20 private RegexPatterns() {
25 * Check if the specified {@link Pattern} is the result of {@link #negatePatternString(String)}. This method
26 * assumes the pattern was not hand-coded but rather was automatically-generated, such that its non-automated
27 * parts come from XSD regular expressions. If this constraint is violated, this method may result false positives.
29 * @param pattern Pattern to check
30 * @return True if this pattern is a negation.
31 * @throws NullPointerException if pattern is null
32 * @throws IllegalArgumentException if the pattern does not conform to expected structure
34 public static boolean isNegatedPattern(final Pattern pattern) {
35 final String str = pattern.toString();
36 return str.startsWith(RegexPatterns.NEGATED_PATTERN_PREFIX)
37 && str.endsWith(RegexPatterns.NEGATED_PATTERN_SUFFIX);
41 * Create a {@link Pattern} expression which performs inverted match to the specified pattern. The input pattern
42 * is expected to be a valid regular expression passing {@link Pattern#compile(String)} and to have both start and
43 * end of string anchors as the first and last characters.
45 * @param pattern Pattern regular expression to negate
46 * @return Negated regular expression
47 * @throws IllegalArgumentException if the pattern does not conform to expected structure
48 * @throws NullPointerException if pattern is null
50 public static String negatePatternString(final String pattern) {
51 checkArgument(pattern.charAt(0) == '^' && pattern.charAt(pattern.length() - 1) == '$',
52 "Pattern '%s' does not have expected format", pattern);
55 * Converting the expression into a negation is tricky. For example, when we have:
57 * pattern "a|b" { modifier invert-match; }
59 * this gets escaped into either "^a|b$" or "^(?:a|b)$". Either format can occur, as the non-capturing group
60 * strictly needed only in some cases. From that we want to arrive at:
63 * ^^^ original expression
64 * ^^^^^^^^ tail of a grouped expression (without head anchor)
65 * ^^^^ ^^^^ inversion of match
67 * Inversion works by explicitly anchoring at the start of the string and then:
68 * - specifying a negative lookahead until the end of string
69 * - matching any string
70 * - anchoring at the end of the string
72 final boolean hasGroup = pattern.startsWith("^(?:") && pattern.endsWith(")$");
73 final int len = pattern.length();
74 final StringBuilder sb = new StringBuilder(len + (hasGroup ? 7 : 11))
75 .append(NEGATED_PATTERN_PREFIX);
78 sb.append(pattern, 1, len);
80 sb.append("(?:").append(pattern, 1, len - 1).append(")$");
82 return sb.append(NEGATED_PATTERN_SUFFIX).toString();