| /* |
| * 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.felix.http.base.internal.registry; |
| |
| import java.util.regex.Pattern; |
| |
| import javax.annotation.CheckForNull; |
| import javax.annotation.Nonnull; |
| |
| import org.apache.felix.http.base.internal.handler.ServletHandler; |
| import org.apache.felix.http.base.internal.service.HttpServiceFactory; |
| |
| /** |
| * The path resolver factory creates a path resolver for a pattern. |
| * The servlet spec supports different patterns |
| * - path mapping, a pattern starting with / and ending with /* |
| * - extension mapping, a pattern starting with *. |
| * - default mapping, the pattern / |
| * - root mapping, the pattern is the empty string |
| * - exact match |
| * |
| * Exact match is tried first, followed by longest match and finally |
| * extension match. |
| */ |
| public abstract class PathResolverFactory { |
| |
| public static @Nonnull PathResolver createPatternMatcher(@CheckForNull final ServletHandler handler, @Nonnull final String pattern) |
| { |
| if ( pattern.length() == 0 ) |
| { |
| return new RootMatcher(handler); |
| } |
| else if ( pattern.equals("/") ) |
| { |
| return new DefaultMatcher(handler); |
| } |
| else if ( pattern.startsWith("*.") ) |
| { |
| return new ExtensionMatcher(handler, pattern); |
| } |
| else if ( pattern.endsWith("/*") ) |
| { |
| return new PathMatcher(handler, pattern); |
| } |
| else if ( handler != null && handler.getContextServiceId() == HttpServiceFactory.HTTP_SERVICE_CONTEXT_SERVICE_ID ) |
| { |
| return new ExactAndPathMatcher(handler, pattern); |
| } |
| return new ExactMatcher(handler, pattern); |
| } |
| |
| public static @Nonnull PathResolver createRegexMatcher(@Nonnull final String regex) |
| { |
| return new RegexMatcher(regex); |
| } |
| |
| public static abstract class AbstractMatcher implements PathResolver |
| { |
| private final int ranking; |
| |
| private final String pattern; |
| |
| private final ServletHandler handler; |
| |
| public AbstractMatcher(final ServletHandler handler, final String pattern, final int ranking) |
| { |
| this.handler = handler; |
| this.ranking = ranking; |
| this.pattern = pattern; |
| } |
| |
| @Override |
| public int compareTo(final PathResolver o) |
| { |
| int result = o.getRanking() - this.ranking; |
| if ( result == 0 ) |
| { |
| result = o.getOrdering() - this.getOrdering(); |
| } |
| return result; |
| } |
| |
| @Override |
| public ServletHandler getServletHandler() |
| { |
| return this.handler; |
| } |
| |
| @Override |
| public int getRanking() |
| { |
| return this.ranking; |
| } |
| |
| @Override |
| public int getOrdering() |
| { |
| return 0; |
| } |
| |
| @Override |
| public String getPattern() |
| { |
| return this.pattern; |
| } |
| |
| @Override |
| public int hashCode() |
| { |
| return pattern.hashCode(); |
| } |
| |
| @Override |
| public boolean equals(final Object obj) { |
| if (this == obj) |
| { |
| return true; |
| } |
| if (obj == null || getClass() != obj.getClass()) |
| { |
| return false; |
| } |
| final AbstractMatcher other = (AbstractMatcher) obj; |
| return this.pattern.equals(other.pattern); |
| } |
| } |
| |
| public static final class RootMatcher extends AbstractMatcher |
| { |
| public RootMatcher(final ServletHandler handler) |
| { |
| super(handler, "", 2); |
| } |
| |
| @Override |
| public PathResolution resolve(final String uri) { |
| if ( uri.length() == 0 || uri.equals("/") ) |
| { |
| final PathResolution pr = new PathResolution(); |
| pr.pathInfo = "/"; |
| pr.servletPath = ""; |
| pr.requestURI = uri; |
| pr.handler = this.getServletHandler(); |
| |
| return pr; |
| } |
| return null; |
| } |
| } |
| |
| public static final class DefaultMatcher extends AbstractMatcher |
| { |
| public DefaultMatcher(final ServletHandler handler) |
| { |
| super(handler, "/", 1); |
| } |
| |
| @Override |
| public PathResolution resolve(final String uri) { |
| final PathResolution pr = new PathResolution(); |
| pr.pathInfo = null; |
| pr.servletPath = uri; |
| pr.requestURI = uri; |
| pr.handler = this.getServletHandler(); |
| |
| return pr; |
| } |
| } |
| |
| public static final class ExactAndPathMatcher extends AbstractMatcher |
| { |
| private final String path; |
| |
| private final String prefix; |
| |
| public ExactAndPathMatcher(final ServletHandler handler, final String pattern) |
| { |
| super(handler, pattern, 5); |
| this.path = pattern; |
| this.prefix = pattern.concat("/"); |
| } |
| |
| @Override |
| public PathResolution resolve(final String uri) { |
| if ( uri.equals(this.path) ) |
| { |
| final PathResolution pr = new PathResolution(); |
| pr.pathInfo = null; |
| pr.servletPath = uri; |
| pr.requestURI = uri; |
| pr.handler = this.getServletHandler(); |
| |
| return pr; |
| } |
| else if ( uri.startsWith(prefix) ) |
| { |
| final PathResolution pr = new PathResolution(); |
| pr.servletPath = this.prefix.substring(0, this.prefix.length() - 1); |
| pr.pathInfo = uri.substring(pr.servletPath.length()); |
| pr.requestURI = uri; |
| pr.handler = this.getServletHandler(); |
| |
| return pr; |
| } |
| return null; |
| } |
| |
| @Override |
| public int getOrdering() |
| { |
| return this.path.length(); |
| } |
| } |
| |
| public static final class ExactMatcher extends AbstractMatcher |
| { |
| private final String path; |
| |
| public ExactMatcher(final ServletHandler handler, final String pattern) |
| { |
| super(handler, pattern, 5); |
| this.path = pattern; |
| } |
| |
| @Override |
| public PathResolution resolve(final String uri) { |
| if ( uri.equals(this.path) ) |
| { |
| final PathResolution pr = new PathResolution(); |
| pr.servletPath = uri; |
| pr.pathInfo = null; |
| pr.requestURI = uri; |
| pr.handler = this.getServletHandler(); |
| |
| return pr; |
| } |
| return null; |
| } |
| |
| @Override |
| public int getOrdering() |
| { |
| return this.path.length(); |
| } |
| } |
| |
| public static final class PathMatcher extends AbstractMatcher |
| { |
| private final String prefix; |
| |
| private final String path; |
| |
| public PathMatcher(final ServletHandler handler, final String pattern) |
| { |
| super(handler, pattern, 4); |
| this.prefix = pattern.substring(0, pattern.length() - 1); |
| this.path = pattern.substring(0, pattern.length() - 2); |
| } |
| |
| @Override |
| public PathResolution resolve(final String uri) { |
| if ( uri.equals(this.path) ) |
| { |
| final PathResolution pr = new PathResolution(); |
| pr.servletPath = this.path; |
| pr.pathInfo = null; |
| pr.requestURI = uri; |
| pr.handler = this.getServletHandler(); |
| |
| return pr; |
| } |
| if ( uri.startsWith(this.prefix) ) |
| { |
| final PathResolution pr = new PathResolution(); |
| pr.servletPath = this.prefix.substring(0, this.prefix.length() - 1); |
| pr.pathInfo = uri.substring(pr.servletPath.length()); |
| pr.requestURI = uri; |
| pr.handler = this.getServletHandler(); |
| |
| return pr; |
| } |
| return null; |
| } |
| |
| @Override |
| public int getOrdering() |
| { |
| return this.prefix.length() + 1; |
| } |
| } |
| |
| public static final class ExtensionMatcher extends AbstractMatcher |
| { |
| private final String extension; |
| |
| public ExtensionMatcher(final ServletHandler handler, final String pattern) |
| { |
| super(handler, pattern, 3); |
| this.extension = pattern.substring(1); |
| } |
| |
| @Override |
| public PathResolution resolve(final String uri) { |
| if ( uri.endsWith(this.extension) ) |
| { |
| final PathResolution pr = new PathResolution(); |
| pr.pathInfo = null; |
| pr.servletPath = uri; |
| pr.requestURI = uri; |
| pr.handler = this.getServletHandler(); |
| |
| return pr; |
| } |
| return null; |
| } |
| |
| @Override |
| public int getOrdering() |
| { |
| return this.extension.length(); |
| } |
| } |
| |
| public static final class RegexMatcher extends AbstractMatcher |
| { |
| private final Pattern pattern; |
| |
| public RegexMatcher(final String regex) |
| { |
| super(null, regex, 0); |
| this.pattern = Pattern.compile(regex); |
| } |
| |
| @Override |
| public @CheckForNull PathResolution resolve(@Nonnull final String uri) { |
| if ( pattern.matcher(uri).matches() ) |
| { |
| final PathResolution pr = new PathResolution(); |
| pr.pathInfo = null; |
| pr.servletPath = uri; |
| pr.requestURI = uri; |
| |
| return pr; |
| } |
| return null; |
| } |
| |
| @Override |
| public int getOrdering() |
| { |
| return this.pattern.toString().length(); |
| } |
| } |
| } |