Author: johnh
Date: Thu Mar 26 22:15:43 2009
New Revision: 758884
URL: http://svn.apache.org/viewvc?rev=758884&view=rev
Log:
Implements Uri.resolve(...) natively instead of in terms of java.net.URI.
In addition to being a performance improvement (40%+), this allows
UriParser-parsed Uris that don't conform to java.net.URI's restrictions
(eg. chars like | or ^) to resolve appropriately.
Modified:
incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/common/uri/Uri.java
incubator/shindig/trunk/java/common/src/test/java/org/apache/shindig/common/uri/UriTest.java
Modified:
incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/common/uri/Uri.java
URL:
http://svn.apache.org/viewvc/incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/common/uri/Uri.java?rev=758884&r1=758883&r2=758884&view=diff
==============================================================================
---
incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/common/uri/Uri.java
(original)
+++
incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/common/uri/Uri.java
Thu Mar 26 22:15:43 2009
@@ -18,6 +18,7 @@
*/
package org.apache.shindig.common.uri;
+import com.google.common.base.Join;
import com.google.common.base.Objects;
import com.google.common.collect.Maps;
import com.google.inject.Inject;
@@ -26,8 +27,10 @@
import java.net.URISyntaxException;
import java.util.Collection;
import java.util.Collections;
+import java.util.LinkedList;
import java.util.List;
import java.util.Map;
+import java.util.StringTokenizer;
/**
* Represents a Uniform Resource Identifier (URI) reference as defined by <a
@@ -124,11 +127,99 @@
* @return The new url.
*/
public Uri resolve(Uri other) {
- // TODO: We can probably make this more efficient by implementing resolve
ourselves.
if (other == null) {
return null;
}
- return fromJavaUri(toJavaUri().resolve(other.toJavaUri()));
+
+ String scheme = other.getScheme();
+ String authority = other.getAuthority();
+ String path = other.getPath();
+ String query = other.getQuery();
+ String fragment = other.getFragment();
+
+ if (scheme != null && scheme.length() > 0) {
+ // Do nothing - this will accept other's fields verbatim.
+ } else if (authority != null) {
+ // Schema-relative ie. "//newhost.com/foo?q=s". Take base scheme.
+ scheme = getScheme();
+ } else if (path != null && path.length() > 0) {
+ // Resolve other path against current. Keep prerequisites.
+ scheme = getScheme();
+ authority = getAuthority();
+ path = resolvePath(path);
+ } else if (query != null && query.length() > 0) {
+ // Accept query + fragment verbatim. Use base scheme/authority/path.
+ scheme = getScheme();
+ authority = getAuthority();
+ // Treat query-relative as ""-path with query.
+ path = resolvePath("");
+ } else if (fragment != null && fragment.length() > 0) {
+ // Accept fragment verbatim. Use base scheme/authority/path/query.
+ scheme = getScheme();
+ authority = getAuthority();
+ path = getPath();
+ query = getQuery();
+ }
+
+ return new UriBuilder()
+ .setScheme(scheme)
+ .setAuthority(authority)
+ .setPath(path)
+ .setQuery(query)
+ .setFragment(fragment)
+ .toUri();
+ }
+
+ /**
+ * Resolves {...@code otherPath} against the current path, returning the
result.
+ * Implements RFC 2396 resolution rules.
+ */
+ private String resolvePath(String otherPath) {
+ if (otherPath.startsWith("/")) {
+ // Optimization: just accept other.
+ return otherPath;
+ }
+ // Relative path. Treat current path as a stack, otherPath as a List
+ // in order to merge.
+ LinkedList<String> pathStack = new LinkedList<String>();
+ String curPath = getPath() != null ? getPath() : "/"; // Just in case.
+ StringTokenizer tok = new StringTokenizer(curPath, "/");
+ while (tok.hasMoreTokens()) {
+ pathStack.add(tok.nextToken());
+ }
+ if (!curPath.endsWith("/")) {
+ // The first entry in mergePath overwrites the last in the pathStack.
+ // eg. curPath = "/foo/bar", otherPath = "baz" --> "/foo/baz".
+ pathStack.removeLast();
+ }
+
+ LinkedList<String> mergePath = new LinkedList<String>();
+ StringTokenizer tok2 = new StringTokenizer(otherPath, "/");
+ while (tok2.hasMoreTokens()) {
+ mergePath.add(tok2.nextToken());
+ }
+ if (otherPath.endsWith("/") || otherPath.equals("")) {
+ // Retains the ending slash in the final join.
+ mergePath.add("");
+ }
+
+ // Merge mergePath into pathStack.
+ for (String mergeComponent : mergePath) {
+ if (mergeComponent.equals(".")) {
+ // Retain current position in the path. Continue.
+ continue;
+ } else if (mergeComponent.equals("..")) {
+ // Pop one off the path stack if available. If not do nothing.
+ if (!pathStack.isEmpty()) {
+ pathStack.removeLast();
+ }
+ } else {
+ // Append latest to the path.
+ pathStack.add(mergeComponent);
+ }
+ }
+
+ return "/" + Join.join("/", pathStack);
}
/**
Modified:
incubator/shindig/trunk/java/common/src/test/java/org/apache/shindig/common/uri/UriTest.java
URL:
http://svn.apache.org/viewvc/incubator/shindig/trunk/java/common/src/test/java/org/apache/shindig/common/uri/UriTest.java?rev=758884&r1=758883&r2=758884&view=diff
==============================================================================
---
incubator/shindig/trunk/java/common/src/test/java/org/apache/shindig/common/uri/UriTest.java
(original)
+++
incubator/shindig/trunk/java/common/src/test/java/org/apache/shindig/common/uri/UriTest.java
Thu Mar 26 22:15:43 2009
@@ -174,6 +174,22 @@
}
@Test
+ public void resolvePathIncludesSubdirs() throws Exception {
+ Uri base = Uri.parse("http://example.org/foo/bar/baz?blah=blah#boo");
+ Uri other = Uri.parse("fez/../huey/./dewey/../louis");
+
+ assertEquals("http://example.org/foo/bar/huey/louis",
base.resolve(other).toString());
+ }
+
+ @Test
+ public void resolvePathSubdirsExtendsBeyondRoot() throws Exception {
+ Uri base = Uri.parse("http://example.org/foo/bar/baz?blah=blah#boo");
+ Uri other = Uri.parse("../random/../../../../../home");
+
+ assertEquals("http://example.org/home", base.resolve(other).toString());
+ }
+
+ @Test
public void resolvePathRelative() throws Exception {
Uri base = Uri.parse("http://example.org/foo/bar/baz?blah=blah#boo");
Uri other = Uri.parse("wee");