From 5205e47e8a2e84e793b26305391b1c4f8648597c Mon Sep 17 00:00:00 2001
From: Sven Gothel <sgothel@jausoft.com>
Date: Sun, 7 Sep 2014 10:20:12 +0200
Subject: Bug 1063 - Utilize own Uri and Uri.Encoded class w/ proper encoding
 and differentiating encoded/decoded variants by type [Part 1]

- Add immutable com.jogamp.common.net.Uri class impl. RFC 2396 w/ encoding of RFC 3986

- Class Uri.Encoded is used to represent encoded parts

- IOUtil: Remove unused methods (residing in Uri) and mark others deprecated (will move to Uri)

- Adopt usage of Uri: This must be completet in follow-up commits!
---
 src/java/com/jogamp/common/net/Uri.java | 2085 +++++++++++++++++++++++++++++++
 1 file changed, 2085 insertions(+)
 create mode 100644 src/java/com/jogamp/common/net/Uri.java

(limited to 'src/java/com/jogamp/common/net/Uri.java')

diff --git a/src/java/com/jogamp/common/net/Uri.java b/src/java/com/jogamp/common/net/Uri.java
new file mode 100644
index 0000000..b116cb4
--- /dev/null
+++ b/src/java/com/jogamp/common/net/Uri.java
@@ -0,0 +1,2085 @@
+/**
+ * Copyright 2013 JogAmp Community. All rights reserved.
+ *
+ * This code is derived from the Apache Harmony project's {@code class java.net.URI.Helper},
+ * copyright 2006, 2010 The Apache Software Foundation (http://www.apache.org/).
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the LICENSE.txt file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.jogamp.common.net;
+
+import java.io.File;
+import java.io.UnsupportedEncodingException;
+import java.net.MalformedURLException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.util.StringTokenizer;
+import java.util.regex.Pattern;
+
+import com.jogamp.common.util.IOUtil;
+
+/**
+ * This class implements an immutable Uri as defined by <a href="https://tools.ietf.org/html/rfc2396">RFC 2396</a>.
+ * <p>
+ * Character encoding is employed as defined by <a href="https://tools.ietf.org/html/rfc3986">RFC 3986</a>,
+ * see <a href="https://tools.ietf.org/html/rfc3986#section-2.1">RFC 3986 section 2.1</a>,
+ * while multibyte unicode characters are preserved in encoded parts.
+ * </p>
+ *
+ * <pre>
+     1 [scheme:]scheme-specific-part[#fragment]
+     2 [scheme:][//authority]path[?query][#fragment]
+     3 [scheme:][//[user-info@]host[:port]]path[?query][#fragment]
+
+        scheme-specific-part: [//authority]path[?query]
+        authority:            [user-info@]host[:port]
+ * </pre>
+ * <p>
+ * <a href="https://tools.ietf.org/html/rfc3986#section-2.2">RFC 3986 section 2.2</a> <i>Reserved Characters</i> (January 2005)
+ * <table border="1">
+    <tr>
+    <td><code>!</code></td>
+    <td><code>*</code></td>
+    <td><code>'</code></td>
+    <td><code>(</code></td>
+    <td><code>)</code></td>
+    <td><code>;</code></td>
+    <td><code>:</code></td>
+    <td><code>@</code></td>
+    <td><code>&amp;</code></td>
+    <td><code>=</code></td>
+    <td><code>+</code></td>
+    <td><code>$</code></td>
+    <td><code>,</code></td>
+    <td><code>/</code></td>
+    <td><code>?</code></td>
+    <td><code>#</code></td>
+    <td><code>[</code></td>
+    <td><code>]</code></td>
+    </tr>
+ * </table>
+ * </p>
+ * <p>
+ * <a href="https://tools.ietf.org/html/rfc3986#section-2.3">RFC 3986 section 2.3</a> <i>Unreserved Characters</i> (January 2005)
+ * <table border="1">
+    <tr>
+    <td><code>A</code></td>
+    <td><code>B</code></td>
+    <td><code>C</code></td>
+    <td><code>D</code></td>
+    <td><code>E</code></td>
+    <td><code>F</code></td>
+    <td><code>G</code></td>
+    <td><code>H</code></td>
+    <td><code>I</code></td>
+    <td><code>J</code></td>
+    <td><code>K</code></td>
+    <td><code>L</code></td>
+    <td><code>M</code></td>
+    <td><code>N</code></td>
+    <td><code>O</code></td>
+    <td><code>P</code></td>
+    <td><code>Q</code></td>
+    <td><code>R</code></td>
+    <td><code>S</code></td>
+    <td><code>T</code></td>
+    <td><code>U</code></td>
+    <td><code>V</code></td>
+    <td><code>W</code></td>
+    <td><code>X</code></td>
+    <td><code>Y</code></td>
+    <td><code>Z</code></td>
+    </tr>
+    <tr>
+    <td><code>a</code></td>
+    <td><code>b</code></td>
+    <td><code>c</code></td>
+    <td><code>d</code></td>
+    <td><code>e</code></td>
+    <td><code>f</code></td>
+    <td><code>g</code></td>
+    <td><code>h</code></td>
+    <td><code>i</code></td>
+    <td><code>j</code></td>
+    <td><code>k</code></td>
+    <td><code>l</code></td>
+    <td><code>m</code></td>
+    <td><code>n</code></td>
+    <td><code>o</code></td>
+    <td><code>p</code></td>
+    <td><code>q</code></td>
+    <td><code>r</code></td>
+    <td><code>s</code></td>
+    <td><code>t</code></td>
+    <td><code>u</code></td>
+    <td><code>v</code></td>
+    <td><code>w</code></td>
+    <td><code>x</code></td>
+    <td><code>y</code></td>
+    <td><code>z</code></td>
+    </tr>
+    <tr>
+    <td><code>0</code></td>
+    <td><code>1</code></td>
+    <td><code>2</code></td>
+    <td><code>3</code></td>
+    <td><code>4</code></td>
+    <td><code>5</code></td>
+    <td><code>6</code></td>
+    <td><code>7</code></td>
+    <td><code>8</code></td>
+    <td><code>9</code></td>
+    <td><code>-</code></td>
+    <td><code>_</code></td>
+    <td><code>.</code></td>
+    <td><code>~</code></td>
+    </tr>
+ * </table>
+ * </p>
+ * <p>
+ * Other characters in a Uri must be percent encoded.
+ * </p>
+ * @since 2.2.1
+ */
+public class Uri {
+    private static final String DIGITS = "0123456789ABCDEF";
+
+    private static final String ENCODING = "UTF8";
+    private static final String MSG_ENCODING_NA = "Charset UTF8 not available";
+    private static final Pattern patternSingleFS = Pattern.compile("/{1}");
+
+    /**
+     * RFC 3986 section 2.3 Unreserved Characters (January 2005)
+     * <p>
+     * {@value} + {@code alphanum}
+     * </p>
+     */
+    public static final String UNRESERVED = "_-.~"; // Harmony: "_-!.~\'()*"
+
+    private static final String punct = ",;:$&+=";
+
+    /**
+     * RFC 3986 section 2.2 Reserved Characters (January 2005)
+     * <p>
+     * {@value} + {@code alphanum}
+     * </p>
+     */
+    public static final String RESERVED = punct + "!*\'()@/?#[]"; // Harmony: punct + "?/[]@";
+
+    public static final String RESERVED_2 = punct + "!*\'()@/?[]"; // Harmony: punct + "?/[]@";
+
+    // Bug 908, issues w/ windows file path char: $ ^ ~ # [ ]
+
+    /**
+     * Valid charset for RFC 2396 {@code authority}'s {@code user-info},
+     * additional to legal {@code alphanum} characters.
+     * <p>
+     * {@value} + {@code alphanum}
+     * </p>
+     */
+    public static final String USERINFO_LEGAL = UNRESERVED + punct;
+
+    /**
+     * Valid charset for RFC 2396 {@code authority},
+     * additional to legal {@code alphanum} characters.
+     * <p>
+     * {@value} + {@code alphanum}
+     * </p>
+     */
+    public static final String AUTHORITY_LEGAL = "@[]" + USERINFO_LEGAL;
+
+    /**
+     * Valid charset for RFC 2396 {@code path},
+     * additional to legal {@code alphanum} characters.
+     * <p>
+     * {@value} + {@code alphanum}
+     * </p>
+     */
+    public static final String PATH_LEGAL = "/!" + USERINFO_LEGAL;
+    // @ is reserved; Harmony: "/@" + SOME_LEGAL, '!' is re-added, Harmony had it in UNRESERVED
+
+    /**
+     * Reduced valid charset for RFC 2396 {@code path},
+     * additional to legal {@code alphanum} characters.
+     * <p>
+     * Excluding special native filesystem characters to be encoded.
+     * </p>
+     * <p>
+     * {@value} + {@code alphanum}
+     * </p>
+     */
+    public static final String PATH_MIN_LEGAL = "/!_-."; // "/!" + ( unreserved - '~' )
+
+    /**
+     * Valid charset for RFC 2396 {@code query},
+     * additional to legal {@code alphanum} characters.
+     * <p>
+     * {@value} + {@code alphanum}
+     * </p>
+     */
+    public static final String QUERY_LEGAL = UNRESERVED + RESERVED_2 + "\\\"";
+
+    /**
+     * Valid charset for RFC 2396 {@code scheme-specific-part},
+     * additional to legal {@code alphanum} characters.
+     * <p>
+     * {@value} + {@code alphanum}
+     * </p>
+     */
+    public static final String SSP_LEGAL = QUERY_LEGAL;
+
+    /**
+     * Valid charset for RFC 2396 {@code fragment},
+     * additional to legal {@code alphanum} characters.
+     * <p>
+     * {@value} + {@code alphanum}
+     * </p>
+     */
+    public static final String FRAG_LEGAL = UNRESERVED + RESERVED;
+
+    /**
+     * Immutable RFC3986 encoded string.
+     */
+    public static class Encoded implements Comparable<Encoded>, CharSequence {
+        public static final Encoded EMPTY = new Encoded("");
+
+        private final String s;
+
+        /**
+         * Casts the given encoded String by creating a new Encoded instance.
+         * <p>
+         * No encoding will be performed, use with care.
+         * </p>
+         */
+        public static Encoded cast(final String encoded) {
+            return new Encoded(encoded);
+        }
+
+        Encoded(final String encodedString) {
+            this.s = encodedString;
+        }
+
+        /**
+         * Encodes all characters into their hexadecimal value prepended by '%', except:
+         * <ol>
+         *   <li>letters ('a'..'z', 'A'..'Z')</li>
+         *   <li>numbers ('0'..'9')</li>
+         *   <li>characters in the legal-set parameter</li>
+         *   <li> others (unicode characters that are not in
+         *        US-ASCII set, and are not ISO Control or are not ISO Space characters)</li>
+         * </ol>
+         * <p>
+         * Uses {@link Uri#encode(String, String)} for implementation..
+         * </p>
+         *
+         * @param vanilla the string to be encoded
+         * @param legal extended character set, allowed to be preserved in the vanilla string
+         */
+        public Encoded(final String vanilla, final String legal) {
+            this.s = encode(vanilla, legal);
+        }
+
+        public boolean isASCII() { return false; }
+
+        public final String get() { return s; }
+
+        /**
+         * Decodes the string argument which is assumed to be encoded in the {@code
+         * x-www-form-urlencoded} MIME content type using the UTF-8 encoding scheme.
+         * <p>
+         *'%' and two following hex digit characters are converted to the
+         * equivalent byte value. All other characters are passed through
+         * unmodified.
+         * </p>
+         * <p>
+         * e.g. "A%20B%20C %24%25" -> "A B C $%"
+         * </p>
+         * <p>
+         * Uses {@link Uri#decode(String)} for implementation..
+         * </p>
+         */
+        public final String decode() { return Uri.decode(s); }
+
+        //
+        // Basic Object / Identity
+        //
+
+        @Override
+        public final String toString() { return s; }
+
+        @Override
+        public final int hashCode() { return s.hashCode(); }
+
+        /**
+         * {@inheritDoc}
+         *
+         * @param  o The comparison argument, either a {@link Encoded} or a {@link String}
+         *
+         * @return {@code true} if the given object is equivalent to this instance,
+         *         otherwise {@code false}.
+         *
+         * @see  #compareTo(Encoded)
+         * @see  #equalsIgnoreCase(Encoded)
+         */
+        @Override
+        public final boolean equals(final Object o) {
+            if (this == o) {
+                return true;
+            }
+            if (o instanceof Encoded) {
+                return s.equals(((Encoded)o).s);
+            }
+            return s.equals(o);
+        }
+
+        //
+        // CharSequence
+        //
+
+        @Override
+        public final int length() { return s.length(); }
+
+        @Override
+        public final char charAt(final int index) { return s.charAt(index); }
+
+        @Override
+        public final CharSequence subSequence(final int start, final int end) { return s.subSequence(start, end); }
+
+        @Override
+        public final int compareTo(final Encoded o) { return s.compareTo(o.s); }
+
+        //
+        // String derived ..
+        //
+        /** See {@link String#concat(String)}. */
+        public Encoded concat(final Encoded encoded) { return new Encoded(s.concat(encoded.s)); }
+
+        /** See {@link String#substring(int)}. */
+        public final String substring(final int start) { return s.substring(start); }
+        /** See {@link String#substring(int, int)}. */
+        public final String substring(final int start, final int end) { return s.substring(start, end); }
+
+        /** See {@link String#indexOf(int)}. */
+        public final int indexOf(final int ch) { return s.indexOf(ch); }
+        /** See {@link String#indexOf(int, int)}. */
+        public final int indexOf(final int ch, final int fromIndex) { return s.indexOf(ch, fromIndex); }
+        /** See {@link String#indexOf(String)}. */
+        public final int indexOf(final String str) { return s.indexOf(str); }
+        /** See {@link String#indexOf(String, int)}. */
+        public final int indexOf(final String str, final int fromIndex) { return s.indexOf(str, fromIndex); }
+
+        /** See {@link String#lastIndexOf(int)}. */
+        public final int lastIndexOf(final int ch) { return s.lastIndexOf(ch); }
+        /** See {@link String#lastIndexOf(int, int)}. */
+        public int lastIndexOf(final int ch, final int fromIndex) { return s.lastIndexOf(ch, fromIndex); }
+        /** See {@link String#lastIndexOf(String)}. */
+        public int lastIndexOf(final String str) { return s.lastIndexOf(str); }
+        /** See {@link String#lastIndexOf(String, int)}. */
+        public int lastIndexOf(final String str, final int fromIndex) { return s.lastIndexOf(str, fromIndex); }
+
+        /** See {@link String#equalsIgnoreCase(String)}. */
+        public final boolean equalsIgnoreCase(final Encoded anotherEncoded) { return s.equalsIgnoreCase(anotherEncoded.s); }
+    }
+
+    public static class ASCIIEncoded extends Encoded {
+        /**
+         * Casts the given encoded String by creating a new ASCIIEncoded instance.
+         * <p>
+         * No encoding will be performed, use with care.
+         * </p>
+         */
+        public static ASCIIEncoded cast(final String encoded) {
+            return new ASCIIEncoded(encoded, null);
+        }
+        private ASCIIEncoded(final String encoded, final Object unused) {
+            super(encoded);
+        }
+
+        /**
+         * Other characters, which are Unicode chars that are not US-ASCII, and are
+         * not ISO Control or are not ISO Space chars are not preserved
+         * and encoded into their hexidecimal value prepended by '%'.
+         * <p>
+         * For example: Euro currency symbol -> "%E2%82%AC".
+         * </p>
+         * <p>
+         * Uses {@link Uri#encodeToASCIIString(String)} for implementation.
+         * </p>
+         * @param unicode unencoded input
+         */
+        public ASCIIEncoded(final String unicode) {
+            super(encodeToASCIIString(unicode));
+        }
+        public boolean isASCII() { return true; }
+    }
+
+    private static void encodeChar2UTF8(final StringBuilder buf, final char ch) {
+        final byte[] bytes;
+        try {
+            bytes = new String(new char[] { ch }).getBytes(ENCODING);
+        } catch (final UnsupportedEncodingException e) {
+            throw new RuntimeException(MSG_ENCODING_NA, e);
+        }
+        // FIXME: UTF-8 produces more than one byte ? Optimization might be possible.
+        for (int j = 0; j < bytes.length; j++) {
+            final byte b = bytes[j];
+            buf.append('%');
+            buf.append(DIGITS.charAt( ( b & 0xf0 ) >> 4 ));
+            buf.append(DIGITS.charAt(   b & 0xf         ));
+        }
+    }
+
+    /**
+     * All characters are encoded into their hexadecimal value prepended by '%', except:
+     * <ol>
+     *   <li>letters ('a'..'z', 'A'..'Z')</li>
+     *   <li>numbers ('0'..'9')</li>
+     *   <li>characters in the legal-set parameter</li>
+     *   <li> others (unicode characters that are not in
+     *        US-ASCII set, and are not ISO Control or are not ISO Space characters)</li>
+     * </ol>
+     * <p>
+     * Use {@link #encodeToASCIIString(String)} for US-ASCII encoding.
+     * </p>
+     * <p>
+     * Consider using {@link Encoded#Encoded(String, String)} in APIs
+     * to distinguish encoded from unencoded data by type.
+     * </p>
+     *
+     * @param vanilla the string to be encoded
+     * @param legal extended character set, allowed to be preserved in the vanilla string
+     * @return java.lang.String the converted string
+     */
+    public static String encode(final String vanilla, final String legal) {
+        if( null == vanilla ) {
+            return null;
+        }
+        final StringBuilder buf = new StringBuilder();
+        for (int i = 0; i < vanilla.length(); i++) {
+            final char ch = vanilla.charAt(i);
+            if ( (ch >= 'a' && ch <= 'z') ||
+                 (ch >= 'A' && ch <= 'Z')  ||
+                 (ch >= '0' && ch <= '9')  ||
+                 legal.indexOf(ch) > -1 ||
+                 ( ch > 127 && !Character.isSpaceChar(ch) && !Character.isISOControl(ch) )
+               ) {
+                buf.append(ch);
+            } else {
+                encodeChar2UTF8(buf, ch);
+            }
+        }
+        return buf.toString();
+    }
+
+    /**
+     * Other characters, which are Unicode chars that are not US-ASCII, and are
+     * not ISO Control or are not ISO Space chars are not preserved
+     * and encoded into their hexidecimal value prepended by '%'.
+     * <p>
+     * For example: Euro currency symbol -> "%E2%82%AC".
+     * </p>
+     * <p>
+     * Consider using {@link ASCIIEncoded#ASCIIEncoded(String)} in APIs
+     * to distinguish encoded from unencoded data by type.
+     * </p>
+     * @param unicode string to be converted
+     * @return java.lang.String the converted string
+     */
+    public static String encodeToASCIIString(final String unicode) {
+        final StringBuilder buf = new StringBuilder();
+        for (int i = 0; i < unicode.length(); i++) {
+            final char ch = unicode.charAt(i);
+            if (ch <= 127) {
+                buf.append(ch);
+            } else {
+                encodeChar2UTF8(buf, ch);
+            }
+        }
+        return buf.toString();
+    }
+
+    /**
+     * Safe {@link Encoded#decode()} call on optional {@code encoded} instance.
+     * @param encoded {@link Encoded} instance to be decoded, may be {@code null}.
+     * @return the {@link Encoded#decode() decoded} String or {@code null} if {@code encoded} was {@code null}.
+     */
+    public static String decode(final Encoded encoded) {
+        return null != encoded ? encoded.decode() : null;
+    }
+
+    /**
+     * Decodes the string argument which is assumed to be encoded in the {@code
+     * x-www-form-urlencoded} MIME content type using the UTF-8 encoding scheme.
+     * <p>
+     *'%' and two following hex digit characters are converted to the
+     * equivalent byte value. All other characters are passed through
+     * unmodified.
+     * </p>
+     * <p>
+     * e.g. "A%20B%20C %24%25" -> "A B C $%"
+     * </p>
+     *
+     * @param encoded The encoded string.
+     * @return java.lang.String The decoded version.
+     */
+    public static String decode(final String encoded) {
+        if( null == encoded ) {
+            return null;
+        }
+        final StringBuilder result = new StringBuilder();
+        final byte[] buf = new byte[32];
+        int bufI = 0;
+        for (int i = 0; i < encoded.length();) {
+            final char c = encoded.charAt(i);
+            if (c == '%') {
+                bufI = 0;
+                do {
+                    if (i + 2 >= encoded.length()) {
+                        throw new IllegalArgumentException("missing '%' hex-digits at index "+i);
+                    }
+                    final int d1 = Character.digit(encoded.charAt(i + 1), 16);
+                    final int d2 = Character.digit(encoded.charAt(i + 2), 16);
+                    if (d1 == -1 || d2 == -1) {
+                        throw new IllegalArgumentException("invalid hex-digits at index "+i+": "+encoded.substring(i, i + 3));
+                    }
+                    buf[bufI++] = (byte) ((d1 << 4) + d2);
+                    if( 32 == bufI ) {
+                        appendUTF8(result, buf, bufI);
+                        bufI = 0;
+                    }
+                    i += 3;
+                } while (i < encoded.length() && encoded.charAt(i) == '%');
+                if( 0 < bufI ) {
+                    appendUTF8(result, buf, bufI);
+                }
+            } else {
+                result.append(c);
+                i++;
+            }
+        }
+        return result.toString();
+    }
+    private static void appendUTF8(final StringBuilder sb, final byte[] buf, final int count) {
+        try {
+            sb.append(new String(buf, 0, count, ENCODING));
+        } catch (final UnsupportedEncodingException e) {
+            throw new RuntimeException(MSG_ENCODING_NA, e);
+        }
+    }
+
+    /**
+     * Creates a new Uri instance using the given arguments.
+     * <p>
+     * This constructor first creates a temporary Uri string from the given components. This
+     * string will be parsed later on to create the Uri instance.
+     * </p>
+     * <p>
+     * {@code [scheme:]scheme-specific-part[#fragment]}
+     * </p>
+     *
+     * @param scheme the unencoded scheme part of the Uri.
+     * @param ssp the unencoded scheme-specific-part of the Uri.
+     * @param fragment the unencoded 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 String scheme, final String ssp, final String 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(IOUtil.SCHEME_SEPARATOR);
+        }
+        if ( !emptyString(ssp) ) {
+            // QUOTE ILLEGAL CHARACTERS
+            uri.append(encode(ssp, SSP_LEGAL));
+        }
+        if ( !emptyString(fragment) ) {
+            uri.append(IOUtil.FRAGMENT_SEPARATOR);
+            // QUOTE ILLEGAL CHARACTERS
+            uri.append(encode(fragment, FRAG_LEGAL));
+        }
+        return new Uri(new Encoded(uri.toString()), false);
+    }
+
+    /**
+     * Creates a new Uri instance using the given arguments.
+     * <p>
+     * This constructor first creates a temporary Uri string from the given 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>
+     *
+     * @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 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.
+     * @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 String scheme, final String userinfo, String host, final int port,
+                              final String path, final String query, final String 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, "path doesn't start with '/'");
+        }
+
+        final boolean extendedPath;
+        if( emptyString(query) || emptyString(fragment) ) {
+            extendedPath = true;
+        } else {
+            extendedPath = false;
+        }
+
+        final StringBuilder uri = new StringBuilder();
+        if ( !emptyString(scheme) ) {
+            uri.append(scheme);
+            uri.append(IOUtil.SCHEME_SEPARATOR);
+        }
+
+        if ( !emptyString(userinfo) || !emptyString(host) || port != -1) {
+            uri.append("//");
+        }
+
+        if ( !emptyString(userinfo) ) {
+            // QUOTE ILLEGAL CHARACTERS in userinfo
+            uri.append(encode(userinfo, USERINFO_LEGAL));
+            uri.append('@');
+        }
+
+        if ( !emptyString(host) ) {
+            // check for ipv6 addresses that hasn't been enclosed
+            // in square brackets
+            if (host.indexOf(IOUtil.SCHEME_SEPARATOR) != -1 && host.indexOf(']') == -1
+                    && host.indexOf('[') == -1) {
+                host = "[" + host + "]";
+            }
+            uri.append(host);
+        }
+
+        if ( port != -1 ) {
+            uri.append(IOUtil.SCHEME_SEPARATOR);
+            uri.append(port);
+        }
+
+        if ( !emptyString(path) ) {
+            // QUOTE ILLEGAL CHARS
+            uri.append(encode(path, extendedPath ? PATH_MIN_LEGAL : PATH_LEGAL));
+        }
+
+        if ( !emptyString(query) ) {
+            uri.append('?');
+            // QUOTE ILLEGAL CHARS
+            uri.append(encode(query, QUERY_LEGAL));
+        }
+
+        if ( !emptyString(fragment) ) {
+            // QUOTE ILLEGAL CHARS
+            uri.append(IOUtil.FRAGMENT_SEPARATOR);
+            uri.append(encode(fragment, FRAG_LEGAL));
+        }
+        return new Uri(new Encoded(uri.toString()), true);
+    }
+
+    /**
+     * Creates a new Uri instance using the given arguments.
+     * <p>
+     * This constructor first creates a temporary Uri string from the given components. This
+     * string will be parsed later on to create the Uri instance.
+     * </p>
+     * <p>
+     * {@code [scheme:]host[path][#fragment]}
+     * </p>
+     *
+     * @param scheme the unencoded scheme part of the Uri.
+     * @param host the unencoded host name of the Uri.
+     * @param path the unencoded path to the resource on the host.
+     * @param fragment the unencoded 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 String scheme, final String host, final String path, final String fragment) throws URISyntaxException {
+        return create(scheme, null, host, -1, path, null, fragment);
+    }
+
+    /**
+     * Creates a new Uri instance using the given arguments.
+     * <p>
+     * This constructor first creates a temporary Uri string from the given components. This
+     * string will be parsed later on to create the Uri instance.
+     * </p>
+     * <p>
+     * {@code [scheme:][//authority][path][?query][#fragment]}
+     * </p>
+     *
+     * @param scheme the unencoded scheme part of the Uri.
+     * @param authority the unencoded authority part of the Uri.
+     * @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.
+     *
+     * @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 String scheme, final String authority, final String path, final String query, final String 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, "path doesn't start with '/'");
+        }
+
+        final boolean extendedPath;
+        if( emptyString(query) || emptyString(fragment) ) {
+            extendedPath = true;
+        } else {
+            extendedPath = false;
+        }
+
+        final StringBuilder uri = new StringBuilder();
+        if ( !emptyString(scheme) ) {
+            uri.append(scheme);
+            uri.append(IOUtil.SCHEME_SEPARATOR);
+        }
+        if ( !emptyString(authority) ) {
+            uri.append("//");
+            // QUOTE ILLEGAL CHARS
+            uri.append(encode(authority, AUTHORITY_LEGAL));
+        }
+
+        if ( !emptyString(path) ) {
+            // QUOTE ILLEGAL CHARS
+            uri.append(encode(path, extendedPath ? PATH_MIN_LEGAL : PATH_LEGAL));
+        }
+        if ( !emptyString(query) ) {
+            // QUOTE ILLEGAL CHARS
+            uri.append('?');
+            uri.append(encode(query, QUERY_LEGAL));
+        }
+        if ( !emptyString(fragment) ) {
+            // QUOTE ILLEGAL CHARS
+            uri.append(IOUtil.FRAGMENT_SEPARATOR);
+            uri.append(encode(fragment, FRAG_LEGAL));
+        }
+        return new Uri(new Encoded(uri.toString()), false);
+    }
+
+    /**
+     * 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>
+     * No encoding will be performed on the given {@code encodedUri}, use with care.
+     * </p>
+     * @throws URISyntaxException
+     */
+    public static Uri cast(final String encodedUri) throws URISyntaxException {
+        return new Uri(Encoded.cast(encodedUri));
+    }
+
+    /**
+     * Creates a new Uri instance using the given file-path argument.
+     * <p>
+     * This constructor first creates a temporary Uri string from the given components. This
+     * string will be parsed later on to create the Uri instance.
+     * </p>
+     * <p>
+     * {@code file:path}
+     * </p>
+     *
+     * @param path the 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.
+     */
+    public static Uri valueOfFilepath(final String path) throws URISyntaxException {
+        if ( emptyString(path) ) {
+            throw new URISyntaxException("", "empty path");
+        }
+        if ( path.charAt(0) != '/' ) {
+            throw new URISyntaxException(path, "path doesn't start with '/'");
+        }
+
+        final StringBuilder uri = new StringBuilder();
+        uri.append(IOUtil.FILE_SCHEME);
+        uri.append(IOUtil.SCHEME_SEPARATOR);
+
+        // QUOTE ILLEGAL CHARS
+        uri.append(encode(path, PATH_MIN_LEGAL));
+
+        return new Uri(new Encoded(uri.toString()), false);
+    }
+
+    /**
+     * Creates a new Uri instance using the given File instance.
+     * <p>
+     * This constructor first creates a temporary Uri string from the given components. This
+     * string will be parsed later on to create the Uri instance.
+     * </p>
+     * <p>
+     * {@code file:path}
+     * </p>
+     *
+     * @param file using {@link IOUtil#slashify(String, boolean, boolean) slashified} {@link File#getAbsolutePath() absolute-path}
+     *             for the path of the {@code file} {@code schema}, utilizing {@link #valueOfFilepath(String)}.
+     * @throws URISyntaxException
+     *             if the temporary created string doesn't fit to the
+     *             specification RFC2396 or could not be parsed correctly.
+     */
+    public static Uri valueOf(final File file) throws URISyntaxException {
+        return Uri.valueOfFilepath(IOUtil.slashify(file.getAbsolutePath(), true, file.isDirectory()));
+    }
+
+    /**
+     * Creates a new Uri instance using the given URI instance.
+     * <p>
+     * If {@code reencode} is {@code true}, decomposes and decoded parts of the given URI
+     * and reconstruct a new encoded Uri instance, re-encoding will be performed.
+     * </p>
+     * <p>
+     * If {@code reencode} is {@code false}, the encoded {@link URI#toString()} is being used for the new Uri instance,
+     * i.e. no re-encoding will be performed.
+     * </p>
+     *
+     * @param uri A given URI instance
+     * @param reencode flag whether re-encoding shall be performed
+     *
+     * @throws URISyntaxException
+     *             if the temporary created string doesn't fit to the
+     *             specification RFC2396 or could not be parsed correctly.
+     */
+    public static Uri valueOf(final URI uri, final boolean reencode) throws URISyntaxException {
+        if( !reencode) {
+            return new Uri(new Encoded(uri.toString()));
+        }
+        System.err.println("Uri.createByURI(reencode):");
+        System.err.println("       source: "+uri.toString());
+        final Uri recomposedUri;
+        if( uri.isOpaque()) {
+            // opaque, without host validation
+            System.err.println("  opaq source: ssp: "+uri.getSchemeSpecificPart());
+            System.err.println("  opaq source: frg: "+uri.getFragment());
+            recomposedUri = Uri.create(uri.getScheme(), uri.getSchemeSpecificPart(), uri.getFragment());
+        } else if( null != uri.getHost() ) {
+            // with host validation
+            System.err.println("  host source: ssp: "+uri.getSchemeSpecificPart());
+            System.err.println("  host source: frg: "+uri.getFragment());
+            recomposedUri = Uri.create(uri.getScheme(), uri.getUserInfo(), uri.getHost(), uri.getPort(),
+                                       uri.getPath(), uri.getQuery(), uri.getFragment());
+        } else {
+            // without host validation
+            System.err.println("       source: ssp: "+uri.getSchemeSpecificPart());
+            System.err.println("       source: frg: "+uri.getFragment());
+            recomposedUri = Uri.create(uri.getScheme(), uri.getAuthority(),
+                                       uri.getPath(), uri.getQuery(), uri.getFragment());
+        }
+        System.err.println("       result: "+recomposedUri.toString());
+        return recomposedUri;
+    }
+
+    /**
+     * Creates a new Uri instance using the given URL instance.
+     * <p>
+     * No re-encoding will be performed.
+     * </p>
+     *
+     * @param url A given URL instance
+     *
+     * @throws URISyntaxException
+     *             if the temporary created string doesn't fit to the
+     *             specification RFC2396 or could not be parsed correctly.
+     */
+    public static Uri valueOf(final URL url) throws URISyntaxException {
+        return new Uri(new Encoded(url.toString()));
+    }
+
+    //
+    // All string fields are encoded!
+    //
+
+    /** Encoded input string used at construction, never {@code null}. */
+    public final Encoded input;
+
+    private final Object lazyLock = new Object();
+
+    /** Encoded input string used at construction, in US-ASCII encoding. */
+    private ASCIIEncoded inputASCII;
+
+    private int hash;
+
+    /** Plain {@code scheme}, {@code null} if undefined. */
+    public final String scheme;
+
+    /** Encoded {@code scheme-specific-part}, never {@code null}. */
+    public final Encoded schemeSpecificPart;
+    /** Encoded {@code path} part of {@code scheme-specific-part}, never {@code null}. */
+    public final Encoded path;
+
+    /** Indicating whether {@code authority} part is defined or not. */
+    public final boolean hasAuthority;
+    /** Encoded {@code authority} part of {@code scheme-specific-part}, {@code null} if undefined. */
+    public final Encoded authority;
+    /** Encoded {@code userinfo} part of {@code authority} and {@code scheme-specific-part}, {@code null} if undefined. */
+    public final Encoded userInfo; // part of authority
+    /** Encoded {@code host} part of {@code authority} and {@code scheme-specific-part}, {@code null} if undefined. */
+    public final Encoded host;     // part of authority
+    /** Encoded {@code port} part of {@code authority} and {@code scheme-specific-part}, {@code -1} if undefined. */
+    public final int port;        // part of authority
+
+    /** Encoded {@code query} part of {@code scheme-specific-part}, {@code null} if undefined. */
+    public final Encoded query;
+
+    /** Encoded {@code fragment}, {@code null} if undefined. */
+    public final Encoded fragment;
+
+    /** Indicating whether this Uri is absolute, i.e. has a {@code scheme} and hence an absolute {@code scheme-specific-part}. */
+    public final boolean absolute;
+
+    /**
+     * Indicating whether this Uri is opaque, i.e. non-hierarchical {@code scheme-specific-part}.
+     * <p>
+     * An opaque Uri has no {@code scheme-specific-part} being parsed,
+     * i.e. {@code path}, {@code query} and {@code authority} are {@code null}.
+     * </p>
+     */
+    public final boolean opaque;
+
+    /**
+     * Creates a new Uri instance according to the given encoded string {@code uri}.
+     *
+     * @param uri the RFC3986 encoded RFC2396 Uri representation to be parsed into a Uri object
+     * @throws URISyntaxException
+     *             if the given string {@code uri} doesn't fit to the
+     *             specification RFC2396 and RFC3986 or could not be parsed correctly.
+     */
+    public Uri(final Encoded uri) throws URISyntaxException {
+        this(uri, false);
+    }
+
+    /** Returns true, if this instance is a {@code file} {@code scheme}, otherwise false. */
+    public final boolean isFileScheme() {
+        return IOUtil.FILE_SCHEME.equals( scheme );
+    }
+
+    /**
+     * Returns the encoded {@link #input}, never {@code null}.
+     */
+    public final Encoded getEncoded() {
+        return input;
+    }
+
+    /**
+     * Returns the encoded {@link #input} as String, never {@code null}, same as {@link #getEncoded()}.
+     */
+    @Override
+    public final String toString() {
+        return input.get();
+    }
+
+    /**
+     * Returns the encoded {@link #input} encoded in US-ASCII.
+     */
+    public ASCIIEncoded toASCIIString() {
+        synchronized( lazyLock ) {
+            if( null == inputASCII ) {
+                inputASCII = new ASCIIEncoded(input.get());
+            }
+            return inputASCII;
+        }
+    }
+
+    /**
+     * Returns a new {@link URI} instance using the encoded {@link #input} string, {@code new URI(uri.input)},
+     * i.e. no re-encoding will be performed.
+     * @see #toURI(boolean)
+     */
+    public final URI toURI() {
+        try {
+            return new URI(input.get());
+        } catch (final URISyntaxException e) {
+            throw new Error(e); // Can't happen
+        }
+    }
+
+    /**
+     * Returns a new {@link URI} instance based upon this instance.
+     * <p>
+     * If {@code reencode} is {@code true}, all Uri parts of this instance will be decoded
+     * and encoded by the URI constructor, i.e. re-encoding will be performed.
+     * </p>
+     * <p>
+     * If {@code reencode} is {@code false}, this instance encoded {@link #input} string is being used, see {@link #toURI()},
+     * i.e. no re-encoding will be performed.
+     * </p>
+     *
+     * @throws URISyntaxException
+     *             if the given string {@code uri} doesn't fit to the
+     *             specification RFC2396 or could not be parsed correctly.
+     * @see #valueOf(URI, boolean)
+     */
+    public final URI toURI(final boolean reencode) throws URISyntaxException {
+        if( !reencode ) {
+            return toURI();
+        }
+        final URI recomposedURI;
+        if( opaque ) {
+            // opaque, without host validation
+            recomposedURI = new URI(decode(scheme), decode(schemeSpecificPart), decode(fragment));
+        } else if( null != host ) {
+            // with host validation
+            recomposedURI = new URI(decode(scheme), decode(userInfo), decode(host), port,
+                                    decode(path), decode(query), decode(fragment));
+        } else {
+            // without host validation
+            recomposedURI = new URI(decode(scheme), decode(authority),
+                                    decode(path), decode(query), decode(fragment));
+        }
+        return recomposedURI;
+    }
+
+
+    /**
+     * Returns a new {@link URL} instance using the encoded {@link #input} string, {@code new URL(uri.input)},
+     * i.e. no re-encoding will be performed.
+     * @throws MalformedURLException
+     *             if an error occurs while creating the URL or no protocol
+     *             handler could be found.
+     */
+    public final URL toURL() throws MalformedURLException {
+        if (!absolute) {
+            throw new IllegalArgumentException("Cannot convert relative Uri: "+input);
+        }
+        return new URL(input.get());
+    }
+
+    /**
+     * If this instance {@link #isFileScheme() is a file scheme},
+     * implementation decodes <i>[ "//"+{@link #authority} ] + {@link #path}</i>,<br>
+     * then it processes the result if {@link File#separatorChar} <code> == '\\'</code>
+     * as follows:
+     * <ul>
+     *   <li>slash -> backslash</li>
+     *   <li>drop a starting single backslash, preserving windows UNC</li>
+     * </ul>
+     * <p>
+     * Otherwise implementation returns {@code null}.
+     * </p>
+     */
+    public final String getNativeFilePath() {
+        if( isFileScheme() ) {
+            final String authorityS;
+            if( null == authority ) {
+                authorityS = "";
+            } else {
+                authorityS = "//"+authority.decode();
+            }
+            // return IOUtil.decodeURIToFilePath(authorityS + path);
+            final String path = authorityS+this.path.decode();
+            if( File.separator.equals("\\") ) {
+                final String r = patternSingleFS.matcher(path).replaceAll("\\\\");
+                if( r.startsWith("\\") && !r.startsWith("\\\\") ) { // '\\\\' denotes UNC hostname, which shall not be cut-off
+                    return r.substring(1);
+                } else {
+                    return r;
+                }
+            }
+            return path;
+        }
+        return null;
+    }
+
+    /**
+     * If this instance's {@link #schemeSpecificPart} contains a Uri itself, a sub-Uri,
+     * return {@link #schemeSpecificPart} + {@code #} {@link #fragment} via it's own new Uri instance.
+     * <p>
+     * In case this Uri is a {@code jar-scheme}, the {@code query} is omitted,
+     * since it shall be invalid for {@code jar-schemes} anyway.
+     * </p>
+     * <p>
+     * Otherwise method returns {@code null}.
+     * </p>
+     * <pre>
+     * Example 1:
+     *     This instance: <code>jar:<i>scheme2</i>:/some/path/gluegen-rt.jar!/com/jogamp/common/GlueGenVersion.class</code>
+     *     Returned Uri:  <code><i>scheme2</i>:/some/path/gluegen-rt.jar</code>
+     *
+     * Example 2:
+     *     This instance: <code>jar:<i>scheme2</i>:/some/path/gluegen-rt.jar!/com/jogamp/common/GlueGenVersion.class?lala=01#fragment</code>
+     *     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>
+     *     Returned Uri:  <code><i>scheme2</i>:/some/path/gluegen-rt.jar?lala=01#fragment</code>
+     * </pre>
+     * @throws URISyntaxException
+     */
+    public final Uri getContainedUri() throws URISyntaxException {
+        if( !emptyString(schemeSpecificPart) ) {
+            final StringBuilder sb = new StringBuilder();
+
+            if( scheme.equals(IOUtil.JAR_SCHEME) ) {
+                final int idx = schemeSpecificPart.lastIndexOf(IOUtil.JAR_SCHEME_SEPARATOR);
+                if (0 > idx) {
+                    throw new URISyntaxException(input.get(), "missing jar separator");
+                }
+                sb.append( schemeSpecificPart.substring(0, idx) ); // exclude '!/'
+            } else {
+                sb.append( schemeSpecificPart.get() );
+            }
+            if ( !emptyString(fragment) ) {
+                sb.append(IOUtil.FRAGMENT_SEPARATOR);
+                sb.append(fragment);
+            }
+            try {
+                final Uri res = new Uri(new Encoded(sb.toString()), false);
+                if( null != res.scheme ) {
+                    return res;
+                }
+            } catch(final URISyntaxException e) {
+                // OK, does not contain uri
+            }
+        }
+        return null;
+    }
+
+
+    /**
+     * 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>
+     * <p>
+     * If this Uri does not contain any path separator, 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>
+     *
+     * 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>
+     * </pre>
+     */
+    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+IOUtil.SCHEME_SEPARATOR+schemeSpecificPart.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+IOUtil.SCHEME_SEPARATOR+schemeSpecificPart.substring(0, p+1) ));
+                    } catch (final URISyntaxException ue) {
+                        // not complete, hence removed authority, or even root folder -> return null
+                    }
+                }
+            }
+        }
+        return null;
+    }
+
+    /// 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.
+     * <p>
+     * <i>protocol</i> may be "file", "http", etc..
+     * </p>
+     *
+     * @return "<i>protocol</i>:/some/path/"
+     * @throws IllegalArgumentException if the URI doesn't match the expected formatting, or is null
+     * @throws URISyntaxException
+     */
+    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 ':'");
+            }
+        }
+        try {
+            return Uri.cast(uriS.substring(0, idx+1)); // exclude jar name, include terminal '/' or ':'
+        } catch (final URISyntaxException ue) {}
+        return null;
+    }
+
+    /**
+     * Generates a URI for the <i>relativePath</i> relative to the <i>baseURI</i>,
+     * hence the result is a absolute location.
+     * <p>
+     * Impl. operates on the <i>scheme-specific-part</i>, and hence is sub-protocol savvy.
+     * </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.
+     * </p>
+     *
+     * @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>
+     */
+    public Uri getRelativeOf(final Encoded relativePath) throws URISyntaxException {
+        return compose(scheme, schemeSpecificPart, relativePath, fragment);
+    }
+
+    static Uri compose(final String 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('?');
+        if( queryI >= 0 ) {
+            query = schemeSpecificPartS.substring(queryI+1);
+            schemeSpecificPartS = schemeSpecificPartS.substring(0, queryI);
+        } 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);
+        uri.append(':');
+        uri.append(schemeSpecificPartS);
+        if ( null != query ) {
+            uri.append('?');
+            uri.append(query);
+        }
+        if ( null != fragment ) {
+            uri.append('#');
+            uri.append(fragment.get());
+        }
+        return Uri.cast(uri.toString());
+    }
+
+    /// NEW END
+
+    /**
+     * {@inheritDoc}
+     * <p>
+     * Compares this Uri instance with the given argument {@code o} and
+     * determines if both are equal. Two Uri instances are equal if all single
+     * parts are identical in their meaning.
+     * </p>
+     *
+     * @param o
+     *            the Uri this instance has to be compared with.
+     * @return {@code true} if both Uri instances point to the same resource,
+     *         {@code false} otherwise.
+     */
+    @Override
+    public final boolean equals(final Object o) {
+        if (!(o instanceof Uri)) {
+            return false;
+        }
+        final Uri uri = (Uri) o;
+
+        if (uri.fragment == null && fragment != null || uri.fragment != null && fragment == null) {
+            return false;
+        } else if (uri.fragment != null && fragment != null) {
+            if (!equalsHexCaseInsensitive(uri.fragment, fragment)) {
+                return false;
+            }
+        }
+
+        if (uri.scheme == null && scheme != null || uri.scheme != null && scheme == null) {
+            return false;
+        } else if (uri.scheme != null && scheme != null) {
+            if (!uri.scheme.equalsIgnoreCase(scheme)) {
+                return false;
+            }
+        }
+
+        if (uri.opaque && opaque) {
+            return equalsHexCaseInsensitive(uri.schemeSpecificPart, schemeSpecificPart);
+        } else if (!uri.opaque && !opaque) {
+            if (!equalsHexCaseInsensitive(path, uri.path)) {
+                return false;
+            }
+
+            if (uri.query != null && query == null || uri.query == null && query != null) {
+                return false;
+            } else if (uri.query != null && query != null) {
+                if (!equalsHexCaseInsensitive(uri.query, query)) {
+                    return false;
+                }
+            }
+
+            if (uri.authority != null && authority == null || uri.authority == null && authority != null) {
+                return false;
+            } else if (uri.authority != null && authority != null) {
+                if (uri.host != null && host == null || uri.host == null && host != null) {
+                    return false;
+                } else if (uri.host == null && host == null) {
+                    // both are registry based, so compare the whole authority
+                    return equalsHexCaseInsensitive(uri.authority, authority);
+                } else { // uri.host != null && host != null, so server-based
+                    if (!host.equalsIgnoreCase(uri.host)) {
+                        return false;
+                    }
+
+                    if (port != uri.port) {
+                        return false;
+                    }
+
+                    if ( uri.userInfo != null && userInfo == null ||
+                         uri.userInfo == null && userInfo != null
+                       ) {
+                        return false;
+                    } else if (uri.userInfo != null && userInfo != null) {
+                        return equalsHexCaseInsensitive(userInfo, uri.userInfo);
+                    } else {
+                        return true;
+                    }
+                }
+            } else {
+                // no authority
+                return true;
+            }
+
+        } else {
+            // one is opaque, the other hierarchical
+            return false;
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     * <p>
+     * Gets the hashcode value of this Uri instance.
+     * </p>
+     */
+    @Override
+    public final int hashCode() {
+        synchronized( lazyLock ) {
+            if (hash == -1) {
+                hash = getHashString().hashCode();
+            }
+            return hash;
+        }
+    }
+
+    /*
+     * Takes a string that may contain hex sequences like %F1 or %2b and
+     * converts the hex values following the '%' to lowercase
+     */
+    private String convertHexToLowerCase(final String s) {
+        if (s.indexOf('%') == -1) {
+            return s;
+        }
+        final StringBuilder result = new StringBuilder("");
+        int index = 0, previndex = 0;
+        while ((index = s.indexOf('%', previndex)) != -1) {
+            result.append(s.substring(previndex, index + 1));
+            result.append(s.substring(index + 1, index + 3).toLowerCase());
+            index += 3;
+            previndex = index;
+        }
+        return result.toString();
+    }
+
+    /*
+     * Takes two strings that may contain hex sequences like %F1 or %2b and
+     * compares them, ignoring case for the hex values. Hex values must always
+     * occur in pairs as above
+     */
+    private boolean equalsHexCaseInsensitive(final Encoded first, final Encoded second) {
+        if (first.indexOf('%') != second.indexOf('%')) {
+            return first.equals(second);
+        }
+
+        int index = 0, previndex = 0;
+        while ( ( index = first.indexOf('%', previndex) ) != -1 &&
+                second.indexOf('%', previndex) == index
+              ) {
+            if( !first.substring(previndex, index).equals( second.substring(previndex, index) ) ) {
+                return false;
+            }
+            if( !first.substring(index + 1, index + 3).equalsIgnoreCase( second.substring(index + 1, index + 3) ) ) {
+                return false;
+            }
+            index += 3;
+            previndex = index;
+        }
+        return first.substring(previndex).equals( second.substring(previndex) );
+    }
+
+    /*
+     * Form a string from the components of this Uri, similarly to the
+     * toString() method. But this method converts scheme and host to lowercase,
+     * and converts escaped octets to lowercase.
+     */
+    private String getHashString() {
+        final StringBuilder result = new StringBuilder();
+        if (scheme != null) {
+            result.append(scheme.toLowerCase());
+            result.append(IOUtil.SCHEME_SEPARATOR);
+        }
+        if (opaque) {
+            result.append(schemeSpecificPart.get());
+        } else {
+            if (authority != null) {
+                result.append("//");
+                if (host == null) {
+                    result.append(authority.get());
+                } else {
+                    if (userInfo != null) {
+                        result.append(userInfo.get() + "@"); //$NON-NLS-1$
+                    }
+                    result.append(host.get().toLowerCase());
+                    if (port != -1) {
+                        result.append(IOUtil.SCHEME_SEPARATOR + port);
+                    }
+                }
+            }
+
+            if (path != null) {
+                result.append(path.get());
+            }
+
+            if (query != null) {
+                result.append('?');
+                result.append(query.get());
+            }
+        }
+
+        if (fragment != null) {
+            result.append(IOUtil.FRAGMENT_SEPARATOR);
+            result.append(fragment.get());
+        }
+        return convertHexToLowerCase(result.toString());
+    }
+
+    /**
+     *
+     * @param input
+     * @param expectServer
+     * @throws URISyntaxException
+     */
+    private Uri(final Encoded input, final boolean expectServer) throws URISyntaxException {
+        if( emptyString(input) ) {
+            throw new URISyntaxException(input.get(), "empty input");
+        }
+        this.input = input;
+
+        String temp = input.get();
+        int index;
+        // parse into Fragment, Scheme, and SchemeSpecificPart
+        // then parse SchemeSpecificPart if necessary
+
+        // Fragment
+        index = temp.indexOf(IOUtil.FRAGMENT_SEPARATOR);
+        if (index != -1) {
+            // remove the fragment from the end
+            fragment = new Encoded( temp.substring(index + 1) );
+            validateFragment(input, fragment, index + 1);
+            temp = temp.substring(0, index);
+        } else {
+            fragment = null;
+        }
+
+        // Scheme and SchemeSpecificPart
+        final int indexSchemeSep = temp.indexOf(IOUtil.SCHEME_SEPARATOR);
+        index = indexSchemeSep;
+        final int indexSSP = temp.indexOf('/');
+        final int indexQuerySep = temp.indexOf('?');
+        final String schemeSpecificPartS;
+
+        // if a '/' or '?' occurs before the first ':' the uri has no
+        // specified scheme, and is therefore not absolute
+        if ( indexSchemeSep != -1 &&
+             ( indexSSP >= indexSchemeSep || indexSSP == -1 ) &&
+             ( indexQuerySep >= indexSchemeSep || indexQuerySep == -1 )
+           ) {
+            // the characters up to the first ':' comprise the scheme
+            absolute = true;
+            scheme = temp.substring(0, indexSchemeSep);
+            if (scheme.length() == 0) {
+                failExpecting(input, "scheme", indexSchemeSep);
+            }
+            validateScheme(input, scheme, 0);
+            schemeSpecificPartS = temp.substring(indexSchemeSep + 1);
+            schemeSpecificPart = new Encoded( schemeSpecificPartS );
+            if (schemeSpecificPart.length() == 0) {
+                failExpecting(input, "scheme-specific-part", indexSchemeSep);
+            }
+        } else {
+            absolute = false;
+            scheme = null;
+            schemeSpecificPartS = temp;
+            schemeSpecificPart = new Encoded( schemeSpecificPartS );
+        }
+
+        if ( scheme == null ||  schemeSpecificPartS.length() > 0 && schemeSpecificPartS.charAt(0) == '/' ) {
+            // Uri is hierarchical, not opaque
+            opaque = false;
+
+            // Query
+            temp = schemeSpecificPartS;
+            index = temp.indexOf('?');
+            if (index != -1) {
+                query = new Encoded( temp.substring(index + 1) );
+                temp = temp.substring(0, index);
+                validateQuery(input, query, indexSSP + 1 + index);
+            } else {
+                query = null;
+            }
+
+            // Authority and Path
+            if (temp.startsWith("//")) {
+                index = temp.indexOf('/', 2);
+                final String authorityS;
+                if (index != -1) {
+                    authorityS = temp.substring(2, index);
+                    path = new Encoded( temp.substring(index) );
+                } else {
+                    authorityS = temp.substring(2);
+                    if (authorityS.length() == 0 && query == null && fragment == null) {
+                        failExpecting(input, "authority, path [, query, fragment]", index);
+                    }
+                    path = Encoded.EMPTY;
+                    // nothing left, so path is empty
+                    // (not null, path should never be null if hierarchical/non-opaque)
+                }
+                if ( emptyString(authorityS) ) {
+                    authority = null;
+                } else {
+                    authority = new Encoded( authorityS );
+                    validateAuthority(input, authority, indexSchemeSep + 3);
+                }
+            } else { // no authority specified
+                path = new Encoded( temp );
+                authority = null;
+            }
+
+            int pathIndex = 0;
+            if (indexSSP > -1) {
+                pathIndex += indexSSP;
+            }
+            if (index > -1) {
+                pathIndex += index;
+            }
+            validatePath(input, path, pathIndex);
+        } else {
+            // Uri is not hierarchical, Uri is opaque
+            opaque = true;
+            query = null;
+            path = null;
+            authority = null;
+            validateSsp(input, schemeSpecificPart, indexSchemeSep + 1);
+        }
+
+        /**
+         * determine the host, port and userinfo if the authority parses
+         * successfully to a server based authority
+         *
+         * Behavior in error cases: if forceServer is true, throw
+         * URISyntaxException with the proper diagnostic messages. if
+         * forceServer is false assume this is a registry based uri, and just
+         * return leaving the host, port and userinfo fields undefined.
+         *
+         * and there are some error cases where URISyntaxException is thrown
+         * regardless of the forceServer parameter e.g. malformed ipv6 address
+         */
+        Encoded tempUserinfo = null, tempHost = null;
+        int tempPort = -1;
+        boolean authorityComplete;
+
+        if ( null != authority ) {
+            authorityComplete = true; // set to false later
+            int hostindex = 0;
+
+            temp = authority.get();
+            index = temp.indexOf('@');
+            if (index != -1) {
+                // remove user info
+                tempUserinfo = new Encoded( temp.substring(0, index) );
+                validateUserinfo(authority, tempUserinfo, 0);
+                temp = temp.substring(index + 1); // host[:port] is left
+                hostindex = index + 1;
+            }
+
+            index = temp.lastIndexOf(IOUtil.SCHEME_SEPARATOR);
+            final int endindex = temp.indexOf(']');
+
+            if (index != -1 && endindex < index) {
+                // determine port and host
+                tempHost = new Encoded( temp.substring(0, index) );
+
+                if (index < (temp.length() - 1)) { // port part is not empty
+                    try {
+                        tempPort = Integer.parseInt(temp.substring(index + 1));
+                        if (tempPort < 0) {
+                            if (expectServer) {
+                                fail(authority, "invalid port <"+authority+">", hostindex + index + 1);
+                            }
+                            authorityComplete = false;
+                        }
+                    } catch (final NumberFormatException e) {
+                        if (expectServer) {
+                            fail(authority, "invalid port <"+authority+">, "+e.getMessage(), hostindex + index + 1);
+                        }
+                        authorityComplete = false;
+                    }
+                }
+            } else {
+                tempHost = new Encoded( temp );
+            }
+
+            if( authorityComplete ) {
+                if ( emptyString(tempHost) ) {
+                    if (expectServer) {
+                        fail(authority, "empty host <"+authority+">", hostindex);
+                    }
+                    authorityComplete = false;
+                } else if (!isValidHost(expectServer, tempHost)) {
+                    if (expectServer) {
+                        fail(authority, "invalid host <"+tempHost+">", hostindex);
+                    }
+                    authorityComplete = false;
+                }
+            }
+        } else {
+            authorityComplete = false;
+        }
+
+        if( authorityComplete ) {
+            // this is a server based uri,
+            // fill in the userinfo, host and port fields
+            userInfo = tempUserinfo;
+            host = tempHost;
+            port = tempPort;
+            hasAuthority = true;
+        } else {
+            userInfo = null;
+            host = null;
+            port = -1;
+            hasAuthority = false;
+        }
+    }
+
+    private static void validateScheme(final Encoded uri, final String scheme, final int index) throws URISyntaxException {
+        // first char needs to be an alpha char
+        final char ch = scheme.charAt(0);
+        if ( !((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z')) ) {
+            fail(uri, "invalid scheme", index);
+        }
+        final int errIdx = validateAlphaNum(scheme, "+-.");
+        if( 0 <= errIdx ) {
+            fail(uri, "invalid scheme", index+errIdx);
+        }
+    }
+
+    private static void validateSsp(final Encoded uri, final Encoded ssp, final int index) throws URISyntaxException {
+        final int errIdx = validateEncoded(ssp, SSP_LEGAL);
+        if( 0 <= errIdx ) {
+            fail(uri, "invalid scheme-specific-part", index+errIdx);
+        }
+    }
+
+    private static void validateAuthority(final Encoded uri, final Encoded authority, final int index) throws URISyntaxException {
+        final int errIdx = validateEncoded(authority, AUTHORITY_LEGAL);
+        if( 0 <= errIdx ) {
+            fail(uri, "invalid authority", index+errIdx);
+        }
+    }
+
+    private static void validatePath(final Encoded uri, final Encoded path, final int index) throws URISyntaxException {
+        final int errIdx = validateEncoded(path, PATH_LEGAL);
+        if( 0 <= errIdx ) {
+            fail(uri, "invalid path", index+errIdx);
+        }
+    }
+
+    private static void validateQuery(final Encoded uri, final Encoded query, final int index) throws URISyntaxException {
+        final int errIdx = validateEncoded(query, QUERY_LEGAL);
+        if( 0 <= errIdx ) {
+            fail(uri, "invalid query", index+errIdx);
+        }
+    }
+
+    private static void validateFragment(final Encoded uri, final Encoded fragment, final int index) throws URISyntaxException {
+        final int errIdx = validateEncoded(fragment, FRAG_LEGAL);
+        if( 0 <= errIdx ) {
+            fail(uri, "invalid fragment", index+errIdx);
+        }
+    }
+
+    private static void validateUserinfo(final Encoded uri, final Encoded userinfo, final int index) throws URISyntaxException {
+        for (int i = 0; i < userinfo.length(); i++) {
+            final char ch = userinfo.charAt(i);
+            if (ch == ']' || ch == '[') {
+                fail(uri, "invalid userinfo", index+i);
+            }
+        }
+    }
+
+    /**
+     * distinguish between IPv4, IPv6, domain name and validate it based on
+     * its type
+     */
+    private boolean isValidHost(final boolean expectServer, final Encoded host) throws URISyntaxException {
+        if (host.charAt(0) == '[') {
+            // ipv6 address
+            if (host.charAt(host.length() - 1) != ']') {
+                fail(input, "invalid host, missing closing ipv6: "+host, 0);
+            }
+            if (!isValidIP6Address(host.get())) {
+                fail(input, "invalid ipv6: "+host, 0);
+            }
+            return true;
+        }
+
+        // '[' and ']' can only be the first char and last char
+        // of the host name
+        if (host.indexOf('[') != -1 || host.indexOf(']') != -1) {
+            fail(input, "invalid host: "+host, 0);
+        }
+
+        final int index = host.lastIndexOf('.');
+        if ( index < 0 || index == host.length() - 1 ||
+             !Character.isDigit(host.charAt(index + 1)) )
+        {
+            // domain name
+            if (isValidDomainName(host)) {
+                return true;
+            }
+            if (expectServer) {
+                fail(input, "invalid host, invalid domain-name or ipv4: "+host, 0);
+            }
+            return false;
+        }
+
+        // IPv4 address
+        if (isValidIPv4Address(host.get())) {
+            return true;
+        }
+        if (expectServer) {
+            fail(input, "invalid host, invalid ipv4: "+host, 0);
+        }
+        return false;
+    }
+
+    private static boolean isValidDomainName(final Encoded host) {
+        final String hostS = host.get();
+        if( 0 <= validateAlphaNum(hostS, "-.") ) {
+            return false;
+        }
+        String label = null;
+        final StringTokenizer st = new StringTokenizer(hostS, "."); //$NON-NLS-1$
+        while (st.hasMoreTokens()) {
+            label = st.nextToken();
+            if (label.startsWith("-") || label.endsWith("-")) { //$NON-NLS-1$ //$NON-NLS-2$
+                return false;
+            }
+        }
+
+        if (!label.equals(hostS)) {
+            final char ch = label.charAt(0);
+            if (ch >= '0' && ch <= '9') {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    private static boolean isValidIPv4Address(final String ipv4Address) {
+        int index;
+        int index2;
+        try {
+            int num;
+            index = ipv4Address.indexOf('.');
+            num = Integer.parseInt(ipv4Address.substring(0, index));
+            if (num < 0 || num > 255) {
+                return false;
+            }
+            index2 = ipv4Address.indexOf('.', index + 1);
+            num = Integer.parseInt(ipv4Address.substring(index + 1, index2));
+            if (num < 0 || num > 255) {
+                return false;
+            }
+            index = ipv4Address.indexOf('.', index2 + 1);
+            num = Integer.parseInt(ipv4Address.substring(index2 + 1, index));
+            if (num < 0 || num > 255) {
+                return false;
+            }
+            num = Integer.parseInt(ipv4Address.substring(index + 1));
+            if (num < 0 || num > 255) {
+                return false;
+            }
+        } catch (final Exception e) {
+            return false;
+        }
+        return true;
+    }
+
+    private static boolean isValidIP6Address(final String ipv6Address) {
+        final int length = ipv6Address.length();
+        boolean doubleColon = false;
+        int numberOfColons = 0;
+        int numberOfPeriods = 0;
+        String word = ""; //$NON-NLS-1$
+        char c = 0;
+        char prevChar = 0;
+        int offset = 0; // offset for [] ip addresses
+
+        if (length < 2) {
+            return false;
+        }
+
+        for (int i = 0; i < length; i++) {
+            prevChar = c;
+            c = ipv6Address.charAt(i);
+            switch (c) {
+
+                // case for an open bracket [x:x:x:...x]
+                case '[':
+                    if (i != 0) {
+                        return false; // must be first character
+                    }
+                    if (ipv6Address.charAt(length - 1) != ']') {
+                        return false; // must have a close ]
+                    }
+                    if ((ipv6Address.charAt(1) == IOUtil.SCHEME_SEPARATOR_CHAR)
+                            && (ipv6Address.charAt(2) != IOUtil.SCHEME_SEPARATOR_CHAR)) {
+                        return false;
+                    }
+                    offset = 1;
+                    if (length < 4) {
+                        return false;
+                    }
+                    break;
+
+                // case for a closed bracket at end of IP [x:x:x:...x]
+                case ']':
+                    if (i != length - 1) {
+                        return false; // must be last character
+                    }
+                    if (ipv6Address.charAt(0) != '[') {
+                        return false; // must have a open [
+                    }
+                    break;
+
+                // case for the last 32-bits represented as IPv4
+                // x:x:x:x:x:x:d.d.d.d
+                case '.':
+                    numberOfPeriods++;
+                    if (numberOfPeriods > 3) {
+                        return false;
+                    }
+                    if (!isValidIP4Word(word)) {
+                        return false;
+                    }
+                    if (numberOfColons != 6 && !doubleColon) {
+                        return false;
+                    }
+                    // a special case ::1:2:3:4:5:d.d.d.d allows 7 colons
+                    // with
+                    // an IPv4 ending, otherwise 7 :'s is bad
+                    if (numberOfColons == 7
+                            && ipv6Address.charAt(0 + offset) != IOUtil.SCHEME_SEPARATOR_CHAR
+                            && ipv6Address.charAt(1 + offset) != IOUtil.SCHEME_SEPARATOR_CHAR) {
+                        return false;
+                    }
+                    word = ""; //$NON-NLS-1$
+                    break;
+
+                case IOUtil.SCHEME_SEPARATOR_CHAR:
+                    numberOfColons++;
+                    if (numberOfColons > 7) {
+                        return false;
+                    }
+                    if (numberOfPeriods > 0) {
+                        return false;
+                    }
+                    if (prevChar == IOUtil.SCHEME_SEPARATOR_CHAR) {
+                        if (doubleColon) {
+                            return false;
+                        }
+                        doubleColon = true;
+                    }
+                    word = ""; //$NON-NLS-1$
+                    break;
+
+                default:
+                    if (word.length() > 3) {
+                        return false;
+                    }
+                    if (!isValidHexChar(c)) {
+                        return false;
+                    }
+                    word += c;
+            }
+        }
+
+        // Check if we have an IPv4 ending
+        if (numberOfPeriods > 0) {
+            if (numberOfPeriods != 3 || !isValidIP4Word(word)) {
+                return false;
+            }
+        } else {
+            // If we're at then end and we haven't had 7 colons then there
+            // is a problem unless we encountered a doubleColon
+            if (numberOfColons != 7 && !doubleColon) {
+                return false;
+            }
+
+            // If we have an empty word at the end, it means we ended in
+            // either a : or a .
+            // If we did not end in :: then this is invalid
+            if (word == "" && ipv6Address.charAt(length - 1 - offset) != IOUtil.SCHEME_SEPARATOR_CHAR //$NON-NLS-1$
+                    && ipv6Address.charAt(length - 2 - offset) != IOUtil.SCHEME_SEPARATOR_CHAR) {
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    private static boolean isValidIP4Word(final String word) {
+        char c;
+        if (word.length() < 1 || word.length() > 3) {
+            return false;
+        }
+        for (int i = 0; i < word.length(); i++) {
+            c = word.charAt(i);
+            if (!(c >= '0' && c <= '9')) {
+                return false;
+            }
+        }
+        if (Integer.parseInt(word) > 255) {
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * Validate a string by checking if it contains any characters other than:
+     * <ol>
+     *   <li>letters ('a'..'z', 'A'..'Z')</li>
+     *   <li>numbers ('0'..'9')</li>
+     *   <li>characters in the legal-set parameter</li>
+     *   <li> others (unicode characters that are not in
+     *        US-ASCII set, and are not ISO Control or are not ISO Space characters)</li>
+     * </ol>
+     *
+     * @param encoded
+     *            {@code java.lang.String} the string to be validated
+     * @param legal
+     *            {@code java.lang.String} the characters allowed in the String
+     *            s
+     */
+    private static int validateEncoded(final Encoded encoded, final String legal) {
+        for (int i = 0; i < encoded.length();) {
+            final char ch = encoded.charAt(i);
+            if (ch == '%') {
+                do {
+                    if (i + 2 >= encoded.length()) {
+                        throw new IllegalArgumentException("missing '%' hex-digits at index "+i);
+                    }
+                    final int d1 = Character.digit(encoded.charAt(i + 1), 16);
+                    final int d2 = Character.digit(encoded.charAt(i + 2), 16);
+                    if (d1 == -1 || d2 == -1) {
+                        throw new IllegalArgumentException("invalid hex-digits at index "+i+": "+encoded.substring(i, i + 3));
+                    }
+                    i += 3;
+                } while (i < encoded.length() && encoded.charAt(i) == '%');
+                continue;
+            }
+            if ( !( (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') ||
+                    (ch >= '0' && ch <= '9') || legal.indexOf(ch) > -1 ||
+                    (ch > 127 && !Character.isSpaceChar(ch) && !Character.isISOControl(ch))
+                  )
+               ) {
+                return i;
+            }
+            i++;
+        }
+        return -1;
+    }
+    private static int validateAlphaNum(final String s, final String legal) {
+        for (int i = 0; i < s.length();) {
+            final char ch = s.charAt(i);
+            if ( !( (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') ||
+                    (ch >= '0' && ch <= '9') || legal.indexOf(ch) > -1
+                  )
+               ) {
+                return i;
+            }
+            i++;
+        }
+        return -1;
+    }
+
+    private static boolean isValidHexChar(final char c) {
+        return (c >= '0' && c <= '9') || (c >= 'A' && c <= 'F') || (c >= 'a' && c <= 'f');
+    }
+    private static boolean emptyString(final Encoded s) {
+        return null == s || 0 == s.length();
+    }
+    private static boolean emptyString(final String s) {
+        return null == s || 0 == s.length();
+    }
+
+    private static void fail(final Encoded input, final String reason, final int p) throws URISyntaxException {
+        throw new URISyntaxException(input.get(), reason, p);
+    }
+    private static void failExpecting(final Encoded input, final String expected, final int p) throws URISyntaxException {
+        fail(input, "Expecting " + expected, p);
+    }
+}
\ No newline at end of file
-- 
cgit v1.2.3


From 2d615932505d4870f3fd0dd37f21fb3d308ab536 Mon Sep 17 00:00:00 2001
From: Sven Gothel <sgothel@jausoft.com>
Date: Sun, 7 Sep 2014 18:15:07 +0200
Subject: Bug 1063 - Uri.PATH_LEGAL: Remove reserved 'punct', encoding all
 reserved chars but '!', allows removal of PATH_MIN_LEGAL ; Add comment for
 Harmony values for review

---
 make/scripts/runtest.sh                            |  4 +-
 src/java/com/jogamp/common/net/Uri.java            | 51 ++++++++--------------
 .../com/jogamp/common/net/TestUri03Resolving.java  | 29 ++++++------
 3 files changed, 33 insertions(+), 51 deletions(-)

(limited to 'src/java/com/jogamp/common/net/Uri.java')

diff --git a/make/scripts/runtest.sh b/make/scripts/runtest.sh
index c996305..5d1e7d7 100755
--- a/make/scripts/runtest.sh
+++ b/make/scripts/runtest.sh
@@ -112,8 +112,8 @@ function onetest() {
 #onetest com.jogamp.common.net.TestURIQueryProps 2>&1 | tee -a $LOG
 #onetest com.jogamp.common.net.TestUri01 2>&1 | tee -a $LOG
 #onetest com.jogamp.common.net.TestUri02Composing 2>&1 | tee -a $LOG
-onetest com.jogamp.common.net.TestUri03Resolving 2>&1 | tee -a $LOG
-#onetest com.jogamp.common.net.TestUri99LaunchOnReservedCharPathBug908 2>&1 | tee -a $LOG
+#onetest com.jogamp.common.net.TestUri03Resolving 2>&1 | tee -a $LOG
+onetest com.jogamp.common.net.TestUri99LaunchOnReservedCharPathBug908 2>&1 | tee -a $LOG
 #onetest com.jogamp.common.net.AssetURLConnectionUnregisteredTest 2>&1 | tee -a $LOG
 #onetest com.jogamp.common.net.AssetURLConnectionRegisteredTest 2>&1 | tee -a $LOG
 #onetest com.jogamp.junit.sec.TestSecIOUtil01 2>&1 | tee -a $LOG
diff --git a/src/java/com/jogamp/common/net/Uri.java b/src/java/com/jogamp/common/net/Uri.java
index b116cb4..e1363ed 100644
--- a/src/java/com/jogamp/common/net/Uri.java
+++ b/src/java/com/jogamp/common/net/Uri.java
@@ -168,9 +168,11 @@ public class Uri {
      * {@value} + {@code alphanum}
      * </p>
      */
-    public static final String UNRESERVED = "_-.~"; // Harmony: "_-!.~\'()*"
+    public static final String UNRESERVED = "_-.~";
+    // Harmony: _ - ! . ~ ' ( ) *
 
     private static final String punct = ",;:$&+=";
+    // Harmony: , ; : $ & + =
 
     /**
      * RFC 3986 section 2.2 Reserved Characters (January 2005)
@@ -178,11 +180,14 @@ public class Uri {
      * {@value} + {@code alphanum}
      * </p>
      */
-    public static final String RESERVED = punct + "!*\'()@/?#[]"; // Harmony: punct + "?/[]@";
+    public static final String RESERVED = punct + "!*\'()@/?#[]";
+    // Harmony: , ; : $ & + = ? / [ ] @
 
-    public static final String RESERVED_2 = punct + "!*\'()@/?[]"; // Harmony: punct + "?/[]@";
+    public static final String RESERVED_2 = punct + "!*\'()@/?[]";
+    // Harmony: , ; : $ & + = ? / [ ] @
 
     // Bug 908, issues w/ windows file path char: $ ^ ~ # [ ]
+    // Windows invalid File characters: * ? " < > |
 
     /**
      * Valid charset for RFC 2396 {@code authority}'s {@code user-info},
@@ -192,6 +197,7 @@ public class Uri {
      * </p>
      */
     public static final String USERINFO_LEGAL = UNRESERVED + punct;
+    // Harmony: someLegal = unreserved + punct -> _ - ! . ~ ' ( ) * , ; : $ & + =
 
     /**
      * Valid charset for RFC 2396 {@code authority},
@@ -209,20 +215,8 @@ public class Uri {
      * {@value} + {@code alphanum}
      * </p>
      */
-    public static final String PATH_LEGAL = "/!" + USERINFO_LEGAL;
-    // @ is reserved; Harmony: "/@" + SOME_LEGAL, '!' is re-added, Harmony had it in UNRESERVED
-
-    /**
-     * Reduced valid charset for RFC 2396 {@code path},
-     * additional to legal {@code alphanum} characters.
-     * <p>
-     * Excluding special native filesystem characters to be encoded.
-     * </p>
-     * <p>
-     * {@value} + {@code alphanum}
-     * </p>
-     */
-    public static final String PATH_MIN_LEGAL = "/!_-."; // "/!" + ( unreserved - '~' )
+    public static final String PATH_LEGAL = "/!" + UNRESERVED; // no RESERVED chars but '!',  to allow JAR Uris;
+    // Harmony: "/@" + unreserved + punct -> / @ _ - ! . ~ \ ' ( ) * , ; : $ & + =
 
     /**
      * Valid charset for RFC 2396 {@code query},
@@ -232,6 +226,7 @@ public class Uri {
      * </p>
      */
     public static final String QUERY_LEGAL = UNRESERVED + RESERVED_2 + "\\\"";
+    // Harmony: unreserved + reserved + "\\\""
 
     /**
      * Valid charset for RFC 2396 {@code scheme-specific-part},
@@ -241,6 +236,7 @@ public class Uri {
      * </p>
      */
     public static final String SSP_LEGAL = QUERY_LEGAL;
+    // Harmony: unreserved + reserved
 
     /**
      * Valid charset for RFC 2396 {@code fragment},
@@ -250,6 +246,7 @@ public class Uri {
      * </p>
      */
     public static final String FRAG_LEGAL = UNRESERVED + RESERVED;
+    // Harmony: unreserved + reserved
 
     /**
      * Immutable RFC3986 encoded string.
@@ -651,13 +648,6 @@ public class Uri {
             throw new URISyntaxException(path, "path doesn't start with '/'");
         }
 
-        final boolean extendedPath;
-        if( emptyString(query) || emptyString(fragment) ) {
-            extendedPath = true;
-        } else {
-            extendedPath = false;
-        }
-
         final StringBuilder uri = new StringBuilder();
         if ( !emptyString(scheme) ) {
             uri.append(scheme);
@@ -691,7 +681,7 @@ public class Uri {
 
         if ( !emptyString(path) ) {
             // QUOTE ILLEGAL CHARS
-            uri.append(encode(path, extendedPath ? PATH_MIN_LEGAL : PATH_LEGAL));
+            uri.append(encode(path, PATH_LEGAL));
         }
 
         if ( !emptyString(query) ) {
@@ -759,13 +749,6 @@ public class Uri {
             throw new URISyntaxException(path, "path doesn't start with '/'");
         }
 
-        final boolean extendedPath;
-        if( emptyString(query) || emptyString(fragment) ) {
-            extendedPath = true;
-        } else {
-            extendedPath = false;
-        }
-
         final StringBuilder uri = new StringBuilder();
         if ( !emptyString(scheme) ) {
             uri.append(scheme);
@@ -779,7 +762,7 @@ public class Uri {
 
         if ( !emptyString(path) ) {
             // QUOTE ILLEGAL CHARS
-            uri.append(encode(path, extendedPath ? PATH_MIN_LEGAL : PATH_LEGAL));
+            uri.append(encode(path, PATH_LEGAL));
         }
         if ( !emptyString(query) ) {
             // QUOTE ILLEGAL CHARS
@@ -834,7 +817,7 @@ public class Uri {
         uri.append(IOUtil.SCHEME_SEPARATOR);
 
         // QUOTE ILLEGAL CHARS
-        uri.append(encode(path, PATH_MIN_LEGAL));
+        uri.append(encode(path, PATH_LEGAL));
 
         return new Uri(new Encoded(uri.toString()), false);
     }
diff --git a/src/junit/com/jogamp/common/net/TestUri03Resolving.java b/src/junit/com/jogamp/common/net/TestUri03Resolving.java
index 0d65363..7c1dadf 100644
--- a/src/junit/com/jogamp/common/net/TestUri03Resolving.java
+++ b/src/junit/com/jogamp/common/net/TestUri03Resolving.java
@@ -72,31 +72,30 @@ public class TestUri03Resolving extends JunitTracer {
     };
 
     public static final String[][] uriFileSArrayWindows = new String[][] {
-        new String[] {"file:/C:/gluegen/build-x86_64/gluegen-rt.jar"},
         new String[] {"file:/C%3A/gluegen/build-x86_64/gluegen-rt.jar"},
 
-        new String[] {"file:/C:/gluegen/"+'\u0394'+"/gluegen-rt.jar"},
+        new String[] {"file:/C%3A/gluegen/"+'\u0394'+"/gluegen-rt.jar"},
 
-        new String[] {"file:/C:/gluegen/build-x86_64%20lala/gluegen-rt.jar"},
+        new String[] {"file:/C%3A/gluegen/build-x86_64%20lala/gluegen-rt.jar"},
 
-        new String[] {"file:/C:/gluegen/build-x86_64%20öä%20lala/gluegen-rt.jar"},
+        new String[] {"file:/C%3A/gluegen/build-x86_64%20öä%20lala/gluegen-rt.jar"},
 
-        new String[] {"jar:file:/C:/gluegen/build-x86_64/gluegen-rt.jar!/"},
+        new String[] {"jar:file:/C%3A/gluegen/build-x86_64/gluegen-rt.jar!/"},
 
-        new String[] {"jar:file:/C:/gluegen/build-x86_64%20öä%20lala/gluegen-rt.jar!/"},
+        new String[] {"jar:file:/C%3A/gluegen/build-x86_64%20öä%20lala/gluegen-rt.jar!/"},
 
-        new String[] {"jar:file:/C:/gluegen/build-x86_64/gluegen-rt.jar!/com/jogamp/common/os/Platform.class"},
+        new String[] {"jar:file:/C%3A/gluegen/build-x86_64/gluegen-rt.jar!/com/jogamp/common/os/Platform.class"},
 
-        new String[] {"jar:file:/C:/gluegen/build-x86_64%20öä%20lala/gluegen-rt.jar!/com/jogamp/common/os/Platform.class"},
+        new String[] {"jar:file:/C%3A/gluegen/build-x86_64%20öä%20lala/gluegen-rt.jar!/com/jogamp/common/os/Platform.class"},
         new String[] {"jar:file:/C%3A/gluegen/build-x86_64%20öä%20lala/gluegen-rt.jar!/com/jogamp/common/os/Platform.class"},
 
-        new String[] {"jar:file:///C:/gluegen/build-x86_64%20öä%20lala/gluegen-rt.jar!/com/jogamp/common/os/Platform.class"},
+        new String[] {"jar:file:///C%3A/gluegen/build-x86_64%20öä%20lala/gluegen-rt.jar!/com/jogamp/common/os/Platform.class"},
 
         new String[] {"jar:file://filehost/gluegen/build-x86_64%20öä%20lala/gluegen-rt.jar!/com/jogamp/common/os/Platform.class"},
 
-        new String[] {"jar:file:/C:/gluegen/R%23/gluegen-rt.jar!/"},
+        new String[] {"jar:file:/C%3A/gluegen/R%23/gluegen-rt.jar!/"},
 
-        new String[] {"jar:file:/C:/gluegen/"+'\u0394'+"/gluegen-rt.jar!/"},
+        new String[] {"jar:file:/C%3A/gluegen/"+'\u0394'+"/gluegen-rt.jar!/"},
     };
 
     public static final String[][] fileSArrayUnix = new String[][] {
@@ -117,11 +116,11 @@ public class TestUri03Resolving extends JunitTracer {
                       "/gluegen/build-x86_64 öä lala/gluegen-rt.jar"},
 
         new String[] {"/gluegen/A$/B^/C~/D#/E[/F]/gluegen-rt.jar",
-                      "file:/gluegen/A%24/B%5E/C%7E/D%23/E%5B/F%5D/gluegen-rt.jar",
+                      "file:/gluegen/A%24/B%5E/C~/D%23/E%5B/F%5D/gluegen-rt.jar",
                       "/gluegen/A$/B^/C~/D#/E[/F]/gluegen-rt.jar" },
 
         new String[] {"/gluegen/$/^/~/#/[/]/gluegen-rt.jar",
-                      "file:/gluegen/%24/%5E/%7E/%23/%5B/%5D/gluegen-rt.jar",
+                      "file:/gluegen/%24/%5E/~/%23/%5B/%5D/gluegen-rt.jar",
                       "/gluegen/$/^/~/#/[/]/gluegen-rt.jar" },
     };
 
@@ -151,11 +150,11 @@ public class TestUri03Resolving extends JunitTracer {
                       "\\\\filehost\\gluegen\\build-x86_64 öä lala\\gluegen-rt.jar"},
 
         new String[] {"C:/gluegen/A$/B^/C~/D#/E[/F]/gluegen-rt.jar",
-                      "file:/C%3A/gluegen/A%24/B%5E/C%7E/D%23/E%5B/F%5D/gluegen-rt.jar",
+                      "file:/C%3A/gluegen/A%24/B%5E/C~/D%23/E%5B/F%5D/gluegen-rt.jar",
                       "C:\\gluegen\\A$\\B^\\C~\\D#\\E[\\F]\\gluegen-rt.jar" },
 
         new String[] {"C:/gluegen/$/^/~/#/[/]/gluegen-rt.jar",
-                      "file:/C%3A/gluegen/%24/%5E/%7E/%23/%5B/%5D/gluegen-rt.jar",
+                      "file:/C%3A/gluegen/%24/%5E/~/%23/%5B/%5D/gluegen-rt.jar",
                       "C:\\gluegen\\$\\^\\~\\#\\[\\]\\gluegen-rt.jar" },
     };
 
-- 
cgit v1.2.3


From 6322991799268166e46aeeacb142c11d45950f48 Mon Sep 17 00:00:00 2001
From: Sven Gothel <sgothel@jausoft.com>
Date: Sun, 7 Sep 2014 19:02:48 +0200
Subject: Bug 1063 - Refine Uri and Uri.Encode ; Fix unit tests (passing on
 GNU/Linux, OSX and Windows)

- Refine Uri
  - remove System.err.print*
  - scheme is of type Encode

- Refine Uri.Encode
  - substring(..) shall return type Encode

- Fix unit tests (passing on GNU/Linux, OSX and Windows)
---
 make/scripts/runtest-x64.bat                       |  4 +-
 make/scripts/runtest.sh                            |  4 +-
 src/java/com/jogamp/common/net/Uri.java            | 50 +++++++--------
 src/junit/com/jogamp/common/net/TestUri01.java     |  8 +--
 .../com/jogamp/common/net/TestUri03Resolving.java  | 75 ++++++++--------------
 5 files changed, 59 insertions(+), 82 deletions(-)

(limited to 'src/java/com/jogamp/common/net/Uri.java')

diff --git a/make/scripts/runtest-x64.bat b/make/scripts/runtest-x64.bat
index f88e795..f6dda45 100755
--- a/make/scripts/runtest-x64.bat
+++ b/make/scripts/runtest-x64.bat
@@ -10,6 +10,6 @@ REM scripts\java-win64.bat com.jogamp.common.net.TestUrisWithAssetHandler
 REM scripts\java-win64.bat com.jogamp.common.net.TestURIQueryProps
 REM scripts\java-win64.bat com.jogamp.common.net.TestUri01
 REM scripts\java-win64.bat com.jogamp.common.net.TestUri02Composing
-REM scripts\java-win64.bat com.jogamp.common.net.TestUri03Resolving
-scripts\java-win64.bat com.jogamp.common.net.TestUri99LaunchOnReservedCharPathBug908
+scripts\java-win64.bat com.jogamp.common.net.TestUri03Resolving
+REM scripts\java-win64.bat com.jogamp.common.net.TestUri99LaunchOnReservedCharPathBug908
 
diff --git a/make/scripts/runtest.sh b/make/scripts/runtest.sh
index 5d1e7d7..c996305 100755
--- a/make/scripts/runtest.sh
+++ b/make/scripts/runtest.sh
@@ -112,8 +112,8 @@ function onetest() {
 #onetest com.jogamp.common.net.TestURIQueryProps 2>&1 | tee -a $LOG
 #onetest com.jogamp.common.net.TestUri01 2>&1 | tee -a $LOG
 #onetest com.jogamp.common.net.TestUri02Composing 2>&1 | tee -a $LOG
-#onetest com.jogamp.common.net.TestUri03Resolving 2>&1 | tee -a $LOG
-onetest com.jogamp.common.net.TestUri99LaunchOnReservedCharPathBug908 2>&1 | tee -a $LOG
+onetest com.jogamp.common.net.TestUri03Resolving 2>&1 | tee -a $LOG
+#onetest com.jogamp.common.net.TestUri99LaunchOnReservedCharPathBug908 2>&1 | tee -a $LOG
 #onetest com.jogamp.common.net.AssetURLConnectionUnregisteredTest 2>&1 | tee -a $LOG
 #onetest com.jogamp.common.net.AssetURLConnectionRegisteredTest 2>&1 | tee -a $LOG
 #onetest com.jogamp.junit.sec.TestSecIOUtil01 2>&1 | tee -a $LOG
diff --git a/src/java/com/jogamp/common/net/Uri.java b/src/java/com/jogamp/common/net/Uri.java
index e1363ed..8c641dd 100644
--- a/src/java/com/jogamp/common/net/Uri.java
+++ b/src/java/com/jogamp/common/net/Uri.java
@@ -292,6 +292,7 @@ public class Uri {
 
         public boolean isASCII() { return false; }
 
+        /** Returns the encoded String */
         public final String get() { return s; }
 
         /**
@@ -315,6 +316,12 @@ public class Uri {
         // Basic Object / Identity
         //
 
+        /**
+         * {@inheritDoc}
+         * <p>
+         * Returns the encoded String, same as {@link #get()}.
+         * </p>
+         */
         @Override
         public final String toString() { return s; }
 
@@ -366,9 +373,9 @@ public class Uri {
         public Encoded concat(final Encoded encoded) { return new Encoded(s.concat(encoded.s)); }
 
         /** See {@link String#substring(int)}. */
-        public final String substring(final int start) { return s.substring(start); }
+        public final Encoded substring(final int start) { return new Encoded(s.substring(start)); }
         /** See {@link String#substring(int, int)}. */
-        public final String substring(final int start, final int end) { return s.substring(start, end); }
+        public final Encoded substring(final int start, final int end) { return new Encoded(s.substring(start, end)); }
 
         /** See {@link String#indexOf(int)}. */
         public final int indexOf(final int ch) { return s.indexOf(ch); }
@@ -864,28 +871,19 @@ public class Uri {
         if( !reencode) {
             return new Uri(new Encoded(uri.toString()));
         }
-        System.err.println("Uri.createByURI(reencode):");
-        System.err.println("       source: "+uri.toString());
         final Uri recomposedUri;
         if( uri.isOpaque()) {
             // opaque, without host validation
-            System.err.println("  opaq source: ssp: "+uri.getSchemeSpecificPart());
-            System.err.println("  opaq source: frg: "+uri.getFragment());
             recomposedUri = Uri.create(uri.getScheme(), uri.getSchemeSpecificPart(), uri.getFragment());
         } else if( null != uri.getHost() ) {
             // with host validation
-            System.err.println("  host source: ssp: "+uri.getSchemeSpecificPart());
-            System.err.println("  host source: frg: "+uri.getFragment());
             recomposedUri = Uri.create(uri.getScheme(), uri.getUserInfo(), uri.getHost(), uri.getPort(),
                                        uri.getPath(), uri.getQuery(), uri.getFragment());
         } else {
             // without host validation
-            System.err.println("       source: ssp: "+uri.getSchemeSpecificPart());
-            System.err.println("       source: frg: "+uri.getFragment());
             recomposedUri = Uri.create(uri.getScheme(), uri.getAuthority(),
                                        uri.getPath(), uri.getQuery(), uri.getFragment());
         }
-        System.err.println("       result: "+recomposedUri.toString());
         return recomposedUri;
     }
 
@@ -919,8 +917,8 @@ public class Uri {
 
     private int hash;
 
-    /** Plain {@code scheme}, {@code null} if undefined. */
-    public final String scheme;
+    /** Encoded {@code scheme}, {@code null} if undefined. */
+    public final Encoded scheme;
 
     /** Encoded {@code scheme-specific-part}, never {@code null}. */
     public final Encoded schemeSpecificPart;
@@ -970,7 +968,7 @@ public class Uri {
 
     /** Returns true, if this instance is a {@code file} {@code scheme}, otherwise false. */
     public final boolean isFileScheme() {
-        return IOUtil.FILE_SCHEME.equals( scheme );
+        return IOUtil.FILE_SCHEME.equals( scheme.get() );
     }
 
     /**
@@ -1134,7 +1132,7 @@ public class Uri {
                 if (0 > idx) {
                     throw new URISyntaxException(input.get(), "missing jar separator");
                 }
-                sb.append( schemeSpecificPart.substring(0, idx) ); // exclude '!/'
+                sb.append( schemeSpecificPart.get().substring(0, idx) ); // exclude '!/'
             } else {
                 sb.append( schemeSpecificPart.get() );
             }
@@ -1184,7 +1182,7 @@ public class Uri {
                 if( e <  pl - 1 ) {
                     // path is file or has a query
                     try {
-                        return new Uri(new Encoded( scheme+IOUtil.SCHEME_SEPARATOR+schemeSpecificPart.substring(0, e+1) ));
+                        return new Uri( new Encoded( scheme.get()+IOUtil.SCHEME_SEPARATOR+schemeSpecificPart.get().substring(0, e+1) ) );
                     } catch (final URISyntaxException ue) {
                         // not complete, hence removed authority, or even root folder -> return null
                     }
@@ -1193,7 +1191,7 @@ public class Uri {
                 final int p = schemeSpecificPart.lastIndexOf("/", e-1);
                 if( p > 0 ) {
                     try {
-                        return new Uri(new Encoded( scheme+IOUtil.SCHEME_SEPARATOR+schemeSpecificPart.substring(0, p+1) ));
+                        return new Uri( new Encoded( scheme.get()+IOUtil.SCHEME_SEPARATOR+schemeSpecificPart.get().substring(0, p+1) ) );
                     } catch (final URISyntaxException ue) {
                         // not complete, hence removed authority, or even root folder -> return null
                     }
@@ -1256,7 +1254,7 @@ public class Uri {
         return compose(scheme, schemeSpecificPart, relativePath, fragment);
     }
 
-    static Uri compose(final String scheme, final Encoded schemeSpecificPart, final Encoded relativePath, final Encoded fragment) throws URISyntaxException {
+    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
@@ -1276,7 +1274,7 @@ public class Uri {
         }
         schemeSpecificPartS = IOUtil.cleanPathString( schemeSpecificPartS );
         final StringBuilder uri = new StringBuilder();
-        uri.append(scheme);
+        uri.append(scheme.get());
         uri.append(':');
         uri.append(schemeSpecificPartS);
         if ( null != query ) {
@@ -1430,16 +1428,16 @@ public class Uri {
         while ( ( index = first.indexOf('%', previndex) ) != -1 &&
                 second.indexOf('%', previndex) == index
               ) {
-            if( !first.substring(previndex, index).equals( second.substring(previndex, index) ) ) {
+            if( !first.get().substring(previndex, index).equals( second.get().substring(previndex, index) ) ) {
                 return false;
             }
-            if( !first.substring(index + 1, index + 3).equalsIgnoreCase( second.substring(index + 1, index + 3) ) ) {
+            if( !first.get().substring(index + 1, index + 3).equalsIgnoreCase( second.get().substring(index + 1, index + 3) ) ) {
                 return false;
             }
             index += 3;
             previndex = index;
         }
-        return first.substring(previndex).equals( second.substring(previndex) );
+        return first.get().substring(previndex).equals( second.get().substring(previndex) );
     }
 
     /*
@@ -1450,7 +1448,7 @@ public class Uri {
     private String getHashString() {
         final StringBuilder result = new StringBuilder();
         if (scheme != null) {
-            result.append(scheme.toLowerCase());
+            result.append(scheme.get().toLowerCase());
             result.append(IOUtil.SCHEME_SEPARATOR);
         }
         if (opaque) {
@@ -1531,7 +1529,7 @@ public class Uri {
            ) {
             // the characters up to the first ':' comprise the scheme
             absolute = true;
-            scheme = temp.substring(0, indexSchemeSep);
+            scheme = new Encoded( temp.substring(0, indexSchemeSep) );
             if (scheme.length() == 0) {
                 failExpecting(input, "scheme", indexSchemeSep);
             }
@@ -1696,13 +1694,13 @@ public class Uri {
         }
     }
 
-    private static void validateScheme(final Encoded uri, final String scheme, final int index) throws URISyntaxException {
+    private static void validateScheme(final Encoded uri, final Encoded scheme, final int index) throws URISyntaxException {
         // first char needs to be an alpha char
         final char ch = scheme.charAt(0);
         if ( !((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z')) ) {
             fail(uri, "invalid scheme", index);
         }
-        final int errIdx = validateAlphaNum(scheme, "+-.");
+        final int errIdx = validateAlphaNum(scheme.get(), "+-.");
         if( 0 <= errIdx ) {
             fail(uri, "invalid scheme", index+errIdx);
         }
diff --git a/src/junit/com/jogamp/common/net/TestUri01.java b/src/junit/com/jogamp/common/net/TestUri01.java
index 361304c..652caa6 100644
--- a/src/junit/com/jogamp/common/net/TestUri01.java
+++ b/src/junit/com/jogamp/common/net/TestUri01.java
@@ -28,7 +28,7 @@ public class TestUri01 extends JunitTracer {
             final String vanilla = "XXX ! # $ & ' ( ) * + , / : ; = ? @ [ ]";
             final Uri.Encoded escaped = Uri.Encoded.cast("XXX%20!%20%23%20%24%20%26%20%27%20%28%20%29%20%2A%20%2B%20%2C%20/%20%3A%20%3B%20%3D%20%3F%20%40%20%5B%20%5D");
             System.err.println("vanilla "+vanilla);
-            final Uri.Encoded esc1 = new Uri.Encoded(vanilla, Uri.PATH_MIN_LEGAL);
+            final Uri.Encoded esc1 = new Uri.Encoded(vanilla, Uri.PATH_LEGAL);
             System.err.println("esc1 "+esc1);
             Assert.assertEquals(escaped, esc1);
 
@@ -40,7 +40,7 @@ public class TestUri01 extends JunitTracer {
             final String vanilla = "/XXX R!# R$&'()*+,/:;=?z@y[x]";
             final Uri.Encoded escaped = Uri.Encoded.cast("/XXX%20R!%23%20R%24%26%27%28%29%2A%2B%2C/%3A%3B%3D%3Fz%40y%5Bx%5D");
             System.err.println("vanilla "+vanilla);
-            final Uri.Encoded esc1 = new Uri.Encoded(vanilla, Uri.PATH_MIN_LEGAL);
+            final Uri.Encoded esc1 = new Uri.Encoded(vanilla, Uri.PATH_LEGAL);
             System.err.println("esc1 "+esc1);
             Assert.assertEquals(escaped, esc1);
 
@@ -63,9 +63,9 @@ public class TestUri01 extends JunitTracer {
     public void test01URIEscapeCommonChars() throws IOException, URISyntaxException {
         {
             final String vanilla = "/XXX \"%-.<>\\^_`{|}~";
-            final Uri.Encoded escaped = Uri.Encoded.cast("/XXX%20%22%25-.%3C%3E%5C%5E_%60%7B%7C%7D%7E");
+            final Uri.Encoded escaped = Uri.Encoded.cast("/XXX%20%22%25-.%3C%3E%5C%5E_%60%7B%7C%7D~");
             System.err.println("vanilla "+vanilla);
-            final Uri.Encoded esc1 = new Uri.Encoded(vanilla, Uri.PATH_MIN_LEGAL);
+            final Uri.Encoded esc1 = new Uri.Encoded(vanilla, Uri.PATH_LEGAL);
             System.err.println("esc1 "+esc1);
             Assert.assertEquals(escaped, esc1);
 
diff --git a/src/junit/com/jogamp/common/net/TestUri03Resolving.java b/src/junit/com/jogamp/common/net/TestUri03Resolving.java
index 7c1dadf..9445100 100644
--- a/src/junit/com/jogamp/common/net/TestUri03Resolving.java
+++ b/src/junit/com/jogamp/common/net/TestUri03Resolving.java
@@ -2,7 +2,6 @@ package com.jogamp.common.net;
 
 import java.io.File;
 import java.io.IOException;
-import java.net.URI;
 import java.net.URISyntaxException;
 import java.net.URL;
 import java.net.URLConnection;
@@ -146,7 +145,7 @@ public class TestUri03Resolving extends JunitTracer {
                       "C:\\gluegen\\build-x86_64 öä lala\\gluegen-rt.jar"},
 
         new String[] {"\\\\filehost\\gluegen\\build-x86_64 öä lala\\gluegen-rt.jar",
-                      "file:////filehost/gluegen/build-x86_64%20öä%20lala/gluegen-rt.jar",
+                      "file://filehost/gluegen/build-x86_64%20öä%20lala/gluegen-rt.jar",
                       "\\\\filehost\\gluegen\\build-x86_64 öä lala\\gluegen-rt.jar"},
 
         new String[] {"C:/gluegen/A$/B^/C~/D#/E[/F]/gluegen-rt.jar",
@@ -167,71 +166,51 @@ public class TestUri03Resolving extends JunitTracer {
         System.err.println("file.path.dec "+file.getPath());
         System.err.println("file.path.abs "+file.getAbsolutePath());
         System.err.println("file.path.can "+file.getCanonicalPath());
-        final URI uri0 = file.toURI();
-        System.err.println("uri0.string: "+uri0.toString());
-        System.err.println("uri0.path  : "+uri0.getPath());
-        System.err.println("uri0.ascii : "+uri0.toASCIIString());
+        final Uri uri0 = Uri.valueOf(file);
+        URIDumpUtil.showUri(uri0);
+        URIDumpUtil.showReencodedURIOfUri(uri0);
+
         boolean ok = true;
-        {
-            final URI uri1 = Uri.valueOf(file).toURI();
-            final boolean equalString= uri0.toString().equals(uri1.toString());
-            final boolean equalPath = uri0.getPath().equals(uri1.getPath());
-            final boolean equalASCII= uri0.toASCIIString().equals(uri1.toASCIIString());
-            System.err.println("uri1.string: "+uri1.toString()+" - "+(equalString?"OK":"ERROR"));
-            System.err.println("uri1.path  : "+uri1.getPath()+" - "+(equalPath?"OK":"ERROR"));
-            System.err.println("uri1.ascii : "+uri1.toASCIIString()+" - "+(equalASCII?"OK":"ERROR"));
-            ok = equalString && equalPath && equalASCII && ok;
-        }
         {
             final String s2 = IOUtil.slashify(file.getAbsolutePath(), true /* startWithSlash */, file.isDirectory() /* endWithSlash */);
             System.err.println("uri2.slashify: "+s2);
-            {
-                final URI uri1 = new URI(IOUtil.FILE_SCHEME, null, s2, null);
-                final boolean equalString= uri0.toString().equals(uri1.toString());
-                final boolean equalPath = uri0.getPath().equals(uri1.getPath());
-                final boolean equalASCII= uri0.toASCIIString().equals(uri1.toASCIIString());
-                System.err.println("uri2.string: "+uri1.toString()+" - "+(equalString?"OK":"ERROR"));
-                System.err.println("uri2.path  : "+uri1.getPath()+" - "+(equalPath?"OK":"ERROR"));
-                System.err.println("uri2.ascii : "+uri1.toASCIIString()+" - "+(equalASCII?"OK":"ERROR"));
-                ok = equalString && equalPath && equalASCII && ok;
-            }
-            final URI uri1 = new URI(IOUtil.FILE_SCHEME, null, s2, null);
-            final boolean equalString= uri0.toString().equals(uri1.toString());
-            final boolean equalPath = uri0.getPath().equals(uri1.getPath());
-            final boolean equalASCII= uri0.toASCIIString().equals(uri1.toASCIIString());
-            System.err.println("uri2.string: "+uri1.toString()+" - "+(equalString?"OK":"ERROR"));
-            System.err.println("uri2.path  : "+uri1.getPath()+" - "+(equalPath?"OK":"ERROR"));
-            System.err.println("uri2.ascii : "+uri1.toASCIIString()+" - "+(equalASCII?"OK":"ERROR"));
-            ok = equalString && equalPath && equalASCII && ok;
+            final Uri uri1 = Uri.create(IOUtil.FILE_SCHEME, null, s2, null);
+            final boolean equalEncoded= uri0.getEncoded().equals(uri1.getEncoded());
+            final boolean equalPath = uri0.path.decode().equals(uri1.path.decode());
+            final boolean equalASCII= uri0.toASCIIString().equals(uri1.toASCIIString().get());
+            System.err.println("uri2.enc   : "+uri1.getEncoded()+" - "+(equalEncoded?"OK":"ERROR"));
+            System.err.println("uri2.pathD : "+uri1.path.decode()+" - "+(equalPath?"OK":"ERROR"));
+            System.err.println("uri2.asciiE: "+uri1.toASCIIString()+" - "+(equalASCII?"OK":"ERROR"));
+            ok = equalEncoded && equalPath && equalASCII && ok;
         }
         {
             final String s2 = "/"+string;
             System.err.println("uri3.orig: "+s2);
-            final URI uri1 = new URI(IOUtil.FILE_SCHEME, s2, null);
+            final Uri uri1 = Uri.create(IOUtil.FILE_SCHEME, s2, null);
             final String rString = "file:/Hallo%20Welt%20öä";
             final String rPath = s2;
             final String rASCII = "file:/Hallo%20Welt%20%C3%B6%C3%A4";
-            final boolean equalString= rString.equals(uri1.toString());
-            final boolean equalPath = rPath.equals(uri1.getPath());
-            final boolean equalASCII= rASCII.equals(uri1.toASCIIString());
-            System.err.println("uri3.string: "+uri1.toString()+" - "+(equalString?"OK":"ERROR"));
-            System.err.println("uri3.path  : "+uri1.getPath()+" - "+(equalPath?"OK":"ERROR"));
-            System.err.println("uri3.ascii : "+uri1.toASCIIString()+" - "+(equalASCII?"OK":"ERROR"));
-            ok = equalString && equalPath && equalASCII && ok;
+            final boolean equalEncoded = rString.equals(uri1.toString());
+            final boolean equalPath = rPath.equals(uri1.path.decode());
+            final boolean equalASCII= rASCII.equals(uri1.toASCIIString().get());
+            System.err.println("uri3.enc   : "+uri1.toString()+" - "+(equalEncoded?"OK":"ERROR"));
+            System.err.println("uri3.pathD : "+uri1.path.decode()+" - "+(equalPath?"OK":"ERROR"));
+            System.err.println("uri3.asciiE: "+uri1.toASCIIString()+" - "+(equalASCII?"OK":"ERROR"));
+            ok = equalEncoded && equalPath && equalASCII && ok;
         }
         {
             final String s2 = "//lala.org/"+string;
             System.err.println("uri4.orig: "+s2);
-            final URI uri1 = new URI(IOUtil.HTTP_SCHEME, s2, null);
+            final Uri uri1 = Uri.create(IOUtil.HTTP_SCHEME, s2, null);
             final String rString = "http://lala.org/Hallo%20Welt%20öä";
             final String rPath = "/"+string;
             final String rASCII = "http://lala.org/Hallo%20Welt%20%C3%B6%C3%A4";
             final boolean equalString= rString.equals(uri1.toString());
-            final boolean equalPath = rPath.equals(uri1.getPath());
-            final boolean equalASCII= rASCII.equals(uri1.toASCIIString());
-            System.err.println("uri4.string: "+uri1.toString()+" - "+(equalString?"OK":"ERROR"));
-            System.err.println("uri4.path  : "+uri1.getPath()+" - "+(equalPath?"OK":"ERROR"));
-            System.err.println("uri4.ascii : "+uri1.toASCIIString()+" - "+(equalASCII?"OK":"ERROR"));
+            final boolean equalPath = rPath.equals(uri1.path.decode());
+            final boolean equalASCII= rASCII.equals(uri1.toASCIIString().get());
+            System.err.println("uri4.enc   : "+uri1.toString()+" - "+(equalString?"OK":"ERROR"));
+            System.err.println("uri4.pathD : "+uri1.path.decode()+" - "+(equalPath?"OK":"ERROR"));
+            System.err.println("uri4.asciiE: "+uri1.toASCIIString()+" - "+(equalASCII?"OK":"ERROR"));
             ok = equalString && equalPath && equalASCII && ok;
         }
         Assert.assertTrue("One or more errors occured see stderr above", ok);
-- 
cgit v1.2.3


From dde91a061cb0bc209442fe0e74a532b91d1bb4b8 Mon Sep 17 00:00:00 2001
From: Sven Gothel <sgothel@jausoft.com>
Date: Mon, 8 Sep 2014 05:09:02 +0200
Subject: Bug 1063: Complete Uri impl. and adoption throughout GlueGen

- Uri:
  - Add error correction (fix) of path at ctor
    in case a a contained Uri is requested from an opaque one.
    The latter might come from URI/URL and is not properly encoded!
    See Uri.PARSE_HINT_FIX_PATH

  - Simplify conversion methods:
    - valueOf(URI/URL): Re-encode only if not opaque
    - getNativeFilePath() -> toFile()

  - Move IOUtil Uri related constants to class Uri

  - Add DEBUG and DEBUG_SHOWFIX

- Complete adoption of URI -> Uri changes
  - IOUtil, JarUtil and TempJarCache still holds some
    left over deprecated methods, which will be removed
    after officially starting 2.3.0 (JOGL dependencies)

  - Otherwise not URI utilization left

- Tests
  - Cleaned ip TestUri01 and TestUri03Resolving

  - TestUri03Resolving also tests URL -> Uri,
    and hence mentioned Uri.PARSE_HINT_FIX_PATH
---
 LICENSE.txt                                        |   3 +-
 make/scripts/java-win64.bat                        |   2 +-
 make/scripts/runtest-x64.bat                       |   1 +
 make/scripts/runtest.sh                            |   3 +-
 .../com/jogamp/common/jvm/JNILibLoaderBase.java    |  36 +--
 src/java/com/jogamp/common/net/Uri.java            | 352 +++++++++++++--------
 src/java/com/jogamp/common/os/Platform.java        |   8 +-
 src/java/com/jogamp/common/util/IOUtil.java        | 126 ++------
 src/java/com/jogamp/common/util/JarUtil.java       | 301 ++++++++----------
 .../com/jogamp/common/util/cache/TempJarCache.java | 155 +++++----
 src/junit/com/jogamp/common/net/TestUri01.java     | 268 +++++++---------
 .../com/jogamp/common/net/TestUri03Resolving.java  | 258 +++++++++------
 .../TestUri99LaunchOnReservedCharPathBug908.java   |  26 +-
 src/junit/com/jogamp/common/net/URIDumpUtil.java   |   6 +-
 src/junit/com/jogamp/common/util/TestJarUtil.java  |  40 ++-
 .../com/jogamp/common/util/TestTempJarCache.java   |  29 +-
 src/junit/com/jogamp/junit/sec/Applet01.java       |  93 +++---
 .../com/jogamp/junit/sec/TestSecIOUtil01.java      |  91 +++---
 .../jogamp/junit/util/VersionSemanticsUtil.java    |   9 +-
 19 files changed, 926 insertions(+), 881 deletions(-)

(limited to 'src/java/com/jogamp/common/net/Uri.java')

diff --git a/LICENSE.txt b/LICENSE.txt
index ba830c7..dcf6f8e 100755
--- a/LICENSE.txt
+++ b/LICENSE.txt
@@ -154,6 +154,7 @@ A.1) The GlueGen source tree contains code from The Apache Software Foundation
    Or within this repository: doc/licenses/Apache.LICENSE-2.0
 
    Files:
-    src/java/com/jogamp/common/net/Uri.java
+    - src/java/com/jogamp/common/net/Uri.java 
+      (derived from java.net.URI.Helper and heavily modified)
 
 
diff --git a/make/scripts/java-win64.bat b/make/scripts/java-win64.bat
index 27dff84..b467458 100755
--- a/make/scripts/java-win64.bat
+++ b/make/scripts/java-win64.bat
@@ -10,7 +10,7 @@ set LIB_DIR=..\%BLD_SUB%\test\build\natives
 
 set PATH=%JAVA_HOME%\bin;%ANT_PATH%\bin;c:\mingw\bin;%LIB_DIR%;%PATH%
 
-set CP_ALL=.;lib\junit.jar;%ANT_PATH%\lib\ant.jar;%ANT_PATH%\lib\ant-junit.jar;lib/semantic-versioning/semver.jar;%BLD_DIR%\gluegen-rt.jar;%BLD_DIR%\gluegen.jar;%BLD_DIR%\gluegen-test-util.jar;%BLD_DIR%\test\build\gluegen-test.jar
+set CP_ALL=.;lib\junit.jar;%ANT_PATH%\lib\ant.jar;%ANT_PATH%\lib\ant-junit.jar;lib/semantic-versioning/semver.jar;lib\TestJarsInJar.jar;%BLD_DIR%\gluegen-rt.jar;%BLD_DIR%\gluegen.jar;%BLD_DIR%\gluegen-test-util.jar;%BLD_DIR%\test\build\gluegen-test.jar
 
 echo CP_ALL %CP_ALL%
 
diff --git a/make/scripts/runtest-x64.bat b/make/scripts/runtest-x64.bat
index f6dda45..4344c75 100755
--- a/make/scripts/runtest-x64.bat
+++ b/make/scripts/runtest-x64.bat
@@ -6,6 +6,7 @@ REM scripts\java-win64.bat com.jogamp.common.util.TestTempJarCache
 REM scripts\java-win64.bat com.jogamp.common.os.TestElfReader01
 
 REM scripts\java-win64.bat com.jogamp.common.util.TestIOUtil01
+REM scripts\java-win64.bat com.jogamp.common.util.TestJarUtil
 REM scripts\java-win64.bat com.jogamp.common.net.TestUrisWithAssetHandler
 REM scripts\java-win64.bat com.jogamp.common.net.TestURIQueryProps
 REM scripts\java-win64.bat com.jogamp.common.net.TestUri01
diff --git a/make/scripts/runtest.sh b/make/scripts/runtest.sh
index c996305..a02f22e 100755
--- a/make/scripts/runtest.sh
+++ b/make/scripts/runtest.sh
@@ -46,7 +46,8 @@ rm -f $LOG
 #D_ARGS="-Djogamp.debug.TempJarCache"
 #D_ARGS="-Djogamp.debug.TempFileCache"
 #D_ARGS="-Djogamp.debug.IOUtil -Djogamp.debug.JNILibLoader -Djogamp.debug.TempFileCache -Djogamp.debug.JarUtil -Djava.io.tmpdir=/run/tmp"
-D_ARGS="-Djogamp.debug.IOUtil -Djogamp.debug.JNILibLoader -Djogamp.debug.TempFileCache -Djogamp.debug.JarUtil -Djogamp.debug.TempJarCache"
+#D_ARGS="-Djogamp.debug.IOUtil -Djogamp.debug.JNILibLoader -Djogamp.debug.TempFileCache -Djogamp.debug.JarUtil -Djogamp.debug.TempJarCache"
+#D_ARGS="-Djogamp.debug.IOUtil -Djogamp.debug.JarUtil -Djogamp.debug.TempJarCache -Djogamp.debug.Uri -Djogamp.debug.Uri.ShowFix"
 #D_ARGS="-Djogamp.debug.JNILibLoader -Djogamp.gluegen.UseTempJarCache=false"
 #D_ARGS="-Djogamp.debug.JNILibLoader -Djogamp.debug.TempJarCache"
 #D_ARGS="-Djogamp.debug.JNILibLoader"
diff --git a/src/java/com/jogamp/common/jvm/JNILibLoaderBase.java b/src/java/com/jogamp/common/jvm/JNILibLoaderBase.java
index 4bb68d3..9b1865f 100644
--- a/src/java/com/jogamp/common/jvm/JNILibLoaderBase.java
+++ b/src/java/com/jogamp/common/jvm/JNILibLoaderBase.java
@@ -42,7 +42,6 @@ package com.jogamp.common.jvm;
 import java.io.IOException;
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
-import java.net.URI;
 import java.net.URISyntaxException;
 import java.net.URL;
 import java.security.AccessController;
@@ -52,8 +51,8 @@ import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
 
+import com.jogamp.common.net.Uri;
 import com.jogamp.common.os.NativeLibrary;
-import com.jogamp.common.util.IOUtil;
 import com.jogamp.common.util.JarUtil;
 import com.jogamp.common.util.PropertyAccess;
 import com.jogamp.common.util.cache.TempJarCache;
@@ -155,7 +154,7 @@ public class JNILibLoaderBase {
   /**
    *
    * @param classFromJavaJar
-   * @param classJarURI
+   * @param classJarUri
    * @param jarBasename jar basename w/ suffix
    * @param nativeJarBasename native jar basename w/ suffix
    * @return
@@ -163,15 +162,15 @@ public class JNILibLoaderBase {
    * @throws SecurityException
    * @throws URISyntaxException
    */
-  private static final boolean addNativeJarLibsImpl(final Class<?> classFromJavaJar, final URI classJarURI,
-                                                    final String jarBasename, final String nativeJarBasename)
+  private static final boolean addNativeJarLibsImpl(final Class<?> classFromJavaJar, final Uri classJarUri,
+                                                    final Uri.Encoded jarBasename, final Uri.Encoded nativeJarBasename)
     throws IOException, SecurityException, URISyntaxException
   {
     if (DEBUG) {
         final StringBuilder msg = new StringBuilder();
         msg.append("JNILibLoaderBase: addNativeJarLibsImpl(").append(PlatformPropsImpl.NEWLINE);
         msg.append("  classFromJavaJar  = ").append(classFromJavaJar).append(PlatformPropsImpl.NEWLINE);
-        msg.append("  classJarURI       = ").append(classJarURI).append(PlatformPropsImpl.NEWLINE);
+        msg.append("  classJarURI       = ").append(classJarUri).append(PlatformPropsImpl.NEWLINE);
         msg.append("  jarBasename       = ").append(jarBasename).append(PlatformPropsImpl.NEWLINE);
         msg.append("  os.and.arch       = ").append(PlatformPropsImpl.os_and_arch).append(PlatformPropsImpl.NEWLINE);
         msg.append("  nativeJarBasename = ").append(nativeJarBasename).append(PlatformPropsImpl.NEWLINE);
@@ -181,15 +180,15 @@ public class JNILibLoaderBase {
 
     boolean ok = false;
 
-    final URI jarSubURI = JarUtil.getJarSubURI( classJarURI );
+    final Uri jarSubURI = classJarUri.getContainedUri();
     if (null == jarSubURI) {
-        throw new IllegalArgumentException("JarSubURI is null of: "+classJarURI);
+        throw new IllegalArgumentException("JarSubURI is null of: "+classJarUri);
     }
 
-    final String jarUriRoot_s = IOUtil.getURIDirname( jarSubURI.toString() );
+    final Uri jarSubUriRoot = jarSubURI.getDirectory();
 
     if (DEBUG) {
-        System.err.printf("JNILibLoaderBase: addNativeJarLibsImpl: initial: %s -> %s%n", jarSubURI, jarUriRoot_s);
+        System.err.printf("JNILibLoaderBase: addNativeJarLibsImpl: initial: %s -> %s%n", jarSubURI, jarSubUriRoot);
     }
 
     final String nativeLibraryPath = String.format("natives/%s/", PlatformPropsImpl.os_and_arch);
@@ -201,7 +200,7 @@ public class JNILibLoaderBase {
     if (null != nativeLibraryURI) {
         // We probably have one big-fat jar file, containing java classes
         // and all native platform libraries under 'natives/os.and.arch'!
-        final URI nativeJarURI = JarUtil.getJarFileURI(jarUriRoot_s+jarBasename);
+        final Uri nativeJarURI = JarUtil.getJarFileUri( jarSubUriRoot.getEncoded().concat(jarBasename) );
         try {
             if( TempJarCache.addNativeLibs(classFromJavaJar, nativeJarURI, nativeLibraryPath) ) {
                 ok = true;
@@ -218,7 +217,7 @@ public class JNILibLoaderBase {
     }
     if (!ok) {
         // We assume one slim native jar file per 'os.and.arch'!
-        final URI nativeJarURI = JarUtil.getJarFileURI(jarUriRoot_s+nativeJarBasename);
+        final Uri nativeJarURI = JarUtil.getJarFileUri( jarSubUriRoot.getEncoded().concat(nativeJarBasename) );
 
         if (DEBUG) {
             System.err.printf("JNILibLoaderBase: addNativeJarLibsImpl: module: %s -> %s%n", nativeJarBasename, nativeJarURI);
@@ -247,13 +246,13 @@ public class JNILibLoaderBase {
             }
         }
         final String os_and_arch_dot = PlatformPropsImpl.os_and_arch.replace('-', '.');
-        final String nativeJarTagClassName = nativeJarTagPackage + "." + moduleName + "." + os_and_arch_dot + ".TAG" ; // TODO: sync with gluegen-cpptasks-base.xml
+        final String nativeJarTagClassName = nativeJarTagPackage + "." + moduleName + "." + os_and_arch_dot + ".TAG"; // TODO: sync with gluegen-cpptasks-base.xml
         try {
             if(DEBUG) {
                 System.err.printf("JNILibLoaderBase: addNativeJarLibsImpl: ClassLoader/TAG: Locating module %s, os.and.arch %s: %s%n",
                         moduleName, os_and_arch_dot, nativeJarTagClassName);
             }
-            final URI nativeJarTagClassJarURI = JarUtil.getJarURI(nativeJarTagClassName, cl);
+            final Uri nativeJarTagClassJarURI = JarUtil.getJarUri(nativeJarTagClassName, cl);
             if (DEBUG) {
                 System.err.printf("JNILibLoaderBase: addNativeJarLibsImpl: ClassLoader/TAG: %s -> %s%n", nativeJarTagClassName, nativeJarTagClassJarURI);
             }
@@ -399,14 +398,14 @@ public class JNILibLoaderBase {
               }
 
               final ClassLoader cl = c.getClassLoader();
-              final URI classJarURI = JarUtil.getJarURI(c.getName(), cl);
-              final String jarName = JarUtil.getJarBasename(classJarURI);
+              final Uri classJarURI = JarUtil.getJarUri(c.getName(), cl);
+              final Uri.Encoded jarName = JarUtil.getJarBasename(classJarURI);
 
               if (jarName == null) {
                   continue;
               }
 
-              final String jarBasename = jarName.substring(0, jarName.indexOf(".jar"));
+              final Uri.Encoded jarBasename = jarName.substring(0, jarName.indexOf(".jar"));
 
               if(DEBUG) {
                   System.err.printf("JNILibLoaderBase: jarBasename: %s%n", jarBasename);
@@ -423,7 +422,8 @@ public class JNILibLoaderBase {
                   }
               }
 
-              final String nativeJarBasename = String.format("%s-natives-%s.jar", jarBasename, PlatformPropsImpl.os_and_arch);
+              final Uri.Encoded nativeJarBasename =
+                      Uri.Encoded.cast( String.format("%s-natives-%s.jar", jarBasename.get(), PlatformPropsImpl.os_and_arch) );
 
               ok = JNILibLoaderBase.addNativeJarLibsImpl(c, classJarURI, jarName, nativeJarBasename);
               if (ok) {
diff --git a/src/java/com/jogamp/common/net/Uri.java b/src/java/com/jogamp/common/net/Uri.java
index 8c641dd..d1f2016 100644
--- a/src/java/com/jogamp/common/net/Uri.java
+++ b/src/java/com/jogamp/common/net/Uri.java
@@ -1,8 +1,9 @@
 /**
- * Copyright 2013 JogAmp Community. All rights reserved.
+ * Copyright 2014 JogAmp Community. All rights reserved.
+ * Copyright 2006, 2010 The Apache Software Foundation.
  *
  * This code is derived from the Apache Harmony project's {@code class java.net.URI.Helper},
- * copyright 2006, 2010 The Apache Software Foundation (http://www.apache.org/).
+ * and has been heavily modified for GlueGen/JogAmp.
  *
  * Licensed to the Apache Software Foundation (ASF) under one or more
  * contributor license agreements.  See the LICENSE.txt file distributed with
@@ -24,13 +25,14 @@ package com.jogamp.common.net;
 import java.io.File;
 import java.io.UnsupportedEncodingException;
 import java.net.MalformedURLException;
-import java.net.URI;
 import java.net.URISyntaxException;
-import java.net.URL;
 import java.util.StringTokenizer;
 import java.util.regex.Pattern;
 
+import jogamp.common.Debug;
+
 import com.jogamp.common.util.IOUtil;
+import com.jogamp.common.util.PropertyAccess;
 
 /**
  * This class implements an immutable Uri as defined by <a href="https://tools.ietf.org/html/rfc2396">RFC 2396</a>.
@@ -156,6 +158,29 @@ import com.jogamp.common.util.IOUtil;
  * @since 2.2.1
  */
 public class Uri {
+    private static final boolean DEBUG;
+    private static final boolean DEBUG_SHOWFIX;
+
+    static {
+        Debug.initSingleton();
+        DEBUG = IOUtil.DEBUG || Debug.debug("Uri");
+        DEBUG_SHOWFIX = PropertyAccess.isPropertyDefined("jogamp.debug.Uri.ShowFix", true);
+    }
+
+    /**
+     * Usually used to fix a path from a previously contained and opaque Uri,
+     * i.e. {@link #getContainedUri()}.
+     * <p>
+     * Such an opaque Uri w/ erroneous encoding may have been injected via
+     * {@link #valueOf(URI)} and {@link #valueOf(URL)} where the given URL or URI was opaque!
+     * </p>
+     * <p>
+     * This remedies issues when dealing w/ java URI/URL opaque sources,
+     * which do not comply to the spec, i.e. containe un-encoded chars, e.g. ':', '$', ..
+     * </p>
+     */
+    private static final int PARSE_HINT_FIX_PATH = 1 << 0;
+
     private static final String DIGITS = "0123456789ABCDEF";
 
     private static final String ENCODING = "UTF8";
@@ -248,12 +273,25 @@ public class Uri {
     public static final String FRAG_LEGAL = UNRESERVED + RESERVED;
     // Harmony: unreserved + reserved
 
+    /** {@value} */
+    public static final char SCHEME_SEPARATOR = ':';
+    /** {@value} */
+    public static final char FRAGMENT_SEPARATOR = '#';
+    /** {@value} */
+    public static final String FILE_SCHEME = "file";
+    /** {@value} */
+    public static final String HTTP_SCHEME = "http";
+    /** {@value} */
+    public static final String HTTPS_SCHEME = "https";
+    /** {@value} */
+    public static final String JAR_SCHEME = "jar";
+    /** A JAR sub-protocol is separated from the JAR entry w/ this separator {@value}. Even if no class is specified '!/' must follow!. */
+    public static final char JAR_SCHEME_SEPARATOR = '!';
+
     /**
      * Immutable RFC3986 encoded string.
      */
     public static class Encoded implements Comparable<Encoded>, CharSequence {
-        public static final Encoded EMPTY = new Encoded("");
-
         private final String s;
 
         /**
@@ -395,6 +433,11 @@ 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, int)} */
+        public boolean startsWith(final String prefix, final int toffset) { return s.startsWith(prefix, toffset); }
+        /** See {@link String#endsWith(String)} */
+        public boolean endsWith(final String suffix) { return s.endsWith(suffix); }
+
         /** See {@link String#equalsIgnoreCase(String)}. */
         public final boolean equalsIgnoreCase(final Encoded anotherEncoded) { return s.equalsIgnoreCase(anotherEncoded.s); }
     }
@@ -609,18 +652,18 @@ public class Uri {
         final StringBuilder uri = new StringBuilder();
         if ( !emptyString(scheme) ) {
             uri.append(scheme);
-            uri.append(IOUtil.SCHEME_SEPARATOR);
+            uri.append(SCHEME_SEPARATOR);
         }
         if ( !emptyString(ssp) ) {
             // QUOTE ILLEGAL CHARACTERS
             uri.append(encode(ssp, SSP_LEGAL));
         }
         if ( !emptyString(fragment) ) {
-            uri.append(IOUtil.FRAGMENT_SEPARATOR);
+            uri.append(FRAGMENT_SEPARATOR);
             // QUOTE ILLEGAL CHARACTERS
             uri.append(encode(fragment, FRAG_LEGAL));
         }
-        return new Uri(new Encoded(uri.toString()), false);
+        return new Uri(new Encoded(uri.toString()), false, 0);
     }
 
     /**
@@ -658,7 +701,7 @@ public class Uri {
         final StringBuilder uri = new StringBuilder();
         if ( !emptyString(scheme) ) {
             uri.append(scheme);
-            uri.append(IOUtil.SCHEME_SEPARATOR);
+            uri.append(SCHEME_SEPARATOR);
         }
 
         if ( !emptyString(userinfo) || !emptyString(host) || port != -1) {
@@ -674,7 +717,7 @@ public class Uri {
         if ( !emptyString(host) ) {
             // check for ipv6 addresses that hasn't been enclosed
             // in square brackets
-            if (host.indexOf(IOUtil.SCHEME_SEPARATOR) != -1 && host.indexOf(']') == -1
+            if (host.indexOf(SCHEME_SEPARATOR) != -1 && host.indexOf(']') == -1
                     && host.indexOf('[') == -1) {
                 host = "[" + host + "]";
             }
@@ -682,7 +725,7 @@ public class Uri {
         }
 
         if ( port != -1 ) {
-            uri.append(IOUtil.SCHEME_SEPARATOR);
+            uri.append(SCHEME_SEPARATOR);
             uri.append(port);
         }
 
@@ -699,10 +742,10 @@ public class Uri {
 
         if ( !emptyString(fragment) ) {
             // QUOTE ILLEGAL CHARS
-            uri.append(IOUtil.FRAGMENT_SEPARATOR);
+            uri.append(FRAGMENT_SEPARATOR);
             uri.append(encode(fragment, FRAG_LEGAL));
         }
-        return new Uri(new Encoded(uri.toString()), true);
+        return new Uri(new Encoded(uri.toString()), true, 0);
     }
 
     /**
@@ -759,7 +802,7 @@ public class Uri {
         final StringBuilder uri = new StringBuilder();
         if ( !emptyString(scheme) ) {
             uri.append(scheme);
-            uri.append(IOUtil.SCHEME_SEPARATOR);
+            uri.append(SCHEME_SEPARATOR);
         }
         if ( !emptyString(authority) ) {
             uri.append("//");
@@ -778,10 +821,10 @@ public class Uri {
         }
         if ( !emptyString(fragment) ) {
             // QUOTE ILLEGAL CHARS
-            uri.append(IOUtil.FRAGMENT_SEPARATOR);
+            uri.append(FRAGMENT_SEPARATOR);
             uri.append(encode(fragment, FRAG_LEGAL));
         }
-        return new Uri(new Encoded(uri.toString()), false);
+        return new Uri(new Encoded(uri.toString()), false, 0);
     }
 
     /**
@@ -820,13 +863,13 @@ public class Uri {
         }
 
         final StringBuilder uri = new StringBuilder();
-        uri.append(IOUtil.FILE_SCHEME);
-        uri.append(IOUtil.SCHEME_SEPARATOR);
+        uri.append(FILE_SCHEME);
+        uri.append(SCHEME_SEPARATOR);
 
         // QUOTE ILLEGAL CHARS
         uri.append(encode(path, PATH_LEGAL));
 
-        return new Uri(new Encoded(uri.toString()), false);
+        return new Uri(new Encoded(uri.toString()), false, 0);
     }
 
     /**
@@ -852,45 +895,41 @@ public class Uri {
     /**
      * Creates a new Uri instance using the given URI instance.
      * <p>
-     * If {@code reencode} is {@code true}, decomposes and decoded parts of the given URI
-     * and reconstruct a new encoded Uri instance, re-encoding will be performed.
+     * Re-encoding will be performed if the given URI is {@link URI#isOpaque() not opaque}.
      * </p>
      * <p>
-     * If {@code reencode} is {@code false}, the encoded {@link URI#toString()} is being used for the new Uri instance,
-     * i.e. no re-encoding will be performed.
+     * See {@link #PARSE_HINT_FIX_PATH} for issues of injecting opaque URLs.
      * </p>
      *
      * @param uri A given URI instance
-     * @param reencode flag whether re-encoding shall be performed
-     *
      * @throws URISyntaxException
      *             if the temporary created string doesn't fit to the
      *             specification RFC2396 or could not be parsed correctly.
      */
-    public static Uri valueOf(final URI uri, final boolean reencode) throws URISyntaxException {
-        if( !reencode) {
-            return new Uri(new Encoded(uri.toString()));
-        }
-        final Uri recomposedUri;
+    public static Uri valueOf(final java.net.URI uri) throws URISyntaxException {
         if( uri.isOpaque()) {
-            // opaque, without host validation
-            recomposedUri = Uri.create(uri.getScheme(), uri.getSchemeSpecificPart(), uri.getFragment());
+            // 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
-            recomposedUri = Uri.create(uri.getScheme(), uri.getUserInfo(), uri.getHost(), uri.getPort(),
-                                       uri.getPath(), uri.getQuery(), uri.getFragment());
+            return Uri.create(uri.getScheme(), uri.getUserInfo(), uri.getHost(), uri.getPort(),
+                              uri.getPath(), uri.getQuery(), uri.getFragment());
         } else {
             // without host validation
-            recomposedUri = Uri.create(uri.getScheme(), uri.getAuthority(),
-                                       uri.getPath(), uri.getQuery(), uri.getFragment());
+            return Uri.create(uri.getScheme(), uri.getAuthority(),
+                              uri.getPath(), uri.getQuery(), uri.getFragment());
         }
-        return recomposedUri;
     }
 
     /**
-     * Creates a new Uri instance using the given URL instance.
+     * Creates a new Uri instance using the given URL instance,
+     * convenient wrapper for {@link #valueOf(URI)} and {@link URL#toURI()}.
+     * <p>
+     * Re-encoding will be performed if the given URL is {@link URI#isOpaque() not opaque}, see {@link #valueOf(URI)}.
+     * </p>
      * <p>
-     * No re-encoding will be performed.
+     * See {@link #PARSE_HINT_FIX_PATH} for issues of injecting opaque URLs.
      * </p>
      *
      * @param url A given URL instance
@@ -899,8 +938,8 @@ public class Uri {
      *             if the temporary created string doesn't fit to the
      *             specification RFC2396 or could not be parsed correctly.
      */
-    public static Uri valueOf(final URL url) throws URISyntaxException {
-        return new Uri(new Encoded(url.toString()));
+    public static Uri valueOf(final java.net.URL url) throws URISyntaxException {
+        return valueOf(url.toURI());
     }
 
     //
@@ -963,12 +1002,12 @@ public class Uri {
      *             specification RFC2396 and RFC3986 or could not be parsed correctly.
      */
     public Uri(final Encoded uri) throws URISyntaxException {
-        this(uri, false);
+        this(uri, false, 0);
     }
 
     /** Returns true, if this instance is a {@code file} {@code scheme}, otherwise false. */
     public final boolean isFileScheme() {
-        return IOUtil.FILE_SCHEME.equals( scheme.get() );
+        return FILE_SCHEME.equals( scheme.get() );
     }
 
     /**
@@ -1001,11 +1040,12 @@ public class Uri {
     /**
      * Returns a new {@link URI} instance using the encoded {@link #input} string, {@code new URI(uri.input)},
      * i.e. no re-encoding will be performed.
-     * @see #toURI(boolean)
+     * @see #toURIReencoded(boolean)
+     * @see #valueOf(URI)
      */
-    public final URI toURI() {
+    public final java.net.URI toURI() {
         try {
-            return new URI(input.get());
+            return new java.net.URI(input.get());
         } catch (final URISyntaxException e) {
             throw new Error(e); // Can't happen
         }
@@ -1014,35 +1054,29 @@ public class Uri {
     /**
      * Returns a new {@link URI} instance based upon this instance.
      * <p>
-     * If {@code reencode} is {@code true}, all Uri parts of this instance will be decoded
+     * All Uri parts of this instance will be decoded
      * and encoded by the URI constructor, i.e. re-encoding will be performed.
      * </p>
-     * <p>
-     * If {@code reencode} is {@code false}, this instance encoded {@link #input} string is being used, see {@link #toURI()},
-     * i.e. no re-encoding will be performed.
-     * </p>
      *
      * @throws URISyntaxException
      *             if the given string {@code uri} doesn't fit to the
      *             specification RFC2396 or could not be parsed correctly.
-     * @see #valueOf(URI, boolean)
+     * @see #toURI()
+     * @see #valueOf(URI)
      */
-    public final URI toURI(final boolean reencode) throws URISyntaxException {
-        if( !reencode ) {
-            return toURI();
-        }
-        final URI recomposedURI;
+    public final java.net.URI toURIReencoded() throws URISyntaxException {
+        final java.net.URI recomposedURI;
         if( opaque ) {
             // opaque, without host validation
-            recomposedURI = new URI(decode(scheme), decode(schemeSpecificPart), decode(fragment));
+            recomposedURI = new java.net.URI(decode(scheme), decode(schemeSpecificPart), decode(fragment));
         } else if( null != host ) {
             // with host validation
-            recomposedURI = new URI(decode(scheme), decode(userInfo), decode(host), port,
-                                    decode(path), decode(query), decode(fragment));
+            recomposedURI = new java.net.URI(decode(scheme), decode(userInfo), decode(host), port,
+                                             decode(path), decode(query), decode(fragment));
         } else {
             // without host validation
-            recomposedURI = new URI(decode(scheme), decode(authority),
-                                    decode(path), decode(query), decode(fragment));
+            recomposedURI = new java.net.URI(decode(scheme), decode(authority),
+                                             decode(path), decode(query), decode(fragment));
         }
         return recomposedURI;
     }
@@ -1055,11 +1089,11 @@ public class Uri {
      *             if an error occurs while creating the URL or no protocol
      *             handler could be found.
      */
-    public final URL toURL() throws MalformedURLException {
+    public final java.net.URL toURL() throws MalformedURLException {
         if (!absolute) {
             throw new IllegalArgumentException("Cannot convert relative Uri: "+input);
         }
-        return new URL(input.get());
+        return new java.net.URL(input.get());
     }
 
     /**
@@ -1071,11 +1105,12 @@ public class Uri {
      *   <li>slash -> backslash</li>
      *   <li>drop a starting single backslash, preserving windows UNC</li>
      * </ul>
+     * and returns the resulting new {@link File} instance.
      * <p>
      * Otherwise implementation returns {@code null}.
      * </p>
      */
-    public final String getNativeFilePath() {
+    public final File toFile() {
         if( isFileScheme() ) {
             final String authorityS;
             if( null == authority ) {
@@ -1083,17 +1118,16 @@ public class Uri {
             } else {
                 authorityS = "//"+authority.decode();
             }
-            // return IOUtil.decodeURIToFilePath(authorityS + path);
             final String path = authorityS+this.path.decode();
             if( File.separator.equals("\\") ) {
                 final String r = patternSingleFS.matcher(path).replaceAll("\\\\");
                 if( r.startsWith("\\") && !r.startsWith("\\\\") ) { // '\\\\' denotes UNC hostname, which shall not be cut-off
-                    return r.substring(1);
+                    return new File(r.substring(1));
                 } else {
-                    return r;
+                    return new File(r);
                 }
             }
-            return path;
+            return new File(path);
         }
         return null;
     }
@@ -1127,8 +1161,8 @@ public class Uri {
         if( !emptyString(schemeSpecificPart) ) {
             final StringBuilder sb = new StringBuilder();
 
-            if( scheme.equals(IOUtil.JAR_SCHEME) ) {
-                final int idx = schemeSpecificPart.lastIndexOf(IOUtil.JAR_SCHEME_SEPARATOR);
+            if( scheme.equals(JAR_SCHEME) ) {
+                final int idx = schemeSpecificPart.lastIndexOf(JAR_SCHEME_SEPARATOR);
                 if (0 > idx) {
                     throw new URISyntaxException(input.get(), "missing jar separator");
                 }
@@ -1137,16 +1171,20 @@ public class Uri {
                 sb.append( schemeSpecificPart.get() );
             }
             if ( !emptyString(fragment) ) {
-                sb.append(IOUtil.FRAGMENT_SEPARATOR);
+                sb.append(FRAGMENT_SEPARATOR);
                 sb.append(fragment);
             }
             try {
-                final Uri res = new Uri(new Encoded(sb.toString()), false);
+                final int parseHints = opaque ? PARSE_HINT_FIX_PATH : 0;
+                final Uri res = new Uri(new Encoded(sb.toString()), false, parseHints);
                 if( null != res.scheme ) {
                     return res;
                 }
             } catch(final URISyntaxException e) {
                 // OK, does not contain uri
+                if( DEBUG ) {
+                    e.printStackTrace();
+                }
             }
         }
         return null;
@@ -1160,7 +1198,7 @@ public class Uri {
      * Method is {@code jar-file-entry} aware, i.e. will return the parent entry if exists.
      * </p>
      * <p>
-     * If this Uri does not contain any path separator, method returns {@code null}.
+     * 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:
@@ -1182,7 +1220,7 @@ public class Uri {
                 if( e <  pl - 1 ) {
                     // path is file or has a query
                     try {
-                        return new Uri( new Encoded( scheme.get()+IOUtil.SCHEME_SEPARATOR+schemeSpecificPart.get().substring(0, e+1) ) );
+                        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
                     }
@@ -1191,7 +1229,7 @@ public class Uri {
                 final int p = schemeSpecificPart.lastIndexOf("/", e-1);
                 if( p > 0 ) {
                     try {
-                        return new Uri( new Encoded( scheme.get()+IOUtil.SCHEME_SEPARATOR+schemeSpecificPart.get().substring(0, p+1) ) );
+                        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
                     }
@@ -1201,6 +1239,22 @@ public class Uri {
         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) );
+        }
+    }
+
     /// NEW START
 
     /**
@@ -1232,7 +1286,11 @@ public class Uri {
         }
         try {
             return Uri.cast(uriS.substring(0, idx+1)); // exclude jar name, include terminal '/' or ':'
-        } catch (final URISyntaxException ue) {}
+        } catch (final URISyntaxException ue) {
+            if( DEBUG ) {
+                ue.printStackTrace();
+            }
+        }
         return null;
     }
 
@@ -1449,7 +1507,7 @@ public class Uri {
         final StringBuilder result = new StringBuilder();
         if (scheme != null) {
             result.append(scheme.get().toLowerCase());
-            result.append(IOUtil.SCHEME_SEPARATOR);
+            result.append(SCHEME_SEPARATOR);
         }
         if (opaque) {
             result.append(schemeSpecificPart.get());
@@ -1460,11 +1518,11 @@ public class Uri {
                     result.append(authority.get());
                 } else {
                     if (userInfo != null) {
-                        result.append(userInfo.get() + "@"); //$NON-NLS-1$
+                        result.append(userInfo.get() + "@");
                     }
                     result.append(host.get().toLowerCase());
                     if (port != -1) {
-                        result.append(IOUtil.SCHEME_SEPARATOR + port);
+                        result.append(SCHEME_SEPARATOR + port);
                     }
                 }
             }
@@ -1480,7 +1538,7 @@ public class Uri {
         }
 
         if (fragment != null) {
-            result.append(IOUtil.FRAGMENT_SEPARATOR);
+            result.append(FRAGMENT_SEPARATOR);
             result.append(fragment.get());
         }
         return convertHexToLowerCase(result.toString());
@@ -1490,21 +1548,20 @@ public class Uri {
      *
      * @param input
      * @param expectServer
+     * @param parseHints TODO
      * @throws URISyntaxException
      */
-    private Uri(final Encoded input, final boolean expectServer) throws URISyntaxException {
+    private Uri(final Encoded input, final boolean expectServer, final int parseHints) throws URISyntaxException {
         if( emptyString(input) ) {
             throw new URISyntaxException(input.get(), "empty input");
         }
-        this.input = input;
-
         String temp = input.get();
         int index;
         // parse into Fragment, Scheme, and SchemeSpecificPart
         // then parse SchemeSpecificPart if necessary
 
         // Fragment
-        index = temp.indexOf(IOUtil.FRAGMENT_SEPARATOR);
+        index = temp.indexOf(FRAGMENT_SEPARATOR);
         if (index != -1) {
             // remove the fragment from the end
             fragment = new Encoded( temp.substring(index + 1) );
@@ -1514,12 +1571,15 @@ public class Uri {
             fragment = null;
         }
 
+        String inputTemp = input.get(); // may get modified due to error correction
+
         // Scheme and SchemeSpecificPart
-        final int indexSchemeSep = temp.indexOf(IOUtil.SCHEME_SEPARATOR);
+        final int indexSchemeSep = temp.indexOf(SCHEME_SEPARATOR);
         index = indexSchemeSep;
         final int indexSSP = temp.indexOf('/');
         final int indexQuerySep = temp.indexOf('?');
-        final String schemeSpecificPartS;
+
+        String sspTemp; // may get modified due to error correction
 
         // if a '/' or '?' occurs before the first ':' the uri has no
         // specified scheme, and is therefore not absolute
@@ -1534,24 +1594,22 @@ public class Uri {
                 failExpecting(input, "scheme", indexSchemeSep);
             }
             validateScheme(input, scheme, 0);
-            schemeSpecificPartS = temp.substring(indexSchemeSep + 1);
-            schemeSpecificPart = new Encoded( schemeSpecificPartS );
-            if (schemeSpecificPart.length() == 0) {
+            sspTemp = temp.substring(indexSchemeSep + 1);
+            if (sspTemp.length() == 0) {
                 failExpecting(input, "scheme-specific-part", indexSchemeSep);
             }
         } else {
             absolute = false;
             scheme = null;
-            schemeSpecificPartS = temp;
-            schemeSpecificPart = new Encoded( schemeSpecificPartS );
+            sspTemp = temp;
         }
 
-        if ( scheme == null ||  schemeSpecificPartS.length() > 0 && schemeSpecificPartS.charAt(0) == '/' ) {
+        if ( scheme == null ||  sspTemp.length() > 0 && sspTemp.charAt(0) == '/' ) {
             // Uri is hierarchical, not opaque
             opaque = false;
 
             // Query
-            temp = schemeSpecificPartS;
+            temp = sspTemp;
             index = temp.indexOf('?');
             if (index != -1) {
                 query = new Encoded( temp.substring(index + 1) );
@@ -1561,19 +1619,24 @@ public class Uri {
                 query = null;
             }
 
+            String pathTemp; // may get modified due to error correction
+            final int indexPathInSSP;
+
             // Authority and Path
             if (temp.startsWith("//")) {
                 index = temp.indexOf('/', 2);
                 final String authorityS;
                 if (index != -1) {
                     authorityS = temp.substring(2, index);
-                    path = new Encoded( temp.substring(index) );
+                    pathTemp = temp.substring(index);
+                    indexPathInSSP = index;
                 } else {
                     authorityS = temp.substring(2);
                     if (authorityS.length() == 0 && query == null && fragment == null) {
                         failExpecting(input, "authority, path [, query, fragment]", index);
                     }
-                    path = Encoded.EMPTY;
+                    pathTemp = "";
+                    indexPathInSSP = -1;
                     // nothing left, so path is empty
                     // (not null, path should never be null if hierarchical/non-opaque)
                 }
@@ -1584,26 +1647,67 @@ public class Uri {
                     validateAuthority(input, authority, indexSchemeSep + 3);
                 }
             } else { // no authority specified
-                path = new Encoded( temp );
+                pathTemp = temp;
+                indexPathInSSP = 0;
                 authority = null;
             }
 
-            int pathIndex = 0;
+            int indexPath = 0; // in input
             if (indexSSP > -1) {
-                pathIndex += indexSSP;
+                indexPath += indexSSP;
+            }
+            if (indexPathInSSP > -1) {
+                indexPath += indexPathInSSP;
             }
-            if (index > -1) {
-                pathIndex += index;
+
+            final int pathErrIdx = validateEncoded(pathTemp, PATH_LEGAL);
+            if( 0 <= pathErrIdx ) {
+                // Perform error correction on PATH if requested!
+                if( 0 != ( parseHints & PARSE_HINT_FIX_PATH ) ) {
+                    if( DEBUG_SHOWFIX ) {
+                        System.err.println("Uri FIX_FILEPATH: input at index "+(indexPath+pathErrIdx)+": "+inputTemp);
+                        System.err.println("Uri FIX_FILEPATH: ssp at index   "+(indexPathInSSP+pathErrIdx)+": "+sspTemp);
+                        System.err.println("Uri FIX_FILEPATH: path  at index "+pathErrIdx+": "+pathTemp);
+                    }
+                    final int pathTempOldLen = pathTemp.length();
+                    pathTemp = encode( decode( pathTemp ), PATH_LEGAL); // re-encode, and hope for the best!
+                    validatePath(input, pathTemp, indexPath); // re-validate!
+                    {
+                        // Patch SSP + INPUT !
+                        final StringBuilder sb = new StringBuilder();
+                        if( indexPathInSSP > 0 ) {
+                            sb.append( sspTemp.substring(0, indexPathInSSP) );
+                        }
+                        sb.append( pathTemp ).append( sspTemp.substring( indexPathInSSP + pathTempOldLen ) );
+                        sspTemp = sb.toString(); // update
+
+                        sb.setLength(0);
+                        if( indexPath > 0 ) {
+                            sb.append( inputTemp.substring(0, indexPath) );
+                        }
+                        sb.append( pathTemp ).append( inputTemp.substring( indexPath + pathTempOldLen ) );
+                        inputTemp = sb.toString(); // update
+                    }
+                    if( DEBUG_SHOWFIX ) {
+                        System.err.println("Uri FIX_FILEPATH: result          : "+pathTemp);
+                        System.err.println("Uri FIX_FILEPATH: ssp after       : "+sspTemp);
+                        System.err.println("Uri FIX_FILEPATH: input after     : "+inputTemp);
+                    }
+                } else {
+                    fail(input, "invalid path", indexPath+pathErrIdx);
+                }
             }
-            validatePath(input, path, pathIndex);
+            path = new Encoded( pathTemp );
         } else {
             // Uri is not hierarchical, Uri is opaque
             opaque = true;
             query = null;
             path = null;
             authority = null;
-            validateSsp(input, schemeSpecificPart, indexSchemeSep + 1);
+            validateSsp(input, sspTemp, indexSchemeSep + 1);
         }
+        schemeSpecificPart = new Encoded( sspTemp );
+        this.input = inputTemp == input.get() ? input : new Encoded( inputTemp );
 
         /**
          * determine the host, port and userinfo if the authority parses
@@ -1635,7 +1739,7 @@ public class Uri {
                 hostindex = index + 1;
             }
 
-            index = temp.lastIndexOf(IOUtil.SCHEME_SEPARATOR);
+            index = temp.lastIndexOf(SCHEME_SEPARATOR);
             final int endindex = temp.indexOf(']');
 
             if (index != -1 && endindex < index) {
@@ -1706,7 +1810,7 @@ public class Uri {
         }
     }
 
-    private static void validateSsp(final Encoded uri, final Encoded ssp, final int index) throws URISyntaxException {
+    private static void validateSsp(final Encoded uri, final String ssp, final int index) throws URISyntaxException {
         final int errIdx = validateEncoded(ssp, SSP_LEGAL);
         if( 0 <= errIdx ) {
             fail(uri, "invalid scheme-specific-part", index+errIdx);
@@ -1714,13 +1818,13 @@ public class Uri {
     }
 
     private static void validateAuthority(final Encoded uri, final Encoded authority, final int index) throws URISyntaxException {
-        final int errIdx = validateEncoded(authority, AUTHORITY_LEGAL);
+        final int errIdx = validateEncoded(authority.get(), AUTHORITY_LEGAL);
         if( 0 <= errIdx ) {
             fail(uri, "invalid authority", index+errIdx);
         }
     }
 
-    private static void validatePath(final Encoded uri, final Encoded path, final int index) throws URISyntaxException {
+    private static void validatePath(final Encoded uri, final String path, final int index) throws URISyntaxException {
         final int errIdx = validateEncoded(path, PATH_LEGAL);
         if( 0 <= errIdx ) {
             fail(uri, "invalid path", index+errIdx);
@@ -1728,14 +1832,14 @@ public class Uri {
     }
 
     private static void validateQuery(final Encoded uri, final Encoded query, final int index) throws URISyntaxException {
-        final int errIdx = validateEncoded(query, QUERY_LEGAL);
+        final int errIdx = validateEncoded(query.get(), QUERY_LEGAL);
         if( 0 <= errIdx ) {
             fail(uri, "invalid query", index+errIdx);
         }
     }
 
     private static void validateFragment(final Encoded uri, final Encoded fragment, final int index) throws URISyntaxException {
-        final int errIdx = validateEncoded(fragment, FRAG_LEGAL);
+        final int errIdx = validateEncoded(fragment.get(), FRAG_LEGAL);
         if( 0 <= errIdx ) {
             fail(uri, "invalid fragment", index+errIdx);
         }
@@ -1802,10 +1906,10 @@ public class Uri {
             return false;
         }
         String label = null;
-        final StringTokenizer st = new StringTokenizer(hostS, "."); //$NON-NLS-1$
+        final StringTokenizer st = new StringTokenizer(hostS, ".");
         while (st.hasMoreTokens()) {
             label = st.nextToken();
-            if (label.startsWith("-") || label.endsWith("-")) { //$NON-NLS-1$ //$NON-NLS-2$
+            if (label.startsWith("-") || label.endsWith("-")) {
                 return false;
             }
         }
@@ -1854,7 +1958,7 @@ public class Uri {
         boolean doubleColon = false;
         int numberOfColons = 0;
         int numberOfPeriods = 0;
-        String word = ""; //$NON-NLS-1$
+        String word = "";
         char c = 0;
         char prevChar = 0;
         int offset = 0; // offset for [] ip addresses
@@ -1876,8 +1980,8 @@ public class Uri {
                     if (ipv6Address.charAt(length - 1) != ']') {
                         return false; // must have a close ]
                     }
-                    if ((ipv6Address.charAt(1) == IOUtil.SCHEME_SEPARATOR_CHAR)
-                            && (ipv6Address.charAt(2) != IOUtil.SCHEME_SEPARATOR_CHAR)) {
+                    if ((ipv6Address.charAt(1) == SCHEME_SEPARATOR)
+                            && (ipv6Address.charAt(2) != SCHEME_SEPARATOR)) {
                         return false;
                     }
                     offset = 1;
@@ -1913,14 +2017,14 @@ public class Uri {
                     // with
                     // an IPv4 ending, otherwise 7 :'s is bad
                     if (numberOfColons == 7
-                            && ipv6Address.charAt(0 + offset) != IOUtil.SCHEME_SEPARATOR_CHAR
-                            && ipv6Address.charAt(1 + offset) != IOUtil.SCHEME_SEPARATOR_CHAR) {
+                            && ipv6Address.charAt(0 + offset) != SCHEME_SEPARATOR
+                            && ipv6Address.charAt(1 + offset) != SCHEME_SEPARATOR) {
                         return false;
                     }
-                    word = ""; //$NON-NLS-1$
+                    word = "";
                     break;
 
-                case IOUtil.SCHEME_SEPARATOR_CHAR:
+                case SCHEME_SEPARATOR:
                     numberOfColons++;
                     if (numberOfColons > 7) {
                         return false;
@@ -1928,13 +2032,13 @@ public class Uri {
                     if (numberOfPeriods > 0) {
                         return false;
                     }
-                    if (prevChar == IOUtil.SCHEME_SEPARATOR_CHAR) {
+                    if (prevChar == SCHEME_SEPARATOR) {
                         if (doubleColon) {
                             return false;
                         }
                         doubleColon = true;
                     }
-                    word = ""; //$NON-NLS-1$
+                    word = "";
                     break;
 
                 default:
@@ -1963,8 +2067,8 @@ public class Uri {
             // If we have an empty word at the end, it means we ended in
             // either a : or a .
             // If we did not end in :: then this is invalid
-            if (word == "" && ipv6Address.charAt(length - 1 - offset) != IOUtil.SCHEME_SEPARATOR_CHAR //$NON-NLS-1$
-                    && ipv6Address.charAt(length - 2 - offset) != IOUtil.SCHEME_SEPARATOR_CHAR) {
+            if (word == "" && ipv6Address.charAt(length - 1 - offset) != SCHEME_SEPARATOR
+                    && ipv6Address.charAt(length - 2 - offset) != SCHEME_SEPARATOR) {
                 return false;
             }
         }
@@ -2005,7 +2109,7 @@ public class Uri {
      *            {@code java.lang.String} the characters allowed in the String
      *            s
      */
-    private static int validateEncoded(final Encoded encoded, final String legal) {
+    private static int validateEncoded(final String encoded, final String legal) {
         for (int i = 0; i < encoded.length();) {
             final char ch = encoded.charAt(i);
             if (ch == '%') {
diff --git a/src/java/com/jogamp/common/os/Platform.java b/src/java/com/jogamp/common/os/Platform.java
index 12122a2..513d215 100644
--- a/src/java/com/jogamp/common/os/Platform.java
+++ b/src/java/com/jogamp/common/os/Platform.java
@@ -28,12 +28,12 @@
 
 package com.jogamp.common.os;
 
-import java.net.URI;
 import java.security.AccessController;
 import java.security.PrivilegedAction;
 import java.util.concurrent.TimeUnit;
 
 import com.jogamp.common.jvm.JNILibLoaderBase;
+import com.jogamp.common.net.Uri;
 import com.jogamp.common.util.JarUtil;
 import com.jogamp.common.util.PropertyAccess;
 import com.jogamp.common.util.ReflectionUtil;
@@ -184,11 +184,11 @@ public class Platform extends PlatformPropsImpl {
 
                 final ClassLoader cl = Platform.class.getClassLoader();
 
-                final URI platformClassJarURI;
+                final Uri platformClassJarURI;
                 {
-                    URI _platformClassJarURI = null;
+                    Uri _platformClassJarURI = null;
                     try {
-                        _platformClassJarURI = JarUtil.getJarURI(Platform.class.getName(), cl);
+                        _platformClassJarURI = JarUtil.getJarUri(Platform.class.getName(), cl);
                     } catch (final Exception e) { }
                     platformClassJarURI = _platformClassJarURI;
                 }
diff --git a/src/java/com/jogamp/common/util/IOUtil.java b/src/java/com/jogamp/common/util/IOUtil.java
index 7a5b7ec..3a09835 100644
--- a/src/java/com/jogamp/common/util/IOUtil.java
+++ b/src/java/com/jogamp/common/util/IOUtil.java
@@ -40,7 +40,6 @@ import java.io.InputStream;
 import java.io.OutputStream;
 import java.io.PrintStream;
 import java.lang.reflect.Constructor;
-import java.net.URI;
 import java.net.URISyntaxException;
 import java.net.URL;
 import java.net.URLConnection;
@@ -60,23 +59,6 @@ import com.jogamp.common.os.Platform;
 public class IOUtil {
     public static final boolean DEBUG = Debug.debug("IOUtil");
 
-    /** {@value} */
-    public static final String SCHEME_SEPARATOR = ":";
-    /** {@value} */
-    public static final char SCHEME_SEPARATOR_CHAR = ':';
-    /** {@value} */
-    public static final char FRAGMENT_SEPARATOR = '#';
-    /** {@value} */
-    public static final String FILE_SCHEME = "file";
-    /** {@value} */
-    public static final String HTTP_SCHEME = "http";
-    /** {@value} */
-    public static final String HTTPS_SCHEME = "https";
-    /** {@value} */
-    public static final String JAR_SCHEME = "jar";
-    /** A JAR subprotocol is separeted from the JAR entry w/ this separator {@value}. Even if no class is specified '!/' must follow!. */
-    public static final char JAR_SCHEME_SEPARATOR = '!';
-
     /** Std. temporary directory property key <code>java.io.tmpdir</code>. */
     private static final String java_io_tmpdir_propkey = "java.io.tmpdir";
     private static final String user_home_propkey = "user.home";
@@ -295,7 +277,6 @@ public class IOUtil {
      */
 
     private static final Pattern patternSingleBS = Pattern.compile("\\\\{1}");
-    private static final Pattern patternSingleFS = Pattern.compile("/{1}");
 
     /**
      *
@@ -424,67 +405,6 @@ public class IOUtil {
         return fname;
     }
 
-    /**
-     * 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.
-     * <p>
-     * <i>protocol</i> may be "file", "http", etc..
-     * </p>
-     *
-     * @param uri "<i>protocol</i>:/some/path/gluegen-rt.jar"
-     * @return "<i>protocol</i>:/some/path/"
-     * @throws IllegalArgumentException if the URI doesn't match the expected formatting, or is null
-     * @throws URISyntaxException
-     * @deprecated Use {@link Uri#getDirectory()}
-     */
-    public static URI getURIDirname(final URI uri) throws IllegalArgumentException, URISyntaxException {
-        if(null == uri) {
-            throw new IllegalArgumentException("URI is null");
-        }
-        final String uriS = uri.toString();
-        if( DEBUG ) {
-            System.err.println("getURIDirname "+uri+", extForm: "+uriS);
-        }
-        return new URI( getURIDirname(uriS) );
-    }
-
-    /**
-     * 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.
-     * <p>
-     * <i>protocol</i> may be "file", "http", etc..
-     * </p>
-     *
-     * @param uri "<i>protocol</i>:/some/path/gluegen-rt.jar" (URI encoded)
-     * @return "<i>protocol</i>:/some/path/"
-     * @throws IllegalArgumentException if the URI doesn't match the expected formatting, or is null
-     * @throws URISyntaxException
-     * @deprecated Use {@link Uri#getDirectory()}
-     */
-    public static String getURIDirname(String uriS) throws IllegalArgumentException, URISyntaxException {
-        if(null == uriS) {
-            throw new IllegalArgumentException("uriS is null");
-        }
-        // 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 IllegalArgumentException("URI does not contain protocol terminator ':', in <"+uriS+">");
-            }
-        }
-        uriS = uriS.substring(0, idx+1); // exclude jar name, include terminal '/' or ':'
-
-        if( DEBUG ) {
-            System.err.println("getURIDirname res: "+uriS);
-        }
-        return uriS;
-    }
-
     /***
      *
      * RESOURCE LOCATION STUFF
@@ -615,6 +535,20 @@ public class IOUtil {
         return null;
     }
 
+    /**
+     * Wraps {@link #getRelativeOf(URI, String)} for convenience.
+     * @param relativePath denotes a relative file to the baseLocation's parent directory (URI encoded)
+     * @throws IOException
+     * @deprecated Use {@link Uri#getRelativeOf(com.jogamp.common.net.Uri.Encoded)}.
+     */
+    public static URL getRelativeOf(final URL baseURL, final String relativePath) throws IOException {
+        try {
+            return Uri.valueOf(baseURL).getRelativeOf(Uri.Encoded.cast(relativePath)).toURL();
+        } catch (final URISyntaxException e) {
+            throw new IOException(e);
+        }
+    }
+
     /**
      * Generates a path for the 'relativeFile' relative to the 'baseLocation'.
      *
@@ -698,31 +632,19 @@ public class IOUtil {
      * <p>
      * Otherwise it returns the {@link URI#toASCIIString()} encoded URI.
      * </p>
-     * @deprecated Use {@link Uri#getNativeFilePath()}.
+     * @deprecated Use {@link Uri#toFile()}
      */
-    public static String decodeURIIfFilePath(final URI uri) {
-        if( IOUtil.FILE_SCHEME.equals( uri.getScheme() ) ) {
-            final String authorityS;
-            {
-                final String authority = uri.getAuthority();
-                if( null == authority ) {
-                    authorityS = "";
-                } else {
-                    authorityS = "//"+authority;
-                }
-                final String path = patternSpaceEnc.matcher(authorityS+uri.getPath()).replaceAll(" "); // Uri TODO: Uri.decode(encoded);
-                if( File.separator.equals("\\") ) {
-                    final String r = patternSingleFS.matcher(path).replaceAll("\\\\");
-                    if( r.startsWith("\\") && !r.startsWith("\\\\") ) { // '\\\\' denotes UNC hostname, which shall not be cut-off
-                        return r.substring(1);
-                    } else {
-                        return r;
-                    }
-                }
-                return path;
+    public static String decodeURIIfFilePath(final java.net.URI uri) {
+        try {
+            final File file = Uri.valueOf(uri).toFile();
+            if( null != file ) {
+                return file.getPath();
+            } else {
+                return uri.toASCIIString();
             }
+        } catch (final URISyntaxException e) {
+            throw new RuntimeException(e);
         }
-        return uri.toASCIIString();
     }
 
     /**
diff --git a/src/java/com/jogamp/common/util/JarUtil.java b/src/java/com/jogamp/common/util/JarUtil.java
index 329ea51..745dd12 100644
--- a/src/java/com/jogamp/common/util/JarUtil.java
+++ b/src/java/com/jogamp/common/util/JarUtil.java
@@ -35,7 +35,6 @@ import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
 import java.net.JarURLConnection;
-import java.net.URI;
 import java.net.URISyntaxException;
 import java.net.URL;
 import java.net.URLConnection;
@@ -112,18 +111,18 @@ public class JarUtil {
      * @param clazzBinName "com.jogamp.common.GlueGenVersion"
      * @param cl
      * @return true if the class is loaded from a Jar file, otherwise false.
-     * @see {@link #getJarURI(String, ClassLoader)}
+     * @see {@link #getJarUri(String, ClassLoader)}
      */
-    public static boolean hasJarURI(final String clazzBinName, final ClassLoader cl) {
+    public static boolean hasJarUri(final String clazzBinName, final ClassLoader cl) {
         try {
-            return null != getJarURI(clazzBinName, cl);
+            return null != getJarUri(clazzBinName, cl);
         } catch (final Exception e) { /* ignore */ }
         return false;
     }
 
     /**
      * The Class's <code>"com.jogamp.common.GlueGenVersion"</code>
-     * URI <code>jar:<i>sub_protocol</i>:/some/path/gluegen-rt.jar!/com/jogamp/common/GlueGenVersion.class"</code>
+     * Uri <code>jar:<i>sub_protocol</i>:/some/path/gluegen-rt.jar!/com/jogamp/common/GlueGenVersion.class"</code>
      * will be returned.
      * <p>
      * <i>sub_protocol</i> may be "file", "http", etc..
@@ -132,108 +131,107 @@ public class JarUtil {
      * @param clazzBinName "com.jogamp.common.GlueGenVersion"
      * @param cl ClassLoader to locate the JarFile
      * @return "jar:<i>sub_protocol</i>:/some/path/gluegen-rt.jar!/com/jogamp/common/GlueGenVersion.class"
-     * @throws IllegalArgumentException if the URI doesn't match the expected formatting or null arguments
+     * @throws IllegalArgumentException if the Uri doesn't match the expected formatting or null arguments
      * @throws IOException if the class's Jar file could not been found by the ClassLoader
-     * @throws URISyntaxException if the URI could not be translated into a RFC 2396 URI
+     * @throws URISyntaxException if the Uri could not be translated into a RFC 2396 Uri
      * @see {@link IOUtil#getClassURL(String, ClassLoader)}
      */
-    public static URI getJarURI(final String clazzBinName, final ClassLoader cl) throws IllegalArgumentException, IOException, URISyntaxException {
+    public static Uri getJarUri(final String clazzBinName, final ClassLoader cl) throws IllegalArgumentException, IOException, URISyntaxException {
         if(null == clazzBinName || null == cl) {
             throw new IllegalArgumentException("null arguments: clazzBinName "+clazzBinName+", cl "+cl);
         }
-        final URI uri;
+        final Uri uri;
         final URL url;
         {
             url = IOUtil.getClassURL(clazzBinName, cl);
             final String scheme = url.getProtocol();
             if( null != resolver &&
-                !scheme.equals( IOUtil.JAR_SCHEME ) &&
-                !scheme.equals( IOUtil.FILE_SCHEME ) &&
-                !scheme.equals( IOUtil.HTTP_SCHEME ) &&
-                !scheme.equals( IOUtil.HTTPS_SCHEME ) )
+                !scheme.equals( Uri.JAR_SCHEME ) &&
+                !scheme.equals( Uri.FILE_SCHEME ) &&
+                !scheme.equals( Uri.HTTP_SCHEME ) &&
+                !scheme.equals( Uri.HTTPS_SCHEME ) )
             {
                 final URL _url = resolver.resolve( url );
-                uri = _url.toURI();
+                uri = Uri.valueOf(_url);
                 if(DEBUG) {
-                    System.err.println("getJarURI Resolver: "+url+"\n\t-> "+_url+"\n\t-> "+uri);
+                    System.err.println("getJarUri Resolver: "+url+"\n\t-> "+_url+"\n\t-> "+uri);
                 }
             } else {
-                uri = url.toURI();
+                uri = Uri.valueOf(url);
                 if(DEBUG) {
-                    System.err.println("getJarURI Default "+url+"\n\t-> "+uri);
+                    System.err.println("getJarUri Default "+url+"\n\t-> "+uri);
                 }
             }
         }
-        // test name ..
-        if( !uri.getScheme().equals( IOUtil.JAR_SCHEME ) ) {
-            throw new IllegalArgumentException("URI is not using scheme "+IOUtil.JAR_SCHEME+": <"+uri+">");
+        if( !uri.scheme.equals( Uri.JAR_SCHEME ) ) {
+            throw new IllegalArgumentException("Uri is not using scheme "+Uri.JAR_SCHEME+": <"+uri+">");
         }
         if(DEBUG) {
-            System.err.println("getJarURI res: "+clazzBinName+" -> "+url+" -> "+uri);
+            System.err.println("getJarUri res: "+clazzBinName+" -> "+url+" -> "+uri);
         }
         return uri;
     }
 
 
     /**
-     * The Class's Jar URI <code>jar:<i>sub_protocol</i>:/some/path/gluegen-rt.jar!/com/jogamp/common/GlueGenVersion.class</code>
+     * The Class's Jar Uri <code>jar:<i>sub_protocol</i>:/some/path/gluegen-rt.jar!/com/jogamp/common/GlueGenVersion.class</code>
      * Jar basename <code>gluegen-rt.jar</code> will be returned.
      * <p>
      * <i>sub_protocol</i> may be "file", "http", etc..
      * </p>
      *
-     * @param classJarURI as retrieved w/ {@link #getJarURI(String, ClassLoader) getJarURI("com.jogamp.common.GlueGenVersion", cl).toURI()},
+     * @param classJarUri as retrieved w/ {@link #getJarUri(String, ClassLoader) getJarUri("com.jogamp.common.GlueGenVersion", cl)},
      *                    i.e. <code>jar:<i>sub_protocol</i>:/some/path/gluegen-rt.jar!/com/jogamp/common/GlueGenVersion.class</code>
      * @return <code>gluegen-rt.jar</code>
-     * @throws IllegalArgumentException if the URI doesn't match the expected formatting or is null
+     * @throws IllegalArgumentException if the Uri doesn't match the expected formatting or is null
      * @see {@link IOUtil#getClassURL(String, ClassLoader)}
      */
-    public static String getJarBasename(final URI classJarURI) throws IllegalArgumentException {
-        if(null == classJarURI) {
-            throw new IllegalArgumentException("URI is null");
+    public static Uri.Encoded getJarBasename(final Uri classJarUri) throws IllegalArgumentException {
+        if(null == classJarUri) {
+            throw new IllegalArgumentException("Uri is null");
         }
-        if( !classJarURI.getScheme().equals(IOUtil.JAR_SCHEME) ) {
-            throw new IllegalArgumentException("URI is not using scheme "+IOUtil.JAR_SCHEME+": <"+classJarURI+">");
+        if( !classJarUri.scheme.equals(Uri.JAR_SCHEME) ) {
+            throw new IllegalArgumentException("Uri is not using scheme "+Uri.JAR_SCHEME+": <"+classJarUri+">");
         }
-        String uriS = classJarURI.getSchemeSpecificPart();
+        Uri.Encoded ssp = classJarUri.schemeSpecificPart;
 
         // from
         //   file:/some/path/gluegen-rt.jar!/com/jogamp/common/util/cache/TempJarCache.class
         // to
         //   file:/some/path/gluegen-rt.jar
-        int idx = uriS.lastIndexOf(IOUtil.JAR_SCHEME_SEPARATOR);
+        int idx = ssp.lastIndexOf(Uri.JAR_SCHEME_SEPARATOR);
         if (0 <= idx) {
-            uriS = uriS.substring(0, idx); // exclude '!/'
+            ssp = ssp.substring(0, idx); // exclude '!/'
         } else {
-            throw new IllegalArgumentException("URI does not contain jar uri terminator '!', in <"+classJarURI+">");
+            throw new IllegalArgumentException("Uri does not contain jar uri terminator '!', in <"+classJarUri+">");
         }
 
         // from
         //   file:/some/path/gluegen-rt.jar
         // to
         //   gluegen-rt.jar
-        idx = uriS.lastIndexOf('/');
+        idx = ssp.lastIndexOf('/');
         if(0 > idx) {
             // no abs-path, check for protocol terminator ':'
-            idx = uriS.lastIndexOf(':');
+            idx = ssp.lastIndexOf(':');
             if(0 > idx) {
-                throw new IllegalArgumentException("URI does not contain protocol terminator ':', in <"+classJarURI+">");
+                throw new IllegalArgumentException("Uri does not contain protocol terminator ':', in <"+classJarUri+">");
             }
         }
-        uriS = uriS.substring(idx+1); // just the jar name
+        ssp = ssp.substring(idx+1); // just the jar name
 
-        if(0 >= uriS.lastIndexOf(".jar")) {
-            throw new IllegalArgumentException("No Jar name in <"+classJarURI+">");
+        if(0 >= ssp.lastIndexOf(".jar")) {
+            throw new IllegalArgumentException("No Jar name in <"+classJarUri+">");
         }
         if(DEBUG) {
-            System.err.println("getJarName res: "+uriS);
+            System.err.println("getJarName res: "+ssp);
         }
-        return uriS;
+        return ssp;
     }
 
     /**
      * The Class's <code>com.jogamp.common.GlueGenVersion</code>
-     * URI <code>jar:<i>sub_protocol</i>:/some/path/gluegen-rt.jar!/com/jogamp/common/GlueGenVersion.class</code>
+     * Uri <code>jar:<i>sub_protocol</i>:/some/path/gluegen-rt.jar!/com/jogamp/common/GlueGenVersion.class</code>
      * Jar basename <code>gluegen-rt.jar</code> will be returned.
      * <p>
      * <i>sub_protocol</i> may be "file", "http", etc..
@@ -242,120 +240,54 @@ public class JarUtil {
      * @param clazzBinName <code>com.jogamp.common.GlueGenVersion</code>
      * @param cl
      * @return <code>gluegen-rt.jar</code>
-     * @throws IllegalArgumentException if the URI doesn't match the expected formatting
+     * @throws IllegalArgumentException if the Uri doesn't match the expected formatting
      * @throws IOException if the class's Jar file could not been found by the ClassLoader.
-     * @throws URISyntaxException if the URI could not be translated into a RFC 2396 URI
+     * @throws URISyntaxException if the Uri could not be translated into a RFC 2396 Uri
      * @see {@link IOUtil#getClassURL(String, ClassLoader)}
      */
-    public static String getJarBasename(final String clazzBinName, final ClassLoader cl) throws IllegalArgumentException, IOException, URISyntaxException {
-        return getJarBasename( getJarURI(clazzBinName, cl) );
+    public static Uri.Encoded getJarBasename(final String clazzBinName, final ClassLoader cl) throws IllegalArgumentException, IOException, URISyntaxException {
+        return getJarBasename( getJarUri(clazzBinName, cl) );
     }
 
     /**
-     * The Class's Jar URI <code>jar:<i>sub_protocol</i>:/some/path/gluegen-rt.jar!/com/jogamp/common/GlueGenVersion.class</code>
-     * Jar file's sub URI <code><i>sub_protocol</i>:/some/path/gluegen-rt.jar</code> will be returned.
-     * <p>
-     * <i>sub_protocol</i> may be "file", "http", etc..
-     * </p>
-     *
-     * @param classJarURI as retrieved w/ {@link #getJarURI(String, ClassLoader) getJarURI("com.jogamp.common.GlueGenVersion", cl).toURI()},
-     *                    i.e. <code>jar:<i>sub_protocol</i>:/some/path/gluegen-rt.jar!/com/jogamp/common/GlueGenVersion.class</code>
-     * @return <code><i>sub_protocol</i>:/some/path/gluegen-rt.jar</code>
-     * @throws IllegalArgumentException if the URI doesn't match the expected formatting or is null
-     * @throws URISyntaxException if the URI could not be translated into a RFC 2396 URI
-     * @see {@link IOUtil#getClassURL(String, ClassLoader)}
-     * @deprecated Use {@link Uri#getContainedUri()} and validate it's scheme, etc.
-     */
-    public static URI getJarSubURI(final URI classJarURI) throws IllegalArgumentException, URISyntaxException {
-        if(null == classJarURI) {
-            throw new IllegalArgumentException("URI is null");
-        }
-        if( !classJarURI.getScheme().equals(IOUtil.JAR_SCHEME) ) {
-            throw new IllegalArgumentException("URI is not a using scheme "+IOUtil.JAR_SCHEME+": <"+classJarURI+">");
-        }
-
-        // from
-        //   file:/some/path/gluegen-rt.jar!/com/jogamp/common/GlueGenVersion.class
-        // to
-        //   file:/some/path/gluegen-rt.jar
-        final String uriSSP = classJarURI.getRawSchemeSpecificPart();
-        final int idx = uriSSP.lastIndexOf(IOUtil.JAR_SCHEME_SEPARATOR);
-        final String uriSSPJar;
-        if (0 <= idx) {
-            uriSSPJar = uriSSP.substring(0, idx); // exclude '!/'
-        } else {
-            throw new IllegalArgumentException("JAR URI does not contain jar uri terminator '!', uri <"+classJarURI+">");
-        }
-        if(0 >= uriSSPJar.lastIndexOf(".jar")) {
-            throw new IllegalArgumentException("No Jar name in <"+classJarURI+">");
-        }
-        if(DEBUG) {
-            System.err.println("getJarSubURI res: "+classJarURI+" -> "+uriSSP+" -> "+uriSSPJar);
-        }
-        return new URI(uriSSPJar);
-    }
-
-    /**
-     * The Class's Jar URI <code>jar:<i>sub_protocol</i>:/some/path/gluegen-rt.jar!/com/jogamp/common/GlueGenVersion.class</code>
+     * The Class's Jar Uri <code>jar:<i>sub_protocol</i>:/some/path/gluegen-rt.jar!/com/jogamp/common/GlueGenVersion.class</code>
      * Jar file's entry <code>/com/jogamp/common/GlueGenVersion.class</code> will be returned.
      *
-     * @param classJarURI as retrieved w/ {@link #getJarURI(String, ClassLoader) getJarURI("com.jogamp.common.GlueGenVersion", cl).toURI()},
+     * @param classJarUri as retrieved w/ {@link #getJarUri(String, ClassLoader) getJarUri("com.jogamp.common.GlueGenVersion", cl)},
      *                    i.e. <code>jar:<i>sub_protocol</i>:/some/path/gluegen-rt.jar!/com/jogamp/common/GlueGenVersion.class</code>
      * @return <code>/com/jogamp/common/GlueGenVersion.class</code>
      * @see {@link IOUtil#getClassURL(String, ClassLoader)}
-     * @deprecated Useless
      */
-    public static String getJarEntry(final URI classJarURI) {
-        if(null == classJarURI) {
-            throw new IllegalArgumentException("URI is null");
+    public static Uri.Encoded getJarEntry(final Uri classJarUri) {
+        if(null == classJarUri) {
+            throw new IllegalArgumentException("Uri is null");
         }
-        if( !classJarURI.getScheme().equals(IOUtil.JAR_SCHEME) ) {
-            throw new IllegalArgumentException("URI is not a using scheme "+IOUtil.JAR_SCHEME+": <"+classJarURI+">");
+        if( !classJarUri.scheme.equals(Uri.JAR_SCHEME) ) {
+            throw new IllegalArgumentException("Uri is not a using scheme "+Uri.JAR_SCHEME+": <"+classJarUri+">");
         }
-        final String uriSSP = classJarURI.getSchemeSpecificPart();
-        // Uri TODO ? final String uriSSP = classJarURI.getRawSchemeSpecificPart();
+        final Uri.Encoded uriSSP = classJarUri.schemeSpecificPart;
 
         // from
         //   file:/some/path/gluegen-rt.jar!/com/jogamp/common/GlueGenVersion.class
         // to
         //   /com/jogamp/common/GlueGenVersion.class
-        final int idx = uriSSP.lastIndexOf(IOUtil.JAR_SCHEME_SEPARATOR);
+        final int idx = uriSSP.lastIndexOf(Uri.JAR_SCHEME_SEPARATOR);
         if (0 <= idx) {
-            final String res = uriSSP.substring(idx+1); // right of '!'
+            final Uri.Encoded res = uriSSP.substring(idx+1); // right of '!'
             // Uri TODO ? final String res = Uri.decode(uriSSP.substring(idx+1)); // right of '!'
             if(DEBUG) {
-                System.err.println("getJarEntry res: "+classJarURI+" -> "+uriSSP+" -> "+idx+" -> "+res);
+                System.err.println("getJarEntry res: "+classJarUri+" -> "+uriSSP+" -> "+idx+" -> "+res);
             }
             return res;
         } else {
-            throw new IllegalArgumentException("JAR URI does not contain jar uri terminator '!', uri <"+classJarURI+">");
+            throw new IllegalArgumentException("JAR Uri does not contain jar uri terminator '!', uri <"+classJarUri+">");
         }
     }
 
-    /**
-     * The Class's <code>com.jogamp.common.GlueGenVersion</code>
-     * URI <code>jar:<i>sub_protocol</i>:/some/path/gluegen-rt.jar!/com/jogamp/common/GlueGenVersion.class</code>
-     * Jar file's sub URI <code><i>sub_protocol</i>:/some/path/gluegen-rt.jar</code> will be returned.
-     * <p>
-     * <i>sub_protocol</i> may be "file", "http", etc..
-     * </p>
-     *
-     * @param clazzBinName <code>com.jogamp.common.GlueGenVersion</code>
-     * @param cl
-     * @return <code><i>sub_protocol</i>:/some/path/gluegen-rt.jar</code>
-     * @throws IllegalArgumentException if the URI doesn't match the expected formatting
-     * @throws IOException if the class's Jar file could not been found by the ClassLoader
-     * @throws URISyntaxException if the URI could not be translated into a RFC 2396 URI
-     * @see {@link IOUtil#getClassURL(String, ClassLoader)}
-     */
-    public static URI getJarSubURI(final String clazzBinName, final ClassLoader cl) throws IllegalArgumentException, IOException, URISyntaxException {
-        return getJarSubURI( getJarURI(clazzBinName, cl) );
-    }
-
     /**
      * The Class's <code>"com.jogamp.common.GlueGenVersion"</code>
-     * URI <code>jar:<i>sub_protocol</i>:/some/path/gluegen-rt.jar!/com/jogamp/common/GlueGenVersion.class"</code>
-     * Jar file URI <code>jar:<i>sub_protocol</i>:/some/path/gluegen-rt.jar!/</code> will be returned.
+     * Uri <code>jar:<i>sub_protocol</i>:/some/path/gluegen-rt.jar!/com/jogamp/common/GlueGenVersion.class"</code>
+     * Jar file Uri <code>jar:<i>sub_protocol</i>:/some/path/gluegen-rt.jar!/</code> will be returned.
      * <p>
      * <i>sub_protocol</i> may be "file", "http", etc..
      * </p>
@@ -363,34 +295,35 @@ public class JarUtil {
      * @param clazzBinName "com.jogamp.common.GlueGenVersion"
      * @param cl
      * @return "jar:<i>sub_protocol</i>:/some/path/gluegen-rt.jar!/"
-     * @throws IllegalArgumentException if the URI doesn't match the expected formatting or null arguments
+     * @throws IllegalArgumentException if the Uri doesn't match the expected formatting or null arguments
      * @throws IOException if the class's Jar file could not been found by the ClassLoader
-     * @throws URISyntaxException if the URI could not be translated into a RFC 2396 URI
+     * @throws URISyntaxException if the Uri could not be translated into a RFC 2396 Uri
      * @see {@link IOUtil#getClassURL(String, ClassLoader)}
      */
-    public static URI getJarFileURI(final String clazzBinName, final ClassLoader cl) throws IllegalArgumentException, IOException, URISyntaxException {
+    public static Uri getJarFileUri(final String clazzBinName, final ClassLoader cl) throws IllegalArgumentException, IOException, URISyntaxException {
         if(null == clazzBinName || null == cl) {
             throw new IllegalArgumentException("null arguments: clazzBinName "+clazzBinName+", cl "+cl);
         }
-        final URI uri = new URI(IOUtil.JAR_SCHEME+IOUtil.SCHEME_SEPARATOR+getJarSubURI(clazzBinName, cl).toString()+"!/");
+        final Uri jarSubUri = getJarUri(clazzBinName, cl).getContainedUri();
+        final Uri uri = Uri.cast(Uri.JAR_SCHEME+Uri.SCHEME_SEPARATOR+jarSubUri.toString()+"!/");
         if(DEBUG) {
-            System.err.println("getJarFileURI res: "+uri);
+            System.err.println("getJarFileUri res: "+uri);
         }
         return uri;
     }
 
     /**
      * @param baseUri file:/some/path/
-     * @param jarFileName gluegen-rt.jar (URI encoded)
+     * @param jarFileName gluegen-rt.jar (Uri encoded)
      * @return jar:file:/some/path/gluegen-rt.jar!/
      * @throws URISyntaxException
      * @throws IllegalArgumentException null arguments
      */
-    public static URI getJarFileURI(final URI baseUri, final String jarFileName) throws IllegalArgumentException, URISyntaxException {
+    public static Uri getJarFileUri(final Uri baseUri, final Uri.Encoded jarFileName) throws IllegalArgumentException, URISyntaxException {
         if(null == baseUri || null == jarFileName) {
-            throw new IllegalArgumentException("null arguments: baseURI "+baseUri+", jarFileName "+jarFileName);
+            throw new IllegalArgumentException("null arguments: baseUri "+baseUri+", jarFileName "+jarFileName);
         }
-        return new URI(IOUtil.JAR_SCHEME+IOUtil.SCHEME_SEPARATOR+baseUri.toString()+jarFileName+"!/");
+        return Uri.cast(Uri.JAR_SCHEME+Uri.SCHEME_SEPARATOR+baseUri.toString()+jarFileName+"!/");
     }
 
     /**
@@ -399,38 +332,38 @@ public class JarUtil {
      * @throws IllegalArgumentException null arguments
      * @throws URISyntaxException
      */
-    public static URI getJarFileURI(final URI jarSubUri) throws IllegalArgumentException, URISyntaxException {
+    public static Uri getJarFileUri(final Uri jarSubUri) throws IllegalArgumentException, URISyntaxException {
         if(null == jarSubUri) {
-            throw new IllegalArgumentException("jarSubURI is null");
+            throw new IllegalArgumentException("jarSubUri is null");
         }
-        return new URI(IOUtil.JAR_SCHEME+IOUtil.SCHEME_SEPARATOR+jarSubUri.toString()+"!/");
+        return Uri.cast(Uri.JAR_SCHEME+Uri.SCHEME_SEPARATOR+jarSubUri.toString()+"!/");
     }
 
     /**
-     * @param jarSubUriS file:/some/path/gluegen-rt.jar (URI encoded)
+     * @param jarSubUriS file:/some/path/gluegen-rt.jar (Uri encoded)
      * @return jar:file:/some/path/gluegen-rt.jar!/
      * @throws IllegalArgumentException null arguments
      * @throws URISyntaxException
      */
-    public static URI getJarFileURI(final String jarSubUriS) throws IllegalArgumentException, URISyntaxException {
+    public static Uri getJarFileUri(final Uri.Encoded jarSubUriS) throws IllegalArgumentException, URISyntaxException {
         if(null == jarSubUriS) {
-            throw new IllegalArgumentException("jarSubURIS is null");
+            throw new IllegalArgumentException("jarSubUriS is null");
         }
-        return new URI(IOUtil.JAR_SCHEME+IOUtil.SCHEME_SEPARATOR+jarSubUriS+"!/");
+        return Uri.cast(Uri.JAR_SCHEME+Uri.SCHEME_SEPARATOR+jarSubUriS+"!/");
     }
 
     /**
-     * @param jarFileURI jar:file:/some/path/gluegen-rt.jar!/
+     * @param jarFileUri jar:file:/some/path/gluegen-rt.jar!/
      * @param jarEntry com/jogamp/common/GlueGenVersion.class
      * @return jar:file:/some/path/gluegen-rt.jar!/com/jogamp/common/GlueGenVersion.class
      * @throws IllegalArgumentException null arguments
      * @throws URISyntaxException
      */
-    public static URI getJarEntryURI(final URI jarFileURI, final String jarEntry) throws IllegalArgumentException, URISyntaxException {
+    public static Uri getJarEntryUri(final Uri jarFileUri, final Uri.Encoded jarEntry) throws IllegalArgumentException, URISyntaxException {
         if(null == jarEntry) {
             throw new IllegalArgumentException("jarEntry is null");
         }
-        return new URI(jarFileURI.toString()+jarEntry);
+        return Uri.cast(jarFileUri.toString()+jarEntry);
     }
 
     /**
@@ -439,28 +372,28 @@ public class JarUtil {
      * @return JarFile containing the named class within the given ClassLoader
      * @throws IOException if the class's Jar file could not been found by the ClassLoader
      * @throws IllegalArgumentException null arguments
-     * @throws URISyntaxException if the URI could not be translated into a RFC 2396 URI
-     * @see {@link #getJarFileURI(String, ClassLoader)}
+     * @throws URISyntaxException if the Uri could not be translated into a RFC 2396 Uri
+     * @see {@link #getJarFileUri(String, ClassLoader)}
      */
     public static JarFile getJarFile(final String clazzBinName, final ClassLoader cl) throws IOException, IllegalArgumentException, URISyntaxException {
-        return getJarFile( getJarFileURI(clazzBinName, cl) );
+        return getJarFile( getJarFileUri(clazzBinName, cl) );
     }
 
     /**
-     * @param jarFileURI jar:file:/some/path/gluegen-rt.jar!/
-     * @return JarFile as named by URI within the given ClassLoader
+     * @param jarFileUri jar:file:/some/path/gluegen-rt.jar!/
+     * @return JarFile as named by Uri within the given ClassLoader
      * @throws IllegalArgumentException null arguments
      * @throws IOException if the Jar file could not been found
      * @throws URISyntaxException
      */
-    public static JarFile getJarFile(final URI jarFileURI) throws IOException, IllegalArgumentException, URISyntaxException {
-        if(null == jarFileURI) {
-            throw new IllegalArgumentException("null jarFileURI");
+    public static JarFile getJarFile(final Uri jarFileUri) throws IOException, IllegalArgumentException, URISyntaxException {
+        if(null == jarFileUri) {
+            throw new IllegalArgumentException("null jarFileUri");
         }
         if(DEBUG) {
-            System.err.println("getJarFile.0: "+jarFileURI.toString());
+            System.err.println("getJarFile.0: "+jarFileUri.toString());
         }
-        final URL jarFileURL = jarFileURI.toURL();
+        final URL jarFileURL = jarFileUri.toURL();
         if(DEBUG) {
             System.err.println("getJarFile.1: "+jarFileURL.toString());
         }
@@ -480,8 +413,22 @@ public class JarUtil {
     }
 
     /**
-     * Locates the {@link JarUtil#getJarFileURI(URI) Jar file URI} of a given resource
-     * relative to a given class's Jar's URI.
+     * See {@link #getRelativeOf(Class, com.jogamp.common.net.Uri.Encoded, com.jogamp.common.net.Uri.Encoded)}.
+     * @param classFromJavaJar URI encoded!
+     * @param cutOffInclSubDir URI encoded!
+     * @param relResPath URI encoded!
+     * @return
+     * @throws IllegalArgumentException
+     * @throws IOException
+     * @throws URISyntaxException
+     * @deprecated Use {@link #getRelativeOf(Class, com.jogamp.common.net.Uri.Encoded, com.jogamp.common.net.Uri.Encoded)}.
+     */
+    public static java.net.URI getRelativeOf(final Class<?> classFromJavaJar, final String cutOffInclSubDir, final String relResPath) throws IllegalArgumentException, IOException, URISyntaxException {
+        return getRelativeOf(classFromJavaJar, Uri.Encoded.cast(cutOffInclSubDir), Uri.Encoded.cast(relResPath)).toURI();
+    }
+    /**
+     * Locates the {@link JarUtil#getJarFileUri(Uri) Jar file Uri} of a given resource
+     * relative to a given class's Jar's Uri.
      * <pre>
      *   class's jar url path + cutOffInclSubDir + relResPath,
      * </pre>
@@ -502,47 +449,47 @@ public class JarUtil {
      *
      * TODO: Enhance documentation!
      *
-     * @param classFromJavaJar Used to get the root URI for the class's Jar URI.
+     * @param classFromJavaJar Used to get the root Uri for the class's Jar Uri.
      * @param cutOffInclSubDir The <i>cut off</i> included sub-directory prepending the relative resource path.
-     *                         If the root URI includes cutOffInclSubDir, it is no more added to the result.
-     * @param relResPath The relative resource path. (URI encoded)
-     * @return The resulting resource URI, which is not tested.
+     *                         If the root Uri includes cutOffInclSubDir, it is no more added to the result.
+     * @param relResPath The relative resource path. (Uri encoded)
+     * @return The resulting resource Uri, which is not tested.
      * @throws IllegalArgumentException
      * @throws IOException
      * @throws URISyntaxException
      */
-    public static URI getRelativeOf(final Class<?> classFromJavaJar, final String cutOffInclSubDir, final String relResPath) throws IllegalArgumentException, IOException, URISyntaxException {
+    public static Uri getRelativeOf(final Class<?> classFromJavaJar, final Uri.Encoded cutOffInclSubDir, final Uri.Encoded relResPath) throws IllegalArgumentException, IOException, URISyntaxException {
         final ClassLoader cl = classFromJavaJar.getClassLoader();
-        final URI classJarURI = JarUtil.getJarURI(classFromJavaJar.getName(), cl);
+        final Uri classJarUri = JarUtil.getJarUri(classFromJavaJar.getName(), cl);
         if( DEBUG ) {
-            System.err.println("JarUtil.getRelativeOf: "+"(classFromJavaJar "+classFromJavaJar+", classJarURI "+classJarURI+
+            System.err.println("JarUtil.getRelativeOf: "+"(classFromJavaJar "+classFromJavaJar+", classJarUri "+classJarUri+
                     ", cutOffInclSubDir "+cutOffInclSubDir+", relResPath "+relResPath+"): ");
         }
 
-        final URI jarSubURI = JarUtil.getJarSubURI( classJarURI );
-        if(null == jarSubURI) {
-            throw new IllegalArgumentException("JarSubURI is null of: "+classJarURI);
+        final Uri jarSubUri = classJarUri.getContainedUri();
+        if(null == jarSubUri) {
+            throw new IllegalArgumentException("JarSubUri is null of: "+classJarUri);
         }
-        final String jarUriRoot_s = IOUtil.getURIDirname( jarSubURI.toString() );
+        final Uri.Encoded jarUriRoot = jarSubUri.getDirectory().getEncoded();
         if( DEBUG ) {
-            System.err.println("JarUtil.getRelativeOf: "+"uri "+jarSubURI.toString()+" -> "+jarUriRoot_s);
+            System.err.println("JarUtil.getRelativeOf: "+"uri "+jarSubUri.toString()+" -> "+jarUriRoot);
         }
 
-        final String resUri_s;
-        if( jarUriRoot_s.endsWith(cutOffInclSubDir) ) {
-            resUri_s = jarUriRoot_s+relResPath;
+        final Uri.Encoded resUri;
+        if( jarUriRoot.endsWith(cutOffInclSubDir.get()) ) {
+            resUri = jarUriRoot.concat(relResPath);
         } else {
-            resUri_s = jarUriRoot_s+cutOffInclSubDir+relResPath;
+            resUri = jarUriRoot.concat(cutOffInclSubDir).concat(relResPath);
         }
         if( DEBUG ) {
-            System.err.println("JarUtil.getRelativeOf: "+"...  -> "+resUri_s);
+            System.err.println("JarUtil.getRelativeOf: "+"...  -> "+resUri);
         }
 
-        final URI resURI = JarUtil.getJarFileURI(resUri_s);
+        final Uri resJarUri = JarUtil.getJarFileUri(resUri);
         if( DEBUG ) {
-            System.err.println("JarUtil.getRelativeOf: "+"fin "+resURI);
+            System.err.println("JarUtil.getRelativeOf: "+"fin "+resJarUri);
         }
-        return resURI;
+        return resJarUri;
     }
 
     /**
diff --git a/src/java/com/jogamp/common/util/cache/TempJarCache.java b/src/java/com/jogamp/common/util/cache/TempJarCache.java
index 8979a43..05d92e1 100644
--- a/src/java/com/jogamp/common/util/cache/TempJarCache.java
+++ b/src/java/com/jogamp/common/util/cache/TempJarCache.java
@@ -29,7 +29,6 @@ package com.jogamp.common.util.cache;
 
 import java.io.File;
 import java.io.IOException;
-import java.net.URI;
 import java.net.URISyntaxException;
 import java.security.cert.Certificate;
 import java.util.HashMap;
@@ -40,7 +39,6 @@ import jogamp.common.Debug;
 
 import com.jogamp.common.net.Uri;
 import com.jogamp.common.os.NativeLibrary;
-import com.jogamp.common.util.IOUtil;
 import com.jogamp.common.util.JarUtil;
 import com.jogamp.common.util.SecurityUtil;
 
@@ -69,9 +67,9 @@ public class TempJarCache {
     }
 
     // Set of jar files added
-    private static Map<URI, LoadState> nativeLibJars;
-    private static Map<URI, LoadState> classFileJars;
-    private static Map<URI, LoadState> resourceFileJars;
+    private static Map<Uri, LoadState> nativeLibJars;
+    private static Map<Uri, LoadState> classFileJars;
+    private static Map<Uri, LoadState> resourceFileJars;
 
     private static TempFileCache tmpFileCache;
 
@@ -97,9 +95,9 @@ public class TempJarCache {
                     if(!staticInitError) {
                         // Initialize the collections of resources
                         nativeLibMap = new HashMap<String, String>();
-                        nativeLibJars = new HashMap<URI, LoadState>();
-                        classFileJars = new HashMap<URI, LoadState>();
-                        resourceFileJars = new HashMap<URI, LoadState>();
+                        nativeLibJars = new HashMap<Uri, LoadState>();
+                        classFileJars = new HashMap<Uri, LoadState>();
+                        resourceFileJars = new HashMap<Uri, LoadState>();
                     }
                     if(DEBUG) {
                         System.err.println("TempJarCache.initSingleton(): ok "+(false==staticInitError)+", "+ tmpFileCache.getTempDir());
@@ -183,62 +181,62 @@ public class TempJarCache {
         return tmpFileCache;
     }
 
-    public synchronized static boolean checkNativeLibs(final URI jarURI, final LoadState exp) throws IOException {
+    public synchronized static boolean checkNativeLibs(final Uri jarUri, final LoadState exp) throws IOException {
         checkInitialized();
-        if(null == jarURI) {
-            throw new IllegalArgumentException("jarURI is null");
+        if(null == jarUri) {
+            throw new IllegalArgumentException("jarUri is null");
         }
-        return testLoadState(nativeLibJars.get(jarURI), exp);
+        return testLoadState(nativeLibJars.get(jarUri), exp);
     }
 
-    public synchronized static boolean checkClasses(final URI jarURI, final LoadState exp) throws IOException {
+    public synchronized static boolean checkClasses(final Uri jarUri, final LoadState exp) throws IOException {
         checkInitialized();
-        if(null == jarURI) {
-            throw new IllegalArgumentException("jarURI is null");
+        if(null == jarUri) {
+            throw new IllegalArgumentException("jarUri is null");
         }
-        return testLoadState(classFileJars.get(jarURI), exp);
+        return testLoadState(classFileJars.get(jarUri), exp);
     }
 
-    public synchronized static boolean checkResources(final URI jarURI, final LoadState exp) throws IOException {
+    public synchronized static boolean checkResources(final Uri jarUri, final LoadState exp) throws IOException {
         checkInitialized();
-        if(null == jarURI) {
-            throw new IllegalArgumentException("jarURI is null");
+        if(null == jarUri) {
+            throw new IllegalArgumentException("jarUri is null");
         }
-        return testLoadState(resourceFileJars.get(jarURI), exp);
+        return testLoadState(resourceFileJars.get(jarUri), exp);
     }
 
     /**
      * Adds native libraries, if not yet added.
      *
      * @param certClass if class is certified, the JarFile entries needs to have the same certificate
-     * @param jarURI
+     * @param jarUri
      * @param nativeLibraryPath if not null, only extracts native libraries within this path.
-     * @return true if native libraries were added or previously loaded from given jarURI, otherwise false
-     * @throws IOException if the <code>jarURI</code> could not be loaded or a previous load attempt failed
+     * @return true if native libraries were added or previously loaded from given jarUri, otherwise false
+     * @throws IOException if the <code>jarUri</code> could not be loaded or a previous load attempt failed
      * @throws SecurityException
      * @throws URISyntaxException
      * @throws IllegalArgumentException
      */
-    public synchronized static final boolean addNativeLibs(final Class<?> certClass, final URI jarURI, final String nativeLibraryPath) throws IOException, SecurityException, IllegalArgumentException, URISyntaxException {
+    public synchronized static final boolean addNativeLibs(final Class<?> certClass, final Uri jarUri, final String nativeLibraryPath) throws IOException, SecurityException, IllegalArgumentException, URISyntaxException {
         checkInitialized();
-        final LoadState nativeLibJarsLS = nativeLibJars.get(jarURI);
+        final LoadState nativeLibJarsLS = nativeLibJars.get(jarUri);
         if( !testLoadState(nativeLibJarsLS, LoadState.LOOKED_UP) ) {
-            nativeLibJars.put(jarURI, LoadState.LOOKED_UP);
-            final JarFile jarFile = JarUtil.getJarFile(jarURI);
+            nativeLibJars.put(jarUri, LoadState.LOOKED_UP);
+            final JarFile jarFile = JarUtil.getJarFile(jarUri);
             if(DEBUG) {
-                System.err.println("TempJarCache: addNativeLibs: "+jarURI+": nativeJar "+jarFile.getName()+" (NEW)");
+                System.err.println("TempJarCache: addNativeLibs: "+jarUri+": nativeJar "+jarFile.getName()+" (NEW)");
             }
             validateCertificates(certClass, jarFile);
             final int num = JarUtil.extract(tmpFileCache.getTempDir(), nativeLibMap, jarFile, nativeLibraryPath, true, false, false);
-            nativeLibJars.put(jarURI, LoadState.LOADED);
+            nativeLibJars.put(jarUri, LoadState.LOADED);
             return num > 0;
         } else if( testLoadState(nativeLibJarsLS, LoadState.LOADED) ) {
             if(DEBUG) {
-                System.err.println("TempJarCache: addNativeLibs: "+jarURI+": nativeJar "+jarURI+" (REUSE)");
+                System.err.println("TempJarCache: addNativeLibs: "+jarUri+": nativeJar "+jarUri+" (REUSE)");
             }
             return true;
         }
-        throw new IOException("TempJarCache: addNativeLibs: "+jarURI+", previous load attempt failed");
+        throw new IOException("TempJarCache: addNativeLibs: "+jarUri+", previous load attempt failed");
     }
 
     /**
@@ -248,56 +246,69 @@ public class TempJarCache {
      * needs Classloader.defineClass(..) access, ie. own derivation - will do when needed ..
      *
      * @param certClass if class is certified, the JarFile entries needs to have the same certificate
-     * @param jarURI
-     * @throws IOException if the <code>jarURI</code> could not be loaded or a previous load attempt failed
+     * @param jarUri
+     * @throws IOException if the <code>jarUri</code> could not be loaded or a previous load attempt failed
      * @throws SecurityException
      * @throws URISyntaxException
      * @throws IllegalArgumentException
      */
-    public synchronized static final void addClasses(final Class<?> certClass, final URI jarURI) throws IOException, SecurityException, IllegalArgumentException, URISyntaxException {
+    public synchronized static final void addClasses(final Class<?> certClass, final Uri jarUri) throws IOException, SecurityException, IllegalArgumentException, URISyntaxException {
         checkInitialized();
-        final LoadState classFileJarsLS = classFileJars.get(jarURI);
+        final LoadState classFileJarsLS = classFileJars.get(jarUri);
         if( !testLoadState(classFileJarsLS, LoadState.LOOKED_UP) ) {
-            classFileJars.put(jarURI, LoadState.LOOKED_UP);
-            final JarFile jarFile = JarUtil.getJarFile(jarURI);
+            classFileJars.put(jarUri, LoadState.LOOKED_UP);
+            final JarFile jarFile = JarUtil.getJarFile(jarUri);
             if(DEBUG) {
-                System.err.println("TempJarCache: addClasses: "+jarURI+": nativeJar "+jarFile.getName());
+                System.err.println("TempJarCache: addClasses: "+jarUri+": nativeJar "+jarFile.getName());
             }
             validateCertificates(certClass, jarFile);
             JarUtil.extract(tmpFileCache.getTempDir(), null, jarFile,
                             null /* nativeLibraryPath */, false, true, false);
-            classFileJars.put(jarURI, LoadState.LOADED);
+            classFileJars.put(jarUri, LoadState.LOADED);
         } else if( !testLoadState(classFileJarsLS, LoadState.LOADED) ) {
-            throw new IOException("TempJarCache: addClasses: "+jarURI+", previous load attempt failed");
+            throw new IOException("TempJarCache: addClasses: "+jarUri+", previous load attempt failed");
         }
     }
 
+    /**
+     * See {@link #addResources(Class, Uri)}
+     * @param certClass
+     * @param jarURI
+     * @throws IOException
+     * @throws SecurityException
+     * @throws IllegalArgumentException
+     * @throws URISyntaxException
+     * @deprecated Use {@link #addResources(Class, Uri)}
+     */
+    public synchronized static final void addResources(final Class<?> certClass, final java.net.URI jarURI) throws IOException, SecurityException, IllegalArgumentException, URISyntaxException {
+        addResources(certClass, Uri.valueOf(jarURI));
+    }
     /**
      * Adds native resources, if not yet added.
      *
      * @param certClass if class is certified, the JarFile entries needs to have the same certificate
-     * @param jarURI
+     * @param jarUri
      * @return
-     * @throws IOException if the <code>jarURI</code> could not be loaded or a previous load attempt failed
+     * @throws IOException if the <code>jarUri</code> could not be loaded or a previous load attempt failed
      * @throws SecurityException
      * @throws URISyntaxException
      * @throws IllegalArgumentException
      */
-    public synchronized static final void addResources(final Class<?> certClass, final URI jarURI) throws IOException, SecurityException, IllegalArgumentException, URISyntaxException {
+    public synchronized static final void addResources(final Class<?> certClass, final Uri jarUri) throws IOException, SecurityException, IllegalArgumentException, URISyntaxException {
         checkInitialized();
-        final LoadState resourceFileJarsLS = resourceFileJars.get(jarURI);
+        final LoadState resourceFileJarsLS = resourceFileJars.get(jarUri);
         if( !testLoadState(resourceFileJarsLS, LoadState.LOOKED_UP) ) {
-            resourceFileJars.put(jarURI, LoadState.LOOKED_UP);
-            final JarFile jarFile = JarUtil.getJarFile(jarURI);
+            resourceFileJars.put(jarUri, LoadState.LOOKED_UP);
+            final JarFile jarFile = JarUtil.getJarFile(jarUri);
             if(DEBUG) {
-                System.err.println("TempJarCache: addResources: "+jarURI+": nativeJar "+jarFile.getName());
+                System.err.println("TempJarCache: addResources: "+jarUri+": nativeJar "+jarFile.getName());
             }
             validateCertificates(certClass, jarFile);
             JarUtil.extract(tmpFileCache.getTempDir(), null, jarFile,
                             null /* nativeLibraryPath */, false, false, true);
-            resourceFileJars.put(jarURI, LoadState.LOADED);
+            resourceFileJars.put(jarUri, LoadState.LOADED);
         } else if( !testLoadState(resourceFileJarsLS, LoadState.LOADED) ) {
-            throw new IOException("TempJarCache: addResources: "+jarURI+", previous load attempt failed");
+            throw new IOException("TempJarCache: addResources: "+jarUri+", previous load attempt failed");
         }
     }
 
@@ -309,20 +320,20 @@ public class TempJarCache {
      * needs Classloader.defineClass(..) access, ie. own derivation - will do when needed ..
      *
      * @param certClass if class is certified, the JarFile entries needs to have the same certificate
-     * @param jarURI
-     * @throws IOException if the <code>jarURI</code> could not be loaded or a previous load attempt failed
+     * @param jarUri
+     * @throws IOException if the <code>jarUri</code> could not be loaded or a previous load attempt failed
      * @throws SecurityException
      * @throws URISyntaxException
      * @throws IllegalArgumentException
      */
-    public synchronized static final void addAll(final Class<?> certClass, final URI jarURI) throws IOException, SecurityException, IllegalArgumentException, URISyntaxException {
+    public synchronized static final void addAll(final Class<?> certClass, final Uri jarUri) throws IOException, SecurityException, IllegalArgumentException, URISyntaxException {
         checkInitialized();
-        if(null == jarURI) {
-            throw new IllegalArgumentException("jarURI is null");
+        if(null == jarUri) {
+            throw new IllegalArgumentException("jarUri is null");
         }
-        final LoadState nativeLibJarsLS = nativeLibJars.get(jarURI);
-        final LoadState classFileJarsLS = classFileJars.get(jarURI);
-        final LoadState resourceFileJarsLS = resourceFileJars.get(jarURI);
+        final LoadState nativeLibJarsLS = nativeLibJars.get(jarUri);
+        final LoadState classFileJarsLS = classFileJars.get(jarUri);
+        final LoadState resourceFileJarsLS = resourceFileJars.get(jarUri);
         if( !testLoadState(nativeLibJarsLS, LoadState.LOOKED_UP) ||
             !testLoadState(classFileJarsLS, LoadState.LOOKED_UP) ||
             !testLoadState(resourceFileJarsLS, LoadState.LOOKED_UP) ) {
@@ -333,18 +344,18 @@ public class TempJarCache {
 
             // mark looked-up (those who are not loaded)
             if(extractNativeLibraries) {
-                nativeLibJars.put(jarURI, LoadState.LOOKED_UP);
+                nativeLibJars.put(jarUri, LoadState.LOOKED_UP);
             }
             if(extractClassFiles) {
-                classFileJars.put(jarURI, LoadState.LOOKED_UP);
+                classFileJars.put(jarUri, LoadState.LOOKED_UP);
             }
             if(extractOtherFiles) {
-                resourceFileJars.put(jarURI, LoadState.LOOKED_UP);
+                resourceFileJars.put(jarUri, LoadState.LOOKED_UP);
             }
 
-            final JarFile jarFile = JarUtil.getJarFile(jarURI);
+            final JarFile jarFile = JarUtil.getJarFile(jarUri);
             if(DEBUG) {
-                System.err.println("TempJarCache: addAll: "+jarURI+": nativeJar "+jarFile.getName());
+                System.err.println("TempJarCache: addAll: "+jarUri+": nativeJar "+jarFile.getName());
             }
             validateCertificates(certClass, jarFile);
             JarUtil.extract(tmpFileCache.getTempDir(), nativeLibMap, jarFile,
@@ -352,18 +363,18 @@ public class TempJarCache {
 
             // mark loaded (those were just loaded)
             if(extractNativeLibraries) {
-                nativeLibJars.put(jarURI, LoadState.LOADED);
+                nativeLibJars.put(jarUri, LoadState.LOADED);
             }
             if(extractClassFiles) {
-                classFileJars.put(jarURI, LoadState.LOADED);
+                classFileJars.put(jarUri, LoadState.LOADED);
             }
             if(extractOtherFiles) {
-                resourceFileJars.put(jarURI, LoadState.LOADED);
+                resourceFileJars.put(jarUri, LoadState.LOADED);
             }
         } else if( !testLoadState(nativeLibJarsLS, LoadState.LOADED) ||
                    !testLoadState(classFileJarsLS, LoadState.LOADED) ||
                    !testLoadState(resourceFileJarsLS, LoadState.LOADED) ) {
-            throw new IOException("TempJarCache: addAll: "+jarURI+", previous load attempt failed");
+            throw new IOException("TempJarCache: addAll: "+jarUri+", previous load attempt failed");
         }
     }
 
@@ -408,12 +419,20 @@ public class TempJarCache {
         return null;
     }
 
+    /**
+     * See {@link #getResourceUri(String)}
+     * @deprecated Use {@link #getResourceUri(String)}
+     */
+    public synchronized static final java.net.URI getResource(final String name) throws URISyntaxException {
+        return getResourceUri(name).toURI();
+    }
+
     /** Similar to {@link ClassLoader#getResource(String)}. */
-    public synchronized static final URI getResource(final String name) throws URISyntaxException {
+    public synchronized static final Uri getResourceUri(final String name) throws URISyntaxException {
         checkInitialized();
         final File f = new File(tmpFileCache.getTempDir(), name);
         if(f.exists()) {
-            return Uri.valueOf(f).toURI();
+            return Uri.valueOf(f);
         }
         return null;
     }
diff --git a/src/junit/com/jogamp/common/net/TestUri01.java b/src/junit/com/jogamp/common/net/TestUri01.java
index 652caa6..bcc7d27 100644
--- a/src/junit/com/jogamp/common/net/TestUri01.java
+++ b/src/junit/com/jogamp/common/net/TestUri01.java
@@ -4,16 +4,12 @@ import java.io.File;
 import java.io.IOException;
 import java.net.URI;
 import java.net.URISyntaxException;
-import java.net.URL;
-import java.net.URLConnection;
-
-import jogamp.common.os.PlatformPropsImpl;
 
 import org.junit.Assert;
 import org.junit.Test;
 
 import com.jogamp.common.net.URIDumpUtil;
-import com.jogamp.common.os.Platform;
+import com.jogamp.common.util.IOUtil;
 import com.jogamp.junit.util.JunitTracer;
 
 import org.junit.FixMethodOrder;
@@ -23,7 +19,66 @@ import org.junit.runners.MethodSorters;
 public class TestUri01 extends JunitTracer {
 
     @Test
-    public void test00URIEscapeSpecialChars() throws IOException, URISyntaxException {
+    public void test00BasicCoding() throws IOException, URISyntaxException {
+        final String string = "Hallo Welt öä";
+        System.err.println("sp1 "+string);
+        final File file = new File(string);
+        System.err.println("file "+file);
+        System.err.println("file.path.dec "+file.getPath());
+        System.err.println("file.path.abs "+file.getAbsolutePath());
+        System.err.println("file.path.can "+file.getCanonicalPath());
+        final Uri uri0 = Uri.valueOf(file);
+        URIDumpUtil.showUri(uri0);
+        URIDumpUtil.showReencodedURIOfUri(uri0);
+
+        boolean ok = true;
+        {
+            final String s2 = IOUtil.slashify(file.getAbsolutePath(), true /* startWithSlash */, file.isDirectory() /* endWithSlash */);
+            System.err.println("uri2.slashify: "+s2);
+            final Uri uri1 = Uri.create(Uri.FILE_SCHEME, null, s2, null);
+            final boolean equalEncoded= uri0.getEncoded().equals(uri1.getEncoded());
+            final boolean equalPath = uri0.path.decode().equals(uri1.path.decode());
+            final boolean equalASCII= uri0.toASCIIString().equals(uri1.toASCIIString().get());
+            System.err.println("uri2.enc   : "+uri1.getEncoded()+" - "+(equalEncoded?"OK":"ERROR"));
+            System.err.println("uri2.pathD : "+uri1.path.decode()+" - "+(equalPath?"OK":"ERROR"));
+            System.err.println("uri2.asciiE: "+uri1.toASCIIString()+" - "+(equalASCII?"OK":"ERROR"));
+            ok = equalEncoded && equalPath && equalASCII && ok;
+        }
+        {
+            final String s2 = "/"+string;
+            System.err.println("uri3.orig: "+s2);
+            final Uri uri1 = Uri.create(Uri.FILE_SCHEME, s2, null);
+            final String rString = "file:/Hallo%20Welt%20öä";
+            final String rPath = s2;
+            final String rASCII = "file:/Hallo%20Welt%20%C3%B6%C3%A4";
+            final boolean equalEncoded = rString.equals(uri1.toString());
+            final boolean equalPath = rPath.equals(uri1.path.decode());
+            final boolean equalASCII= rASCII.equals(uri1.toASCIIString().get());
+            System.err.println("uri3.enc   : "+uri1.toString()+" - "+(equalEncoded?"OK":"ERROR"));
+            System.err.println("uri3.pathD : "+uri1.path.decode()+" - "+(equalPath?"OK":"ERROR"));
+            System.err.println("uri3.asciiE: "+uri1.toASCIIString()+" - "+(equalASCII?"OK":"ERROR"));
+            ok = equalEncoded && equalPath && equalASCII && ok;
+        }
+        {
+            final String s2 = "//lala.org/"+string;
+            System.err.println("uri4.orig: "+s2);
+            final Uri uri1 = Uri.create(Uri.HTTP_SCHEME, s2, null);
+            final String rString = "http://lala.org/Hallo%20Welt%20öä";
+            final String rPath = "/"+string;
+            final String rASCII = "http://lala.org/Hallo%20Welt%20%C3%B6%C3%A4";
+            final boolean equalString= rString.equals(uri1.toString());
+            final boolean equalPath = rPath.equals(uri1.path.decode());
+            final boolean equalASCII= rASCII.equals(uri1.toASCIIString().get());
+            System.err.println("uri4.enc   : "+uri1.toString()+" - "+(equalString?"OK":"ERROR"));
+            System.err.println("uri4.pathD : "+uri1.path.decode()+" - "+(equalPath?"OK":"ERROR"));
+            System.err.println("uri4.asciiE: "+uri1.toASCIIString()+" - "+(equalASCII?"OK":"ERROR"));
+            ok = equalString && equalPath && equalASCII && ok;
+        }
+        Assert.assertTrue("One or more errors occured see stderr above", ok);
+    }
+
+    @Test
+    public void test02URIEscapeSpecialChars() throws IOException, URISyntaxException {
         {
             final String vanilla = "XXX ! # $ & ' ( ) * + , / : ; = ? @ [ ]";
             final Uri.Encoded escaped = Uri.Encoded.cast("XXX%20!%20%23%20%24%20%26%20%27%20%28%20%29%20%2A%20%2B%20%2C%20/%20%3A%20%3B%20%3D%20%3F%20%40%20%5B%20%5D");
@@ -60,7 +115,7 @@ public class TestUri01 extends JunitTracer {
         }
     }
     @Test
-    public void test01URIEscapeCommonChars() throws IOException, URISyntaxException {
+    public void test03URIEscapeCommonChars() throws IOException, URISyntaxException {
         {
             final String vanilla = "/XXX \"%-.<>\\^_`{|}~";
             final Uri.Encoded escaped = Uri.Encoded.cast("/XXX%20%22%25-.%3C%3E%5C%5E_%60%7B%7C%7D~");
@@ -95,16 +150,16 @@ public class TestUri01 extends JunitTracer {
         URIDumpUtil.showURI(uri3);
 
         System.err.println("URI -> Uri (keep encoding):");
-        final Uri uri4 = Uri.valueOf(uri3, false);
+        final Uri uri4 = Uri.valueOf(uri3);
         URIDumpUtil.showUri(uri4);
 
         System.err.println("URI -> Uri (re-encode):");
-        final Uri uri5 = Uri.valueOf(uri3, true);
+        final Uri uri5 = Uri.valueOf(uri3);
         URIDumpUtil.showUri(uri5);
     }
 
     @Test
-    public void test03EqualsAndHashCode() throws IOException, URISyntaxException {
+    public void test04EqualsAndHashCode() throws IOException, URISyntaxException {
         {
             final Uri uri0 = Uri.cast("http://localhost/test01.html#tag01");
             final Uri uri1 = Uri.create("http", null, "localhost", -1, "/test01.html", null, "tag01");
@@ -155,7 +210,7 @@ public class TestUri01 extends JunitTracer {
     }
 
     @Test
-    public void test04ContainedUri() throws IOException, URISyntaxException {
+    public void test05Contained() throws IOException, URISyntaxException {
         {
             final Uri input = Uri.cast("http://localhost/test01.html#tag01");
             final Uri contained = input.getContainedUri();
@@ -185,7 +240,7 @@ public class TestUri01 extends JunitTracer {
     }
 
     @Test
-    public void test05ParentUri() throws IOException, URISyntaxException {
+    public void test06ParentAndDir() throws IOException, URISyntaxException {
         {
             final Uri input = Uri.cast("http://localhost/");
             final Uri parent = input.getParent();
@@ -193,167 +248,58 @@ public class TestUri01 extends JunitTracer {
         }
         {
             final Uri input     = Uri.cast("jar:http://localhost/test01.jar!/com/Lala.class");
-            final Uri expected1 = Uri.cast("jar:http://localhost/test01.jar!/com/");
-            final Uri expected2 = Uri.cast("jar:http://localhost/test01.jar!/");
-            final Uri expected3 = Uri.cast("jar:http://localhost/");
+            final Uri expParen1 = Uri.cast("jar:http://localhost/test01.jar!/com/");
+            final Uri expFolde1 = expParen1;
+            final Uri expParen2 = Uri.cast("jar:http://localhost/test01.jar!/");
+            final Uri expFolde2 = expParen1; // is folder already
+            final Uri expParen3 = Uri.cast("jar:http://localhost/");
+            final Uri expFolde3 = expParen2; // is folder already
+            Assert.assertNotEquals(input, expParen1);
+            Assert.assertNotEquals(expParen1, expParen2);
+            Assert.assertNotEquals(expParen1, expParen3);
+
             final Uri parent1 = input.getParent();
+            final Uri folder1 = input.getDirectory();
             final Uri parent2 = parent1.getParent();
+            final Uri folder2 = parent1.getDirectory();
             final Uri parent3 = parent2.getParent();
-            Assert.assertEquals(expected1, parent1);
-            Assert.assertEquals(expected1.hashCode(), parent1.hashCode());
-            Assert.assertEquals(expected2, parent2);
-            Assert.assertEquals(expected2.hashCode(), parent2.hashCode());
-            Assert.assertEquals(expected3, parent3);
-            Assert.assertEquals(expected3.hashCode(), parent3.hashCode());
-        }
-        {
-            final Uri input     = Uri.cast("http://localhost/dir/test01.jar?lala=01#frag01");
-            final Uri expected1 = Uri.cast("http://localhost/dir/");
-            final Uri expected2 = Uri.cast("http://localhost/");
-            final Uri parent1 = input.getParent();
-            final Uri parent2 = parent1.getParent();
-            Assert.assertEquals(expected1, parent1);
-            Assert.assertEquals(expected1.hashCode(), parent1.hashCode());
-            Assert.assertEquals(expected2, parent2);
-            Assert.assertEquals(expected2.hashCode(), parent2.hashCode());
-        }
-    }
+            final Uri folder3 = parent2.getDirectory();
 
-    @Test
-    public void test10HttpUri2URL() throws IOException, URISyntaxException {
-        testUri2URL(getSimpleTestName("."), TestUri03Resolving.uriHttpSArray);
-    }
+            Assert.assertEquals(expParen1, parent1);
+            Assert.assertEquals(expParen1.hashCode(), parent1.hashCode());
+            Assert.assertEquals(expFolde1, folder1);
 
-    @Test
-    public void test20FileUnixUri2URL() throws IOException, URISyntaxException {
-        testUri2URL(getSimpleTestName("."), TestUri03Resolving.uriFileSArrayUnix);
-    }
+            Assert.assertEquals(expParen2, parent2);
+            Assert.assertEquals(expParen2.hashCode(), parent2.hashCode());
+            Assert.assertEquals(expFolde2, folder2);
 
-    @Test
-    public void test21FileWindowsUri2URL() throws IOException, URISyntaxException {
-        testUri2URL(getSimpleTestName("."), TestUri03Resolving.uriFileSArrayWindows);
-    }
-
-    @Test
-    public void test30FileUnixUri2URL() throws IOException, URISyntaxException {
-        if( Platform.OSType.WINDOWS != PlatformPropsImpl.OS_TYPE ) {
-            testFile2Uri(getSimpleTestName("."), TestUri03Resolving.fileSArrayUnix);
-        }
-    }
-
-    @Test
-    public void test31FileWindowsUri2URL() throws IOException, URISyntaxException {
-        if( Platform.OSType.WINDOWS == PlatformPropsImpl.OS_TYPE ) {
-            testFile2Uri(getSimpleTestName("."), TestUri03Resolving.fileSArrayWindows);
-        }
-    }
+            Assert.assertEquals(expParen3, parent3);
+            Assert.assertEquals(expParen3.hashCode(), parent3.hashCode());
+            Assert.assertEquals(expFolde3, folder3);
 
-    static void testUri2URL(final String testname, final String[][] uriSArray) throws IOException, URISyntaxException {
-        boolean ok = true;
-        for(int i=0; i<uriSArray.length; i++) {
-            final String[] uriSPair = uriSArray[i];
-            final String uriSource = uriSPair[0];
-            System.err.println("SSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSS "+testname+": "+(i+1)+"/"+uriSArray.length);
-            ok = testUri2URL(Uri.Encoded.cast(uriSource)) && ok;
-            System.err.println("EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE "+testname+": "+(i+1)+"/"+uriSArray.length);
         }
-        Assert.assertTrue("One or more errors occured see stderr above", ok);
-    }
-
-    static boolean testUri2URL(final Uri.Encoded uriSource) throws IOException, URISyntaxException {
-        System.err.println("uriSource   : "+uriSource);
-        final Uri uri0 = new Uri(uriSource);
-        URIDumpUtil.showUri(uri0);
-
-        final URI actualURI = uri0.toURI();
-        URIDumpUtil.showURI(actualURI);
-        final Uri.Encoded actualURIStr = Uri.Encoded.cast(actualURI.toString());
-
-        final URL actualURL = uri0.toURL();
-        URIDumpUtil.showURL(actualURL);
-        final Uri.Encoded actualURLStr = Uri.Encoded.cast(actualURL.toExternalForm());
-
-        System.err.println("expected_URX: "+uriSource);
-
-        final boolean equalsURI = uriSource.equals(actualURIStr);
-        System.err.println("actual   URI: "+actualURIStr+" - "+(equalsURI?"OK":"ERROR"));
-        final boolean equalsURL = uriSource.equals(actualURLStr);
-        System.err.println("actual   URL: "+actualURLStr+" - "+(equalsURL?"OK":"ERROR"));
-        URIDumpUtil.showReencodedURIOfUri(uri0);
-        URIDumpUtil.showReencodedUriOfURI(actualURI);
-
-        final boolean ok = equalsURL && equalsURI;
+        {
+            final Uri input     = Uri.cast("http://localhost/dir/test01.jar?lala=01#frag01");
+            final Uri expParen1 = Uri.cast("http://localhost/dir/");
+            final Uri expFolde1 = expParen1;
+            final Uri expParen2 = Uri.cast("http://localhost/");
+            final Uri expFolde2 = expParen1; // is folder already
+            Assert.assertNotEquals(input, expParen1);
+            Assert.assertNotEquals(expParen1, expParen2);
 
-        // now test open ..
-        Throwable t = null;
-        URLConnection con = null;
-        try {
-            con = actualURL.openConnection();
-        } catch (final Throwable _t) {
-            t = _t;
-        }
-        if( null != t ) {
-            System.err.println("XXX: "+t.getClass().getName()+": "+t.getMessage());
-            t.printStackTrace();
-        } else {
-            System.err.println("XXX: No openConnection() failure");
-            System.err.println("XXX: "+con);
-        }
-        return ok;
-    }
+            final Uri parent1 = input.getParent();
+            final Uri folder1 = input.getDirectory();
+            final Uri parent2 = parent1.getParent();
+            final Uri folder2 = parent1.getDirectory();
 
-    static void testFile2Uri(final String testname, final String[][] uriSArray) throws IOException, URISyntaxException {
-        boolean ok = true;
-        for(int i=0; i<uriSArray.length; i++) {
-            final String[] uriSPair = uriSArray[i];
-            final String uriSource = uriSPair[0];
-            final String uriEncExpected= uriSPair[1];
-            final String fileExpected= uriSPair[2];
-            System.err.println("SSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSS "+testname+": "+(i+1)+"/"+uriSArray.length);
-            ok = testFile2Uri(uriSource, Uri.Encoded.cast(uriEncExpected), fileExpected) && ok;
-            System.err.println("EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE "+testname+": "+(i+1)+"/"+uriSArray.length);
-        }
-        Assert.assertTrue("One or more errors occured see stderr above", ok);
-    }
+            Assert.assertEquals(expParen1, parent1);
+            Assert.assertEquals(expParen1.hashCode(), parent1.hashCode());
+            Assert.assertEquals(expFolde1, folder1);
 
-    static boolean testFile2Uri(final String fileSource, final Uri.Encoded uriEncExpected, final String fileExpected) throws IOException, URISyntaxException {
-        System.err.println("fileSource:         "+fileSource);
-        final File file = new File(fileSource);
-        System.err.println("file:               "+file.getAbsolutePath());
-        final Uri uri1 = Uri.valueOf(file);
-        System.err.println("uri1.string:        "+uri1.toString());
-        URIDumpUtil.showUri(uri1);
-        URIDumpUtil.showURL(uri1.toURL());
-        URIDumpUtil.showReencodedURIOfUri(uri1);
-
-        final URL actualUrl = uri1.toURL();
-        final String actualFileS = uri1.getNativeFilePath();
-        final boolean equalsFilePath = fileExpected.equals(actualFileS);
-        System.err.println("expected_path:      "+fileExpected);
-        System.err.println("actual___file-path: "+actualFileS+" - "+(equalsFilePath?"OK":"ERROR"));
-        final boolean equalsEncUri = uriEncExpected.equals(uri1.input);
-        System.err.println("expected__encUri:   "+uriEncExpected);
-        System.err.println("actual_______Uri:   "+uri1.input+" - "+(equalsEncUri?"OK":"ERROR"));
-        final boolean ok = equalsEncUri && equalsFilePath;
-
-        System.err.println("actual_______URL:   "+actualUrl.toExternalForm());
-
-        // now test open ..
-        Throwable t = null;
-        URLConnection con = null;
-        try {
-            con = actualUrl.openConnection();
-        } catch (final Throwable _t) {
-            t = _t;
-        }
-        if( null != t ) {
-            System.err.println("XXX: "+t.getClass().getName()+": "+t.getMessage());
-            t.printStackTrace();
-        } else {
-            System.err.println("XXX: No openConnection() failure");
-            System.err.println("XXX: "+con);
+            Assert.assertEquals(expParen2, parent2);
+            Assert.assertEquals(expParen2.hashCode(), parent2.hashCode());
+            Assert.assertEquals(expFolde2, folder2);
         }
-        return ok;
     }
 
     public static void main(final String args[]) throws IOException {
diff --git a/src/junit/com/jogamp/common/net/TestUri03Resolving.java b/src/junit/com/jogamp/common/net/TestUri03Resolving.java
index 9445100..54e2976 100644
--- a/src/junit/com/jogamp/common/net/TestUri03Resolving.java
+++ b/src/junit/com/jogamp/common/net/TestUri03Resolving.java
@@ -2,6 +2,7 @@ package com.jogamp.common.net;
 
 import java.io.File;
 import java.io.IOException;
+import java.net.URI;
 import java.net.URISyntaxException;
 import java.net.URL;
 import java.net.URLConnection;
@@ -13,7 +14,6 @@ import org.junit.Test;
 
 import com.jogamp.common.net.Uri;
 import com.jogamp.common.os.Platform;
-import com.jogamp.common.util.IOUtil;
 import com.jogamp.junit.util.JunitTracer;
 
 import org.junit.FixMethodOrder;
@@ -24,7 +24,7 @@ public class TestUri03Resolving extends JunitTracer {
 
     // Bug 908, issues w/ windows file path char: $ ^ ~ # [ ]
 
-    public static final String[][] uriHttpSArray = new String[][] {
+    private static final String[][] uriHttpSArray = new String[][] {
         new String[] {"http://localhost/gluegen/build-x86_64/gluegen-rt.jar"},
 
         new String[] {"http://localhost/gluegen/"+'\u0394'+"/gluegen-rt.jar"},
@@ -43,10 +43,14 @@ public class TestUri03Resolving extends JunitTracer {
 
         new String[] {"jar:http://localhost/gluegen/R%23/gluegen-rt.jar!/"},
 
+        new String[] {"jar:http://localhost/gluegen/A%24/B%5E/C~/D%23/E%5B/F%5D/gluegen-rt.jar!/"},
+
+        new String[] {"jar:http://localhost/gluegen/%24/%5E/~/%23/%5B/%5D/gluegen-rt.jar!/"},
+
         new String[] {"jar:http://localhost/gluegen/"+'\u0394'+"/gluegen-rt.jar!/"},
     };
 
-    public static final String[][] uriFileSArrayUnix = new String[][] {
+    private static final String[][] uriFileSArrayUnix = new String[][] {
         new String[] {"file:/gluegen/build-x86_64/gluegen-rt.jar"},
 
         new String[] {"file:/gluegen/"+'\u0394'+"/gluegen-rt.jar"},
@@ -67,10 +71,14 @@ public class TestUri03Resolving extends JunitTracer {
 
         new String[] {"jar:file:/gluegen/R%23/gluegen-rt.jar!/"},
 
+        new String[] {"jar:file:/gluegen/A%24/B%5E/C~/D%23/E%5B/F%5D/gluegen-rt.jar!/"},
+
+        new String[] {"jar:file:/gluegen/%24/%5E/~/%23/%5B/%5D/gluegen-rt.jar!/"},
+
         new String[] {"jar:file:/gluegen/"+'\u0394'+"/gluegen-rt.jar!/"},
     };
 
-    public static final String[][] uriFileSArrayWindows = new String[][] {
+    private static final String[][] uriFileSArrayWindows = new String[][] {
         new String[] {"file:/C%3A/gluegen/build-x86_64/gluegen-rt.jar"},
 
         new String[] {"file:/C%3A/gluegen/"+'\u0394'+"/gluegen-rt.jar"},
@@ -85,7 +93,6 @@ public class TestUri03Resolving extends JunitTracer {
 
         new String[] {"jar:file:/C%3A/gluegen/build-x86_64/gluegen-rt.jar!/com/jogamp/common/os/Platform.class"},
 
-        new String[] {"jar:file:/C%3A/gluegen/build-x86_64%20öä%20lala/gluegen-rt.jar!/com/jogamp/common/os/Platform.class"},
         new String[] {"jar:file:/C%3A/gluegen/build-x86_64%20öä%20lala/gluegen-rt.jar!/com/jogamp/common/os/Platform.class"},
 
         new String[] {"jar:file:///C%3A/gluegen/build-x86_64%20öä%20lala/gluegen-rt.jar!/com/jogamp/common/os/Platform.class"},
@@ -94,9 +101,43 @@ public class TestUri03Resolving extends JunitTracer {
 
         new String[] {"jar:file:/C%3A/gluegen/R%23/gluegen-rt.jar!/"},
 
+        new String[] {"jar:file:/C%3A/gluegen/A%24/B%5E/C~/D%23/E%5B/F%5D/gluegen-rt.jar!/"},
+
+        new String[] {"jar:file:/C%3A/gluegen/%24/%5E/~/%23/%5B/%5D/gluegen-rt.jar!/"},
+
         new String[] {"jar:file:/C%3A/gluegen/"+'\u0394'+"/gluegen-rt.jar!/"},
     };
 
+    private static final String[][] urlFileSArrayWindows = new String[][] {
+        new String[] {"file:/C:/gluegen/build-x86_64/gluegen-rt.jar"},
+
+        new String[] {"file:/C:/gluegen/"+'\u0394'+"/gluegen-rt.jar"},
+
+        new String[] {"file:/C:/gluegen/build-x86_64%20lala/gluegen-rt.jar"},
+
+        new String[] {"file:/C:/gluegen/build-x86_64%20öä%20lala/gluegen-rt.jar"},
+
+        new String[] {"jar:file:/C:/gluegen/build-x86_64/gluegen-rt.jar!/"},
+
+        new String[] {"jar:file:/C:/gluegen/build-x86_64%20öä%20lala/gluegen-rt.jar!/"},
+
+        new String[] {"jar:file:/C:/gluegen/build-x86_64/gluegen-rt.jar!/com/jogamp/common/os/Platform.class"},
+
+        new String[] {"jar:file:/C:/gluegen/build-x86_64%20öä%20lala/gluegen-rt.jar!/com/jogamp/common/os/Platform.class"},
+
+        new String[] {"jar:file:///C:/gluegen/build-x86_64%20öä%20lala/gluegen-rt.jar!/com/jogamp/common/os/Platform.class"},
+
+        new String[] {"jar:file://filehost/gluegen/build-x86_64%20öä%20lala/gluegen-rt.jar!/com/jogamp/common/os/Platform.class"},
+
+        new String[] {"jar:file:/C:/gluegen/R%23/gluegen-rt.jar!/"},
+
+        new String[] {"jar:file:/C:/gluegen/A%24/B%5E/C~/D%23/E%5B/F%5D/gluegen-rt.jar!/"},
+
+        new String[] {"jar:file:/C:/gluegen/%24/%5E/~/%23/%5B/%5D/gluegen-rt.jar!/"},
+
+        new String[] {"jar:file:/C:/gluegen/"+'\u0394'+"/gluegen-rt.jar!/"},
+    };
+
     public static final String[][] fileSArrayUnix = new String[][] {
         new String[] {"/gluegen/build-x86_64/gluegen-rt.jar",
                       "file:/gluegen/build-x86_64/gluegen-rt.jar",
@@ -158,122 +199,84 @@ public class TestUri03Resolving extends JunitTracer {
     };
 
     @Test
-    public void test00BasicCoding() throws IOException, URISyntaxException {
-        final String string = "Hallo Welt öä";
-        System.err.println("sp1 "+string);
-        final File file = new File(string);
-        System.err.println("file "+file);
-        System.err.println("file.path.dec "+file.getPath());
-        System.err.println("file.path.abs "+file.getAbsolutePath());
-        System.err.println("file.path.can "+file.getCanonicalPath());
-        final Uri uri0 = Uri.valueOf(file);
-        URIDumpUtil.showUri(uri0);
-        URIDumpUtil.showReencodedURIOfUri(uri0);
+    public void test01HttpUri2URL() throws IOException, URISyntaxException {
+        testUri2URL(getSimpleTestName("."), uriHttpSArray);
+    }
 
-        boolean ok = true;
-        {
-            final String s2 = IOUtil.slashify(file.getAbsolutePath(), true /* startWithSlash */, file.isDirectory() /* endWithSlash */);
-            System.err.println("uri2.slashify: "+s2);
-            final Uri uri1 = Uri.create(IOUtil.FILE_SCHEME, null, s2, null);
-            final boolean equalEncoded= uri0.getEncoded().equals(uri1.getEncoded());
-            final boolean equalPath = uri0.path.decode().equals(uri1.path.decode());
-            final boolean equalASCII= uri0.toASCIIString().equals(uri1.toASCIIString().get());
-            System.err.println("uri2.enc   : "+uri1.getEncoded()+" - "+(equalEncoded?"OK":"ERROR"));
-            System.err.println("uri2.pathD : "+uri1.path.decode()+" - "+(equalPath?"OK":"ERROR"));
-            System.err.println("uri2.asciiE: "+uri1.toASCIIString()+" - "+(equalASCII?"OK":"ERROR"));
-            ok = equalEncoded && equalPath && equalASCII && ok;
-        }
-        {
-            final String s2 = "/"+string;
-            System.err.println("uri3.orig: "+s2);
-            final Uri uri1 = Uri.create(IOUtil.FILE_SCHEME, s2, null);
-            final String rString = "file:/Hallo%20Welt%20öä";
-            final String rPath = s2;
-            final String rASCII = "file:/Hallo%20Welt%20%C3%B6%C3%A4";
-            final boolean equalEncoded = rString.equals(uri1.toString());
-            final boolean equalPath = rPath.equals(uri1.path.decode());
-            final boolean equalASCII= rASCII.equals(uri1.toASCIIString().get());
-            System.err.println("uri3.enc   : "+uri1.toString()+" - "+(equalEncoded?"OK":"ERROR"));
-            System.err.println("uri3.pathD : "+uri1.path.decode()+" - "+(equalPath?"OK":"ERROR"));
-            System.err.println("uri3.asciiE: "+uri1.toASCIIString()+" - "+(equalASCII?"OK":"ERROR"));
-            ok = equalEncoded && equalPath && equalASCII && ok;
-        }
-        {
-            final String s2 = "//lala.org/"+string;
-            System.err.println("uri4.orig: "+s2);
-            final Uri uri1 = Uri.create(IOUtil.HTTP_SCHEME, s2, null);
-            final String rString = "http://lala.org/Hallo%20Welt%20öä";
-            final String rPath = "/"+string;
-            final String rASCII = "http://lala.org/Hallo%20Welt%20%C3%B6%C3%A4";
-            final boolean equalString= rString.equals(uri1.toString());
-            final boolean equalPath = rPath.equals(uri1.path.decode());
-            final boolean equalASCII= rASCII.equals(uri1.toASCIIString().get());
-            System.err.println("uri4.enc   : "+uri1.toString()+" - "+(equalString?"OK":"ERROR"));
-            System.err.println("uri4.pathD : "+uri1.path.decode()+" - "+(equalPath?"OK":"ERROR"));
-            System.err.println("uri4.asciiE: "+uri1.toASCIIString()+" - "+(equalASCII?"OK":"ERROR"));
-            ok = equalString && equalPath && equalASCII && ok;
-        }
-        Assert.assertTrue("One or more errors occured see stderr above", ok);
+    @Test
+    public void test02FileUnixUri2URL() throws IOException, URISyntaxException {
+        testUri2URL(getSimpleTestName("."), uriFileSArrayUnix);
     }
 
     @Test
-    public void test01HttpURI2URL() throws IOException, URISyntaxException {
-        testURI2URL(getSimpleTestName("."), uriHttpSArray);
+    public void test03FileWindowsUri2URL() throws IOException, URISyntaxException {
+        testUri2URL(getSimpleTestName("."), uriFileSArrayWindows);
     }
 
     @Test
-    public void test02FileUnixURI2URL() throws IOException, URISyntaxException {
-        testURI2URL(getSimpleTestName("."), uriFileSArrayUnix);
+    public void test11HttpURL2Uri() throws IOException, URISyntaxException {
+        testURL2Uri(getSimpleTestName("."), uriHttpSArray);
     }
 
     @Test
-    public void test03FileWindowsURI2URL() throws IOException, URISyntaxException {
-        testURI2URL(getSimpleTestName("."), uriFileSArrayWindows);
+    public void test12FileUnixURL2Uri() throws IOException, URISyntaxException {
+        testURL2Uri(getSimpleTestName("."), uriFileSArrayUnix);
     }
 
     @Test
-    public void test04FileUnixURI2URL() throws IOException, URISyntaxException {
+    public void test13FileWindowsURL2Uri() throws IOException, URISyntaxException {
+        testURL2Uri(getSimpleTestName("."), urlFileSArrayWindows);
+    }
+
+    @Test
+    public void test24FileUnixURI2URL() throws IOException, URISyntaxException {
         if( Platform.OSType.WINDOWS != PlatformPropsImpl.OS_TYPE ) {
-            testFile2URI(getSimpleTestName("."), fileSArrayUnix);
+            testFile2Uri(getSimpleTestName("."), fileSArrayUnix);
         }
     }
 
     @Test
-    public void test05FileWindowsURI2URL() throws IOException, URISyntaxException {
+    public void test25FileWindowsURI2URL() throws IOException, URISyntaxException {
         if( Platform.OSType.WINDOWS == PlatformPropsImpl.OS_TYPE ) {
-            testFile2URI(getSimpleTestName("."), fileSArrayWindows);
+            testFile2Uri(getSimpleTestName("."), fileSArrayWindows);
         }
     }
 
-    static void testURI2URL(final String testname, final String[][] uriSArray) throws IOException, URISyntaxException {
+    static void testUri2URL(final String testname, final String[][] uriSArray) throws IOException, URISyntaxException {
         boolean ok = true;
         for(int i=0; i<uriSArray.length; i++) {
             final String[] uriSPair = uriSArray[i];
             final String uriSource = uriSPair[0];
             System.err.println("SSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSS "+testname+": "+(i+1)+"/"+uriSArray.length);
-            ok = testURI2URL(Uri.Encoded.cast(uriSource)) && ok;
+            ok = testUri2URL(Uri.Encoded.cast(uriSource)) && ok;
             System.err.println("EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE "+testname+": "+(i+1)+"/"+uriSArray.length);
         }
         Assert.assertTrue("One or more errors occured see stderr above", ok);
     }
 
-    static boolean testURI2URL(final Uri.Encoded uriEncodedSource) throws IOException, URISyntaxException {
-        final Uri uri0 = new Uri(uriEncodedSource);
+    static boolean testUri2URL(final Uri.Encoded uriSource) throws IOException, URISyntaxException {
+        System.err.println("uriSource   : "+uriSource);
+        final Uri uri0 = new Uri(uriSource);
         URIDumpUtil.showUri(uri0);
 
-        final String expected1 = uriEncodedSource.toString();
-        System.err.println("expected__s0: "+uriEncodedSource);
-        System.err.println("expected__d1: "+expected1);
+        final URI actualURI = uri0.toURI();
+        URIDumpUtil.showURI(actualURI);
+        final Uri.Encoded actualURIStr = Uri.Encoded.cast(actualURI.toString());
 
         final URL actualURL = uri0.toURL();
-        final String actualURLStr = actualURL.toString();
-        final boolean equalsURLSrc = uriEncodedSource.equals(actualURLStr);
-        final boolean equalsURLDec1 = expected1.equals(actualURLStr);
-        final boolean equalsURL = equalsURLSrc || equalsURLDec1;
-        System.err.println("actual      : "+actualURLStr+" - "+(equalsURL?"OK":"ERROR")+
-                                          " - equalSrc "+equalsURLSrc+", equalDec1 "+equalsURLDec1);
+        URIDumpUtil.showURL(actualURL);
+        final Uri.Encoded actualURLStr = Uri.Encoded.cast(actualURL.toExternalForm());
 
-        final boolean ok = equalsURL;
+        System.err.println("expected_URX: "+uriSource);
+
+        final boolean equalsURI = uriSource.equals(actualURIStr);
+        System.err.println("actual   URI: "+actualURIStr+" - "+(equalsURI?"OK":"ERROR"));
+        final boolean equalsURL = uriSource.equals(actualURLStr);
+        System.err.println("actual   URL: "+actualURLStr+" - "+(equalsURL?"OK":"ERROR"));
+        URIDumpUtil.showReencodedURIOfUri(uri0);
+        URIDumpUtil.showReencodedUriOfURI(actualURI);
+
+        boolean ok = equalsURL && equalsURI;
 
         // now test open ..
         Throwable t = null;
@@ -290,10 +293,85 @@ public class TestUri03Resolving extends JunitTracer {
             System.err.println("XXX: No openConnection() failure");
             System.err.println("XXX: "+con);
         }
+
+        if( uri0.scheme.equals(Uri.JAR_SCHEME) ) {
+            // Extended tests on JAR Uri
+            final Uri uriSub0 = uri0.getContainedUri();
+            Assert.assertNotNull(uriSub0);
+            System.err.println("EXT JAR contained:");
+            URIDumpUtil.showUri(uriSub0);
+            final Uri uriSubDir0 = uriSub0.getDirectory();
+            final Uri uriSubParent0 = uriSub0.getParent();
+            System.err.println("EXT JAR contained Dir:");
+            URIDumpUtil.showUri(uriSubDir0);
+            System.err.println("EXT JAR contained Parent:");
+            URIDumpUtil.showUri(uriSubParent0);
+            ok = uriSubDir0.equals(uriSubParent0) && ok;
+        }
+        return ok;
+    }
+
+    static void testURL2Uri(final String testname, final String[][] urlSArray) throws IOException, URISyntaxException {
+        boolean ok = true;
+        for(int i=0; i<urlSArray.length; i++) {
+            final String[] uriSPair = urlSArray[i];
+            final String uriSource = uriSPair[0];
+            System.err.println("SSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSS "+testname+": "+(i+1)+"/"+urlSArray.length);
+            ok = testURL2Uri(new URL(uriSource)) && ok;
+            System.err.println("EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE "+testname+": "+(i+1)+"/"+urlSArray.length);
+        }
+        Assert.assertTrue("One or more errors occured see stderr above", ok);
+    }
+
+    static boolean testURL2Uri(final URL urlSource) throws IOException, URISyntaxException {
+        System.err.println("URL Source   : "+urlSource);
+        URIDumpUtil.showURL(urlSource);
+
+        final URI uriSource = urlSource.toURI();
+        URIDumpUtil.showURI(uriSource);
+
+        final Uri uri0 = Uri.valueOf(urlSource);
+        URIDumpUtil.showUri(uri0);
+
+        final URL uriToURL = uri0.toURL();
+        URIDumpUtil.showURL(uriToURL);
+
+        // now test open ..
+        Throwable t = null;
+        URLConnection con = null;
+        try {
+            con = uriToURL.openConnection();
+        } catch (final Throwable _t) {
+            t = _t;
+        }
+        if( null != t ) {
+            System.err.println("XXX: "+t.getClass().getName()+": "+t.getMessage());
+            t.printStackTrace();
+        } else {
+            System.err.println("XXX: No openConnection() failure");
+            System.err.println("XXX: "+con);
+        }
+
+        boolean ok = true;
+
+        if( uri0.scheme.equals(Uri.JAR_SCHEME) ) {
+            // Extended tests on JAR Uri
+            final Uri uriSub0 = uri0.getContainedUri();
+            Assert.assertNotNull(uriSub0);
+            System.err.println("EXT JAR contained:");
+            URIDumpUtil.showUri(uriSub0);
+            final Uri uriSubDir0 = uriSub0.getDirectory();
+            final Uri uriSubParent0 = uriSub0.getParent();
+            System.err.println("EXT JAR contained Dir:");
+            URIDumpUtil.showUri(uriSubDir0);
+            System.err.println("EXT JAR contained Parent:");
+            URIDumpUtil.showUri(uriSubParent0);
+            ok = uriSubDir0.equals(uriSubParent0) && ok;
+        }
         return ok;
     }
 
-    static void testFile2URI(final String testname, final String[][] uriSArray) throws IOException, URISyntaxException {
+    static void testFile2Uri(final String testname, final String[][] uriSArray) throws IOException, URISyntaxException {
         boolean ok = true;
         for(int i=0; i<uriSArray.length; i++) {
             final String[] uriSPair = uriSArray[i];
@@ -301,13 +379,13 @@ public class TestUri03Resolving extends JunitTracer {
             final String uriEncExpected= uriSPair[1];
             final String fileExpected= uriSPair[2];
             System.err.println("SSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSS "+testname+": "+(i+1)+"/"+uriSArray.length);
-            ok = testFile2URI(uriSource, Uri.Encoded.cast(uriEncExpected), fileExpected) && ok;
+            ok = testFile2Uri(uriSource, Uri.Encoded.cast(uriEncExpected), fileExpected) && ok;
             System.err.println("EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE "+testname+": "+(i+1)+"/"+uriSArray.length);
         }
         Assert.assertTrue("One or more errors occured see stderr above", ok);
     }
 
-    static boolean testFile2URI(final String fileSource, final Uri.Encoded uriEncExpected, final String fileExpected) throws IOException, URISyntaxException {
+    static boolean testFile2Uri(final String fileSource, final Uri.Encoded uriEncExpected, final String fileExpected) throws IOException, URISyntaxException {
         System.err.println("fileSource:         "+fileSource);
         final File file = new File(fileSource);
         System.err.println("file:               "+file.getAbsolutePath());
@@ -316,10 +394,10 @@ public class TestUri03Resolving extends JunitTracer {
         URIDumpUtil.showReencodedURIOfUri(uri0);
 
         final URL actualUrl = uri0.toURL();
-        final String actualFileS = uri0.getNativeFilePath();
-        final boolean equalsFilePath = fileExpected.equals(actualFileS);
+        final File actualFile = uri0.toFile();
+        final boolean equalsFilePath = fileExpected.equals(actualFile.getPath());
         System.err.println("expected_path:      "+fileExpected);
-        System.err.println("actual___file-path: "+actualFileS+" - "+(equalsFilePath?"OK":"ERROR"));
+        System.err.println("actual___file-path: "+actualFile+" - "+(equalsFilePath?"OK":"ERROR"));
         final boolean equalsEncUri = uriEncExpected.equals(uri0.getEncoded());
         System.err.println("expected__encUri:   "+uriEncExpected);
         System.err.println("actual_______Uri:   "+uri0.getEncoded()+" - "+(equalsEncUri?"OK":"ERROR"));
diff --git a/src/junit/com/jogamp/common/net/TestUri99LaunchOnReservedCharPathBug908.java b/src/junit/com/jogamp/common/net/TestUri99LaunchOnReservedCharPathBug908.java
index 011b18c..09748a1 100644
--- a/src/junit/com/jogamp/common/net/TestUri99LaunchOnReservedCharPathBug908.java
+++ b/src/junit/com/jogamp/common/net/TestUri99LaunchOnReservedCharPathBug908.java
@@ -42,7 +42,6 @@ import org.junit.runners.MethodSorters;
 
 import com.jogamp.common.os.AndroidVersion;
 import com.jogamp.common.os.Platform;
-import com.jogamp.common.util.IOUtil;
 import com.jogamp.common.util.JarUtil;
 import com.jogamp.common.util.ReflectionUtil;
 import com.jogamp.junit.util.JunitTracer;
@@ -84,16 +83,16 @@ public class TestUri99LaunchOnReservedCharPathBug908 extends JunitTracer {
 
     @Test
     public void test00TempJarCacheSimplePath() throws IOException, IllegalArgumentException, URISyntaxException {
-        testTempJarCacheOddJarPathImpl("simpletons/", "simpletons/");
+        testTempJarCacheOddJarPathImpl("simpletons/");
     }
 
     @Test
     public void test01TempJarCacheOddPath() throws IOException, IllegalArgumentException, URISyntaxException {
         // Bug 908, issues w/ windows file path char: $ ^ ~ # [ ]
-        testTempJarCacheOddJarPathImpl("A$-B^-C~-D#-E]-F[-öä/",
+        testTempJarCacheOddJarPathImpl("A$-B^-C~-D#-E]-F[-öä/");
                                     // "A$-B%5E-C~-D#-E]-F[-%C3%B6%C3%A4/");    <- Firefox URI encoding! '#' -> [1]
                                     //   "A$-B%5E-C~-D%23-E]-F[-%C3%B6%C3%A4/"); <- '[' ']' -> [2]
-                                    "A$-B%5E-C~-D%23-E%5D-F%5B-%C3%B6%C3%A4/");
+                                    // "A$-B%5E-C~-D%23-E%5D-F%5B-%C3%B6%C3%A4/");
         /**
          * [1] '#'
             java.lang.IllegalArgumentException: URI has a fragment component
@@ -111,34 +110,35 @@ public class TestUri99LaunchOnReservedCharPathBug908 extends JunitTracer {
          */
 
     }
-    private void testTempJarCacheOddJarPathImpl(final String subPathUTF, final String subPathEncoded) throws IOException, IllegalArgumentException, URISyntaxException {
+    private void testTempJarCacheOddJarPathImpl(final String subPathUTF) throws IOException, IllegalArgumentException, URISyntaxException {
         if(AndroidVersion.isAvailable) { System.err.println("n/a on Android"); return; }
 
+        final Uri.Encoded subPathEncoded = new Uri.Encoded(subPathUTF, Uri.PATH_LEGAL);
         final String reservedCharPathUnencoded = "test/build/"+getClass().getSimpleName()+"/"+getTestMethodName()+"/"+subPathUTF;
-        final String reservedCharPathEncoded = "test/build/"+getClass().getSimpleName()+"/"+getTestMethodName()+"/"+subPathEncoded;
+        final Uri.Encoded reservedCharPathEncoded = Uri.Encoded.cast("test/build/"+getClass().getSimpleName()+"/"+getTestMethodName()+"/").concat(subPathEncoded);
 
         System.err.println("0 Unencoded:             "+reservedCharPathUnencoded);
         System.err.println("0 Encoded:               "+reservedCharPathEncoded);
 
         // jar:file:/dir1/dir2/gluegen-rt.jar!/
-        final URI jarFileURI = JarUtil.getJarFileURI(Platform.class.getName(), getClass().getClassLoader());
+        final Uri jarFileURI = JarUtil.getJarFileUri(Platform.class.getName(), getClass().getClassLoader());
         System.err.println("1 jarFileURI:            "+jarFileURI.toString());
         // gluegen-rt.jar
-        final String jarBasename = JarUtil.getJarBasename(jarFileURI);
+        final Uri.Encoded jarBasename = JarUtil.getJarBasename(jarFileURI);
         System.err.println("2 jarBasename:           "+jarBasename);
 
         // file:/dir1/build/gluegen-rt.jar
-        final URI fileURI = JarUtil.getJarSubURI(jarFileURI);
+        final Uri fileURI = jarFileURI.getContainedUri();
         System.err.println("3 fileURI:               "+fileURI.toString());
         // file:/dir1/build/
-        final URI fileFolderURI = new URI(IOUtil.getParentOf(fileURI.toString()));
+        final Uri fileFolderURI = fileURI.getParent();
         System.err.println("4 fileFolderURI:         "+fileFolderURI.toString());
         // file:/dir1/build/test/build/A$-B^-C~-D#-E]-F[/
-        final URI fileNewFolderURI = new URI(fileFolderURI.toString()+reservedCharPathEncoded);
+        final Uri fileNewFolderURI = fileFolderURI.concat(reservedCharPathEncoded);
         System.err.println("5 fileNewFolderURI:      "+fileNewFolderURI.toString());
 
-        final File srcFolder = new File(fileFolderURI);
-        final File dstFolder = new File(fileNewFolderURI);
+        final File srcFolder = fileFolderURI.toFile();
+        final File dstFolder = fileNewFolderURI.toFile();
         System.err.println("6 srcFolder:             "+srcFolder.toString());
         System.err.println("7 dstFolder:             "+dstFolder.toString());
         try {
diff --git a/src/junit/com/jogamp/common/net/URIDumpUtil.java b/src/junit/com/jogamp/common/net/URIDumpUtil.java
index a7d050a..c5ba51f 100644
--- a/src/junit/com/jogamp/common/net/URIDumpUtil.java
+++ b/src/junit/com/jogamp/common/net/URIDumpUtil.java
@@ -52,7 +52,7 @@ public class URIDumpUtil {
 
         System.err.println("0.0.0 string:      "+uri.toString());
         System.err.println("0.0.0 ascii :      "+uri.toASCIIString());
-        System.err.println("0.0.0 native-file: "+uri.getNativeFilePath());
+        System.err.println("0.0.0 native-file: "+uri.toFile());
         System.err.println("0.0.0 contained:   "+uri.getContainedUri());
 
         System.err.println("1.0.0 scheme:      "+uri.scheme);
@@ -73,7 +73,7 @@ public class URIDumpUtil {
      * @throws URISyntaxException
      */
     public static void showReencodedURIOfUri(final Uri uri) throws URISyntaxException {
-        final URI recomposedURI = uri.toURI(true);
+        final URI recomposedURI = uri.toURIReencoded();
         showURI("YYYYYY Recomposed URI "+recomposedURI+", isOpaque "+recomposedURI.isOpaque()+", isAbs "+recomposedURI.isAbsolute(), recomposedURI);
         final String recomposedURIStr = recomposedURI.toString();
         final boolean equalsRecompURI = uri.input.equals(recomposedURIStr);
@@ -88,7 +88,7 @@ public class URIDumpUtil {
      * @throws URISyntaxException
      */
     public static void showReencodedUriOfURI(final URI uri) throws URISyntaxException {
-        final Uri recomposedUri = Uri.valueOf(uri, true);
+        final Uri recomposedUri = Uri.valueOf(uri);
         showUri("ZZZZZZ Recomposed Uri "+recomposedUri+", isOpaque "+recomposedUri.opaque+", isAbs "+recomposedUri.absolute+", hasAuth "+recomposedUri.hasAuthority, recomposedUri);
         final String recomposedUriStr = recomposedUri.toString();
         final boolean equalsRecompUri = uri.toString().equals(recomposedUriStr);
diff --git a/src/junit/com/jogamp/common/util/TestJarUtil.java b/src/junit/com/jogamp/common/util/TestJarUtil.java
index db5c268..06a2e43 100644
--- a/src/junit/com/jogamp/common/util/TestJarUtil.java
+++ b/src/junit/com/jogamp/common/util/TestJarUtil.java
@@ -30,7 +30,6 @@ package com.jogamp.common.util;
 
 import java.io.IOException;
 import java.net.MalformedURLException;
-import java.net.URI;
 import java.net.URISyntaxException;
 import java.net.URL;
 import java.net.URLClassLoader;
@@ -46,6 +45,8 @@ import org.junit.BeforeClass;
 import org.junit.Test;
 
 import com.jogamp.common.GlueGenVersion;
+import com.jogamp.common.net.URIDumpUtil;
+import com.jogamp.common.net.Uri;
 import com.jogamp.common.os.AndroidVersion;
 import com.jogamp.common.util.cache.TempCacheReg;
 import com.jogamp.common.util.cache.TempFileCache;
@@ -97,7 +98,7 @@ public class TestJarUtil extends JunitTracer {
         }
     }
 
-    void validateJarFileURL(final URI jarFileURI) throws IllegalArgumentException, IOException, URISyntaxException {
+    void validateJarFileURL(final Uri jarFileURI) throws IllegalArgumentException, IOException, URISyntaxException {
         Assert.assertNotNull(jarFileURI);
         final URL jarFileURL = jarFileURI.toURL();
         final URLConnection aURLc = jarFileURL.openConnection();
@@ -110,18 +111,27 @@ public class TestJarUtil extends JunitTracer {
     }
 
     void validateJarUtil(final String expJarName, final String clazzBinName, final ClassLoader cl) throws IllegalArgumentException, IOException, URISyntaxException {
-        final String jarName= JarUtil.getJarBasename(clazzBinName, cl);
+        final Uri.Encoded expJarNameE = Uri.Encoded.cast(expJarName);
+        final Uri.Encoded jarName= JarUtil.getJarBasename(clazzBinName, cl);
         Assert.assertNotNull(jarName);
-        Assert.assertEquals(expJarName, jarName);
+        Assert.assertEquals(expJarNameE, jarName);
 
-        final URI jarSubURI = JarUtil.getJarSubURI(clazzBinName, cl);
-        Assert.assertNotNull(jarSubURI);
-        final URL jarSubURL= jarSubURI.toURL();
+        final Uri jarUri = JarUtil.getJarUri(clazzBinName, cl);
+        Assert.assertNotNull(jarUri);
+        System.err.println("1 - jarUri:");
+        URIDumpUtil.showUri(jarUri);
+
+        final Uri jarSubUri = jarUri.getContainedUri();
+        Assert.assertNotNull(jarSubUri);
+        System.err.println("2 - jarSubUri:");
+        URIDumpUtil.showUri(jarSubUri);
+
+        final URL jarSubURL= jarSubUri.toURL();
         final URLConnection urlConn = jarSubURL.openConnection();
         Assert.assertTrue("jarSubURL has zero content: "+jarSubURL, urlConn.getContentLength()>0);
         System.err.println("URLConnection of jarSubURL: "+urlConn);
 
-        final URI jarFileURL = JarUtil.getJarFileURI(clazzBinName, cl);
+        final Uri jarFileURL = JarUtil.getJarFileUri(clazzBinName, cl);
         validateJarFileURL(jarFileURL);
 
         final JarFile jarFile = JarUtil.getJarFile(clazzBinName, cl);
@@ -146,10 +156,10 @@ public class TestJarUtil extends JunitTracer {
         final ClassLoader rootCL = this.getClass().getClassLoader();
 
         // Get containing JAR file "TestJarsInJar.jar" and add it to the TempJarCache
-        TempJarCache.addAll(GlueGenVersion.class, JarUtil.getJarFileURI("ClassInJar0", rootCL));
+        TempJarCache.addAll(GlueGenVersion.class, JarUtil.getJarFileUri("ClassInJar0", rootCL));
 
         // Fetch and load the contained "ClassInJar1.jar"
-        final URL ClassInJar1_jarFileURL = JarUtil.getJarFileURI(TempJarCache.getResource("ClassInJar1.jar")).toURL();
+        final URL ClassInJar1_jarFileURL = JarUtil.getJarFileUri(TempJarCache.getResourceUri("ClassInJar1.jar")).toURL();
         final ClassLoader cl = new URLClassLoader(new URL[] { ClassInJar1_jarFileURL }, rootCL);
         Assert.assertNotNull(cl);
         validateJarUtil("ClassInJar1.jar", "ClassInJar1", cl);
@@ -167,10 +177,10 @@ public class TestJarUtil extends JunitTracer {
         final ClassLoader rootCL = this.getClass().getClassLoader();
 
         // Get containing JAR file "TestJarsInJar.jar" and add it to the TempJarCache
-        TempJarCache.addAll(GlueGenVersion.class, JarUtil.getJarFileURI("ClassInJar0", rootCL));
+        TempJarCache.addAll(GlueGenVersion.class, JarUtil.getJarFileUri("ClassInJar0", rootCL));
 
         // Fetch and load the contained "ClassInJar1.jar"
-        final URL ClassInJar2_jarFileURL = JarUtil.getJarFileURI(TempJarCache.getResource("sub/ClassInJar2.jar")).toURL();
+        final URL ClassInJar2_jarFileURL = JarUtil.getJarFileUri(TempJarCache.getResourceUri("sub/ClassInJar2.jar")).toURL();
         final ClassLoader cl = new URLClassLoader(new URL[] { ClassInJar2_jarFileURL }, rootCL);
         Assert.assertNotNull(cl);
         validateJarUtil("ClassInJar2.jar", "ClassInJar2", cl);
@@ -231,7 +241,7 @@ public class TestJarUtil extends JunitTracer {
             public URL resolve( final URL url ) {
                 if( url.getProtocol().equals("bundleresource") ) {
                     try {
-                        return new URL( IOUtil.JAR_SCHEME, "", url.getFile() );
+                        return new URL( Uri.JAR_SCHEME, "", url.getFile() );
                     } catch(final MalformedURLException e) {
                         return url;
                     }
@@ -244,10 +254,10 @@ public class TestJarUtil extends JunitTracer {
         final ClassLoader rootCL = new CustomClassLoader();
 
         // Get containing JAR file "TestJarsInJar.jar" and add it to the TempJarCache
-        TempJarCache.addAll(GlueGenVersion.class, JarUtil.getJarFileURI("ClassInJar0", rootCL));
+        TempJarCache.addAll(GlueGenVersion.class, JarUtil.getJarFileUri("ClassInJar0", rootCL));
 
         // Fetch and load the contained "ClassInJar1.jar"
-        final URL ClassInJar2_jarFileURL = JarUtil.getJarFileURI(TempJarCache.getResource("sub/ClassInJar2.jar")).toURL();
+        final URL ClassInJar2_jarFileURL = JarUtil.getJarFileUri(TempJarCache.getResourceUri("sub/ClassInJar2.jar")).toURL();
         final ClassLoader cl = new URLClassLoader(new URL[] { ClassInJar2_jarFileURL }, rootCL);
         Assert.assertNotNull(cl);
         validateJarUtil("ClassInJar2.jar", "ClassInJar2", cl);
diff --git a/src/junit/com/jogamp/common/util/TestTempJarCache.java b/src/junit/com/jogamp/common/util/TestTempJarCache.java
index cd825fe..474a17a 100644
--- a/src/junit/com/jogamp/common/util/TestTempJarCache.java
+++ b/src/junit/com/jogamp/common/util/TestTempJarCache.java
@@ -31,7 +31,6 @@ package com.jogamp.common.util;
 import java.io.File;
 import java.io.IOException;
 import java.lang.reflect.Method;
-import java.net.URI;
 import java.net.URISyntaxException;
 import java.net.URL;
 import java.net.URLClassLoader;
@@ -44,6 +43,8 @@ import org.junit.Test;
 import org.junit.runners.MethodSorters;
 
 import com.jogamp.common.GlueGenVersion;
+import com.jogamp.common.net.URIDumpUtil;
+import com.jogamp.common.net.Uri;
 import com.jogamp.common.os.AndroidVersion;
 import com.jogamp.common.os.NativeLibrary;
 import com.jogamp.common.os.Platform;
@@ -175,7 +176,7 @@ public class TestTempJarCache extends JunitTracer {
         if(AndroidVersion.isAvailable) { System.err.println("n/a on Android"); return; }
 
         final ClassLoader cl = getClass().getClassLoader();
-        TempJarCache.addAll(GlueGenVersion.class, JarUtil.getJarFileURI(GlueGenVersion.class.getName(), cl));
+        TempJarCache.addAll(GlueGenVersion.class, JarUtil.getJarFileUri(GlueGenVersion.class.getName(), cl));
 
         File f0 = new File(TempJarCache.getTempFileCache().getTempDir(), "META-INF/MANIFEST.MF");
         Assert.assertTrue(f0.exists());
@@ -195,14 +196,28 @@ public class TestTempJarCache extends JunitTracer {
     @Test
     public void testTempJarCache02AddNativeLibs() throws IOException, IllegalArgumentException, URISyntaxException {
         if(AndroidVersion.isAvailable) { System.err.println("n/a on Android"); return; }
-        final String nativeJarName = "gluegen-rt-natives-"+Platform.getOSAndArch()+".jar";
+        final Uri.Encoded nativeJarName = Uri.Encoded.cast("gluegen-rt-natives-"+Platform.getOSAndArch()+".jar");
         final String libBaseName = "gluegen-rt";
         final ClassLoader cl = getClass().getClassLoader();
 
-        URI jarUriRoot = JarUtil.getJarSubURI(TempJarCache.class.getName(), cl);
-        jarUriRoot = IOUtil.getURIDirname(jarUriRoot);
+        final Uri jarUri = JarUtil.getJarUri(TempJarCache.class.getName(), cl);
+        Assert.assertNotNull(jarUri);
+        System.err.println("1 - jarUri:");
+        URIDumpUtil.showUri(jarUri);
 
-        final URI nativeJarURI = JarUtil.getJarFileURI(jarUriRoot, nativeJarName);
+        final Uri jarFileUri = jarUri.getContainedUri();
+        Assert.assertNotNull(jarFileUri);
+        System.err.println("2 - jarFileUri:");
+        URIDumpUtil.showUri(jarFileUri);
+
+        final Uri jarFileDir = jarFileUri.getParent();
+        Assert.assertNotNull(jarFileDir);
+        System.err.println("3 - jarFileDir:");
+        URIDumpUtil.showUri(jarFileDir);
+
+        final Uri nativeJarURI = JarUtil.getJarFileUri(jarFileDir, nativeJarName);
+        System.err.println("4 - nativeJarURI:");
+        URIDumpUtil.showUri(nativeJarURI);
 
         TempJarCache.addNativeLibs(TempJarCache.class, nativeJarURI, null /* nativeLibraryPath */);
         final String libFullPath = TempJarCache.findLibrary(libBaseName);
@@ -225,7 +240,7 @@ public class TestTempJarCache extends JunitTracer {
     @Test
     public void testTempJarCache04bDiffClassLoader() throws IOException, IllegalArgumentException, URISyntaxException {
         if(AndroidVersion.isAvailable) { System.err.println("n/a on Android"); return; }
-        final URL[] urls = new URL[] { JarUtil.getJarFileURI(TempJarCache.class.getName(), getClass().getClassLoader()).toURL() };
+        final URL[] urls = new URL[] { JarUtil.getJarFileUri(TempJarCache.class.getName(), getClass().getClassLoader()).toURL() };
         System.err.println("url: "+urls[0]);
         final ClassLoader cl2 = new TestClassLoader(urls, null);
         final ClassLoader cl3 = new TestClassLoader(urls, null);
diff --git a/src/junit/com/jogamp/junit/sec/Applet01.java b/src/junit/com/jogamp/junit/sec/Applet01.java
index a335efe..f0188a2 100644
--- a/src/junit/com/jogamp/junit/sec/Applet01.java
+++ b/src/junit/com/jogamp/junit/sec/Applet01.java
@@ -34,14 +34,13 @@ import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.OutputStream;
 import java.net.URISyntaxException;
-import java.net.URL;
 import java.security.AccessControlException;
 
+import com.jogamp.common.net.Uri;
 import com.jogamp.common.os.MachineDescription;
 import com.jogamp.common.os.NativeLibrary;
 import com.jogamp.common.os.Platform;
 import com.jogamp.common.util.IOUtil;
-import com.jogamp.common.util.JarUtil;
 
 /**
  * Applet: Provoke AccessControlException while writing to file!
@@ -156,64 +155,64 @@ public class Applet01 extends Applet {
         }
     }
 
-    private void testOpenLibrary(final boolean global) {
+    private void testOpenLibrary(final boolean global) throws URISyntaxException {
         final ClassLoader cl = getClass().getClassLoader();
         System.err.println("CL "+cl);
 
         String libBaseName = null;
         final Class<?> clazz = this.getClass();
-        URL libURL = clazz.getResource("/libtest1.so");
-        if( null != libURL ) {
+        Uri libUri = null;
+        try {
+            libUri = Uri.valueOf(clazz.getResource("/libtest1.so"));
+        } catch (final URISyntaxException e2) {
+            // not found .. OK
+        }
+        if( null != libUri ) {
             libBaseName = "libtest1.so";
         } else {
-            libURL = clazz.getResource("/test1.dll");
-            if( null != libURL ) {
-                libBaseName = "test1.dll";
+            try {
+                libUri = Uri.valueOf(clazz.getResource("/test1.dll"));
+                if( null != libUri ) {
+                    libBaseName = "test1.dll";
+                }
+            } catch (final URISyntaxException e) {
+                // not found
             }
         }
-        System.err.println("Untrusted Library (URL): "+libURL);
+        System.err.println("Untrusted Library (URL): "+libUri);
 
-        String libDir1 = null;
-        if( null != libURL ) {
+        if( null != libUri ) {
+            Uri libDir1 = libUri.getContainedUri();
+            System.err.println("libDir1.1: "+libDir1);
+            libDir1= libDir1.getParent();
+            System.err.println("libDir1.2: "+libDir1);
+            System.err.println("Untrusted Library Dir1 (abs): "+libDir1);
+            final Uri absLib = libDir1.concat(Uri.Encoded.cast("natives/" + libBaseName));
+            Exception sec01 = null;
             try {
-                libDir1 = JarUtil.getJarSubURI(libURL.toURI()).getPath();
-            } catch (final Exception e) {
-                e.printStackTrace();
-            }
-            if( null != libDir1 ) {
-                System.err.println("libDir1.1: "+libDir1);
-                try {
-                    libDir1= IOUtil.getParentOf(libDir1);
-                } catch (final URISyntaxException e) {
-                    e.printStackTrace();
+                final NativeLibrary nlib = NativeLibrary.open(absLib.toFile().getPath(), cl);
+                System.err.println("NativeLibrary: "+nlib);
+            } catch (final SecurityException e) {
+                sec01 = e;
+                if( usesSecurityManager ) {
+                    System.err.println("Expected exception for loading native library");
+                    System.err.println("Message: "+sec01.getMessage());
+                } else {
+                    System.err.println("Unexpected exception for loading native library");
+                    sec01.printStackTrace();
                 }
-                System.err.println("libDir1.2: "+libDir1);
             }
-        }
-        System.err.println("Untrusted Library Dir1 (abs): "+libDir1);
-        final String absLib = libDir1 + "natives/" + libBaseName;
-        Exception sec01 = null;
-        try {
-            final NativeLibrary nlib = NativeLibrary.open(absLib, cl);
-            System.err.println("NativeLibrary: "+nlib);
-        } catch (final SecurityException e) {
-            sec01 = e;
-            if( usesSecurityManager ) {
-                System.err.println("Expected exception for loading native library");
-                System.err.println("Message: "+sec01.getMessage());
+            if( !usesSecurityManager ) {
+                if( null != sec01 ) {
+                    throw new Error("SecurityException thrown on loading native library", sec01);
+                }
             } else {
-                System.err.println("Unexpected exception for loading native library");
-                sec01.printStackTrace();
-            }
-        }
-        if( !usesSecurityManager ) {
-            if( null != sec01 ) {
-                throw new Error("SecurityException thrown on loading native library", sec01);
+                if( null == sec01 ) {
+                    throw new Error("SecurityException not thrown on loading native library");
+                }
             }
         } else {
-            if( null == sec01 ) {
-                throw new Error("SecurityException not thrown on loading native library");
-            }
+            System.err.println("No library found");
         }
     }
 
@@ -244,7 +243,11 @@ public class Applet01 extends Applet {
         testWriteFile();
         System.err.println("writeFile: OK");
 
-        testOpenLibrary(true);
+        try {
+            testOpenLibrary(true);
+        } catch (final URISyntaxException e) {
+            e.printStackTrace();
+        }
         System.err.println("lib0: OK");
     }
 
diff --git a/src/junit/com/jogamp/junit/sec/TestSecIOUtil01.java b/src/junit/com/jogamp/junit/sec/TestSecIOUtil01.java
index e4e1b34..b347680 100644
--- a/src/junit/com/jogamp/junit/sec/TestSecIOUtil01.java
+++ b/src/junit/com/jogamp/junit/sec/TestSecIOUtil01.java
@@ -29,7 +29,6 @@
 package com.jogamp.junit.sec;
 
 import java.net.URISyntaxException;
-import java.net.URL;
 import java.security.AccessControlException;
 import java.io.File;
 import java.io.IOException;
@@ -38,10 +37,10 @@ import org.junit.Assert;
 import org.junit.BeforeClass;
 import org.junit.Test;
 
+import com.jogamp.common.net.Uri;
 import com.jogamp.common.os.NativeLibrary;
 import com.jogamp.common.os.Platform;
 import com.jogamp.common.util.IOUtil;
-import com.jogamp.common.util.JarUtil;
 import com.jogamp.junit.util.JunitTracer;
 
 import org.junit.FixMethodOrder;
@@ -137,73 +136,75 @@ public class TestSecIOUtil01 extends JunitTracer {
         testTempDirImpl(false);
     }
 
-    private NativeLibrary openLibraryImpl(final boolean global) {
+    private NativeLibrary openLibraryImpl(final boolean global) throws URISyntaxException {
         final ClassLoader cl = getClass().getClassLoader();
         System.err.println("CL "+cl);
 
         String libBaseName = null;
         final Class<?> clazz = this.getClass();
-        URL libURL = clazz.getResource("/libtest1.so");
-        if( null != libURL ) {
+        Uri libUri = null;
+        try {
+            libUri = Uri.valueOf(clazz.getResource("/libtest1.so"));
+        } catch (final URISyntaxException e2) {
+            // not found .. OK
+        }
+        if( null != libUri ) {
             libBaseName = "libtest1.so";
         } else {
-            libURL = clazz.getResource("/test1.dll");
-            if( null != libURL ) {
-                libBaseName = "test1.dll";
+            try {
+                libUri = Uri.valueOf(clazz.getResource("/test1.dll"));
+                if( null != libUri ) {
+                    libBaseName = "test1.dll";
+                }
+            } catch (final URISyntaxException e) {
+                // not found
             }
         }
-        System.err.println("Untrusted Library (URL): "+libURL);
-
-        String libDir1 = null;
-        if( null != libURL ) {
+        System.err.println("Untrusted Library (URL): "+libUri);
+
+        if( null != libUri ) {
+            Uri libDir1 = libUri.getContainedUri();
+            System.err.println("libDir1.1: "+libDir1);
+            libDir1= libDir1.getParent();
+            System.err.println("libDir1.2: "+libDir1);
+            System.err.println("Untrusted Library Dir1 (abs): "+libDir1);
+            final Uri absLib = libDir1.concat(Uri.Encoded.cast("natives/" + libBaseName));
+            Exception se0 = null;
+            NativeLibrary nlib = null;
             try {
-                libDir1 = JarUtil.getJarSubURI(libURL.toURI()).getPath();
-            } catch (final Exception e) {
-                e.printStackTrace();
-            }
-            if( null != libDir1 ) {
-                System.err.println("libDir1.1: "+libDir1);
-                try {
-                    libDir1= IOUtil.getParentOf(libDir1);
-                } catch (final URISyntaxException e) {
-                    e.printStackTrace();
+                nlib = NativeLibrary.open(absLib.toFile().getPath(), cl);
+                System.err.println("NativeLibrary: "+nlib);
+            } catch (final SecurityException e) {
+                se0 = e;
+                if( usesSecurityManager ) {
+                    System.err.println("Expected exception for loading native library");
+                    System.err.println("Message: "+se0.getMessage());
+                } else {
+                    System.err.println("Unexpected exception for loading native library");
+                    se0.printStackTrace();
                 }
-                System.err.println("libDir1.2: "+libDir1);
             }
-        }
-        System.err.println("Untrusted Library Dir1 (abs): "+libDir1);
-        final String absLib = libDir1 + "natives/" + libBaseName;
-        Exception se0 = null;
-        NativeLibrary nlib = null;
-        try {
-            nlib = NativeLibrary.open(absLib, cl);
-            System.err.println("NativeLibrary: "+nlib);
-        } catch (final SecurityException e) {
-            se0 = e;
-            if( usesSecurityManager ) {
-                System.err.println("Expected exception for loading native library");
-                System.err.println("Message: "+se0.getMessage());
+            if( !usesSecurityManager ) {
+                Assert.assertNull("SecurityException thrown on loading native library", se0);
             } else {
-                System.err.println("Unexpected exception for loading native library");
-                se0.printStackTrace();
+                Assert.assertNotNull("SecurityException not thrown on loading native library", se0);
             }
-        }
-        if( !usesSecurityManager ) {
-            Assert.assertNull("SecurityException thrown on loading native library", se0);
+            return nlib;
         } else {
-            Assert.assertNotNull("SecurityException not thrown on loading native library", se0);
+            System.err.println("No library found");
+            return null;
         }
-        return nlib;
+
     }
 
-    public void testOpenLibrary() {
+    public void testOpenLibrary() throws URISyntaxException {
         final NativeLibrary nlib = openLibraryImpl(true);
         if( null != nlib ) {
             nlib.close();
         }
     }
 
-    public static void main(final String args[]) throws IOException {
+    public static void main(final String args[]) throws IOException, URISyntaxException {
         TestSecIOUtil01.setup();
 
         final TestSecIOUtil01 aa = new TestSecIOUtil01();
diff --git a/src/junit/com/jogamp/junit/util/VersionSemanticsUtil.java b/src/junit/com/jogamp/junit/util/VersionSemanticsUtil.java
index 953c795..78f4460 100644
--- a/src/junit/com/jogamp/junit/util/VersionSemanticsUtil.java
+++ b/src/junit/com/jogamp/junit/util/VersionSemanticsUtil.java
@@ -29,7 +29,6 @@ package com.jogamp.junit.util;
 
 import java.io.File;
 import java.io.IOException;
-import java.net.URI;
 import java.net.URISyntaxException;
 import java.util.HashSet;
 import java.util.Set;
@@ -40,7 +39,7 @@ import org.semver.Comparer;
 import org.semver.Delta;
 import org.semver.Dumper;
 
-import com.jogamp.common.util.IOUtil;
+import com.jogamp.common.net.Uri;
 import com.jogamp.common.util.JarUtil;
 import com.jogamp.common.util.VersionNumberString;
 
@@ -54,12 +53,10 @@ public class VersionSemanticsUtil {
                                        throws IllegalArgumentException, IOException, URISyntaxException
     {
         // Get containing JAR file "TestJarsInJar.jar" and add it to the TempJarCache
-        final URI currentJarURI = JarUtil.getJarSubURI(currentJarClazz.getName(), currentJarCL);
-        final String currentJarLocS = IOUtil.decodeURIIfFilePath(currentJarURI);
-        final File currentJar = new File(currentJarLocS);
+        final Uri currentJarUri = JarUtil.getJarUri(currentJarClazz.getName(), currentJarCL).getContainedUri();
         testVersion(diffCriteria, expectedCompatibilityType,
                     previousJar, preVersionNumber,
-                    currentJar, curVersionNumber,
+                    currentJarUri.toFile(), curVersionNumber,
                     excludesRegExp);
     }
 
-- 
cgit v1.2.3