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

org.apache.juneau.rest.util.UrlPathMatcher Maven / Gradle / Ivy

// ***************************************************************************************************************************
// * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements.  See the NOTICE file *
// * distributed with this work for additional information regarding copyright ownership.  The ASF licenses this file        *
// * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance            *
// * with the License.  You may obtain a copy of the License at                                                              *
// *                                                                                                                         *
// *  http://www.apache.org/licenses/LICENSE-2.0                                                                             *
// *                                                                                                                         *
// * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an  *
// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the License for the        *
// * specific language governing permissions and limitations under the License.                                              *
// ***************************************************************************************************************************
package org.apache.juneau.rest.util;

import static org.apache.juneau.common.internal.StringUtils.*;
import static org.apache.juneau.internal.CollectionUtils.*;
import static org.apache.juneau.internal.FileUtils.*;

import java.util.*;
import java.util.regex.*;

import org.apache.juneau.rest.annotation.*;

/**
 * A parsed path pattern constructed from a {@link RestOp#path() @RestOp(path)} value.
 *
 * 

* Handles aspects of matching and precedence ordering. * *

See Also:
    *
*/ public abstract class UrlPathMatcher implements Comparable { /** * Constructs a matcher from the specified pattern string. * * @param pattern The pattern string. * @return A new matcher. */ public static UrlPathMatcher of(String pattern) { pattern = emptyIfNull(pattern); boolean isFilePattern = pattern.matches("[^\\/]+\\.[^\\/]+"); return isFilePattern ? new FileNameMatcher(pattern) : new PathMatcher(pattern); } private final String pattern; UrlPathMatcher(String pattern) { this.pattern = pattern; } /** * A file name pattern such as "favicon.ico" or "*.jsp". */ private static class FileNameMatcher extends UrlPathMatcher { private final String basePattern, extPattern, comparator; FileNameMatcher(String pattern) { super(pattern); String base = getBaseName(pattern), ext = getExtension(pattern); basePattern = base.equals("*") ? null : base; extPattern = ext.equals("*") ? null : ext; this.comparator = pattern.replaceAll("\\w+", "X").replace("*", "W"); } @Override /* UrlPathMatcher */ public UrlPathMatch match(UrlPath pathInfo) { Optional fileName = pathInfo.getFileName(); if (fileName.isPresent()) { String base = getBaseName(fileName.get()), ext = getExtension(fileName.get()); if ((basePattern == null || basePattern.equals(base)) && (extPattern == null || extPattern.equals(ext))) return new UrlPathMatch(pathInfo.getPath(), pathInfo.getParts().length, new String[0], new String[0]); } return null; } @Override /* UrlPathMatcher */ public String getComparator() { return comparator; } } /** * A dir name pattern such as "/foo" or "/*". */ private static class PathMatcher extends UrlPathMatcher { private static final Pattern VAR_PATTERN = Pattern.compile("\\{([^\\}]+)\\}"); private final String pattern, comparator; private final String[] parts, vars, varKeys; private final boolean hasRemainder; PathMatcher(String patternString) { super(patternString); this.pattern = isEmpty(patternString) ? "/" : patternString.charAt(0) != '/' ? '/' + patternString : patternString; String c = patternString.replaceAll("\\{[^\\}]+\\}", ".").replaceAll("\\w+", "X").replaceAll("\\.", "W"); if (c.isEmpty()) c = "+"; if (! c.endsWith("/*")) c = c + "/W"; this.comparator = c; String[] parts = new UrlPath(pattern).getParts(); this.hasRemainder = parts.length > 0 && "*".equals(parts[parts.length-1]); parts = hasRemainder ? Arrays.copyOf(parts, parts.length-1) : parts; this.parts = parts; this.vars = new String[parts.length]; List vars = list(); for (int i = 0; i < parts.length; i++) { Matcher m = VAR_PATTERN.matcher(parts[i]); if (m.matches()) { this.vars[i] = m.group(1); vars.add(this.vars[i]); } } this.varKeys = vars.isEmpty() ? null : vars.toArray(new String[vars.size()]); } /** * Returns a non-null value if the specified path matches this pattern. * * @param urlPath The path to match against. * @return * A pattern match object, or null if the path didn't match this pattern. */ @Override public UrlPathMatch match(UrlPath urlPath) { String[] pip = urlPath.getParts(); if (parts.length != pip.length) { if (hasRemainder) { if (pip.length == parts.length - 1 && ! urlPath.isTrailingSlash()) return null; else if (pip.length < parts.length) return null; } else { if (pip.length != parts.length + 1 || ! urlPath.isTrailingSlash()) return null; } } for (int i = 0; i < parts.length; i++) if (vars[i] == null && (pip.length <= i || ! ("*".equals(parts[i]) || pip[i].equals(parts[i])))) return null; String[] vals = varKeys == null ? null : new String[varKeys.length]; int j = 0; if (vals != null) for (int i = 0; i < parts.length; i++) if (vars[i] != null) vals[j++] = pip[i]; return new UrlPathMatch(urlPath.getPath(), parts.length, varKeys, vals); } @Override public String[] getVars() { return varKeys == null ? new String[0] : Arrays.copyOf(varKeys, varKeys.length); } @Override public boolean hasVars() { return varKeys != null; } @Override public String getComparator() { return comparator; } } /** * Returns a non-null value if the specified path matches this pattern. * * @param pathInfo The path to match against. * @return * A pattern match object, or null if the path didn't match this pattern. */ public abstract UrlPathMatch match(UrlPath pathInfo); /** * Returns a string that can be used to compare this matcher with other matchers to provide the ability to * order URL patterns from most-specific to least-specific. * * @return A comparison string. */ protected abstract String getComparator(); /** * Returns the variable names found in the pattern. * * @return * The variable names or an empty array if no variables found. *
Modifying the returned array does not modify this object. */ public String[] getVars() { return new String[0]; } /** * Returns true if this path pattern contains variables. * * @return true if this path pattern contains variables. */ public boolean hasVars() { return false; } /** * Comparator for this object. * *

* The comparator is designed to order URL pattern from most-specific to least-specific. * For example, the following patterns would be ordered as follows: *

    *
  1. foo.bar *
  2. *.bar *
  3. /foo/bar *
  4. /foo/bar/* *
  5. /foo/{id}/bar *
  6. /foo/{id}/bar/* *
  7. /foo/{id} *
  8. /foo/{id}/* *
  9. /foo *
  10. /foo/* *
*/ @Override /* Comparable */ public int compareTo(UrlPathMatcher o) { return o.getComparator().compareTo(getComparator()); } @Override /* Object */ public String toString() { return pattern; } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy