class UriUtil {

  public static String resolve(String baseUri, String referenceUri) {
    StringBuilder uri = new StringBuilder();

    // Map null onto empty string, to make the following logic simpler.
    baseUri = baseUri == null ? "" : baseUri;
    referenceUri = referenceUri == null ? "" : referenceUri;

    int[] refIndices = getUriIndices(referenceUri);
    if (refIndices[SCHEME_COLON] != -1) {
      // The reference is absolute. The target Uri is the reference.
      uri.append(referenceUri);
      removeDotSegments(uri, refIndices[PATH], refIndices[QUERY]);
      return uri.toString();
    }

    int[] baseIndices = getUriIndices(baseUri);
    if (refIndices[FRAGMENT] == 0) {
      // The reference is empty or contains just the fragment part, then the target Uri is the
      // concatenation of the base Uri without its fragment, and the reference.
      return uri.append(baseUri, 0, baseIndices[FRAGMENT]).append(referenceUri).toString();
    }

    if (refIndices[QUERY] == 0) {
      // The reference starts with the query part. The target is the base up to (but excluding) the
      // query, plus the reference.
      return uri.append(baseUri, 0, baseIndices[QUERY]).append(referenceUri).toString();
    }

    if (refIndices[PATH] != 0) {
      // The reference has authority. The target is the base scheme plus the reference.
      int baseLimit = baseIndices[SCHEME_COLON] + 1;
      uri.append(baseUri, 0, baseLimit).append(referenceUri);
      return removeDotSegments(uri, baseLimit + refIndices[PATH], baseLimit + refIndices[QUERY]);
    }

    if (referenceUri.charAt(refIndices[PATH]) == '/') {
      // The reference path is rooted. The target is the base scheme and authority (if any), plus
      // the reference.
      uri.append(baseUri, 0, baseIndices[PATH]).append(referenceUri);
      return removeDotSegments(uri, baseIndices[PATH], baseIndices[PATH] + refIndices[QUERY]);
    }

    // The target Uri is the concatenation of the base Uri up to (but excluding) the last segment,
    // and the reference. This can be split into 2 cases:
    if (baseIndices[SCHEME_COLON] + 2 < baseIndices[PATH]
        && baseIndices[PATH] == baseIndices[QUERY]) {
      // Case 1: The base hier-part is just the authority, with an empty path. An additional '/' is
      // needed after the authority, before appending the reference.
      uri.append(baseUri, 0, baseIndices[PATH]).append('/').append(referenceUri);
      return removeDotSegments(uri, baseIndices[PATH], baseIndices[PATH] + refIndices[QUERY] + 1);
    } else {
      // Case 2: Otherwise, find the last '/' in the base hier-part and append the reference after
      // it. If base hier-part has no '/', it could only mean that it is completely empty or
      // contains only one segment, in which case the whole hier-part is excluded and the reference
      // is appended right after the base scheme colon without an added '/'.
      int lastSlashIndex = baseUri.lastIndexOf('/', baseIndices[QUERY] - 1);
      int baseLimit = lastSlashIndex == -1 ? baseIndices[PATH] : lastSlashIndex + 1;
      uri.append(baseUri, 0, baseLimit).append(referenceUri);
      return removeDotSegments(uri, baseIndices[PATH], baseLimit + refIndices[QUERY]);
    }
  }

  private  UriUtil();

}