summaryrefslogtreecommitdiffstats
path: root/src/java/com/jogamp/common
diff options
context:
space:
mode:
authorSven Gothel <[email protected]>2014-09-10 07:21:03 +0200
committerSven Gothel <[email protected]>2014-09-10 07:21:03 +0200
commitc97e35377aea70cb293cabdd205bcc5da64b95c6 (patch)
tree52b4be4132fca18aff3067c199b9d6e9d54dfcac /src/java/com/jogamp/common
parent6a466e3f1e92a1e831ea61d1bb72c32f56b2a28d (diff)
Bug 1063: Uri: Refine API doc; Add create(Encoded ..) ; Provide common impl. for getNormalized(), getDirectory(), getParent() and getRelativeOf()
- Refine API doc - Add notion of {@code host} and {@code port} validation - Add create(Encoded ..), allowing creation of variants w/o re-encoding - Provide common impl. for getNormalized(), getDirectory(), getParent() and getRelativeOf() Above feature methods share common goals, hence use same implementation: - If opaque, cut-off query and merge after operation - cleanup path, i.e. /dummy/../test/ -> /test/ - cutoff file, dir - if requested - append optional appendix and cleanup again Return behavior various thought, i.e. null, this or allow exception. Enhanced test of above features.
Diffstat (limited to 'src/java/com/jogamp/common')
-rw-r--r--src/java/com/jogamp/common/net/Uri.java633
-rw-r--r--src/java/com/jogamp/common/net/UriQueryProps.java8
2 files changed, 482 insertions, 159 deletions
diff --git a/src/java/com/jogamp/common/net/Uri.java b/src/java/com/jogamp/common/net/Uri.java
index a2c8833..6bafba2 100644
--- a/src/java/com/jogamp/common/net/Uri.java
+++ b/src/java/com/jogamp/common/net/Uri.java
@@ -435,6 +435,8 @@ public class Uri {
/** See {@link String#lastIndexOf(String, int)}. */
public int lastIndexOf(final String str, final int fromIndex) { return s.lastIndexOf(str, fromIndex); }
+ /** See {@link String#startsWith(String)} */
+ public boolean startsWith(final String prefix) { return s.startsWith(prefix); }
/** See {@link String#startsWith(String, int)} */
public boolean startsWith(final String prefix, final int toffset) { return s.startsWith(prefix, toffset); }
/** See {@link String#endsWith(String)} */
@@ -631,14 +633,17 @@ public class Uri {
}
/**
- * Creates a new Uri instance using the given arguments.
+ * Creates a new Uri instance using the given unencoded arguments.
* <p>
- * This constructor first creates a temporary Uri string from the given components. This
+ * This constructor first creates a temporary Uri string from the given unencoded components. This
* string will be parsed later on to create the Uri instance.
* </p>
* <p>
* {@code [scheme:]scheme-specific-part[#fragment]}
* </p>
+ * <p>
+ * {@code host} and {@code port} <i>may</i> be undefined or invalid within {@code scheme-specific-part}.
+ * </p>
*
* @param scheme the unencoded scheme part of the Uri.
* @param ssp the unencoded scheme-specific-part of the Uri.
@@ -669,19 +674,66 @@ public class Uri {
}
/**
- * Creates a new Uri instance using the given arguments.
+ * Creates a new Uri instance using the given encoded arguments.
* <p>
- * This constructor first creates a temporary Uri string from the given components. This
+ * This constructor first creates a temporary Uri string from the given encoded components. This
+ * string will be parsed later on to create the Uri instance.
+ * </p>
+ * <p>
+ * The given encoded components are taken as-is, i.e. no re-encoding will be performed!
+ * However, Uri parsing will re-evaluate encoding of the resulting components.
+ * </p>
+ * <p>
+ * {@code [scheme:]scheme-specific-part[#fragment]}
+ * </p>
+ * <p>
+ * {@code host} and {@code port} <i>may</i> be undefined or invalid within {@code scheme-specific-part}.
+ * </p>
+ *
+ * @param scheme the encoded scheme part of the Uri.
+ * @param ssp the encoded scheme-specific-part of the Uri.
+ * @param fragment the encoded fragment part of the Uri.
+ * @throws URISyntaxException
+ * if the temporary created string doesn't fit to the
+ * specification RFC2396 or could not be parsed correctly.
+ */
+ public static Uri create(final Encoded scheme, final Encoded ssp, final Encoded fragment) throws URISyntaxException {
+ if ( emptyString(scheme) && emptyString(ssp) && emptyString(fragment) ) {
+ throw new URISyntaxException("", "all empty parts");
+ }
+ final StringBuilder uri = new StringBuilder();
+ if ( !emptyString(scheme) ) {
+ uri.append(scheme);
+ uri.append(SCHEME_SEPARATOR);
+ }
+ if ( !emptyString(ssp) ) {
+ uri.append(ssp.get());
+ }
+ if ( !emptyString(fragment) ) {
+ uri.append(FRAGMENT_SEPARATOR);
+ uri.append(fragment.get());
+ }
+ return new Uri(new Encoded(uri.toString()), false, 0);
+ }
+
+ /**
+ * Creates a new Uri instance using the given unencoded arguments.
+ * <p>
+ * This constructor first creates a temporary Uri string from the given unencoded components. This
* string will be parsed later on to create the Uri instance.
* </p>
* <p>
* {@code [scheme:][user-info@]host[:port][path][?query][#fragment]}
* </p>
+ * <p>
+ * {@code host} and {@code port} <i>must</i> be defined and valid, if any {@code authority} components are defined,
+ * i.e. {@code user-info}, {@code host} or {@code port}.
+ * </p>
*
* @param scheme the unencoded scheme part of the Uri.
- * @param userinfo the unencoded user information of the Uri for authentication and authorization.
- * @param host the unencoded host name of the Uri.
- * @param port the port number of the Uri.
+ * @param userinfo the unencoded user information of the Uri for authentication and authorization, {@code null} for undefined.
+ * @param host the unencoded host name of the Uri, {@code null} for undefined.
+ * @param port the port number of the Uri, -1 for undefined.
* @param path the unencoded path to the resource on the host.
* @param query the unencoded query part of the Uri to specify parameters for the resource.
* @param fragment the unencoded fragment part of the Uri.
@@ -751,14 +803,97 @@ public class Uri {
}
/**
- * Creates a new Uri instance using the given arguments.
+ * Creates a new Uri instance using the given encoded arguments.
* <p>
- * This constructor first creates a temporary Uri string from the given components. This
+ * This constructor first creates a temporary Uri string from the given encoded components. This
+ * string will be parsed later on to create the Uri instance.
+ * </p>
+ * <p>
+ * The given encoded components are taken as-is, i.e. no re-encoding will be performed!
+ * However, Uri parsing will re-evaluate encoding of the resulting components.
+ * </p>
+ * <p>
+ * {@code [scheme:][user-info@]host[:port][path][?query][#fragment]}
+ * </p>
+ * <p>
+ * {@code host} and {@code port} <i>must</i> be defined and valid, if any {@code authority} components are defined,
+ * i.e. {@code user-info}, {@code host} or {@code port}.
+ * </p>
+ *
+ * @param scheme the encoded scheme part of the Uri.
+ * @param userinfo the encoded user information of the Uri for authentication and authorization, {@code null} for undefined.
+ * @param host the encoded host name of the Uri, {@code null} for undefined.
+ * @param port the port number of the Uri, -1 for undefined.
+ * @param path the encoded path to the resource on the host.
+ * @param query the encoded query part of the Uri to specify parameters for the resource.
+ * @param fragment the encoded fragment part of the Uri.
+ * @throws URISyntaxException
+ * if the temporary created string doesn't fit to the
+ * specification RFC2396 or could not be parsed correctly.
+ */
+ public static Uri create (final Encoded scheme, final Encoded userinfo, final Encoded host, final int port,
+ final Encoded path, final Encoded query, final Encoded fragment) throws URISyntaxException {
+ if ( emptyString(scheme) && emptyString(userinfo) && emptyString(host) && emptyString(path) &&
+ emptyString(query) && emptyString(fragment) ) {
+ throw new URISyntaxException("", "all empty parts");
+ }
+
+ if ( !emptyString(scheme) && !emptyString(path) && path.length() > 0 && path.charAt(0) != '/') {
+ throw new URISyntaxException(path.get(), "path doesn't start with '/'");
+ }
+
+ final StringBuilder uri = new StringBuilder();
+ if ( !emptyString(scheme) ) {
+ uri.append(scheme);
+ uri.append(SCHEME_SEPARATOR);
+ }
+
+ if ( !emptyString(userinfo) || !emptyString(host) || port != -1) {
+ uri.append("//");
+ }
+
+ if ( !emptyString(userinfo) ) {
+ uri.append(userinfo.get());
+ uri.append('@');
+ }
+
+ if ( !emptyString(host) ) {
+ uri.append(host.get());
+ }
+
+ if ( port != -1 ) {
+ uri.append(SCHEME_SEPARATOR);
+ uri.append(port);
+ }
+
+ if ( !emptyString(path) ) {
+ uri.append(path.get());
+ }
+
+ if ( !emptyString(query) ) {
+ uri.append(QUERY_SEPARATOR);
+ uri.append(query.get());
+ }
+
+ if ( !emptyString(fragment) ) {
+ uri.append(FRAGMENT_SEPARATOR);
+ uri.append(fragment.get());
+ }
+ return new Uri(new Encoded(uri.toString()), true, 0);
+ }
+
+ /**
+ * Creates a new Uri instance using the given unencoded arguments.
+ * <p>
+ * This constructor first creates a temporary Uri string from the given unencoded components. This
* string will be parsed later on to create the Uri instance.
* </p>
* <p>
* {@code [scheme:]host[path][#fragment]}
* </p>
+ * <p>
+ * {@code host} <i>must</i> be valid, if defined.
+ * </p>
*
* @param scheme the unencoded scheme part of the Uri.
* @param host the unencoded host name of the Uri.
@@ -773,14 +908,46 @@ public class Uri {
}
/**
- * Creates a new Uri instance using the given arguments.
+ * Creates a new Uri instance using the given encoded arguments.
* <p>
- * This constructor first creates a temporary Uri string from the given components. This
+ * This constructor first creates a temporary Uri string from the given encoded components. This
+ * string will be parsed later on to create the Uri instance.
+ * </p>
+ * <p>
+ * The given encoded components are taken as-is, i.e. no re-encoding will be performed!
+ * However, Uri parsing will re-evaluate encoding of the resulting components.
+ * </p>
+ * <p>
+ * {@code [scheme:]host[path][#fragment]}
+ * </p>
+ * <p>
+ * {@code host} <i>must</i> be valid, if defined.
+ * </p>
+ *
+ * @param scheme the encoded scheme part of the Uri.
+ * @param host the encoded host name of the Uri.
+ * @param path the encoded path to the resource on the host.
+ * @param fragment the encoded fragment part of the Uri.
+ * @throws URISyntaxException
+ * if the temporary created string doesn't fit to the
+ * specification RFC2396 or could not be parsed correctly.
+ */
+ public static Uri create(final Encoded scheme, final Encoded host, final Encoded path, final Encoded fragment) throws URISyntaxException {
+ return create(scheme, null, host, -1, path, null, fragment);
+ }
+
+ /**
+ * Creates a new Uri instance using the given unencoded arguments.
+ * <p>
+ * This constructor first creates a temporary Uri string from the given unencoded components. This
* string will be parsed later on to create the Uri instance.
* </p>
* <p>
* {@code [scheme:][//authority][path][?query][#fragment]}
* </p>
+ * <p>
+ * {@code host} and {@code port} <i>may</i> be undefined or invalid, in the optional {@code authority}.
+ * </p>
*
* @param scheme the unencoded scheme part of the Uri.
* @param authority the unencoded authority part of the Uri.
@@ -830,6 +997,66 @@ public class Uri {
}
/**
+ * Creates a new Uri instance using the given encoded arguments.
+ * <p>
+ * This constructor first creates a temporary Uri string from the given encoded encoded components. This
+ * string will be parsed later on to create the Uri instance.
+ * </p>
+ * <p>
+ * The given encoded components are taken as-is, i.e. no re-encoding will be performed!
+ * However, Uri parsing will re-evaluate encoding of the resulting components.
+ * </p>
+ * <p>
+ * {@code [scheme:][//authority][path][?query][#fragment]}
+ * </p>
+ * <p>
+ * {@code host} and {@code port} <i>may</i> be undefined or invalid, in the optional {@code authority}.
+ * </p>
+ *
+ * @param scheme the encoded scheme part of the Uri.
+ * @param authority the encoded authority part of the Uri.
+ * @param path the encoded path to the resource on the host.
+ * @param query the encoded query part of the Uri to specify parameters for the resource.
+ * @param fragment the encoded fragment part of the Uri.
+ *
+ * @throws URISyntaxException
+ * if the temporary created string doesn't fit to the
+ * specification RFC2396 or could not be parsed correctly.
+ */
+ public static Uri create(final Encoded scheme, final Encoded authority, final Encoded path, final Encoded query, final Encoded fragment) throws URISyntaxException {
+ if ( emptyString(scheme) && emptyString(authority) && emptyString(path) &&
+ emptyString(query) && emptyString(fragment) ) {
+ throw new URISyntaxException("", "all empty parts");
+ }
+ if ( !emptyString(scheme) && !emptyString(path) && path.length() > 0 && path.charAt(0) != '/') {
+ throw new URISyntaxException(path.get(), "path doesn't start with '/'");
+ }
+
+ final StringBuilder uri = new StringBuilder();
+ if ( !emptyString(scheme) ) {
+ uri.append(scheme);
+ uri.append(SCHEME_SEPARATOR);
+ }
+ if ( !emptyString(authority) ) {
+ uri.append("//");
+ uri.append(authority.get());
+ }
+
+ if ( !emptyString(path) ) {
+ uri.append(path.get());
+ }
+ if ( !emptyString(query) ) {
+ uri.append(QUERY_SEPARATOR);
+ uri.append(query.get());
+ }
+ if ( !emptyString(fragment) ) {
+ uri.append(FRAGMENT_SEPARATOR);
+ uri.append(fragment.get());
+ }
+ return new Uri(new Encoded(uri.toString()), false, 0);
+ }
+
+ /**
* Casts the given encoded String to a {@link Encoded#cast(String) new Encoded instance}
* used to create the resulting Uri instance via {@link #Uri(Encoded)}.
* <p>
@@ -851,7 +1078,7 @@ public class Uri {
* {@code file:path}
* </p>
*
- * @param path the path of the {@code file} {@code schema}.
+ * @param path the unencoded path of the {@code file} {@code schema}.
* @throws URISyntaxException
* if the temporary created string doesn't fit to the
* specification RFC2396 or could not be parsed correctly.
@@ -913,13 +1140,9 @@ public class Uri {
// opaque, without host validation.
// Note: This may induce encoding errors of authority and path, see {@link #PARSE_HINT_FIX_PATH}
return new Uri(new Encoded( uri.toString() ), false, 0);
- } else if( null != uri.getHost() ) {
- // with host validation
- return Uri.create(uri.getScheme(), uri.getUserInfo(), uri.getHost(), uri.getPort(),
- uri.getPath(), uri.getQuery(), uri.getFragment());
} else {
- // without host validation
- return Uri.create(uri.getScheme(), uri.getAuthority(),
+ // with host validation if authority is defined
+ return Uri.create(uri.getScheme(), uri.getUserInfo(), uri.getHost(), uri.getPort(),
uri.getPath(), uri.getQuery(), uri.getFragment());
}
}
@@ -1113,7 +1336,7 @@ public class Uri {
* </p>
*/
public final File toFile() {
- if( isFileScheme() ) {
+ if( isFileScheme() && !emptyString(path) ) {
final String authorityS;
if( null == authority ) {
authorityS = "";
@@ -1154,10 +1377,10 @@ public class Uri {
* Returned Uri: <code><i>scheme2</i>:/some/path/gluegen-rt.jar#fragment</code>
*
* Example 3:
- * This instance: <code>scheme1:<i>scheme2</i>:/some/path/gluegen-rt.jar?lala=01#fragment</code>
+ * This instance: <code>scheme1:<i>scheme2</i>:/some/path/gluegen-rt.jar!/?lala=01#fragment</code>
* Returned Uri: <code><i>scheme2</i>:/some/path/gluegen-rt.jar?lala=01#fragment</code>
* </pre>
- * @throws URISyntaxException
+ * @throws URISyntaxException if this Uri is a container Uri and does not comply with the container spec, i.e. a JAR Uri
*/
public final Uri getContainedUri() throws URISyntaxException {
if( !emptyString(schemeSpecificPart) ) {
@@ -1185,6 +1408,7 @@ public class Uri {
} catch(final URISyntaxException e) {
// OK, does not contain uri
if( DEBUG ) {
+ System.err.println("Caught "+e.getClass().getSimpleName()+": "+e.getMessage());
e.printStackTrace();
}
}
@@ -1192,184 +1416,283 @@ public class Uri {
return null;
}
+ private static final boolean cutoffLastPathSegementImpl(final StringBuilder pathBuf,
+ final boolean cutoffFile,
+ final boolean cutoffDir,
+ final Encoded appendPath) throws URISyntaxException {
+ final boolean cleaned;
+ {// clean-up existing path
+ final String pathS = pathBuf.toString();
+ if( 0 > pathS.indexOf("/") && emptyString(appendPath) ) {
+ return false; // nothing to cut-off
+ }
+ pathBuf.setLength(0);
+ pathBuf.append( IOUtil.cleanPathString( pathS ) );
+ cleaned = pathBuf.length() != pathS.length();
+ }
+
+ {// cut-off file or last dir-segment
+ final String pathS = pathBuf.toString();
+ final int jarSepIdx = pathS.lastIndexOf(JAR_SCHEME_SEPARATOR);
+ final int e = pathS.lastIndexOf("/");
+ if( 0 > jarSepIdx || e - 1 > jarSepIdx ) { // stop at jar-separator '!/', if exist
+ if( cutoffFile && e < pathS.length() - 1 ) {
+ // cut-off file
+ pathBuf.setLength(0);
+ pathBuf.append( pathS.substring(0, e+1) );
+ } else if( cutoffDir ) {
+ // cut-off dir-segment
+ final int p = pathS.lastIndexOf("/", e-1);
+ if( p >= 0 ) {
+ pathBuf.setLength(0);
+ pathBuf.append( pathS.substring(0, p+1) );
+ } // else keep
+ } // else keep
+ }
+ final boolean cutoff = pathBuf.length() != pathS.length();
+ if( !cutoff && ( cutoffDir || !cleaned ) && emptyString(appendPath) ) {
+ return false; // no modifications!
+ }
+ }
+ if( !emptyString(appendPath) ) {
+ pathBuf.append(appendPath.get());
+ // 2nd round of cleaning!
+ final String pathS = pathBuf.toString();
+ pathBuf.setLength(0);
+ pathBuf.append( IOUtil.cleanPathString( pathS ) );
+ }
+ return true; // continue processing w/ buffer
+ }
+ private final Uri cutoffLastPathSegementImpl(final boolean cutoffFile, final boolean cutoffDir, final Encoded appendPath) throws URISyntaxException {
+ if( opaque ) {
+ if( emptyString(schemeSpecificPart) ) {
+ // nothing to cut-off
+ if( !emptyString(appendPath) ) {
+ return Uri.create(scheme, appendPath, fragment);
+ } else {
+ return null;
+ }
+ }
+ final StringBuilder sspBuf = new StringBuilder(); // without path!
+
+ // save optional query in scheme-specific-part
+ final Encoded queryTemp;
+ final int queryI = schemeSpecificPart.lastIndexOf(QUERY_SEPARATOR);
+ if( queryI >= 0 ) {
+ queryTemp = schemeSpecificPart.substring(queryI+1);
+ sspBuf.append( schemeSpecificPart.substring(0, queryI).get() );
+ } else {
+ queryTemp = null;
+ sspBuf.append( schemeSpecificPart.get() );
+ }
+
+ if( !cutoffLastPathSegementImpl(sspBuf, cutoffFile, cutoffDir, appendPath) ) {
+ return null; // no modifications
+ }
+
+ if ( !emptyString(queryTemp) ) {
+ sspBuf.append(QUERY_SEPARATOR);
+ sspBuf.append( queryTemp.get() );
+ }
+
+ // without host validation if authority is defined
+ return Uri.create(scheme, new Encoded(sspBuf.toString()), fragment);
+ } else {
+ if( emptyString(path) ) {
+ return null; // nothing to cut-off
+ }
+ final StringBuilder pathBuf = new StringBuilder();
+ pathBuf.append( path.get() );
+
+ if( !cutoffLastPathSegementImpl(pathBuf, cutoffFile, cutoffDir, appendPath) ) {
+ return null; // no modifications
+ }
+
+ // with host validation if authority is defined
+ return Uri.create(scheme, userInfo, host, port, new Encoded(pathBuf.toString()), query, fragment);
+ }
+ }
/**
- * Return a new Uri instance representing the parent path of this Uri,
- * while cutting of optional {@code query} and {@code fragment} parts.
- * <p>
- * Method is {@code jar-file-entry} aware, i.e. will return the parent entry if exists.
- * </p>
+ * {@link IOUtil#cleanPathString(String) Normalizes} this Uri's path and return the
+ * {@link IOUtil#cleanPathString(String) normalized} form if it differs, otherwise {@code this} instance.
* <p>
- * If this Uri does not contain any path separator, or a parent folder Uri cannot be found, method returns {@code null}.
- * </p>
* <pre>
* Example-1:
- * This instance : <code>jar:http://some/path/gluegen-rt.jar!/com/jogamp/common/GlueGenVersion.class</code>
- * Returned Uri #1: <code>jar:http://some/path/gluegen-rt.jar!/com/jogamp/common/</code>
- * Returned Uri #2: <code>jar:http://some/path/gluegen-rt.jar!/com/jogamp/</code>
+ * This instance : <code>jar:http://some/path/../gluegen-rt.jar!/com/Test.class?arg=1#frag</code>
+ * Normalized : <code>jar:http://some/gluegen-rt.jar!/com/Test.class?arg=1#frag</code>
*
* Example-2:
- * This instance : <code>http://some/path/gluegen-rt.jar</code>
- * Returned Uri #1: <code>http://some/path/</code>
- * Returned Uri #2: <code>http://some/</code>
+ * This instance : <code>http://some/path/../gluegen-rt.jar?arg=1#frag</code>
+ * Normalized : <code>http://some/gluegen-rt.jar?arg=1#frag</code>
* </pre>
+ * </p>
*/
- public final Uri getParent() {
- final int pl = null!=schemeSpecificPart? schemeSpecificPart.length() : 0;
- if(pl != 0) {
- final int e = schemeSpecificPart.lastIndexOf("/");
- if( e > 0 ) { // 0 == e: no path
- if( e < pl - 1 ) {
- // path is file or has a query
- try {
- return new Uri( new Encoded( scheme.get()+SCHEME_SEPARATOR+schemeSpecificPart.get().substring(0, e+1) ) );
- } catch (final URISyntaxException ue) {
- // not complete, hence removed authority, or even root folder -> return null
- }
- }
- // path is a directory ..
- final int p = schemeSpecificPart.lastIndexOf("/", e-1);
- if( p > 0 ) {
- try {
- return new Uri( new Encoded( scheme.get()+SCHEME_SEPARATOR+schemeSpecificPart.get().substring(0, p+1) ) );
- } catch (final URISyntaxException ue) {
- // not complete, hence removed authority, or even root folder -> return null
- }
- }
+ public final Uri getNormalized() {
+ try {
+ final Uri res = cutoffLastPathSegementImpl(false, false, null);
+ return null != res ? res : this;
+ } catch (final URISyntaxException e) {
+ if( DEBUG ) {
+ System.err.println("Caught "+e.getClass().getSimpleName()+": "+e.getMessage());
+ e.printStackTrace();
}
- }
- return null;
- }
-
- /**
- * Concatenates the given encoded string to the {@link #getEncoded() encoded uri}
- * of this instance and returns {@link #Uri(Encoded) a new Uri instance} with the result.
- *
- * @throws URISyntaxException
- * if the concatenated string {@code uri} doesn't fit to the
- * specification RFC2396 and RFC3986 or could not be parsed correctly.
- */
- public final Uri concat(final Uri.Encoded suffix) throws URISyntaxException {
- if( null == suffix ) {
return this;
- } else {
- return new Uri( input.concat(suffix) );
}
}
/**
- * Returns a new Uri instance w/ the given new query {@code newQuery}.
+ * Returns this Uri's directory Uri.
+ * <p>
+ * This Uri path will be {@link IOUtil#cleanPathString(String) normalized} before returning the directory.
+ * </p>
+ * <p>
+ * If this Uri's directory cannot be found, or already denotes a directory, method returns {@code this} instance.
+ * </p>
+ * <p>
+ * <pre>
+ * Example-1:
+ * this-uri: http:/some/path/gluegen-rt.jar?arg=1#frag
+ * result: http:/some/path/?arg=1#frag
*
- * @throws URISyntaxException if this Uri is {@link #opaque}
- * or if the new string {@code uri} doesn't fit to the
- * specification RFC2396 and RFC3986 or could not be parsed correctly.
+ * Example-2:
+ * this-uri: file:/some/path/
+ * result: file:/some/path/
+ *
+ * Example-3:
+ * this-uri: file:/some/path/lala/lili/../../hello.txt
+ * result: file:/some/path/
+ * </pre>
+ * </p>
+ * @throws URISyntaxException if the new string {@code uri} doesn't fit to the
+ * specification RFC2396 and RFC3986 or could not be parsed correctly.
*/
- public final Uri getNewQuery(final String newQuery) throws URISyntaxException {
- if( opaque ) {
- throw new URISyntaxException(input.decode(), "Opaque Uri cannot permute by query");
- } else if( null != host ) {
- // with host validation
- return Uri.create(decode(scheme), decode(userInfo), decode(host), port,
- decode(path), newQuery, decode(fragment));
- } else {
- // without host validation
- return Uri.create(decode(scheme), decode(authority),
- decode(path), newQuery, decode(fragment));
+ public Uri getDirectory() {
+ try {
+ final Uri res = cutoffLastPathSegementImpl(true, false, null);
+ return null != res ? res : this;
+ } catch (final URISyntaxException e) {
+ if( DEBUG ) {
+ System.err.println("Caught "+e.getClass().getSimpleName()+": "+e.getMessage());
+ e.printStackTrace();
+ }
+ return this;
}
}
- /// NEW START
-
/**
- * The URI's <code><i>protocol</i>:/some/path/gluegen-rt.jar</code>
- * parent dirname URI <code><i>protocol</i>:/some/path/</code> will be returned,
- * or {@code null} if not applicable.
+ * Returns this Uri's parent directory Uri..
* <p>
- * <i>protocol</i> may be "file", "http", etc..
+ * This Uri path will be {@link IOUtil#cleanPathString(String) normalized} before traversing up one directory.
* </p>
+ * <p>
+ * If a parent folder cannot be found, method returns {@code null}.
+ * </p>
+ * <p>
+ * <pre>
+ * Example-1:
+ * This instance : <code>jar:http://some/path/gluegen-rt.jar!/com/Test.class?arg=1#frag</code>
+ * Returned Uri #1: <code>jar:http://some/path/gluegen-rt.jar!/com/?arg=1#frag</code>
+ * Returned Uri #2: <code>jar:http://some/path/gluegen-rt.jar!/?arg=1#frag</code>
+ * Returned Uri #3: <code>null</code>
*
- * @return "<i>protocol</i>:/some/path/"
- * @throws IllegalArgumentException if the URI doesn't match the expected formatting, or is null
- * @throws URISyntaxException
+ * Example-2:
+ * This instance : <code>http://some/path/gluegen-rt.jar?arg=1#frag</code>
+ * Returned Uri #1: <code>http://some/path/?arg=1#frag</code>
+ * Returned Uri #2: <code>http://some/?arg=1#frag</code>
+ * Returned Uri #2: <code>null</code>
+ *
+ * Example-3:
+ * This instance : <code>http://some/path/../gluegen-rt.jar?arg=1#frag</code>
+ * Returned Uri #1: <code>http://some/?arg=1#frag</code>
+ * Returned Uri #2: <code>null</code>
+ * </pre>
+ * </p>
*/
- public Uri getDirectory() throws URISyntaxException {
- final String uriS = input.get();
-
- // from
- // file:/some/path/gluegen-rt.jar _or_ rsrc:gluegen-rt.jar
- // to
- // file:/some/path/ _or_ rsrc:
- int idx = uriS.lastIndexOf('/');
- if(0 > idx) {
- // no abs-path, check for protocol terminator ':'
- idx = uriS.lastIndexOf(':');
- if(0 > idx) {
- throw new URISyntaxException(input.get(), "no scheme terminator ':'");
- }
- }
+ public final Uri getParent() {
try {
- return Uri.cast(uriS.substring(0, idx+1)); // exclude jar name, include terminal '/' or ':'
- } catch (final URISyntaxException ue) {
+ return cutoffLastPathSegementImpl(true, true, null);
+ } catch (final URISyntaxException e) {
if( DEBUG ) {
- ue.printStackTrace();
+ System.err.println("Caught "+e.getClass().getSimpleName()+": "+e.getMessage());
+ e.printStackTrace();
}
+ return null;
}
- return null;
}
/**
- * Generates a URI for the <i>relativePath</i> relative to the <i>baseURI</i>,
- * hence the result is a absolute location.
+ * Returns a new Uri appending the given {@code appendPath}
+ * to this instance's {@link #getDirectory() directory}.
* <p>
- * Impl. operates on the <i>scheme-specific-part</i>, and hence is sub-protocol savvy.
+ * If {@code appendPath} is empty, method behaves like {@link #getNormalized()}.
* </p>
* <p>
- * In case <i>baseURI</i> is not a path ending w/ '/', it's a assumed to be a file and it's parent is being used.
+ * This resulting path will be {@link IOUtil#cleanPathString(String) normalized}.
* </p>
+ * <p>
+ * <pre>
+ * Example-1:
+ * append: null
+ * this-uri: http:/some/path/gluegen-rt.jar
+ * result: http:/some/path/gluegen-rt.jar
*
- * @param baseURI denotes a URI to a directory ending w/ '/', or a file. In the latter case the file's directory is being used.
- * @param relativePath denotes a relative file to the baseLocation's parent directory (URI encoded)
- * @throws URISyntaxException if path is empty or has no parent directory available while resolving <code>../</code>
+ * Example-2:
+ * append: test.txt
+ * this-uri: file:/some/path/gluegen-rt.jar
+ * result: file:/some/path/test.txt
+ *
+ * Example-3:
+ * append: test.txt
+ * this-uri: file:/some/path/lala/lili/../../hello.txt
+ * result: file:/some/path/test.txt
+ * </pre>
+ * </p>
+ *
+ * @param appendPath denotes a relative path to be appended to this Uri's directory
+ * @throws URISyntaxException
+ * if the resulting {@code uri} doesn't fit to the
+ * specification RFC2396 and RFC3986 or could not be parsed correctly.
*/
- public Uri getRelativeOf(final Encoded relativePath) throws URISyntaxException {
- return compose(scheme, schemeSpecificPart, relativePath, fragment);
+ public Uri getRelativeOf(final Encoded appendPath) throws URISyntaxException {
+ if( emptyString(appendPath) ) {
+ return getNormalized();
+ } else {
+ return cutoffLastPathSegementImpl(true, false, appendPath);
+ }
}
- static Uri compose(final Encoded scheme, final Encoded schemeSpecificPart, final Encoded relativePath, final Encoded fragment) throws URISyntaxException {
- String schemeSpecificPartS = schemeSpecificPart.get();
-
- // cut off optional query in scheme-specific-part
- final String query;
- final int queryI = schemeSpecificPartS.lastIndexOf(QUERY_SEPARATOR);
- if( queryI >= 0 ) {
- query = schemeSpecificPartS.substring(queryI+1);
- schemeSpecificPartS = schemeSpecificPartS.substring(0, queryI);
+ /**
+ * Concatenates the given encoded string to the {@link #getEncoded() encoded uri}
+ * of this instance and returns {@link #Uri(Encoded) a new Uri instance} with the result.
+ *
+ * @throws URISyntaxException
+ * if the concatenated string {@code uri} doesn't fit to the
+ * specification RFC2396 and RFC3986 or could not be parsed correctly.
+ */
+ public final Uri concat(final Encoded suffix) throws URISyntaxException {
+ if( null == suffix ) {
+ return this;
} else {
- query = null;
- }
- if( null != relativePath ) {
- if( !schemeSpecificPartS.endsWith("/") ) {
- schemeSpecificPartS = IOUtil.getParentOf(schemeSpecificPartS);
- }
- schemeSpecificPartS = schemeSpecificPartS + relativePath.get();
- }
- schemeSpecificPartS = IOUtil.cleanPathString( schemeSpecificPartS );
- final StringBuilder uri = new StringBuilder();
- uri.append(scheme.get());
- uri.append(':');
- uri.append(schemeSpecificPartS);
- if ( null != query ) {
- uri.append(QUERY_SEPARATOR);
- uri.append(query);
- }
- if ( null != fragment ) {
- uri.append(FRAGMENT_SEPARATOR);
- uri.append(fragment.get());
+ return new Uri( input.concat(suffix) );
}
- return Uri.cast(uri.toString());
}
- /// NEW END
+ /**
+ * Returns a new Uri instance w/ the given new query {@code newQuery}.
+ *
+ * @throws URISyntaxException if this Uri is {@link #opaque}
+ * or if the new string {@code uri} doesn't fit to the
+ * specification RFC2396 and RFC3986 or could not be parsed correctly.
+ */
+ public final Uri getNewQuery(final Encoded newQuery) throws URISyntaxException {
+ if( opaque ) {
+ throw new URISyntaxException(input.decode(), "Opaque Uri cannot permute by query");
+ } else {
+ // with host validation if authority is defined
+ return Uri.create(scheme, userInfo, host, port, path, newQuery, fragment);
+ }
+ }
/**
* {@inheritDoc}
diff --git a/src/java/com/jogamp/common/net/UriQueryProps.java b/src/java/com/jogamp/common/net/UriQueryProps.java
index 8d9bcb4..a93a1eb 100644
--- a/src/java/com/jogamp/common/net/UriQueryProps.java
+++ b/src/java/com/jogamp/common/net/UriQueryProps.java
@@ -63,14 +63,14 @@ public class UriQueryProps {
public final Map<String, String> getProperties() { return properties; }
public final char getQuerySeparator() { return query_separator.charAt(0); }
- public final String appendQuery(String baseQuery) {
+ public final Uri.Encoded appendQuery(Uri.Encoded baseQuery) {
boolean needsSep = false;
final StringBuilder sb = new StringBuilder();
if ( null != baseQuery ) {
if( baseQuery.startsWith(QMARK) ) {
baseQuery = baseQuery.substring(1); // cut off '?'
}
- sb.append(baseQuery);
+ sb.append(baseQuery.get());
if( !baseQuery.endsWith(query_separator) ) {
needsSep = true;
}
@@ -87,11 +87,11 @@ public class UriQueryProps {
}
needsSep = true;
}
- return sb.toString();
+ return new Uri.Encoded(sb.toString(), Uri.QUERY_LEGAL);
}
public final Uri appendQuery(final Uri base) throws URISyntaxException {
- return base.getNewQuery( appendQuery( Uri.decode(base.query) ) );
+ return base.getNewQuery( appendQuery( base.query ) );
}
/**