/**
   * Derived from Harmony Resolves a given url relative to this url. Resolution rules are the same
   * as for {@code java.net.URI.resolve(URI)}
   */
  public Uri resolve(Uri relative) {
    if (relative == null) {
      return null;
    }
    if (relative.isAbsolute()) {
      return relative;
    }

    UriBuilder result;
    if (StringUtils.isEmpty(relative.path)
        && relative.scheme == null
        && relative.authority == null
        && relative.query == null
        && relative.fragment != null) {
      // if the relative URI only consists of fragment,
      // the resolved URI is very similar to this URI,
      // except that it has the fragement from the relative URI.
      result = new UriBuilder(this);
      result.setFragment(relative.fragment);
    } else if (relative.scheme != null) {
      result = new UriBuilder(relative);
    } else if (relative.authority != null) {
      // if the relative URI has authority,
      // the resolved URI is almost the same as the relative URI,
      // except that it has the scheme of this URI.
      result = new UriBuilder(relative);
      result.setScheme(scheme);
    } else {
      // since relative URI has no authority,
      // the resolved URI is very similar to this URI,
      // except that it has the query and fragment of the relative URI,
      // and the path is different.
      result = new UriBuilder(this);
      result.setFragment(relative.fragment);
      result.setQuery(relative.query);
      String relativePath = (relative.path == null) ? "" : relative.path;
      if (relativePath.startsWith("/")) { // $NON-NLS-1$
        result.setPath(relativePath);
      } else {
        // resolve a relative reference
        int endindex = path.lastIndexOf('/') + 1;
        result.setPath(normalizePath(path.substring(0, endindex) + relativePath));
      }
    }
    Uri resolved = result.toUri();
    validate(resolved);
    return resolved;
  }