You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@juneau.apache.org by ja...@apache.org on 2019/06/23 00:08:13 UTC
[juneau] branch master updated: JUNEAU-97 Path Parameter in Parent
RestResource
This is an automated email from the ASF dual-hosted git repository.
jamesbognar pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/juneau.git
The following commit(s) were added to refs/heads/master by this push:
new 3b9f323 JUNEAU-97 Path Parameter in Parent RestResource
3b9f323 is described below
commit 3b9f323f776ae9a2ce3573d0d55f9f01425b6e00
Author: JamesBognar <ja...@apache.org>
AuthorDate: Sat Jun 22 20:07:07 2019 -0400
JUNEAU-97 Path Parameter in Parent RestResource
---
.../juneau/rest/util/UrlPathPatternTest.java | 9 +--
.../apache/juneau/rest/BasicRestCallHandler.java | 74 ++++++++++------------
.../{util/UrlPathParts.java => Constants.java} | 62 ++----------------
.../java/org/apache/juneau/rest/RequestPath.java | 6 ++
.../org/apache/juneau/rest/RestCallRouter.java | 4 +-
.../java/org/apache/juneau/rest/RestContext.java | 21 ++++--
.../org/apache/juneau/rest/RestMethodContext.java | 2 +-
.../util/{UrlPathParts.java => UrlPathInfo.java} | 54 +++++++++-------
.../apache/juneau/rest/util/UrlPathPattern.java | 32 +++++-----
.../juneau/rest/util/UrlPathPatternMatch.java | 64 +++++++++++++++++--
10 files changed, 169 insertions(+), 159 deletions(-)
diff --git a/juneau-rest/juneau-rest-server-test/src/test/java/org/apache/juneau/rest/util/UrlPathPatternTest.java b/juneau-rest/juneau-rest-server-test/src/test/java/org/apache/juneau/rest/util/UrlPathPatternTest.java
index 014b813..5498f2d 100644
--- a/juneau-rest/juneau-rest-server-test/src/test/java/org/apache/juneau/rest/util/UrlPathPatternTest.java
+++ b/juneau-rest/juneau-rest-server-test/src/test/java/org/apache/juneau/rest/util/UrlPathPatternTest.java
@@ -58,7 +58,7 @@ public class UrlPathPatternTest {
l.add(new UrlPathPattern("/foo/{id}/bar/*"));
Collections.sort(l);
- assertEquals("['/foo/bar','/foo/bar/*','/foo/{id}/bar','/foo/{id}/bar/*','/foo/{id}','/foo/{id}/*','/foo','/foo/*','/','/*','','*']", SimpleJsonSerializer.DEFAULT.toString(l));
+ assertEquals("['/foo/bar','/foo/bar/*','/foo/{id}/bar','/foo/{id}/bar/*','/foo/{id}','/foo/{id}/*','/foo','/foo/*','/','/*','/','/*']", SimpleJsonSerializer.DEFAULT.toString(l));
}
@Test
@@ -79,7 +79,7 @@ public class UrlPathPatternTest {
l.add(new UrlPathPattern(""));
Collections.sort(l);
- assertEquals("['/foo/bar','/foo/bar/*','/foo/{id}/bar','/foo/{id}/bar/*','/foo/{id}','/foo/{id}/*','/foo','/foo/*','/','/*','','*']", SimpleJsonSerializer.DEFAULT.toString(l));
+ assertEquals("['/foo/bar','/foo/bar/*','/foo/{id}/bar','/foo/{id}/bar/*','/foo/{id}','/foo/{id}/*','/foo','/foo/*','/','/*','/','/*']", SimpleJsonSerializer.DEFAULT.toString(l));
}
@Test
@@ -118,7 +118,7 @@ public class UrlPathPatternTest {
public void b03_simple_match_2parts() throws Exception {
UrlPathPattern p = new UrlPathPattern("/foo/bar");
shouldMatch(p, "/foo/bar", "{}");
- shouldMatch(p, "foo/bar/", "{r:''}");
+ shouldMatch(p, "/foo/bar/", "{r:''}");
}
@Test
@@ -134,7 +134,6 @@ public class UrlPathPatternTest {
public void b05_simple_match_0parts() throws Exception {
UrlPathPattern p = new UrlPathPattern("/");
shouldMatch(p, "/", "{r:''}");
- shouldMatch(p, "", "{}");
}
@Test
@@ -148,7 +147,6 @@ public class UrlPathPatternTest {
public void b07_simple_match_blank() throws Exception {
UrlPathPattern p = new UrlPathPattern("");
shouldMatch(p, "/", "{r:''}");
- shouldMatch(p, "", "{}");
}
@Test
@@ -186,7 +184,6 @@ public class UrlPathPatternTest {
public void c03_simple_withRemainder_match_2parts() throws Exception {
UrlPathPattern p = new UrlPathPattern("/foo/bar/*");
shouldMatch(p, "/foo/bar", "{}");
- shouldMatch(p, "foo/bar/", "{r:''}");
shouldMatch(p, "/foo/bar/baz", "{r:'baz'}");
shouldMatch(p, "/foo/bar/baz/", "{r:'baz/'}");
}
diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/BasicRestCallHandler.java b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/BasicRestCallHandler.java
index 1a591d1..91a96e2 100644
--- a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/BasicRestCallHandler.java
+++ b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/BasicRestCallHandler.java
@@ -12,6 +12,7 @@
// ***************************************************************************************************************************
package org.apache.juneau.rest;
+import static org.apache.juneau.rest.Constants.*;
import static java.util.logging.Level.*;
import static javax.servlet.http.HttpServletResponse.*;
import static org.apache.juneau.internal.IOUtils.*;
@@ -27,7 +28,7 @@ import javax.servlet.http.*;
import org.apache.juneau.http.StreamResource;
import org.apache.juneau.rest.RestContext.*;
import org.apache.juneau.rest.exception.*;
-import org.apache.juneau.rest.util.RestUtils;
+import org.apache.juneau.rest.util.*;
/**
* Default implementation of {@link RestCallHandler}.
@@ -112,49 +113,38 @@ public class BasicRestCallHandler implements RestCallHandler {
context.checkForInitException();
String pathInfo = RestUtils.getPathInfoUndecoded(r1); // Can't use r1.getPathInfo() because we don't want '%2F' resolved.
+ UrlPathInfo upi = new UrlPathInfo(pathInfo);
// If this resource has child resources, try to recursively call them.
if (pathInfo != null && context.hasChildResources() && (! pathInfo.equals("/"))) {
-// for (Map.Entry<UrlPathPattern,RestContext> e : context.getChildResources().entrySet()) {
-// UrlPathPattern upp = e.getKey();
-// String[] vars = upp.match(pathInfo);
-// if (vars != null) {
-// for (int i = 0; i < vars.length; i++)
-// r1.setAttribute(upp.getVars()[i], vars[i]);
-// final String pathInfoRemainder = upp.(i == -1 ? null : pathInfo.substring(i));
-// final String servletPath = r1.getServletPath() + "/" + pathInfoPart;
-// final HttpServletRequest childRequest = new HttpServletRequestWrapper(r1) {
-// @Override /* ServletRequest */
-// public String getPathInfo() {
-// return urlDecode(pathInfoRemainder);
-// }
-// @Override /* ServletRequest */
-// public String getServletPath() {
-// return servletPath;
-// }
-// };
-// childResource.getCallHandler().service(childRequest, r2);
-// return;
-// }
-// }
- int i = pathInfo.indexOf('/', 1);
- String pathInfoPart = i == -1 ? pathInfo.substring(1) : pathInfo.substring(1, i);
- RestContext childResource = context.getChildResource(pathInfoPart);
- if (childResource != null) {
- final String pathInfoRemainder = (i == -1 ? null : pathInfo.substring(i));
- final String servletPath = r1.getServletPath() + "/" + pathInfoPart;
- final HttpServletRequest childRequest = new HttpServletRequestWrapper(r1) {
- @Override /* ServletRequest */
- public String getPathInfo() {
- return urlDecode(pathInfoRemainder);
+ for (RestContext rc : context.getChildResources().values()) {
+ UrlPathPattern upp = rc.pathPattern;
+ final UrlPathPatternMatch uppm = upp.match(upi);
+ if (uppm != null) {
+ if (uppm.hasVars()) {
+ @SuppressWarnings("unchecked")
+ Map<String,String> vars = (Map<String,String>)r1.getAttribute(REST_PATHVARS_ATTR);
+ if (vars == null) {
+ vars = new TreeMap<>();
+ r1.setAttribute(REST_PATHVARS_ATTR, vars);
+ }
+ vars.putAll(uppm.getVars());
}
- @Override /* ServletRequest */
- public String getServletPath() {
- return servletPath;
- }
- };
- childResource.getCallHandler().service(childRequest, r2);
- return;
+ final String afterMatch = uppm.getSuffix();
+ final String servletPath = r1.getServletPath() + uppm.getPrefix();
+ final HttpServletRequest childRequest = new HttpServletRequestWrapper(r1) {
+ @Override /* ServletRequest */
+ public String getPathInfo() {
+ return urlDecode(afterMatch);
+ }
+ @Override /* ServletRequest */
+ public String getServletPath() {
+ return servletPath;
+ }
+ };
+ rc.getCallHandler().service(childRequest, r2);
+ return;
+ }
}
}
@@ -187,9 +177,9 @@ public class BasicRestCallHandler implements RestCallHandler {
// If the specified method has been defined in a subclass, invoke it.
int rc = SC_METHOD_NOT_ALLOWED;
if (restCallRouters.containsKey(methodUC)) {
- rc = restCallRouters.get(methodUC).invoke(pathInfo, req, res);
+ rc = restCallRouters.get(methodUC).invoke(upi, req, res);
} else if (restCallRouters.containsKey("*")) {
- rc = restCallRouters.get("*").invoke(pathInfo, req, res);
+ rc = restCallRouters.get("*").invoke(upi, req, res);
}
// If not invoked above, see if it's an OPTIONs request
diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/util/UrlPathParts.java b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/Constants.java
similarity index 54%
copy from juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/util/UrlPathParts.java
copy to juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/Constants.java
index 4ca7a15..9158710 100644
--- a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/util/UrlPathParts.java
+++ b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/Constants.java
@@ -10,67 +10,15 @@
// * "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.internal.StringUtils.*;
+package org.apache.juneau.rest;
/**
- * Represents a parsed URL path.
+ * Constant strings.
*/
-public class UrlPathParts {
-
- final String[] parts;
- final String raw;
-
- /**
- * Constructor.
- *
- * @param path The path.
- */
- public UrlPathParts(String path) {
- path = emptyIfNull(path);
- raw = path;
- if (path.length() > 0 && path.charAt(0) == '/')
- path = path.substring(1);
- parts = split(path, '/');
- for (int i = 0; i < parts.length; i++)
- parts[i] = urlDecode(parts[i]);
- }
-
- /**
- * Returns the path parts.
- *
- * @return The path parts.
- */
- public String[] getParts() {
- return parts;
- }
-
- /**
- * Returns a path remainder given the specified number of prefix parts.
- *
- * @param i The number of prefix parts to discard.
- * @return The remainder.
- */
- public String getRemainder(int i) {
- String s = raw;
- if (s.length() > 0 && s.charAt(0) == '/')
- s = s.substring(1);
- for (int j = 0; j < s.length(); j++) {
- if (i == 0)
- return s.substring(j);
- if (i > 0 && s.charAt(j) == '/')
- i--;
- }
- return isTrailingSlash() ? "" : null;
- }
+class Constants {
/**
- * Returns <jk>true</jk> if this path ends with a slash.
- *
- * @return <jk>true</jk> if this path ends with a slash.
+ * Request attribute name for passing path variables from parent to child.
*/
- public boolean isTrailingSlash() {
- return raw.endsWith("/");
- }
+ static final String REST_PATHVARS_ATTR = "juneau.pathVars";
}
diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RequestPath.java b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RequestPath.java
index 44e5d7c..8705e09 100644
--- a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RequestPath.java
+++ b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RequestPath.java
@@ -12,6 +12,7 @@
// ***************************************************************************************************************************
package org.apache.juneau.rest;
+import static org.apache.juneau.rest.Constants.*;
import static org.apache.juneau.internal.StringUtils.*;
import java.lang.reflect.*;
@@ -43,6 +44,11 @@ public class RequestPath extends TreeMap<String,String> {
RequestPath(RestRequest req) {
super(String.CASE_INSENSITIVE_ORDER);
this.req = req;
+ @SuppressWarnings("unchecked")
+ Map<String,String> parentVars = (Map<String,String>)req.getAttribute(REST_PATHVARS_ATTR);
+ if (parentVars != null)
+ for (Map.Entry<String,String> e : parentVars.entrySet())
+ put(e.getKey(), e.getValue());
}
RequestPath parser(HttpPartParser parser) {
diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestCallRouter.java b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestCallRouter.java
index 8a09ba1..57e333b 100644
--- a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestCallRouter.java
+++ b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestCallRouter.java
@@ -18,6 +18,8 @@ import java.util.*;
import javax.servlet.http.*;
+import org.apache.juneau.rest.util.*;
+
/**
* Represents a group of CallMethods on a REST resource that handle the same HTTP Method name but with different
* paths/matchers/guards/etc...
@@ -75,7 +77,7 @@ public class RestCallRouter {
* @param pathInfo The value of {@link HttpServletRequest#getPathInfo()} (sorta)
* @return The HTTP response code.
*/
- int invoke(String pathInfo, RestRequest req, RestResponse res) throws Throwable {
+ int invoke(UrlPathInfo pathInfo, RestRequest req, RestResponse res) throws Throwable {
if (restJavaMethods.length == 1)
return restJavaMethods[0].invoke(pathInfo, req, res);
diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestContext.java b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestContext.java
index 03b805c..d62fb9a 100644
--- a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestContext.java
+++ b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestContext.java
@@ -64,7 +64,7 @@ import org.apache.juneau.rest.annotation.*;
import org.apache.juneau.rest.converters.*;
import org.apache.juneau.rest.exception.*;
import org.apache.juneau.rest.reshandlers.*;
-import org.apache.juneau.rest.util.UrlPathPattern;
+import org.apache.juneau.rest.util.*;
import org.apache.juneau.rest.vars.*;
import org.apache.juneau.rest.widget.*;
import org.apache.juneau.serializer.*;
@@ -3230,6 +3230,7 @@ public final class RestContext extends BeanContext {
uriAuthority,
uriContext;
final String fullPath;
+ final UrlPathPattern pathPattern;
private final Set<String> allowedMethodParams;
@@ -3458,7 +3459,12 @@ public final class RestContext extends BeanContext {
msgs.addSearchPath(mbl[i] != null ? mbl[i].baseClass : resourceClass, mbl[i].bundlePath);
}
- fullPath = (builder.parentContext == null ? "" : (builder.parentContext.fullPath + '/')) + builder.getPath();
+ this.fullPath = (builder.parentContext == null ? "" : (builder.parentContext.fullPath + '/')) + builder.getPath();
+
+ String p = builder.getPath();
+ if (! p.endsWith("/*"))
+ p += "/*";
+ this.pathPattern = new UrlPathPattern(p);
this.childResources = Collections.synchronizedMap(new LinkedHashMap<String,RestContext>()); // Not unmodifiable on purpose so that children can be replaced.
@@ -3513,7 +3519,7 @@ public final class RestContext extends BeanContext {
sm = new RestMethodContext(smb) {
@Override
- int invoke(String pathInfo, RestRequest req, RestResponse res) throws Throwable {
+ int invoke(UrlPathInfo pathInfo, RestRequest req, RestResponse res) throws Throwable {
int rc = super.invoke(pathInfo, req, res);
if (rc != SC_OK)
@@ -3526,10 +3532,11 @@ public final class RestContext extends BeanContext {
return SC_OK;
} else if ("POST".equals(req.getMethod())) {
- if (pathInfo.indexOf('/') != -1)
- pathInfo = pathInfo.substring(pathInfo.lastIndexOf('/')+1);
- pathInfo = urlDecode(pathInfo);
- RemoteInterfaceMethod rmm = rim.getMethodMetaByPath(pathInfo);
+ String pip = pathInfo.getPath();
+ if (pip.indexOf('/') != -1)
+ pip = pip.substring(pip.lastIndexOf('/')+1);
+ pip = urlDecode(pip);
+ RemoteInterfaceMethod rmm = rim.getMethodMetaByPath(pip);
if (rmm != null) {
Method m = rmm.getJavaMethod();
try {
diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestMethodContext.java b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestMethodContext.java
index e0a1425..d500808 100644
--- a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestMethodContext.java
+++ b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestMethodContext.java
@@ -738,7 +738,7 @@ public class RestMethodContext extends BeanContext implements Comparable<RestMet
* @param pathInfo The value of {@link HttpServletRequest#getPathInfo()} (sorta)
* @return The HTTP response code.
*/
- int invoke(String pathInfo, RestRequest req, RestResponse res) throws Throwable {
+ int invoke(UrlPathInfo pathInfo, RestRequest req, RestResponse res) throws Throwable {
UrlPathPatternMatch pm = pathPattern.match(pathInfo);
if (pm == null)
diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/util/UrlPathParts.java b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/util/UrlPathInfo.java
similarity index 68%
rename from juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/util/UrlPathParts.java
rename to juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/util/UrlPathInfo.java
index 4ca7a15..2e79a1d 100644
--- a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/util/UrlPathParts.java
+++ b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/util/UrlPathInfo.java
@@ -14,25 +14,27 @@ package org.apache.juneau.rest.util;
import static org.apache.juneau.internal.StringUtils.*;
+import org.apache.juneau.*;
+import org.apache.juneau.marshall.*;
+
/**
- * Represents a parsed URL path.
+ * Represents a parsed URL path-info string.
*/
-public class UrlPathParts {
+public class UrlPathInfo {
final String[] parts;
- final String raw;
+ final String path;
/**
* Constructor.
*
* @param path The path.
*/
- public UrlPathParts(String path) {
- path = emptyIfNull(path);
- raw = path;
- if (path.length() > 0 && path.charAt(0) == '/')
- path = path.substring(1);
- parts = split(path, '/');
+ public UrlPathInfo(String path) {
+ if (path != null && ! path.startsWith("/"))
+ throw new RuntimeException("Invalid path specified. Must be null or start with '/' per HttpServletRequest.getPathInfo().");
+ this.path = path;
+ parts = path == null ? new String[0] : split(path.substring(1), '/');
for (int i = 0; i < parts.length; i++)
parts[i] = urlDecode(parts[i]);
}
@@ -47,22 +49,12 @@ public class UrlPathParts {
}
/**
- * Returns a path remainder given the specified number of prefix parts.
+ * Returns the raw path passed into this object.
*
- * @param i The number of prefix parts to discard.
- * @return The remainder.
+ * @return The raw path passed into this object.
*/
- public String getRemainder(int i) {
- String s = raw;
- if (s.length() > 0 && s.charAt(0) == '/')
- s = s.substring(1);
- for (int j = 0; j < s.length(); j++) {
- if (i == 0)
- return s.substring(j);
- if (i > 0 && s.charAt(j) == '/')
- i--;
- }
- return isTrailingSlash() ? "" : null;
+ public String getPath() {
+ return path;
}
/**
@@ -71,6 +63,20 @@ public class UrlPathParts {
* @return <jk>true</jk> if this path ends with a slash.
*/
public boolean isTrailingSlash() {
- return raw.endsWith("/");
+ return path.endsWith("/");
+ }
+
+ /**
+ * Converts this object to a map.
+ *
+ * @return This object converted to a map.
+ */
+ public ObjectMap toMap() {
+ return new DefaultFilteringObjectMap().append("raw", path).append("parts", parts);
+ }
+
+ @Override /* Object */
+ public String toString() {
+ return SimpleJson.DEFAULT.toString(toMap());
}
}
diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/util/UrlPathPattern.java b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/util/UrlPathPattern.java
index 3507136..c486b7b 100644
--- a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/util/UrlPathPattern.java
+++ b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/util/UrlPathPattern.java
@@ -12,6 +12,8 @@
// ***************************************************************************************************************************
package org.apache.juneau.rest.util;
+import static org.apache.juneau.internal.StringUtils.*;
+
import java.util.*;
import java.util.regex.*;
@@ -39,7 +41,7 @@ public final class UrlPathPattern implements Comparable<UrlPathPattern> {
* @param patternString The raw pattern string from the {@link RestMethod#path() @RestMethod(path)} annotation.
*/
public UrlPathPattern(String patternString) {
- this.pattern = patternString;
+ this.pattern = isEmpty(patternString) ? "/" : patternString.charAt(0) != '/' ? '/' + patternString : patternString;
String c = patternString.replaceAll("\\{[^\\}]+\\}", ".").replaceAll("\\w+", "X").replaceAll("\\.", "W");
if (c.isEmpty())
@@ -48,7 +50,7 @@ public final class UrlPathPattern implements Comparable<UrlPathPattern> {
c = c + "/W";
this.comparator = c;
- String[] parts = new UrlPathParts(patternString).getParts();
+ String[] parts = new UrlPathInfo(pattern).getParts();
this.hasRemainder = parts.length > 0 && "*".equals(parts[parts.length-1]);
@@ -77,36 +79,36 @@ public final class UrlPathPattern implements Comparable<UrlPathPattern> {
* A pattern match object, or <jk>null</jk> if the path didn't match this pattern.
*/
public UrlPathPatternMatch match(String path) {
- return match(new UrlPathParts(path));
+ return match(new UrlPathInfo(path));
}
/**
* Returns a non-<jk>null</jk> value if the specified path matches this pattern.
*
- * @param path The path to match against.
+ * @param pathInfo The path to match against.
* @return
* A pattern match object, or <jk>null</jk> if the path didn't match this pattern.
*/
- public UrlPathPatternMatch match(UrlPathParts path) {
+ public UrlPathPatternMatch match(UrlPathInfo pathInfo) {
- String[] pp = path.getParts();
+ String[] pip = pathInfo.getParts();
- if (parts.length != pp.length) {
+ if (parts.length != pip.length) {
if (hasRemainder) {
- if (pp.length == parts.length - 1 && ! path.isTrailingSlash())
+ if (pip.length == parts.length - 1 && ! pathInfo.isTrailingSlash())
return null;
- else if (pp.length < parts.length)
+ else if (pip.length < parts.length)
return null;
} else {
- if (pp.length != parts.length + 1)
+ if (pip.length != parts.length + 1)
return null;
- if (! path.isTrailingSlash())
+ if (! pathInfo.isTrailingSlash())
return null;
}
}
for (int i = 0; i < parts.length; i++)
- if (vars[i] == null && (pp.length <= i || ! ("*".equals(parts[i]) || pp[i].equals(parts[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];
@@ -115,11 +117,9 @@ public final class UrlPathPattern implements Comparable<UrlPathPattern> {
if (vals != null)
for (int i = 0; i < parts.length; i++)
if (vars[i] != null)
- vals[j++] = pp[i];
-
- String remainder = path.getRemainder(parts.length);
+ vals[j++] = pip[i];
- return new UrlPathPatternMatch(varKeys, vals, remainder);
+ return new UrlPathPatternMatch(pathInfo.getPath(), parts.length, varKeys, vals);
}
/**
diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/util/UrlPathPatternMatch.java b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/util/UrlPathPatternMatch.java
index fa39d42..f16b56f 100644
--- a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/util/UrlPathPatternMatch.java
+++ b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/util/UrlPathPatternMatch.java
@@ -12,6 +12,8 @@
// ***************************************************************************************************************************
package org.apache.juneau.rest.util;
+import static org.apache.juneau.internal.StringUtils.*;
+
import java.util.*;
import org.apache.juneau.*;
@@ -26,18 +28,21 @@ import org.apache.juneau.marshall.*;
*/
public class UrlPathPatternMatch {
- private final String remainder;
+ private final int matchedParts;
+ private final String path;
private final Map<String,String> vars;
/**
* Constructor.
*
+ * @param path The path being matched against. Can be <jk>null</jk>.
+ * @param matchedParts The number of parts that were matched against the path.
* @param keys The variable keys. Can be <jk>null</jk>.
* @param values The variable values. Can be <jk>null</jk>.
- * @param remainder
*/
- protected UrlPathPatternMatch(String[] keys, String[] values, String remainder) {
- this.remainder = remainder;
+ protected UrlPathPatternMatch(String path, int matchedParts, String[] keys, String[] values) {
+ this.path = path;
+ this.matchedParts = matchedParts;
this.vars = keys == null ? Collections.emptyMap() : new SimpleMap<>(keys, values);
}
@@ -51,12 +56,61 @@ public class UrlPathPatternMatch {
}
/**
+ * Returns <jk>true</jk> if this match contains one or more variables.
+ *
+ * @return <jk>true</jk> if this match contains one or more variables.
+ */
+ public boolean hasVars() {
+ return ! vars.isEmpty();
+ }
+
+ /**
* Returns the remainder of the path after the pattern match has been made.
*
* @return The remainder of the path after the pattern match has been made.
*/
public String getRemainder() {
- return remainder;
+ String suffix = getSuffix();
+ if (isNotEmpty(suffix) && suffix.charAt(0) == '/')
+ suffix = suffix.substring(1);
+ return suffix;
+ }
+
+ /**
+ * Returns the remainder of the URL after the pattern was matched.
+ *
+ * @return
+ * The remainder of the URL after the pattern was matched.
+ * <br>Can be <jk>null</jk> if nothing remains to be matched.
+ * <br>Otherwise, always starts with <js>'/'</js>.
+ */
+ public String getSuffix() {
+ String s = path;
+ for (int j = 0; j < matchedParts; j++) {
+ int k = s.indexOf('/', 1);
+ if (k == -1)
+ return null;
+ s = s.substring(k);
+ }
+ return s;
+ }
+
+ /**
+ * Returns the part of the URL that the pattern matched against.
+ *
+ * @return
+ * The part of the URL that the pattern matched against.
+ * <br>Can be <jk>null</jk> if nothing matched.
+ * <br>Otherwise, always starts with <js>'/'</js>.
+ */
+ public String getPrefix() {
+ int c = 0;
+ for (int j = 0; j < matchedParts; j++) {
+ c = path.indexOf('/', c+1);
+ if (c == -1)
+ c = path.length();
+ }
+ return nullIfEmpty(path.substring(0, c));
}
/**