/*
 * Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
 *
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License v. 2.0, which is available at
 * http://www.eclipse.org/legal/epl-2.0.
 *
 * This Source Code may also be made available under the following Secondary
 * Licenses when the conditions for such availability set forth in the
 * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
 * version 2 with the GNU Classpath Exception, which is available at
 * https://www.gnu.org/software/classpath/license.html.
 *
 * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
 */

package org.glassfish.jersey.uri;

import java.util.List;
import java.util.Map;
import java.util.regex.MatchResult;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;

A pattern for matching a string against a regular expression and returning capturing group values for any capturing groups present in the expression.
Author:Paul Sandoz, Gerard Davison (gerard.davison at oracle.com)
/** * A pattern for matching a string against a regular expression * and returning capturing group values for any capturing groups present in * the expression. * * @author Paul Sandoz * @author Gerard Davison (gerard.davison at oracle.com) */
public class PatternWithGroups { private static final int[] EMPTY_INT_ARRAY = new int[0];
The empty pattern that matches the null or empty string.
/** * The empty pattern that matches the null or empty string. */
public static final PatternWithGroups EMPTY = new PatternWithGroups();
The regular expression for matching and obtaining capturing group values.
/** * The regular expression for matching and obtaining capturing group values. */
private final String regex;
The compiled regular expression of regex.
/** * The compiled regular expression of {@link #regex}. */
private final Pattern regexPattern;
The array of group indexes to capturing groups.
/** * The array of group indexes to capturing groups. */
private final int[] groupIndexes;
Construct an empty pattern.
/** * Construct an empty pattern. */
protected PatternWithGroups() { this.regex = ""; this.regexPattern = null; this.groupIndexes = EMPTY_INT_ARRAY; }
Construct a new pattern.
Params:
  • regex – the regular expression. If the expression is null or an empty string then the pattern will only match a null or empty string.
Throws:
/** * Construct a new pattern. * * @param regex the regular expression. If the expression is {@code null} or an empty string then the pattern will only match * a {@code null} or empty string. * @throws java.util.regex.PatternSyntaxException if the regular expression could not be compiled. */
public PatternWithGroups(final String regex) throws PatternSyntaxException { this(regex, EMPTY_INT_ARRAY); }
Construct a new pattern.
Params:
  • regex – the regular expression. If the expression is null or an empty string then the pattern will only match a null or empty string.
  • groupIndexes – the array of group indexes to capturing groups.
Throws:
/** * Construct a new pattern. * * @param regex the regular expression. If the expression is {@code null} or an empty string then the pattern will * only * match * a {@code null} or empty string. * @param groupIndexes the array of group indexes to capturing groups. * @throws java.util.regex.PatternSyntaxException if the regular expression could not be compiled. */
public PatternWithGroups(final String regex, final int[] groupIndexes) throws PatternSyntaxException { this(compile(regex), groupIndexes); } private static Pattern compile(final String regex) throws PatternSyntaxException { return (regex == null || regex.isEmpty()) ? null : Pattern.compile(regex); }
Construct a new pattern.
Params:
  • regexPattern – the regular expression pattern.
Throws:
/** * Construct a new pattern. * * @param regexPattern the regular expression pattern. * @throws IllegalArgumentException if the regexPattern is {@code null}. */
public PatternWithGroups(final Pattern regexPattern) throws IllegalArgumentException { this(regexPattern, EMPTY_INT_ARRAY); }
Construct a new pattern.
Params:
  • regexPattern – the regular expression pattern.
  • groupIndexes – the array of group indexes to capturing groups.
Throws:
/** * Construct a new pattern. * * @param regexPattern the regular expression pattern. * @param groupIndexes the array of group indexes to capturing groups. * @throws IllegalArgumentException if the regexPattern is {@code null}. */
public PatternWithGroups(final Pattern regexPattern, final int[] groupIndexes) throws IllegalArgumentException { if (regexPattern == null) { throw new IllegalArgumentException(); } this.regex = regexPattern.toString(); this.regexPattern = regexPattern; this.groupIndexes = groupIndexes.clone(); }
Get the regular expression.
Returns:the regular expression.
/** * Get the regular expression. * * @return the regular expression. */
public final String getRegex() { return regex; }
Get the group indexes to capturing groups.

Any nested capturing groups will be ignored and the the group index will refer to the top-level capturing groups associated with the templates variables.

Returns:the group indexes to capturing groups.
/** * Get the group indexes to capturing groups. * <p> * Any nested capturing groups will be ignored and the * the group index will refer to the top-level capturing * groups associated with the templates variables. * * @return the group indexes to capturing groups. */
public final int[] getGroupIndexes() { return groupIndexes.clone(); } private static final class EmptyStringMatchResult implements MatchResult { @Override public int start() { return 0; } @Override public int start(final int group) { if (group != 0) { throw new IndexOutOfBoundsException(); } return start(); } @Override public int end() { return 0; } @Override public int end(final int group) { if (group != 0) { throw new IndexOutOfBoundsException(); } return end(); } @Override public String group() { return ""; } @Override public String group(final int group) { if (group != 0) { throw new IndexOutOfBoundsException(); } return group(); } @Override public int groupCount() { return 0; } } private static final EmptyStringMatchResult EMPTY_STRING_MATCH_RESULT = new EmptyStringMatchResult(); private final class GroupIndexMatchResult implements MatchResult { private final MatchResult result; GroupIndexMatchResult(final MatchResult r) { this.result = r; } @Override public int start() { return result.start(); } @Override public int start(final int group) { if (group > groupCount()) { throw new IndexOutOfBoundsException(); } return (group > 0) ? result.start(groupIndexes[group - 1]) : result.start(); } @Override public int end() { return result.end(); } @Override public int end(final int group) { if (group > groupCount()) { throw new IndexOutOfBoundsException(); } return (group > 0) ? result.end(groupIndexes[group - 1]) : result.end(); } @Override public String group() { return result.group(); } @Override public String group(final int group) { if (group > groupCount()) { throw new IndexOutOfBoundsException(); } return (group > 0) ? result.group(groupIndexes[group - 1]) : result.group(); } @Override public int groupCount() { return groupIndexes.length; } }
Match against the pattern.
Params:
  • cs – the char sequence to match against the template.
Returns:the match result, otherwise null if no match occurs.
/** * Match against the pattern. * * @param cs the char sequence to match against the template. * @return the match result, otherwise null if no match occurs. */
public final MatchResult match(final CharSequence cs) { // Check for match against the empty pattern if (cs == null) { return (regexPattern == null) ? EMPTY_STRING_MATCH_RESULT : null; } else if (regexPattern == null) { return null; } // Match regular expression Matcher m = regexPattern.matcher(cs); if (!m.matches()) { return null; } if (cs.length() == 0) { return EMPTY_STRING_MATCH_RESULT; } return (groupIndexes.length > 0) ? new GroupIndexMatchResult(m) : m; }
Match against the pattern.

If a matched then the capturing group values (if any) will be added to a list passed in as parameter.
Params:
  • cs – the char sequence to match against the template.
  • groupValues – the list to add the values of a pattern's capturing groups if matching is successful. The values are added in the same order as the pattern's capturing groups. The list is cleared before values are added.
Throws:
Returns:true if the char sequence matches the pattern, otherwise false.
/** * Match against the pattern. * <p/> * If a matched then the capturing group values (if any) will be added to a list passed in as parameter. * * @param cs the char sequence to match against the template. * @param groupValues the list to add the values of a pattern's capturing groups if matching is successful. The values are * added in the same order as the pattern's capturing groups. The list is cleared before values are added. * @return {@code true} if the char sequence matches the pattern, otherwise {@code false}. * * @throws IllegalArgumentException if the group values is {@code null}. */
public final boolean match(final CharSequence cs, final List<String> groupValues) throws IllegalArgumentException { if (groupValues == null) { throw new IllegalArgumentException(); } // Check for match against the empty pattern if (cs == null || cs.length() == 0) { return regexPattern == null; } else if (regexPattern == null) { return false; } // Match the regular expression Matcher m = regexPattern.matcher(cs); if (!m.matches()) { return false; } groupValues.clear(); if (groupIndexes.length > 0) { for (int i = 0; i < groupIndexes.length; i++) { groupValues.add(m.group(groupIndexes[i])); } } else { for (int i = 1; i <= m.groupCount(); i++) { groupValues.add(m.group(i)); } } // TODO check for consistency of different capturing groups // that must have the same value return true; }
Match against the pattern.

If a matched then the capturing group values (if any) will be added to a list passed in as parameter.
Params:
  • cs – the char sequence to match against the template.
  • groupNames – the list names associated with a pattern's capturing groups. The names MUST be in the same order as the pattern's capturing groups and the size MUST be equal to or less than the number of capturing groups.
  • groupValues – the map to add the values of a pattern's capturing groups if matching is successful. A values is put into the map using the group name associated with the capturing group. The map is cleared before values are added.
Throws:
Returns:true if the matches the pattern, otherwise false.
/** * Match against the pattern. * <p/> * If a matched then the capturing group values (if any) will be added to a list passed in as parameter. * * @param cs the char sequence to match against the template. * @param groupNames the list names associated with a pattern's capturing groups. The names MUST be in the same order as the * pattern's capturing groups and the size MUST be equal to or less than the number of capturing groups. * @param groupValues the map to add the values of a pattern's capturing groups if matching is successful. A values is put * into the map using the group name associated with the capturing group. The map is cleared before values * are added. * @return {@code true} if the matches the pattern, otherwise {@code false}. * * @throws IllegalArgumentException if group values is {@code null}. */
public final boolean match(final CharSequence cs, final List<String> groupNames, final Map<String, String> groupValues) throws IllegalArgumentException { if (groupValues == null) { throw new IllegalArgumentException(); } // Check for match against the empty pattern if (cs == null || cs.length() == 0) { return regexPattern == null; } else if (regexPattern == null) { return false; } // Match the regular expression Matcher m = regexPattern.matcher(cs); if (!m.matches()) { return false; } // Assign the matched group values to group names groupValues.clear(); for (int i = 0; i < groupNames.size(); i++) { String name = groupNames.get(i); String currentValue = m.group((groupIndexes.length > 0) ? groupIndexes[i] : i + 1); // Group names can have the same name occurring more than once, // check that groups values are same. String previousValue = groupValues.get(name); if (previousValue != null && !previousValue.equals(currentValue)) { return false; } groupValues.put(name, currentValue); } return true; } @Override public final int hashCode() { return regex.hashCode(); } @Override public final boolean equals(final Object obj) { if (obj == null) { return false; } if (getClass() != obj.getClass()) { return false; } final PatternWithGroups that = (PatternWithGroups) obj; if (this.regex != that.regex && (this.regex == null || !this.regex.equals(that.regex))) { return false; } return true; } @Override public final String toString() { return regex; } }