You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@river.apache.org by pe...@apache.org on 2013/03/28 05:11:22 UTC

svn commit: r1461949 [1/3] - in /river/jtsk/skunk/qa_refactor/trunk: src/org/apache/river/api/net/ src/org/apache/river/api/security/ src/org/apache/river/impl/ src/org/apache/river/impl/net/ test/src/org/apache/river/api/net/ test/src/org/apache/river...

Author: peter_firmstone
Date: Thu Mar 28 04:11:21 2013
New Revision: 1461949

URL: http://svn.apache.org/r1461949
Log:
Immutable thread safe, non serializable Uri; drop in replacement for java.net.URI.  Initial version a refactored copy of Apache Harmony's URI class.  Includes junit test cases.

Will be modified to comply with RFC 3986 and integrate UriString functionality, so UriString can be removed.

Added:
    river/jtsk/skunk/qa_refactor/trunk/src/org/apache/river/api/net/
    river/jtsk/skunk/qa_refactor/trunk/src/org/apache/river/api/net/URIEncoderDecoder.java   (with props)
    river/jtsk/skunk/qa_refactor/trunk/src/org/apache/river/api/net/Uri.java   (with props)
    river/jtsk/skunk/qa_refactor/trunk/src/org/apache/river/api/net/UriParser.java   (with props)
    river/jtsk/skunk/qa_refactor/trunk/src/org/apache/river/impl/Messages.java
      - copied, changed from r1460995, river/jtsk/skunk/qa_refactor/trunk/src/org/apache/river/api/security/Messages.java
    river/jtsk/skunk/qa_refactor/trunk/src/org/apache/river/impl/messages.properties
      - copied, changed from r1460995, river/jtsk/skunk/qa_refactor/trunk/src/org/apache/river/api/security/messages.properties
    river/jtsk/skunk/qa_refactor/trunk/test/src/org/apache/river/api/net/
    river/jtsk/skunk/qa_refactor/trunk/test/src/org/apache/river/api/net/UriTest.java   (with props)
Removed:
    river/jtsk/skunk/qa_refactor/trunk/src/org/apache/river/api/security/Messages.java
    river/jtsk/skunk/qa_refactor/trunk/src/org/apache/river/api/security/messages.properties
Modified:
    river/jtsk/skunk/qa_refactor/trunk/src/org/apache/river/api/security/DefaultPolicyParser.java
    river/jtsk/skunk/qa_refactor/trunk/src/org/apache/river/api/security/DefaultPolicyScanner.java
    river/jtsk/skunk/qa_refactor/trunk/src/org/apache/river/api/security/PolicyUtils.java
    river/jtsk/skunk/qa_refactor/trunk/src/org/apache/river/api/security/Segment.java
    river/jtsk/skunk/qa_refactor/trunk/src/org/apache/river/api/security/UnresolvedPrincipal.java
    river/jtsk/skunk/qa_refactor/trunk/src/org/apache/river/impl/net/UriString.java
    river/jtsk/skunk/qa_refactor/trunk/test/src/org/apache/river/api/security/PrincipalGrantTest.java

Added: river/jtsk/skunk/qa_refactor/trunk/src/org/apache/river/api/net/URIEncoderDecoder.java
URL: http://svn.apache.org/viewvc/river/jtsk/skunk/qa_refactor/trunk/src/org/apache/river/api/net/URIEncoderDecoder.java?rev=1461949&view=auto
==============================================================================
--- river/jtsk/skunk/qa_refactor/trunk/src/org/apache/river/api/net/URIEncoderDecoder.java (added)
+++ river/jtsk/skunk/qa_refactor/trunk/src/org/apache/river/api/net/URIEncoderDecoder.java Thu Mar 28 04:11:21 2013
@@ -0,0 +1,224 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE 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 org.apache.river.api.net;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.UnsupportedEncodingException;
+import java.net.URISyntaxException;
+import java.util.HashMap;
+import java.util.Map;
+import org.apache.river.impl.Messages;
+
+
+/**
+ * This class is used to encode a string using the format required by {@code
+ * application/x-www-form-urlencoded} MIME content type. It contains helper
+ * methods used by the URI class, and performs encoding and decoding in a
+ * slightly different way than {@code URLEncoder} and {@code URLDecoder}.
+ */
+class URIEncoderDecoder {
+
+    static final String digits = "0123456789ABCDEF"; //$NON-NLS-1$
+
+    static final String encoding = "UTF8"; //$NON-NLS-1$
+    
+    
+    
+    /**
+     * Validate a string by checking if it contains any characters other than:
+     * 1. letters ('a'..'z', 'A'..'Z') 2. numbers ('0'..'9') 3. characters in
+     * the legalset parameter 4. others (unicode characters that are not in
+     * US-ASCII set, and are not ISO Control or are not ISO Space characters)
+     * <p>
+     * called from {@code URI.Helper.parseURI()} to validate each component
+     *
+     * @param s
+     *            {@code java.lang.String} the string to be validated
+     * @param legal
+     *            {@code java.lang.String} the characters allowed in the String
+     *            s
+     */
+    static void validate(String s, String legal) throws URISyntaxException {
+        for (int i = 0; i < s.length();) {
+            char ch = s.charAt(i);
+            if (ch == '%') {
+                do {
+                    if (i + 2 >= s.length()) {
+                        throw new URISyntaxException(s, Messages.getString("luni.7D"), //$NON-NLS-1$
+                                i);
+                    }
+                    int d1 = Character.digit(s.charAt(i + 1), 16);
+                    int d2 = Character.digit(s.charAt(i + 2), 16);
+                    if (d1 == -1 || d2 == -1) {
+                        throw new URISyntaxException(s, Messages.getString("luni.7E", //$NON-NLS-1$
+                                s.substring(i, i + 3)), i);
+                    }
+
+                    i += 3;
+                } while (i < s.length() && s.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)))) {
+                throw new URISyntaxException(s, Messages.getString("luni.7F"), i); //$NON-NLS-1$
+            }
+            i++;
+        }
+    }
+
+    static void validateSimple(String s, String legal)
+            throws URISyntaxException {
+        for (int i = 0; i < s.length();) {
+            char ch = s.charAt(i);
+            if (!((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z')
+                    || (ch >= '0' && ch <= '9') || legal.indexOf(ch) > -1)) {
+                throw new URISyntaxException(s, Messages.getString("luni.7F"), i); //$NON-NLS-1$
+            }
+            i++;
+        }
+    }
+
+    /**
+     * All characters except letters ('a'..'z', 'A'..'Z') and numbers ('0'..'9')
+     * and legal characters are converted into their hexidecimal value prepended
+     * by '%'.
+     * <p>
+     * For example: '#' -> %23
+     * Other characters, which are unicode chars that are not US-ASCII, and are
+     * not ISO Control or are not ISO Space chars, are preserved.
+     * <p>
+     * Called from {@code URI.quoteComponent()} (for multiple argument
+     * constructors)
+     *
+     * @param s
+     *            java.lang.String the string to be converted
+     * @param legal
+     *            java.lang.String the characters allowed to be preserved in the
+     *            string s
+     * @return java.lang.String the converted string
+     */
+    static String quoteIllegal(String s, String legal)
+            throws UnsupportedEncodingException {
+        StringBuilder buf = new StringBuilder();
+        for (int i = 0; i < s.length(); i++) {
+            char ch = s.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 {
+                byte[] bytes = new String(new char[] { ch }).getBytes(encoding);
+                for (int j = 0; j < bytes.length; j++) {
+                    buf.append('%');
+                    buf.append(digits.charAt((bytes[j] & 0xf0) >> 4));
+                    buf.append(digits.charAt(bytes[j] & 0xf));
+                }
+            }
+        }
+        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. They are
+     * converted into their hexidecimal value prepended by '%'.
+     * <p>
+     * For example: Euro currency symbol -> "%E2%82%AC".
+     * <p>
+     * Called from URI.toASCIIString()
+     *
+     * @param s
+     *            java.lang.String the string to be converted
+     * @return java.lang.String the converted string
+     */
+    static String encodeOthers(String s) throws UnsupportedEncodingException {
+        StringBuilder buf = new StringBuilder();
+        for (int i = 0; i < s.length(); i++) {
+            char ch = s.charAt(i);
+            if (ch <= 127) {
+                buf.append(ch);
+            } else {
+                byte[] bytes = new String(new char[] { ch }).getBytes(encoding);
+                for (int j = 0; j < bytes.length; j++) {
+                    buf.append('%');
+                    buf.append(digits.charAt((bytes[j] & 0xf0) >> 4));
+                    buf.append(digits.charAt(bytes[j] & 0xf));
+                }
+            }
+        }
+        return buf.toString();
+    }
+
+    /**
+     * 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>
+     * e.g. "A%20B%20C %24%25" -> "A B C $%"
+     * <p>
+     * Called from URI.getXYZ() methods
+     * 
+     * @param s
+     *            java.lang.String The encoded string.
+     * @return java.lang.String The decoded version.
+     */
+    static String decode(String s) throws UnsupportedEncodingException {
+
+        StringBuilder result = new StringBuilder();
+        ByteArrayOutputStream out = new ByteArrayOutputStream();
+        for (int i = 0; i < s.length();) {
+            char c = s.charAt(i);
+            if (c == '%') {
+                out.reset();
+                do {
+                    if (i + 2 >= s.length()) {
+                        throw new IllegalArgumentException(Messages.getString(
+                                "luni.80", i)); //$NON-NLS-1$
+                    }
+                    int d1 = Character.digit(s.charAt(i + 1), 16);
+                    int d2 = Character.digit(s.charAt(i + 2), 16);
+                    if (d1 == -1 || d2 == -1) {
+                        throw new IllegalArgumentException(Messages.getString(
+                                "luni.81", s.substring(i, i + 3), //$NON-NLS-1$
+                                String.valueOf(i)));
+                    }
+                    out.write((byte) ((d1 << 4) + d2));
+                    i += 3;
+                } while (i < s.length() && s.charAt(i) == '%');
+                result.append(out.toString(encoding));
+                continue;
+            }
+            result.append(c);
+            i++;
+        }
+        return result.toString();
+    }
+    
+    
+
+}

Propchange: river/jtsk/skunk/qa_refactor/trunk/src/org/apache/river/api/net/URIEncoderDecoder.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: river/jtsk/skunk/qa_refactor/trunk/src/org/apache/river/api/net/Uri.java
URL: http://svn.apache.org/viewvc/river/jtsk/skunk/qa_refactor/trunk/src/org/apache/river/api/net/Uri.java?rev=1461949&view=auto
==============================================================================
--- river/jtsk/skunk/qa_refactor/trunk/src/org/apache/river/api/net/Uri.java (added)
+++ river/jtsk/skunk/qa_refactor/trunk/src/org/apache/river/api/net/Uri.java Thu Mar 28 04:11:21 2013
@@ -0,0 +1,1394 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE 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 org.apache.river.api.net;
+
+import java.io.UnsupportedEncodingException;
+import java.net.MalformedURLException;
+import java.net.URISyntaxException;
+import java.net.URL;
+import org.apache.river.impl.Messages;
+
+
+/**
+ * This class represents an immutable instance of a URI as defined by RFC 3986.
+ * 
+ * This class behaves similarly to java.net.URI and is a drop in replacement, 
+ * however all instances are normalised during construction, to comply with
+ * RFC 3986.
+ * 
+ * Normalisation of java.net.URI was limited to the path, the scheme
+ * and host are also normalised in accordance with RFC 3986.
+ * 
+ * It also has some additional useful static methods that deal with common
+ * scenario's.
+ * 
+ * Unlike java.net.URI this class is not Serializable.
+ * 
+ */
+public final class Uri implements Comparable<Uri> {
+
+    private static final long serialVersionUID = -6052424284110960213l;
+
+    static final String unreserved = "_-!.~\'()*"; //$NON-NLS-1$
+
+    static final String punct = ",;:$&+="; //$NON-NLS-1$
+
+    static final String reserved = punct + "?/[]@"; //$NON-NLS-1$
+
+    static final String someLegal = unreserved + punct;
+
+    static final String queryLegal = unreserved + reserved + "\\\""; //$NON-NLS-1$
+    
+    static final String allLegal = unreserved + reserved;
+
+    private final String string;
+    private final String scheme;
+    private final String schemespecificpart;
+    private final String authority;
+    private final String userinfo;
+    private final String host;
+    private final int port;
+    private final String path;
+    private final String query;
+    private final String fragment;
+    private final boolean opaque;
+    private final boolean absolute;
+    private final boolean serverAuthority;
+    private final String hashString;
+    private final int hash;
+  
+    /**
+     * 
+     * @param string
+     * @param scheme
+     * @param schemespecificpart
+     * @param authority
+     * @param userinfo
+     * @param host
+     * @param port
+     * @param path
+     * @param query
+     * @param fragment
+     * @param opaque
+     * @param absolute
+     * @param serverAuthority
+     * @param hash 
+     */
+    private Uri(String string,
+            String scheme,
+            String schemespecificpart,
+            String authority,
+            String userinfo,
+            String host,
+            int port,
+            String path,
+            String query,
+            String fragment,
+            boolean opaque,
+            boolean absolute,
+            boolean serverAuthority,
+            int hash)
+    {
+        super();
+        this.scheme = scheme;
+        this.schemespecificpart = schemespecificpart;
+        this.authority = authority;
+        this.userinfo = userinfo;
+        this.host = host;
+        this.port = port;
+        this.path = path;
+        this.query = query;
+        this.fragment = fragment;
+        this.opaque = opaque;
+        this.absolute = absolute;
+        this.serverAuthority = serverAuthority;
+        if (string == null) {
+            StringBuilder result = new StringBuilder();
+            if (scheme != null) {
+                result.append(scheme);
+                result.append(':');
+            }
+            if (opaque) {
+                result.append(schemespecificpart);
+            } else {
+                if (authority != null) {
+                    result.append("//"); //$NON-NLS-1$
+                    result.append(authority);
+                }
+
+                if (path != null) {
+                    result.append(path);
+                }
+
+                if (query != null) {
+                    result.append('?');
+                    result.append(query);
+                }
+            }
+
+            if (fragment != null) {
+                result.append('#');
+                result.append(fragment);
+            }
+
+            this.string = result.toString();
+        } else {
+            this.string = string;
+        }
+        this.hashString = getHashString();
+        this.hash = hash == -1 ? hashString.hashCode(): hash;
+        
+    }
+    
+    /**
+     * Private constructor that doesn't throw URISyntaxException, all public
+     * constructors are designed to avoid finalizer attacks by calling static 
+     * methods that throw URISyntaxException, just in case we
+     * decide to make this class non final at some point in future.
+     * @param p 
+     */
+    private Uri(UriParser p){
+        this(p.string,
+        p.scheme,
+        p.schemespecificpart,
+        p.authority,
+        p.userinfo,
+        p.host,
+        p.port,
+        p.path,
+        p.query,
+        p.fragment,
+        p.opaque,
+        p.absolute,
+        p.serverAuthority,
+        p.hash);
+    }
+    
+    /**
+     * Creates a new URI instance according to the given string {@code uri}.
+     *
+     * @param uri
+     *            the textual URI representation to be parsed into a URI object.
+     * @throws URISyntaxException
+     *             if the given string {@code uri} doesn't fit to the
+     *             specification RFC2396 or could not be parsed correctly.
+     */
+    public Uri(String uri) throws URISyntaxException {
+        this(constructor1(uri));
+    }
+    
+    private static UriParser constructor1(String uri) throws URISyntaxException {
+        UriParser p = new UriParser();
+        p.parseURI(uri, false);
+        return p;
+    }
+
+    /**
+     * Creates a new URI instance using the given arguments. 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>
+     * {@code [scheme:]scheme-specific-part[#fragment]}
+     *
+     * @param scheme
+     *            the scheme part of the URI.
+     * @param ssp
+     *            the scheme-specific-part of the URI.
+     * @param frag
+     *            the 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 Uri(String scheme, String ssp, String frag) throws URISyntaxException {
+        this(constructor2(scheme, ssp, frag));
+    }
+    
+    private static UriParser constructor2(String scheme, String ssp, String frag) throws URISyntaxException{
+        StringBuilder uri = new StringBuilder();
+        if (scheme != null) {
+            uri.append(scheme);
+            uri.append(':');
+        }
+        if (ssp != null) {
+            // QUOTE ILLEGAL CHARACTERS
+            uri.append(quoteComponent(ssp, allLegal));
+        }
+        if (frag != null) {
+            uri.append('#');
+            // QUOTE ILLEGAL CHARACTERS
+            uri.append(quoteComponent(frag, allLegal));
+        }
+
+        UriParser p = new UriParser();
+        p.parseURI(uri.toString(), false);
+        return p;
+    }
+
+    /**
+     * Creates a new URI instance using the given arguments. 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>
+     * {@code [scheme:][user-info@]host[:port][path][?query][#fragment]}
+     *
+     * @param scheme
+     *            the scheme part of the URI.
+     * @param userinfo
+     *            the user information of the URI for authentication and
+     *            authorization.
+     * @param host
+     *            the host name of the URI.
+     * @param port
+     *            the port number of the URI.
+     * @param path
+     *            the path to the resource on the host.
+     * @param query
+     *            the query part of the URI to specify parameters for the
+     *            resource.
+     * @param fragment
+     *            the 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 Uri(String scheme, String userinfo, String host, int port,
+            String path, String query, String fragment)
+            throws URISyntaxException {
+        this(constructor3(scheme, userinfo, host, port, path, query, fragment));
+    }
+    
+    private static UriParser constructor3(String scheme, String userinfo, String host, int port,
+            String path, String query, String fragment) throws URISyntaxException {
+        if (scheme == null && userinfo == null && host == null && path == null
+                && query == null && fragment == null) {
+            UriParser p = new UriParser();
+            p.path = ""; //$NON-NLS-1$
+            return p;
+        }
+
+        if (scheme != null && path != null && path.length() > 0
+                && path.charAt(0) != '/') {
+            throw new URISyntaxException(path, Messages.getString("luni.82")); //$NON-NLS-1$
+        }
+
+        StringBuilder uri = new StringBuilder();
+        if (scheme != null) {
+            uri.append(scheme);
+            uri.append(':');
+        }
+
+        if (userinfo != null || host != null || port != -1) {
+            uri.append("//"); //$NON-NLS-1$
+        }
+
+        if (userinfo != null) {
+            // QUOTE ILLEGAL CHARACTERS in userinfo
+            uri.append(quoteComponent(userinfo, someLegal));
+            uri.append('@');
+        }
+
+        if (host != null) {
+            // check for ipv6 addresses that hasn't been enclosed
+            // in square brackets
+            if (host.indexOf(':') != -1 && host.indexOf(']') == -1
+                    && host.indexOf('[') == -1) {
+                host = "[" + host + "]"; //$NON-NLS-1$ //$NON-NLS-2$
+            }
+            uri.append(host);
+        }
+
+        if (port != -1) {
+            uri.append(':');
+            uri.append(port);
+        }
+
+        if (path != null) {
+            // QUOTE ILLEGAL CHARS
+            uri.append(quoteComponent(path, "/@" + someLegal)); //$NON-NLS-1$
+        }
+
+        if (query != null) {
+            uri.append('?');
+            // QUOTE ILLEGAL CHARS
+            uri.append(quoteComponent(query, allLegal));
+        }
+
+        if (fragment != null) {
+            // QUOTE ILLEGAL CHARS
+            uri.append('#');
+            uri.append(quoteComponent(fragment, allLegal));
+        }
+
+        UriParser p = new UriParser();
+        p.parseURI(uri.toString(), true);
+        return p;
+    }
+
+    /**
+     * Creates a new URI instance using the given arguments. 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>
+     * {@code [scheme:]host[path][#fragment]}
+     *
+     * @param scheme
+     *            the scheme part of the URI.
+     * @param host
+     *            the host name of the URI.
+     * @param path
+     *            the path to the resource on the host.
+     * @param fragment
+     *            the 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 Uri(String scheme, String host, String path, String fragment)
+            throws URISyntaxException {
+        this(scheme, null, host, -1, path, null, fragment);
+    }
+
+    /**
+     * Creates a new URI instance using the given arguments. 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>
+     * {@code [scheme:][//authority][path][?query][#fragment]}
+     *
+     * @param scheme
+     *            the scheme part of the URI.
+     * @param authority
+     *            the authority part of the URI.
+     * @param path
+     *            the path to the resource on the host.
+     * @param query
+     *            the query part of the URI to specify parameters for the
+     *            resource.
+     * @param fragment
+     *            the 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 Uri(String scheme, String authority, String path, String query,
+            String fragment) throws URISyntaxException {
+        this(constructor4(scheme, authority, path, query, fragment));
+    }
+
+    private static UriParser constructor4(String scheme, String authority, String path, String query,
+            String fragment) throws URISyntaxException {
+        if (scheme != null && path != null && path.length() > 0
+                && path.charAt(0) != '/') {
+            throw new URISyntaxException(path, Messages.getString("luni.82")); //$NON-NLS-1$
+        }
+
+        StringBuilder uri = new StringBuilder();
+        if (scheme != null) {
+            uri.append(scheme);
+            uri.append(':');
+        }
+        if (authority != null) {
+            uri.append("//"); //$NON-NLS-1$
+            // QUOTE ILLEGAL CHARS
+            uri.append(quoteComponent(authority, "@[]" + someLegal)); //$NON-NLS-1$
+        }
+
+        if (path != null) {
+            // QUOTE ILLEGAL CHARS
+            uri.append(quoteComponent(path, "/@" + someLegal)); //$NON-NLS-1$
+        }
+        if (query != null) {
+            // QUOTE ILLEGAL CHARS
+            uri.append('?');
+            uri.append(quoteComponent(query, allLegal));
+        }
+        if (fragment != null) {
+            // QUOTE ILLEGAL CHARS
+            uri.append('#');
+            uri.append(quoteComponent(fragment, allLegal));
+        }
+
+        UriParser p = new UriParser();
+        p.parseURI(uri.toString(), false);
+        return p;
+    }
+    
+    /*
+     * Quote illegal chars for each component, but not the others
+     * 
+     * @param component java.lang.String the component to be converted @param
+     * legalset java.lang.String the legal character set allowed in the
+     * component s @return java.lang.String the converted string
+     */
+    private static String quoteComponent(String component, String legalset) {
+        try {
+            /*
+             * Use a different encoder than URLEncoder since: 1. chars like "/",
+             * "#", "@" etc needs to be preserved instead of being encoded, 2.
+             * UTF-8 char set needs to be used for encoding instead of default
+             * platform one
+             */
+            return URIEncoderDecoder.quoteIllegal(component, legalset);
+        } catch (UnsupportedEncodingException e) {
+            throw new RuntimeException(e.toString());
+        }
+    }
+
+    /**
+     * Compares this URI with the given argument {@code uri}. This method will
+     * return a negative value if this URI instance is less than the given
+     * argument and a positive value if this URI instance is greater than the
+     * given argument. The return value {@code 0} indicates that the two
+     * instances represent the same URI. To define the order the single parts of
+     * the URI are compared with each other. String components will be orderer
+     * in the natural case-sensitive way. A hierarchical URI is less than an
+     * opaque URI and if one part is {@code null} the URI with the undefined
+     * part is less than the other one.
+     *
+     * @param uri
+     *            the URI this instance has to compare with.
+     * @return the value representing the order of the two instances.
+     */
+    public int compareTo(Uri uri) {
+        int ret = 0;
+
+        // compare schemes
+        if (scheme == null && uri.scheme != null) {
+            return -1;
+        } else if (scheme != null && uri.scheme == null) {
+            return 1;
+        } else if (scheme != null && uri.scheme != null) {
+            ret = scheme.compareToIgnoreCase(uri.scheme);
+            if (ret != 0) {
+                return ret;
+            }
+        }
+
+        // compare opacities
+        if (!opaque && uri.opaque) {
+            return -1;
+        } else if (opaque && !uri.opaque) {
+            return 1;
+        } else if (opaque && uri.opaque) {
+            ret = schemespecificpart.compareTo(uri.schemespecificpart);
+            if (ret != 0) {
+                return ret;
+            }
+        } else {
+
+            // otherwise both must be hierarchical
+
+            // compare authorities
+            if (authority != null && uri.authority == null) {
+                return 1;
+            } else if (authority == null && uri.authority != null) {
+                return -1;
+            } else if (authority != null && uri.authority != null) {
+                if (host != null && uri.host != null) {
+                    // both are server based, so compare userinfo, host, port
+                    if (userinfo != null && uri.userinfo == null) {
+                        return 1;
+                    } else if (userinfo == null && uri.userinfo != null) {
+                        return -1;
+                    } else if (userinfo != null && uri.userinfo != null) {
+                        ret = userinfo.compareTo(uri.userinfo);
+                        if (ret != 0) {
+                            return ret;
+                        }
+                    }
+
+                    // userinfo's are the same, compare hostname
+                    ret = host.compareToIgnoreCase(uri.host);
+                    if (ret != 0) {
+                        return ret;
+                    }
+
+                    // compare port
+                    if (port != uri.port) {
+                        return port - uri.port;
+                    }
+                } else { // one or both are registry based, compare the whole
+                    // authority
+                    ret = authority.compareTo(uri.authority);
+                    if (ret != 0) {
+                        return ret;
+                    }
+                }
+            }
+
+            // authorities are the same
+            // compare paths
+            ret = path.compareTo(uri.path);
+            if (ret != 0) {
+                return ret;
+            }
+
+            // compare queries
+
+            if (query != null && uri.query == null) {
+                return 1;
+            } else if (query == null && uri.query != null) {
+                return -1;
+            } else if (query != null && uri.query != null) {
+                ret = query.compareTo(uri.query);
+                if (ret != 0) {
+                    return ret;
+                }
+            }
+        }
+
+        // everything else is identical, so compare fragments
+        if (fragment != null && uri.fragment == null) {
+            return 1;
+        } else if (fragment == null && uri.fragment != null) {
+            return -1;
+        } else if (fragment != null && uri.fragment != null) {
+            ret = fragment.compareTo(uri.fragment);
+            if (ret != 0) {
+                return ret;
+            }
+        }
+
+        // identical
+        return 0;
+    }
+
+    /**
+     * Parses the given argument {@code rfc3986compliantURI} and creates an appropriate URI
+     * instance.
+     * 
+     * The parameter string is checked for compliance, an IllegalArgumentException
+     * is thrown if the string is non compliant.
+     *
+     * @param rfc3986compliantURI
+     *            the string which has to be parsed to create the URI instance.
+     * @return the created instance representing the given URI.
+     */
+    public static Uri create(String rfc3986compliantURI) {
+        Uri result = null;
+        try {
+            result = new Uri(rfc3986compliantURI);
+        } catch (URISyntaxException e) {
+            throw new IllegalArgumentException(e.getMessage());
+        }
+        return result;
+    }
+    
+    /**
+     * The parameter string doesn't contain any existing escape sequences, any
+     * escape character % found is encoded as %25.
+     * 
+     * The Uri is normalised according to RFC3986.
+     * 
+     * @param unescapedString
+     * @return 
+     */
+    public static Uri escapeAndCreate(String unescapedString){
+        throw new UnsupportedOperationException("not supported");
+    }
+    
+    /**
+     * The parameter string may already contain escaped sequences, any illegal
+     * characters are escaped and any that shouldn't be escaped are un-escaped.
+     * 
+     * The escape character % is not re-encoded.
+     * @param nonCompliantEscapedString 
+     * @return 
+     */
+    public static Uri parseAndCreate(String nonCompliantEscapedString){
+        throw new UnsupportedOperationException("not supported");
+    }
+    
+    // No point cloning an immutable object.
+//    public Uri clone() {
+//        return new Uri( string,
+//                        scheme,
+//                        schemespecificpart,
+//                        authority,
+//                        userinfo,
+//                        host,
+//                        port,
+//                        path,
+//                        query,
+//                        fragment,
+//                        opaque,
+//                        absolute,
+//                        serverAuthority,
+//                        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(String s) {
+        StringBuilder result = new StringBuilder(""); //$NON-NLS-1$
+        if (s.indexOf('%') == -1) {
+            return s;
+        }
+
+        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(String first, String 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) {
+            boolean match = first.substring(previndex, index).equals(
+                    second.substring(previndex, index));
+            if (!match) {
+                return false;
+            }
+
+            match = first.substring(index + 1, index + 3).equalsIgnoreCase(
+                    second.substring(index + 1, index + 3));
+            if (!match) {
+                return false;
+            }
+
+            index += 3;
+            previndex = index;
+        }
+        return first.substring(previndex).equals(second.substring(previndex));
+    }
+
+    /**
+     * 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.
+     *
+     * @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 boolean equals(Object o) {
+        if (!(o instanceof Uri)) {
+            return false;
+        }
+        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;
+        }
+    }
+
+    /**
+     * Gets the decoded authority part of this URI.
+     *
+     * @return the decoded authority part or {@code null} if undefined.
+     */
+    public String getAuthority() {
+        return decode(authority);
+    }
+
+    /**
+     * Gets the decoded fragment part of this URI.
+     * 
+     * @return the decoded fragment part or {@code null} if undefined.
+     */
+    public String getFragment() {
+        return decode(fragment);
+    }
+
+    /**
+     * Gets the host part of this URI.
+     * 
+     * @return the host part or {@code null} if undefined.
+     */
+    public String getHost() {
+        return host;
+    }
+
+    /**
+     * Gets the decoded path part of this URI.
+     * 
+     * @return the decoded path part or {@code null} if undefined.
+     */
+    public String getPath() {
+        return decode(path);
+    }
+
+    /**
+     * Gets the port number of this URI.
+     * 
+     * @return the port number or {@code -1} if undefined.
+     */
+    public int getPort() {
+        return port;
+    }
+
+    /**
+     * Gets the decoded query part of this URI.
+     * 
+     * @return the decoded query part or {@code null} if undefined.
+     */
+    public String getQuery() {
+        return decode(query);
+    }
+
+    /**
+     * Gets the authority part of this URI in raw form.
+     * 
+     * @return the encoded authority part or {@code null} if undefined.
+     */
+    public String getRawAuthority() {
+        return authority;
+    }
+
+    /**
+     * Gets the fragment part of this URI in raw form.
+     * 
+     * @return the encoded fragment part or {@code null} if undefined.
+     */
+    public String getRawFragment() {
+        return fragment;
+    }
+
+    /**
+     * Gets the path part of this URI in raw form.
+     * 
+     * @return the encoded path part or {@code null} if undefined.
+     */
+    public String getRawPath() {
+        return path;
+    }
+
+    /**
+     * Gets the query part of this URI in raw form.
+     * 
+     * @return the encoded query part or {@code null} if undefined.
+     */
+    public String getRawQuery() {
+        return query;
+    }
+
+    /**
+     * Gets the scheme-specific part of this URI in raw form.
+     * 
+     * @return the encoded scheme-specific part or {@code null} if undefined.
+     */
+    public String getRawSchemeSpecificPart() {
+        return schemespecificpart;
+    }
+
+    /**
+     * Gets the user-info part of this URI in raw form.
+     * 
+     * @return the encoded user-info part or {@code null} if undefined.
+     */
+    public String getRawUserInfo() {
+        return userinfo;
+    }
+
+    /**
+     * Gets the scheme part of this URI.
+     * 
+     * @return the scheme part or {@code null} if undefined.
+     */
+    public String getScheme() {
+        return scheme;
+    }
+
+    /**
+     * Gets the decoded scheme-specific part of this URI.
+     * 
+     * @return the decoded scheme-specific part or {@code null} if undefined.
+     */
+    public String getSchemeSpecificPart() {
+        return decode(schemespecificpart);
+    }
+
+    /**
+     * Gets the decoded user-info part of this URI.
+     * 
+     * @return the decoded user-info part or {@code null} if undefined.
+     */
+    public String getUserInfo() {
+        return decode(userinfo);
+    }
+
+    /**
+     * Gets the hashcode value of this URI instance.
+     *
+     * @return the appropriate hashcode value.
+     */
+    @Override
+    public int hashCode() {
+        return hash;
+    }
+
+    /**
+     * Indicates whether this URI is absolute, which means that a scheme part is
+     * defined in this URI.
+     * 
+     * @return {@code true} if this URI is absolute, {@code false} otherwise.
+     */
+    public boolean isAbsolute() {
+        return absolute;
+    }
+
+    /**
+     * Indicates whether this URI is opaque or not. An opaque URI is absolute
+     * and has a scheme-specific part which does not start with a slash
+     * character. All parts except scheme, scheme-specific and fragment are
+     * undefined.
+     * 
+     * @return {@code true} if the URI is opaque, {@code false} otherwise.
+     */
+    public boolean isOpaque() {
+        return opaque;
+    }
+    
+    /**
+     * Normalizes the path part of this URI.
+     *
+     * @return an URI object which represents this instance with a normalized
+     *         path.
+     */
+    public Uri normalize() {
+        if (opaque) {
+            return this;
+        }
+        String normalizedPath = normalize(path);
+        // if the path is already normalized, return this
+        if (path.equals(normalizedPath)) {
+            return this;
+        }
+        // get an exact copy of the URI re-calculate the scheme specific part
+        // since the path of the normalized URI is different from this URI.
+        return new Uri( null,
+                        scheme,
+                        setSchemeSpecificPart(authority, normalizedPath , query),
+                        authority,
+                        userinfo,
+                        host,
+                        port,
+                        normalizedPath,
+                        query,
+                        fragment,
+                        opaque,
+                        absolute,
+                        serverAuthority,
+                        hash);
+    }
+
+
+    /*
+     * normalize path, and return the resulting string
+     */
+    private String normalize(String path) {
+        // count the number of '/'s, to determine number of segments
+        int index = -1;
+        int pathlen = path.length();
+        int size = 0;
+        if (pathlen > 0 && path.charAt(0) != '/') {
+            size++;
+        }
+        while ((index = path.indexOf('/', index + 1)) != -1) {
+            if (index + 1 < pathlen && path.charAt(index + 1) != '/') {
+                size++;
+            }
+        }
+
+        String[] seglist = new String[size];
+        boolean[] include = new boolean[size];
+
+        // break the path into segments and store in the list
+        int current = 0;
+        int index2 = 0;
+        index = (pathlen > 0 && path.charAt(0) == '/') ? 1 : 0;
+        while ((index2 = path.indexOf('/', index + 1)) != -1) {
+            seglist[current++] = path.substring(index, index2);
+            index = index2 + 1;
+        }
+
+        // if current==size, then the last character was a slash
+        // and there are no more segments
+        if (current < size) {
+            seglist[current] = path.substring(index);
+        }
+
+        // determine which segments get included in the normalized path
+        for (int i = 0; i < size; i++) {
+            include[i] = true;
+            if (seglist[i].equals("..")) { //$NON-NLS-1$
+                int remove = i - 1;
+                // search back to find a segment to remove, if possible
+                while (remove > -1 && !include[remove]) {
+                    remove--;
+                }
+                // if we find a segment to remove, remove it and the ".."
+                // segment
+                if (remove > -1 && !seglist[remove].equals("..")) { //$NON-NLS-1$
+                    include[remove] = false;
+                    include[i] = false;
+                }
+            } else if (seglist[i].equals(".")) { //$NON-NLS-1$
+                include[i] = false;
+            }
+        }
+
+        // put the path back together
+        StringBuilder newpath = new StringBuilder();
+        if (path.startsWith("/")) { //$NON-NLS-1$
+            newpath.append('/');
+        }
+
+        for (int i = 0; i < seglist.length; i++) {
+            if (include[i]) {
+                newpath.append(seglist[i]);
+                newpath.append('/');
+            }
+        }
+
+        // if we used at least one segment and the path previously ended with
+        // a slash and the last segment is still used, then delete the extra
+        // trailing '/'
+        if (!path.endsWith("/") && seglist.length > 0 //$NON-NLS-1$
+                && include[seglist.length - 1]) {
+            newpath.deleteCharAt(newpath.length() - 1);
+        }
+
+        String result = newpath.toString();
+
+        // check for a ':' in the first segment if one exists,
+        // prepend "./" to normalize
+        index = result.indexOf(':');
+        index2 = result.indexOf('/');
+        if (index != -1 && (index < index2 || index2 == -1)) {
+            newpath.insert(0, "./"); //$NON-NLS-1$
+            result = newpath.toString();
+        }
+        return result;
+    }
+
+    /**
+     * Tries to parse the authority component of this URI to divide it into the
+     * host, port, and user-info. If this URI is already determined as a
+     * ServerAuthority this instance will be returned without changes.
+     *
+     * @return this instance with the components of the parsed server authority.
+     * @throws URISyntaxException
+     *             if the authority part could not be parsed as a server-based
+     *             authority.
+     */
+    public Uri parseServerAuthority() throws URISyntaxException {
+        if (!serverAuthority) {
+            UriParser p = new UriParser();
+            p.parseURI(this.toString(), false);
+            p.parseAuthority(true);
+            return new Uri(p);
+        }
+        return this;
+    }
+
+    /**
+     * Makes the given URI {@code relative} to a relative URI against the URI
+     * represented by this instance.
+     *
+     * @param relative
+     *            the URI which has to be relativized against this URI.
+     * @return the relative URI.
+     */
+    public Uri relativize(Uri relative) {
+        if (relative.opaque || opaque) {
+            return relative;
+        }
+
+        if (scheme == null ? relative.scheme != null : !scheme
+                .equals(relative.scheme)) {
+            return relative;
+        }
+
+        if (authority == null ? relative.authority != null : !authority
+                .equals(relative.authority)) {
+            return relative;
+        }
+
+        // normalize both paths
+        String thisPath = normalize(path);
+        String relativePath = normalize(relative.path);
+
+        /*
+         * if the paths aren't equal, then we need to determine if this URI's
+         * path is a parent path (begins with) the relative URI's path
+         */
+        if (!thisPath.equals(relativePath)) {
+            // if this URI's path doesn't end in a '/', add one
+            if (!thisPath.endsWith("/")) { //$NON-NLS-1$
+                thisPath = thisPath + '/';
+            }
+            /*
+             * if the relative URI's path doesn't start with this URI's path,
+             * then just return the relative URI; the URIs have nothing in
+             * common
+             */
+            if (!relativePath.startsWith(thisPath)) {
+                return relative;
+            }
+        }
+
+        String query = relative.query;
+        // the result URI is the remainder of the relative URI's path
+        String path = relativePath.substring(thisPath.length());
+        return new Uri( null,
+                        null,
+                        setSchemeSpecificPart(null, path, query),
+                        null,
+                        null,
+                        null,
+                        -1,
+                        path,
+                        query,
+                        relative.fragment,
+                        false,
+                        false,
+                        false,
+                        -1);
+    }
+
+    /**
+     * Resolves the given URI {@code relative} against the URI represented by
+     * this instance.
+     *
+     * @param relative
+     *            the URI which has to be resolved against this URI.
+     * @return the resolved URI.
+     */
+    public Uri resolve(Uri relative) {
+        if (relative.absolute || opaque) {
+            return relative;
+        }
+
+        if (relative.path.equals("") && relative.scheme == null //$NON-NLS-1$
+                && relative.authority == null && relative.query == null
+                && relative.fragment != null) {
+            // if the relative URI only consists of fragment,
+            // the resolved URI is very similar to this URI,
+            // except that it has the fragement from the relative URI.
+            
+            return new Uri( null,
+                        scheme,
+                        schemespecificpart,
+                        authority,
+                        userinfo,
+                        host,
+                        port,
+                        path,
+                        query,
+                        relative.fragment,
+                        opaque,
+                        absolute,
+                        serverAuthority,
+                        hash);
+            // no need to re-calculate the scheme specific part,
+            // since fragment is not part of scheme specific part.
+           
+        }
+
+        if (relative.authority != null) {
+            // if the relative URI has authority,
+            // the resolved URI is almost the same as the relative URI,
+            // except that it has the scheme of this URI.
+            return new Uri( null,
+                        scheme,
+                        relative.schemespecificpart,
+                        relative.authority,
+                        relative.userinfo,
+                        relative.host,
+                        relative.port,
+                        relative.path,
+                        relative.query,
+                        relative.fragment,
+                        relative.opaque,
+                        absolute,
+                        relative.serverAuthority,
+                        relative.hash);
+        } else {
+            // since relative URI has no authority,
+            // the resolved URI is very similar to this URI,
+            // except that it has the query and fragment of the relative URI,
+            // and the path is different.
+            // re-calculate the scheme specific part since
+            // query and path of the resolved URI is different from this URI.
+            int endindex = path.lastIndexOf('/') + 1;
+            String p = relative.path.startsWith("/")? relative.path: 
+                        normalize(path.substring(0, endindex) + relative.path);
+            return new Uri( null,
+                        scheme,
+                        setSchemeSpecificPart(authority, p, relative.query),
+                        authority,
+                        userinfo,
+                        host,
+                        port,
+                        p,
+                        relative.query,
+                        relative.fragment,
+                        opaque,
+                        absolute,
+                        serverAuthority,
+                        hash);
+        }
+    }
+
+    /**
+     * UriParser method used to re-calculate the scheme specific part of the
+     * resolved or normalized URIs
+     */
+    private String setSchemeSpecificPart(String authority,
+                                         String path,
+                                         String query) {
+        // ssp = [//authority][path][?query]
+        StringBuilder ssp = new StringBuilder();
+        if (authority != null) {
+            ssp.append("//" + authority); //$NON-NLS-1$
+        }
+        if (path != null) {
+            ssp.append(path);
+        }
+        if (query != null) {
+            ssp.append("?" + query); //$NON-NLS-1$
+        }
+        // reset string, so that it can be re-calculated correctly when asked.
+        return ssp.toString();
+    }
+
+    /**
+     * Creates a new URI instance by parsing the given string {@code relative}
+     * and resolves the created URI against the URI represented by this
+     * instance.
+     *
+     * @param relative
+     *            the given string to create the new URI instance which has to
+     *            be resolved later on.
+     * @return the created and resolved URI.
+     */
+    public Uri resolve(String relative) {
+        return resolve(create(relative));
+    }
+
+    /*
+     * Encode unicode chars that are not part of US-ASCII char set into the
+     * escaped form
+     * 
+     * i.e. The Euro currency symbol is encoded as "%E2%82%AC".
+     * 
+     * @param component java.lang.String the component to be converted @param
+     * legalset java.lang.String the legal character set allowed in the
+     * component s @return java.lang.String the converted string
+     */
+    private String encodeOthers(String s) {
+        try {
+            /*
+             * Use a different encoder than URLEncoder since: 1. chars like "/",
+             * "#", "@" etc needs to be preserved instead of being encoded, 2.
+             * UTF-8 char set needs to be used for encoding instead of default
+             * platform one 3. Only other chars need to be converted
+             */
+            return URIEncoderDecoder.encodeOthers(s);
+        } catch (UnsupportedEncodingException e) {
+            throw new RuntimeException(e.toString());
+        }
+    }
+
+    private String decode(String s) {
+        if (s == null) {
+            return s;
+        }
+
+        try {
+            return URIEncoderDecoder.decode(s);
+        } catch (UnsupportedEncodingException e) {
+            throw new RuntimeException(e.toString());
+        }
+    }
+
+    /**
+     * Returns the textual string representation of this URI instance using the
+     * US-ASCII encoding.
+     *
+     * @return the US-ASCII string representation of this URI.
+     */
+    public String toASCIIString() {
+        return encodeOthers(toString());
+    }
+
+    /**
+     * Returns the textual string representation of this URI instance.
+     *
+     * @return the textual string representation of this URI.
+     */
+    @Override
+    public String toString() {
+        return string;
+    }
+
+    /*
+     * 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.
+     * 
+     * Should convert octets to uppercase and follow platform specific 
+     * normalization rules for file: uri.
+     */
+    private String getHashString() {
+        StringBuilder result = new StringBuilder();
+        if (scheme != null) {
+            result.append(scheme.toLowerCase());
+            result.append(':');
+        }
+        if (opaque) {
+            result.append(schemespecificpart);
+        } else {
+            if (authority != null) {
+                result.append("//"); //$NON-NLS-1$
+                if (host == null) {
+                    result.append(authority);
+                } else {
+                    if (userinfo != null) {
+                        result.append(userinfo + "@"); //$NON-NLS-1$
+                    }
+                    result.append(host.toLowerCase());
+                    if (port != -1) {
+                        result.append(":" + port); //$NON-NLS-1$
+                    }
+                }
+            }
+
+            if (path != null) {
+                result.append(path);
+            }
+
+            if (query != null) {
+                result.append('?');
+                result.append(query);
+            }
+        }
+
+        if (fragment != null) {
+            result.append('#');
+            result.append(fragment);
+        }
+
+        return convertHexToLowerCase(result.toString());
+    }
+
+    /**
+     * Converts this URI instance to a URL.
+     *
+     * @return the created URL representing the same resource as this URI.
+     * @throws MalformedURLException
+     *             if an error occurs while creating the URL or no protocol
+     *             handler could be found.
+     */
+    public URL toURL() throws MalformedURLException {
+        if (!absolute) {
+            throw new IllegalArgumentException(Messages.getString("luni.91") + ": " //$NON-NLS-1$//$NON-NLS-2$
+                    + toString());
+        }
+        return new URL(toString());
+    }
+}

Propchange: river/jtsk/skunk/qa_refactor/trunk/src/org/apache/river/api/net/Uri.java
------------------------------------------------------------------------------
    svn:eol-style = native