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

io.undertow.util.PathTemplate Maven / Gradle / Ivy

Go to download

This artifact provides a single jar that contains all classes required to use remote EJB and JMS, including all dependencies. It is intended for use by those not using maven, maven users should just import the EJB and JMS BOM's instead (shaded JAR's cause lots of problems with maven, as it is very easy to inadvertently end up with different versions on classes on the class path).

There is a newer version: 34.0.0.Final
Show newest version
/*
 * JBoss, Home of Professional Open Source.
 * Copyright 2014 Red Hat, Inc., and individual contributors
 * as indicated by the @author tags.
 *
 * Licensed 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 io.undertow.util;

import io.undertow.UndertowMessages;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;


/**
 * Represents a parsed web socket path template.
 * 

* This class can be compared to other path templates, with templates that are considered * lower have a higher priority, and should be checked first. *

* This comparison can also be used to check for semantically equal paths, if * a.compareTo(b) == 0 then the two paths are equivalent, which will generally * result in a deployment exception. * * @author Stuart Douglas */ public class PathTemplate implements Comparable { private final String templateString; private final boolean template; private final String base; final List parts; private final Set parameterNames; private final boolean trailingSlash; private PathTemplate(String templateString, final boolean template, final String base, final List parts, Set parameterNames, boolean trailingSlash) { this.templateString = templateString; this.template = template; this.base = base; this.parts = parts; this.parameterNames = Collections.unmodifiableSet(parameterNames); this.trailingSlash = trailingSlash; } public static PathTemplate create(final String inputPath) { // a path is required if(inputPath == null) { throw UndertowMessages.MESSAGES.pathMustBeSpecified(); } // prepend a "/" if none is present if(!inputPath.startsWith("/")) { return PathTemplate.create("/" + inputPath); } // create string from modified string final String path = inputPath; int state = 0; String base = ""; List parts = new ArrayList<>(); int stringStart = 0; //0 parsing base //1 parsing base, last char was / //2 in template part //3 just after template part, expecting / //4 expecting either template or segment //5 in segment for (int i = 0; i < path.length(); ++i) { final int c = path.charAt(i); switch (state) { case 0: { if (c == '/') { state = 1; } else if (c == '*') { base = path.substring(0, i + 1); stringStart = i; state = 5; } else { state = 0; } break; } case 1: { if (c == '{') { base = path.substring(0, i); stringStart = i + 1; state = 2; } else if (c == '*') { base = path.substring(0, i + 1); stringStart = i; state = 5; } else if (c != '/') { state = 0; } break; } case 2: { if (c == '}') { Part part = new Part(true, path.substring(stringStart, i)); parts.add(part); stringStart = i; state = 3; } break; } case 3: { if (c == '/') { state = 4; } else { throw UndertowMessages.MESSAGES.couldNotParseUriTemplate(path, i); } break; } case 4: { if (c == '{') { stringStart = i + 1; state = 2; } else if (c != '/') { stringStart = i; state = 5; } break; } case 5: { if (c == '/') { Part part = new Part(false, path.substring(stringStart, i)); parts.add(part); stringStart = i + 1; state = 4; } break; } } } boolean trailingSlash = false; switch (state) { case 1: trailingSlash = true; //fall through case 0: { base = path; break; } case 2: { throw UndertowMessages.MESSAGES.couldNotParseUriTemplate(path, path.length()); } case 4: { trailingSlash = true; break; } case 5: { Part part = new Part(false, path.substring(stringStart)); parts.add(part); break; } } final Set templates = new HashSet<>(); for(Part part : parts) { if(part.template) { templates.add(part.part); } } return new PathTemplate(path, state > 1 && !base.contains("*"), base, parts, templates, trailingSlash); } /** * Check if the given uri matches the template. If so then it will return true and * place the value of any path parameters into the given map. *

* Note the map may be modified even if the match in unsuccessful, however in this case * it will be emptied before the method returns * * @param path The request path, relative to the context root * @param pathParameters The path parameters map to fill out * @return true if the URI is a match */ public boolean matches(final String path, final Map pathParameters) { if (!template && base.contains("*")) { final int indexOf = base.indexOf("*"); final String startBase = base.substring(0, indexOf); if (!path.startsWith(startBase)) { return false; } pathParameters.put("*", path.substring(indexOf,path.length())); return true; } if (!path.startsWith(base)) { return false; } int baseLength = base.length(); if (!template) { return path.length() == baseLength; } if(trailingSlash) { //the template has a trailing slash //we verify this first as it is cheap //and it simplifies the matching algorithm below if(path.charAt(path.length() -1 ) != '/') { return false; } } int currentPartPosition = 0; PathTemplate.Part current = parts.get(currentPartPosition); int stringStart = baseLength; int i; for (i = baseLength; i < path.length(); ++i) { final char currentChar = path.charAt(i); if (currentChar == '?' || current.part.equals("*")) { break; } else if (currentChar == '/') { String result = path.substring(stringStart, i); if (current.template) { pathParameters.put(current.part, result); } else if (!result.equals(current.part)) { pathParameters.clear(); return false; } ++currentPartPosition; if (currentPartPosition == parts.size()) { //this is a match if this is the last character return i == (path.length() - 1); } current = parts.get(currentPartPosition); stringStart = i + 1; } } if (currentPartPosition + 1 != parts.size()) { pathParameters.clear(); return false; } String result = path.substring(stringStart, i); if (current.part.equals("*")) { pathParameters.put(current.part, path.substring(stringStart,path.length())); return true; } if (current.template) { pathParameters.put(current.part, result); } else if (!result.equals(current.part)) { pathParameters.clear(); return false; } return true; } @Override public boolean equals(Object o) { if (this == o) return true; if (!(o instanceof PathTemplate)) return false; PathTemplate that = (PathTemplate) o; return this.compareTo(that) == 0; } @Override public int hashCode() { int result = getTemplateString() != null ? getTemplateString().hashCode() : 0; result = 31 * result + (template ? 1 : 0); result = 31 * result + (getBase() != null ? getBase().hashCode() : 0); result = 31 * result + (parts != null ? parts.hashCode() : 0); result = 31 * result + (getParameterNames() != null ? getParameterNames().hashCode() : 0); return result; } @Override public int compareTo(final PathTemplate o) { //we want templates with the highest priority to sort first //so we sort in reverse priority order //templates have lower priority if (template && !o.template) { return 1; } else if (o.template && !template) { return -1; } int res = base.compareTo(o.base); if (res > 0) { //our base is longer return -1; } else if (res < 0) { return 1; } else if (!template) { //they are the same path return 0; } //the first path with a non-template element int i = 0; for (; ; ) { if (parts.size() == i) { if (o.parts.size() == i) { return base.compareTo(o.base); } return 1; } else if (o.parts.size() == i) { //we have more parts, so should be checked first return -1; } Part thisPath = parts.get(i); Part otherPart = o.parts.get(i); if (thisPath.template && !otherPart.template) { //non template part sorts first return 1; } else if (!thisPath.template && otherPart.template) { return -1; } else if (!thisPath.template) { int r = thisPath.part.compareTo(otherPart.part); if (r != 0) { return r; } } ++i; } } public String getBase() { return base; } public String getTemplateString() { return templateString; } public Set getParameterNames() { return parameterNames; } private static class Part { final boolean template; final String part; private Part(final boolean template, final String part) { this.template = template; this.part = part; } @Override public String toString() { return "Part{" + "template=" + template + ", part='" + part + '\'' + '}'; } } @Override public String toString() { return "PathTemplate{" + "template=" + template + ", base='" + base + '\'' + ", parts=" + parts + '}'; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy