public static RequestPath parse(String path, Map<String, Object> args, List<String> prefixesToRemove,
      String clusterName) throws RestServiceException {
    int offset = 0;

    // remove prefix.
    String prefixFound = "";
    if (prefixesToRemove != null) {
      for (String prefix : prefixesToRemove) {
        int nextPathSegmentOffset = matchPathSegments(path, offset, prefix, false);
        if (nextPathSegmentOffset >= 0) {
          prefixFound = prefix;
          offset = nextPathSegmentOffset;
          break;
        }
      }
    }

    // check if the next path segment matches the cluster name.
    String clusterNameFound = "";
    if (clusterName != null) {
      int nextPathSegmentOffset = matchPathSegments(path, offset, clusterName, true);
      if (nextPathSegmentOffset >= 0) {
        clusterNameFound = clusterName;
        offset = nextPathSegmentOffset;
      }
    }

    // some use cases just require the path with the prefix and cluster name removed.
    String pathAfterPrefixes = path.substring(offset);

    // if there are at least 2 path segments (*/*) after the current position,
    // test if the last segment is a sub-resource
    SubResource subResource = null;
    int lastSlashOffset = path.lastIndexOf(PATH_SEPARATOR_CHAR);
    int blobSegmentIdx = NO_BLOB_SEGMENT_IDX_SPECIFIED;
    boolean isSegment = false;
    if (lastSlashOffset > offset) {
      try {
        String[] fields = path.split(PATH_SEPARATOR_STRING);
        //if subresource is a Segment subresource, it is expected that 'Segment' is followed
        //by a path separator and an integer number indicating the desired blob segment by
        //segment index, e.g. "Segment/32"
        if (SEGMENT.equals(fields[fields.length - 2])) {
          isSegment = true;
          subResource = SubResource.Segment;
          blobSegmentIdx = Integer.valueOf(fields[fields.length - 1]);
          lastSlashOffset = path.lastIndexOf(PATH_SEPARATOR_CHAR, lastSlashOffset - 1);
        } else {
          subResource = SubResource.valueOf(path.substring(lastSlashOffset + 1));
        }
      } catch (IllegalArgumentException e) {
        if (isSegment) {
          throw new RestServiceException("Segment index given is not an integer", RestServiceErrorCode.BadRequest);
        }
        //otherwise, nothing to do
      } catch (ArrayIndexOutOfBoundsException e) {
        // For eg., errorMessage = "java.lang.ArrayIndexOutOfBoundsException: -1"
        String errorMessage = e.getClass().getCanonicalName() + ": " + e.getMessage();
        throw new RestServiceException(errorMessage, RestServiceErrorCode.BadRequest);
      }
    }

    // the operationOrBlobId is the part in between the prefix/cluster and sub-resource,
    // if these optional path segments exist.
    String operationOrBlobId = path.substring(offset, subResource == null ? path.length() : lastSlashOffset);
    if ((operationOrBlobId.isEmpty() || operationOrBlobId.equals(PATH_SEPARATOR_STRING)) && args.containsKey(
        RestUtils.Headers.BLOB_ID)) {
      operationOrBlobId = args.get(RestUtils.Headers.BLOB_ID).toString();
    }

    return new RequestPath(prefixFound, clusterNameFound, pathAfterPrefixes, operationOrBlobId, subResource,
        blobSegmentIdx);
  }
This is similar to {@link #parse(RestRequest, List, String)} but allows usage with arbitrary paths that are not
part of a {@link RestRequest}.

@param path the path
@param args a map containing any request arguments. This might be used to look for blob IDs supplied via query
            parameters or headers.
@param prefixesToRemove the list of prefixes that could precede the other parts of the URL. Removal of
                        prefixes earlier in the list will be preferred to removal of the ones later in the list.
@param clusterName the cluster name to recognize and handle when parsing the URL. Case is ignored when matching
                   this path segment.
@return a {@link RequestPath} object.
ambry-api/src/main/java/com/github/ambry/rest/RequestPath.java