Skip to content
Merged
Original file line number Diff line number Diff line change
Expand Up @@ -158,60 +158,133 @@ static Immutable from(String scheme, String host, int port, String path, String
return new Immutable(scheme, host, port, path, query, fragment, false);
}

/**
* @return An immutable copy of this URI.
*/
Immutable asImmutable();

/**
* @return The URI as a string.
*/
String asString();

/**
* @return The authority component of the URI in the form {@code host:port},
* or just {@code host} if the port is not set, or {@code null} if no host is set.
*/
String getAuthority();

/**
* @return The decoded path with percent-encoded characters expanded, or {@code null} if no path is set.
* @see #getCanonicalPath()
*/
String getDecodedPath();

/**
* @return The canonical path with path parameters removed and percent-encoded characters decoded,
* or {@code null} if no path is set.
* @see #getDecodedPath()
* @see #getPath()
*/
String getCanonicalPath();

/**
* @return The fragment component of the URI (after the {@code #} character), or {@code null} if not set.
*/
String getFragment();

/**
* @return The host component of the URI, or {@code null} if not set.
*/
String getHost();

/**
* Get a URI path parameter. Only parameters from the last segment are returned.
* @return The last path parameter or null
* <p>Get a URI path parameter.</p>
* <p>Path parameters (also known as matrix parameters) were defined in
* <a href="https://tools.ietf.org/html/rfc2396#section-3.3">RFC2396</a> and appear
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This first shows up in https://www.rfc-editor.org/rfc/rfc2068#section-3.2.1
I've never seen it called matrix parameters before.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

BTW, where did you get "matrix parameters" from?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks.

Looks like Tim Berners-Lee even had comments about this awkward, and historical, piece of the URL/URI specs.

https://www.w3.org/DesignIssues/MatrixURIs.html

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like Jakarta EE Rest (what used to be called JAXRS) even has a @MatrixParam concept that uses this ...

https://github.com/jakartaee/rest/blob/4.0.0-RELEASE/jaxrs-spec/src/main/asciidoc/chapters/appendix/_annotation_table.adoc

It's implementation is a bit limited though, it only cares about the last path segment.
If a param exists in path segments that are not the last it considers that entire URL/URI as ambiguous and doesn't throws an exception.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@joakime should we add the "matrix parameters" back to the Javadoc then, to pay a tribute? Suggest a text

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Interestingly the path parameters are still referenced in the current active URI spec too.

See: https://www.rfc-editor.org/rfc/rfc3986#section-3.3

   Aside from dot-segments in hierarchical paths, a path segment is
   considered opaque by the generic syntax.  URI producing applications
   often use the reserved characters allowed in a segment to delimit
   scheme-specific or dereference-handler-specific subcomponents.  For
   example, the semicolon (";") and equals ("=") reserved characters are
   often used to delimit parameters and parameter values applicable to
   that segment.  The comma (",") reserved character is often used for ...

* after a semicolon in the path, such as {@code /path;param}. This is distinct from
* query parameters which appear after the {@code ?} character.</p>
* @return The last path parameter, or {@code null} if no path parameter is present.
* If there are multiple path parameters, only the last one is returned.
* @see #getQuery()
*/
String getParam();

/**
* @return The raw path component of the URI including path parameters, or {@code null} if not set.
* @see #getCanonicalPath()
* @see #getDecodedPath()
*/
String getPath();

/**
* @return The path and query components combined as {@code path?query},
* or just the path if no query is present, or {@code null} if no path is set.
*/
String getPathQuery();

/**
* @return The port number of the URI, or {@code -1} if not set.
*/
int getPort();

/**
* @return The query string component of the URI (after the {@code ?} character
* but before any {@code #} fragment), or {@code null} if not set.
* @see #getParam()
*/
String getQuery();

/**
* @return The URI scheme such as {@code http} or {@code https}, or {@code null} if not set.
*/
String getScheme();

/**
* @return The user info component of the URI authority (before the {@code @} character),
* or {@code null} if not set.
*/
String getUser();

/**
* @return {@code true} if the URI has a host component.
*/
boolean hasAuthority();

/**
* @return {@code true} if the URI has a scheme component.
*/
boolean isAbsolute();

/**
* @return True if the URI has any ambiguous {@link Violation}s.
* <p>Checks if the URI contains any ambiguous path violations that could be
* interpreted differently by different URI parsers.</p>
* @return {@code true} if the URI has any ambiguous {@link Violation}s.
* @see #hasViolations()
* @see UriCompliance#isAmbiguous(Set)
*/
boolean isAmbiguous();

/**
* @return True if the URI has any {@link Violation}s.
* <p>Checks if the URI has any compliance violations against the URI specification
* or best practices.</p>
* @return {@code true} if the URI has any {@link Violation}s.
* @see #getViolations()
* @see #isAmbiguous()
*/
boolean hasViolations();

/**
* @param violation the violation to check.
* @return true if the URI has the passed violation.
* @param violation The violation to check.
* @return {@code true} if the URI has the specified violation.
* @see #getViolations()
*/
boolean hasViolation(Violation violation);

/**
* @return Set of violations in the URI.
* @return The set of {@link Violation}s detected in the URI, or an empty set if none.
* @see #hasViolations()
* @see #hasViolation(Violation)
*/
Collection<Violation> getViolations();

Expand Down Expand Up @@ -804,6 +877,10 @@ private boolean isPathValidForAuthority(String path)
return path.startsWith("/");
}

/**
* Clears all URI components, resetting this mutable to an empty state.
* @return this mutable for chaining.
*/
public Mutable clear()
{
_scheme = null;
Expand All @@ -822,6 +899,11 @@ public Mutable clear()
return this;
}

/**
* Sets the path from a decoded (non-percent-encoded) string.
* @param path The decoded path to set.
* @return this mutable for chaining.
*/
public Mutable decodedPath(String path)
{
_uri = null;
Expand All @@ -840,6 +922,10 @@ public boolean equals(Object o)
return asString().equals(((HttpURI)o).asString());
}

/**
* @param fragment The fragment to set.
* @return this mutable for chaining.
*/
public Mutable fragment(String fragment)
{
_fragment = fragment;
Expand Down Expand Up @@ -935,6 +1021,11 @@ public int hashCode()
return asString().hashCode();
}

/**
* @param host The host to set.
* @return this mutable for chaining.
* @throws IllegalArgumentException if a relative path is set with an authority.
*/
public Mutable host(String host)
{
if (host != null && !isPathValidForAuthority(_path))
Expand Down Expand Up @@ -974,6 +1065,10 @@ public Collection<Violation> getViolations()
return _violations == null ? Collections.emptySet() : Collections.unmodifiableCollection(_violations);
}

/**
* Normalizes the URI by removing the default port if it matches the scheme's default port.
* @return this mutable for chaining.
*/
public Mutable normalize()
{
HttpScheme scheme = _scheme == null ? null : HttpScheme.CACHE.get(_scheme);
Expand All @@ -985,6 +1080,11 @@ public Mutable normalize()
return this;
}

/**
* Sets the path parameter, appending it to the path after a semicolon.
* @param param The path parameter to set.
* @return this mutable for chaining.
*/
public Mutable param(String param)
{
_param = param;
Expand Down Expand Up @@ -1035,6 +1135,12 @@ public Mutable path(String path)
return this;
}

/**
* Sets the path and query from a combined string in the form {@code path?query}.
* @param pathQuery The path and query string to set.
* @return this mutable for chaining.
* @throws IllegalArgumentException if a relative path is set with an authority.
*/
public Mutable pathQuery(String pathQuery)
{
if (hasAuthority() && !isPathValidForAuthority(pathQuery))
Expand All @@ -1052,25 +1158,41 @@ public Mutable pathQuery(String pathQuery)
return this;
}

/**
* @param port The port to set, or {@code -1} to clear the port.
* @return this mutable for chaining.
*/
public Mutable port(int port)
{
_port = (port > 0) ? port : URIUtil.UNDEFINED_PORT;
_uri = null;
return this;
}

/**
* @param query The query string to set.
* @return this mutable for chaining.
*/
public Mutable query(String query)
{
_query = query;
_uri = null;
return this;
}

/**
* @param scheme The {@link HttpScheme} to set.
* @return this mutable for chaining.
*/
public Mutable scheme(HttpScheme scheme)
{
return scheme(scheme.asString());
}

/**
* @param scheme The scheme to set, which will be normalized to lower-case.
* @return this mutable for chaining.
*/
public Mutable scheme(String scheme)
{
_scheme = URIUtil.normalizeScheme(scheme);
Expand All @@ -1084,6 +1206,11 @@ public String toString()
return asString();
}

/**
* Sets all URI components from another {@link HttpURI}.
* @param uri The URI to copy components from.
* @return this mutable for chaining.
*/
public Mutable uri(HttpURI uri)
{
_scheme = uri.getScheme();
Expand All @@ -1101,6 +1228,11 @@ public Mutable uri(HttpURI uri)
return this;
}

/**
* Parses and sets all URI components from a string.
* @param uri The URI string to parse.
* @return this mutable for chaining.
*/
public Mutable uri(String uri)
{
clear();
Expand All @@ -1109,6 +1241,12 @@ public Mutable uri(String uri)
return this;
}

/**
* Parses and sets URI components from a string, handling HTTP method-specific parsing.
* @param method The HTTP method, which affects parsing for CONNECT requests.
* @param uri The URI string to parse.
* @return this mutable for chaining.
*/
public Mutable uri(String method, String uri)
{
if (HttpMethod.CONNECT.is(method))
Expand All @@ -1129,6 +1267,13 @@ else if (uri.startsWith("/"))
return this;
}

/**
* Parses and sets all URI components from a substring.
* @param uri The string containing the URI.
* @param offset The offset within the string where the URI starts.
* @param length The length of the URI substring.
* @return this mutable for chaining.
*/
public Mutable uri(String uri, int offset, int length)
{
clear();
Expand All @@ -1138,6 +1283,11 @@ public Mutable uri(String uri, int offset, int length)
return this;
}

/**
* Sets the user info component of the URI authority.
* @param user The user info to set, or {@code null} to clear it.
* @return this mutable for chaining.
*/
public Mutable user(String user)
{
_user = user;
Expand Down