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");


Reply via email to