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 2012/08/04 16:06:21 UTC

svn commit: r1369328 - in /river/jtsk/trunk: qa/src/com/sun/jini/qa/resources/ qa/src/com/sun/jini/test/impl/start/aggregatepolicyprovider/ src/com/sun/jini/start/ src/net/jini/loader/pref/ src/org/apache/river/api/security/ src/org/apache/river/impl/n...

Author: peter_firmstone
Date: Sat Aug  4 14:06:20 2012
New Revision: 1369328

URL: http://svn.apache.org/viewvc?rev=1369328&view=rev
Log:
RFC3986 standard compliant URI normalisation for PreferredClassProvider and policy file grants, including some additional fixes for Windows file path warts.

Modified:
    river/jtsk/trunk/qa/src/com/sun/jini/qa/resources/qaDefaults.properties
    river/jtsk/trunk/qa/src/com/sun/jini/test/impl/start/aggregatepolicyprovider/GetContextTest.td
    river/jtsk/trunk/src/com/sun/jini/start/AggregatePolicyProvider.java
    river/jtsk/trunk/src/com/sun/jini/start/SharedActivationPolicyPermission.java
    river/jtsk/trunk/src/net/jini/loader/pref/PreferredClassProvider.java
    river/jtsk/trunk/src/org/apache/river/api/security/DefaultPolicyParser.java
    river/jtsk/trunk/src/org/apache/river/api/security/PolicyUtils.java
    river/jtsk/trunk/src/org/apache/river/api/security/URIGrant.java
    river/jtsk/trunk/src/org/apache/river/impl/net/UriString.java
    river/jtsk/trunk/test/src/org/apache/river/api/security/URIGrantTest.java
    river/jtsk/trunk/test/src/org/apache/river/impl/net/UriStringTest.java

Modified: river/jtsk/trunk/qa/src/com/sun/jini/qa/resources/qaDefaults.properties
URL: http://svn.apache.org/viewvc/river/jtsk/trunk/qa/src/com/sun/jini/qa/resources/qaDefaults.properties?rev=1369328&r1=1369327&r2=1369328&view=diff
==============================================================================
--- river/jtsk/trunk/qa/src/com/sun/jini/qa/resources/qaDefaults.properties (original)
+++ river/jtsk/trunk/qa/src/com/sun/jini/qa/resources/qaDefaults.properties Sat Aug  4 14:06:20 2012
@@ -222,7 +222,6 @@ com.sun.jini.qa.harness.actdeathdelay=5
 # no cosmetic whitespace
 com.sun.jini.qa.harness.globalvmargs=\
 -Djava.ext.dirs=${java.ext.dirs},\
--Djava.security.debug=access:failure,\
 -Dcom.sun.jini.jsk.port=${com.sun.jini.jsk.port},\
 -Dcom.sun.jini.qa.port=${com.sun.jini.qa.port},\
 -Dcom.sun.jini.jsk.home=${com.sun.jini.jsk.home},\

Modified: river/jtsk/trunk/qa/src/com/sun/jini/test/impl/start/aggregatepolicyprovider/GetContextTest.td
URL: http://svn.apache.org/viewvc/river/jtsk/trunk/qa/src/com/sun/jini/test/impl/start/aggregatepolicyprovider/GetContextTest.td?rev=1369328&r1=1369327&r2=1369328&view=diff
==============================================================================
--- river/jtsk/trunk/qa/src/com/sun/jini/test/impl/start/aggregatepolicyprovider/GetContextTest.td (original)
+++ river/jtsk/trunk/qa/src/com/sun/jini/test/impl/start/aggregatepolicyprovider/GetContextTest.td Sat Aug  4 14:06:20 2012
@@ -2,11 +2,11 @@ testClass=GetContextTest
 testCategories=start,start_impl
 #testClasspath=<harnessJar>$:${com.sun.jini.qa.home}$/lib$/qa1-start-tests.jar$:${com.sun.jini.qa.home}$/lib$/$qajinidep$:${com.sun.jini.jsk.home}$/lib$/jsk-platform.jar
 
-testClasspath=${altClasspath}$:<file:lib/qa1-start-tests.jar>
+testClasspath=${altClasspath}$:<file:${com.sun.jini.qa.home}/lib/qa1-start-tests.jar>
 testPolicyfile=GetContextTest.policy
-getContextJarFile=<file:lib/qa1-start-cb1.jar>
-restoreContextJarFile=<file:lib/qa1-start-cb2.jar>
-checkContextActionJarFile=<file:lib/qa1-start-cb3.jar>
+getContextJarFile=<file:${com.sun.jini.qa.home}/lib/qa1-start-cb1.jar>
+restoreContextJarFile=<file:${com.sun.jini.qa.home}/lib/qa1-start-cb2.jar>
+checkContextActionJarFile=<file:${com.sun.jini.qa.home}/lib/qa1-start-cb3.jar>
 include0=../start.properties
 #testjvmargs=\
 #-Xdebug,\

Modified: river/jtsk/trunk/src/com/sun/jini/start/AggregatePolicyProvider.java
URL: http://svn.apache.org/viewvc/river/jtsk/trunk/src/com/sun/jini/start/AggregatePolicyProvider.java?rev=1369328&r1=1369327&r2=1369328&view=diff
==============================================================================
--- river/jtsk/trunk/src/com/sun/jini/start/AggregatePolicyProvider.java (original)
+++ river/jtsk/trunk/src/com/sun/jini/start/AggregatePolicyProvider.java Sat Aug  4 14:06:20 2012
@@ -103,15 +103,12 @@ public class AggregatePolicyProvider 
     // The cache is used to avoid repeat security checks, the subPolicies map
     // cannot be used to cache child ClassLoaders because their policy could
     // change if a policy is updated.
-    private final ConcurrentMap<ClassLoader,Policy> subPolicyChildClassLoaderCache = // put protected by policyRead
-            RC.concurrentMap(                                                        // clear protected by policyWrite
+    private final ConcurrentMap<ClassLoader,Policy> subPolicyChildClassLoaderCache =
+            RC.concurrentMap(
             new ConcurrentHashMap<Referrer<ClassLoader>,Referrer<Policy>>(),
             Ref.WEAK_IDENTITY, Ref.STRONG, 1000L, 0L);
-//    private final ReadWriteLock policyUpdate = new ReentrantReadWriteLock(); 
-//    private final Lock policyRead = policyUpdate.readLock();
-//    private final Lock policyWrite = policyUpdate.writeLock();
     private final Lock lock = new ReentrantLock();
-    private volatile Policy mainPolicy; // protected by policyUpdate
+    private volatile Policy mainPolicy; // write protected by lock
 
     /**
      * Creates a new <code>AggregatePolicyProvider</code> instance, containing
@@ -411,7 +408,7 @@ public class AggregatePolicyProvider 
 	// force class resolution by pre-invoking methods called by implies()
 	trustGetContextClassLoader0(Thread.class);
 	getContextClassLoader();
-        lookupSubPolicy(ldr);
+            lookupSubPolicy(ldr);
     }
 
     /**
@@ -424,33 +421,30 @@ public class AggregatePolicyProvider 
         if ( ccl == null ) return mainPolicy;
         Policy policy = subPolicyChildClassLoaderCache.get(ccl);  // just a cache.
         if ( policy != null ) return policy;
-        lock.lock();
-        try {
-            policy = lookupSubPolicy(ccl);
-            return policy;
-        }finally{
-            lock.unlock();
-        }
+        return lookupSubPolicy(ccl);
     }
 
     /**
-     * Returns sub-policy associated with the given class loader.  This method
-     * should only be called when already synchronized on lock.
+     * Returns sub-policy associated with the given class loader.
      */
     private Policy lookupSubPolicy(final ClassLoader ldr) {
 	return AccessController.doPrivileged(
 	    new PrivilegedAction<Policy>() {
 		public Policy run() {
                     Policy p = null;
-                    for (ClassLoader l = ldr; l != null; l = l.getParent()) {
-                        p = subPolicies.get(l);
-                        if (p != null) break;
+                    lock.lock();
+                    try {
+                        for (ClassLoader l = ldr; l != null; l = l.getParent()) {
+                            p = subPolicies.get(l);
+                            if (p != null) break;
+                        }
+                        if (p == null) p = mainPolicy;
+                        Policy exists = subPolicyChildClassLoaderCache.putIfAbsent(ldr, p);
+                        if ( exists != null && p != exists ) 
+                            throw new IllegalStateException("Policy Mutation occured");
+                    }finally{
+                        lock.unlock();
                     }
-                    if (p == null) p = mainPolicy;
-                    Policy exists =
-                    subPolicyChildClassLoaderCache.putIfAbsent(ldr, p);
-                    if ( exists != null && p != exists ) 
-                        throw new IllegalStateException("Policy Mutation occured");
                     return p;
 		}
 	    });

Modified: river/jtsk/trunk/src/com/sun/jini/start/SharedActivationPolicyPermission.java
URL: http://svn.apache.org/viewvc/river/jtsk/trunk/src/com/sun/jini/start/SharedActivationPolicyPermission.java?rev=1369328&r1=1369327&r2=1369328&view=diff
==============================================================================
--- river/jtsk/trunk/src/com/sun/jini/start/SharedActivationPolicyPermission.java (original)
+++ river/jtsk/trunk/src/com/sun/jini/start/SharedActivationPolicyPermission.java Sat Aug  4 14:06:20 2012
@@ -153,8 +153,9 @@ public final class SharedActivationPolic
             if (policy.startsWith("file:") || policy.startsWith("FILE:")){
                 String path = null;
                 try {
-                    path = UriString.parse(uncanonicalPath);
-                    path = new File(new URI(path)).getPath();
+                    uncanonicalPath = UriString.fixWindowsURI(uncanonicalPath);
+                    uncanonicalPath = UriString.escapeIllegalCharacters(uncanonicalPath);
+                    path = new File(new URI(uncanonicalPath)).getPath();
                 } catch (URISyntaxException ex) {
                     path = uncanonicalPath.replace('/', File.separatorChar);
                 } catch (IllegalArgumentException ex){

Modified: river/jtsk/trunk/src/net/jini/loader/pref/PreferredClassProvider.java
URL: http://svn.apache.org/viewvc/river/jtsk/trunk/src/net/jini/loader/pref/PreferredClassProvider.java?rev=1369328&r1=1369327&r2=1369328&view=diff
==============================================================================
--- river/jtsk/trunk/src/net/jini/loader/pref/PreferredClassProvider.java (original)
+++ river/jtsk/trunk/src/net/jini/loader/pref/PreferredClassProvider.java Sat Aug  4 14:06:20 2012
@@ -144,9 +144,16 @@ import org.cliffc.high_scale_lib.NonBloc
  *
  * <li><code>codebase</code> may be <code>null</code>.  If it is not
  * <code>null</code>, it is interpreted as a path of URLs by parsing
- * it as a list of URLs separated by spaces, where each URL is parsed
- * as with the <code>URL(String)</code> constructor; this could result
- * in a {@link MalformedURLException}.  This path of URLs is the
+ * it as a list of URLs separated by spaces.  It is recommended that
+ * URLs be compliant with RFC3986 Syntax.  Prior to parsing, any file path 
+ * separators converted to '/' and any illegal characters are percentage escaped,
+ * <code>URI(String)<code> is used to parse each URL and then normalised
+ * in compliance with RFC3986, in addition file URL paths are
+ * converted to upper case for case insensitive file systems. The array of 
+ * RFC3986 normalised URIs along with the current threads context ClassLoader
+ * is used to locate the correct ClassLoader.  After normalisation is complete,
+ * each URL is parsed with the <code>URL(String)</code> constructor; this could 
+ * result in a {@link MalformedURLException}.  This path of URLs is the
  * <i>codebase URL path</i> for the invocation.
  *
  * <li>A class loader known as the <i>codebase loader</i> is chosen
@@ -156,11 +163,11 @@ import org.cliffc.high_scale_lib.NonBloc
  * context class loader.  Otherwise, for each non-<code>null</code>
  * loader starting with the current thread's context class loader and
  * continuing with each successive parent class loader, if the
- * codebase URL path is equal to the loader's annotation URL path,
- * then the codebase loader is that loader.  If no such matching
- * loader is found, then the codebase loader is the loader in this
- * <code>PreferredClassProvider</code>'s internal table with the
- * codebase URL path as the key's path of URLs and the current
+ * codebase URI RFC3986 normalised path is equal to the loader's annotation
+ * URI RFC3986 normalised path, then the codebase loader is that loader.  
+ * If no such matching loader is found, then the codebase loader is the loader 
+ * in this <code>PreferredClassProvider</code>'s internal table with the
+ * codebase URI RFC3986 normalised path as the key's path of URLs and the current
  * thread's context class loader as the key's parent class loader.  If
  * no such entry exists in the table, then one is created by invoking
  * {@link #createClassLoader createClassLoader} with the codebase URL
@@ -373,7 +380,6 @@ public class PreferredClassProvider exte
      * URLClassLoader objects.
      */
     private final ConcurrentMap<ClassLoader,PermissionCollection> classLoaderPerms ;
-    //        = new WeakHashMap<ClassLoader,PermissionCollection>();
     
     /*
      * Check permissions to load from the specified loader.  The
@@ -1432,15 +1438,17 @@ public class PreferredClassProvider exte
 	if (path == null) {
 	    return null;
 	}
-        URI[] urls = uriCache.get(path);
+        URI[] urls = uriCache.get(path); // Cache of previously converted strings.
         if (urls != null) return urls;
 	StringTokenizer st = new StringTokenizer(path);	// divide by spaces
 	urls = new URI[st.countTokens()];
 	for (int i = 0; st.hasMoreTokens(); i++) {
             try {
-                urls[i] = new URI(UriString.parse(st.nextToken())).normalize();
+                String uri = st.nextToken();
+                uri = UriString.fixWindowsURI(uri);
+                urls[i] = UriString.normalise(new URI(UriString.escapeIllegalCharacters(uri)));
             } catch (URISyntaxException ex) {
-                throw new MalformedURLException("URL's must be RFC 2396 Compliant: " 
+                throw new MalformedURLException("URL's must be RFC 3986 Compliant: " 
                         + ex.getMessage());
             }
 	}
@@ -1727,7 +1735,7 @@ public class PreferredClassProvider exte
      * It was updated to utilise URI for the following reasons:
      * 
      * 1. Modern environments have dynamically assigned IP addresses, URI can provide a
-     *    level on indirection for Dynamic DNS and Dynamic IP.
+     *    level of indirection for Dynamic DNS and Dynamic IP.
      * 2. Virtual hosting is broken with URL.
      * 4. Testing revealed that all Jini specification tests pass with URI.  
      *    Although this doesn't eliminate the possibility of breakage in user code, 

Modified: river/jtsk/trunk/src/org/apache/river/api/security/DefaultPolicyParser.java
URL: http://svn.apache.org/viewvc/river/jtsk/trunk/src/org/apache/river/api/security/DefaultPolicyParser.java?rev=1369328&r1=1369327&r2=1369328&view=diff
==============================================================================
--- river/jtsk/trunk/src/org/apache/river/api/security/DefaultPolicyParser.java (original)
+++ river/jtsk/trunk/src/org/apache/river/api/security/DefaultPolicyParser.java Sat Aug  4 14:06:20 2012
@@ -146,6 +146,7 @@ class DefaultPolicyParser implements Pol
                 if ( e instanceof SecurityException ) throw (SecurityException) e;
                 System.err.println("Problem parsing policy: "+ location 
                         + "\n" + e);
+                e.printStackTrace(System.err);
             }
         }
         
@@ -268,26 +269,12 @@ class DefaultPolicyParser implements Pol
     }
     
     URI getURI(String uriString) throws MalformedURLException, URISyntaxException{
-        // We do this to support windows, this is to ensure that drive letter
+        // We do this to support windows, this is to ensure that path
         // capitalisation is correct and illegal strings are escaped correctly.
-        String result = UriString.parse(uriString);
-//        boolean isFile = result.startsWith("file:") || result.startsWith("FILE:");
-        URI uri = new URI(result);
-        // Oddly enough, this next part didn't quite work out as expected.
-//        if (isFile){
-//            if ( result.endsWith("*") || result.endsWith("-")){
-//                // We did our best to create a compatible URI,
-//                // upper and lower case may cause issues on windows, if the
-//                // CodeSource url differs.
-//                return uri; 
-//            } else {
-//                // Since file is system dependant, it will guarantee that
-//                // the path is compatible and exists.
-//                File file = new File(uri);
-//                return file.toURI().normalize();
-//            }
-//        }
-        return uri;
+        if (uriString == null) return null;
+        uriString = UriString.fixWindowsURI(uriString);
+        uriString = UriString.escapeIllegalCharacters(uriString);
+        return new URI(uriString);
     }
     
     Segment segment(String s, Properties p) throws ExpansionFailedException{

Modified: river/jtsk/trunk/src/org/apache/river/api/security/PolicyUtils.java
URL: http://svn.apache.org/viewvc/river/jtsk/trunk/src/org/apache/river/api/security/PolicyUtils.java?rev=1369328&r1=1369327&r2=1369328&view=diff
==============================================================================
--- river/jtsk/trunk/src/org/apache/river/api/security/PolicyUtils.java (original)
+++ river/jtsk/trunk/src/org/apache/river/api/security/PolicyUtils.java Sat Aug  4 14:06:20 2012
@@ -46,6 +46,7 @@ import java.util.List;
 import java.util.Properties;
 import java.util.logging.Level;
 import java.util.logging.Logger;
+import org.apache.river.impl.net.UriString;
 
 /**
  * This class consist of a number of static methods, which provide a common functionality 
@@ -211,18 +212,18 @@ import java.util.logging.Logger;
                         // codebase is "file:"
                         path = "*";
                     }
-                    return filePathToURI(new File(path)
-                            .getAbsolutePath()).normalize();
+                    return UriString.normalisation(filePathToURI(new File(path)
+                            .getAbsolutePath()));
                 } else {
                     // codebase is "file://<smth>"
-                    return codebase.toURI().normalize();
+                    return UriString.normalisation(codebase.toURI());
                 }
             } catch (Exception e) {
                 if ( e instanceof SecurityException ) throw (SecurityException) e;
                 // Ignore
             }
         }
-        return codebase.toURI();
+        return UriString.normalisation(codebase.toURI());
     }
 
     /**
@@ -235,8 +236,10 @@ import java.util.logging.Logger;
      * @throw URISyntaxException
      */
     static URI filePathToURI(String path) throws URISyntaxException {
-        path = path.replace(File.separatorChar, '/');
-
+        if (File.separatorChar == '\\' && path != null){
+            path = path.replace(File.separatorChar, '/');
+            path = path.toUpperCase();
+        }
         if (!path.startsWith("/")) { //$NON-NLS-1$
             return new URI("file", null, //$NON-NLS-1$
                     new StringBuilder(path.length() + 1).append('/')

Modified: river/jtsk/trunk/src/org/apache/river/api/security/URIGrant.java
URL: http://svn.apache.org/viewvc/river/jtsk/trunk/src/org/apache/river/api/security/URIGrant.java?rev=1369328&r1=1369327&r2=1369328&view=diff
==============================================================================
--- river/jtsk/trunk/src/org/apache/river/api/security/URIGrant.java (original)
+++ river/jtsk/trunk/src/org/apache/river/api/security/URIGrant.java Sat Aug  4 14:06:20 2012
@@ -21,6 +21,7 @@ package org.apache.river.api.security;
 import java.io.InvalidObjectException;
 import java.io.ObjectInputStream;
 import java.net.URI;
+import java.net.URISyntaxException;
 import java.net.URL;
 import java.security.AccessController;
 import java.security.CodeSource;
@@ -33,6 +34,9 @@ import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.Iterator;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import org.apache.river.impl.net.UriString;
 
 /**
  *
@@ -49,7 +53,14 @@ class URIGrant extends CertificateGrant 
         int l = uri.length;
         Collection<URI> uris = new ArrayList<URI>(l);
         for ( int i = 0; i < l ; i++ ){
-            uris.add(uri[i] != null ? uri[i].normalize() : null);
+            try {
+                // REMIND: Do we need to move all normalisation into the URIGrant and
+                // store the normalised and original forms separately? File uri are platform
+                // dependant and someone may want to make a grant applicable many different platforms.
+                uris.add(uri[i] != null ? UriString.normalise(uri[i]) : null);
+            } catch (URISyntaxException ex) {
+                ex.printStackTrace(System.err);
+            }
         }
         location = Collections.unmodifiableCollection(uris);
         int hash = 3;
@@ -399,7 +410,7 @@ class URIGrant extends CertificateGrant 
         // is applicable here 
         return true;
     }
-
+    
     @Override
     public PermissionGrantBuilder getBuilderTemplate() {
         PermissionGrantBuilder pgb = super.getBuilderTemplate();

Modified: river/jtsk/trunk/src/org/apache/river/impl/net/UriString.java
URL: http://svn.apache.org/viewvc/river/jtsk/trunk/src/org/apache/river/impl/net/UriString.java?rev=1369328&r1=1369327&r2=1369328&view=diff
==============================================================================
--- river/jtsk/trunk/src/org/apache/river/impl/net/UriString.java (original)
+++ river/jtsk/trunk/src/org/apache/river/impl/net/UriString.java Sat Aug  4 14:06:20 2012
@@ -18,85 +18,110 @@
 package org.apache.river.impl.net;
 
 import java.io.File;
-import java.util.ArrayList;
-import java.util.Collection;
+import java.io.IOException;
+import java.net.URI;
+import java.net.URISyntaxException;
 import java.util.HashMap;
 import java.util.Map;
 
 /**
- * Utility that escapes illegal characters in URI strings according to RFC2396
- * as well as converting MS Windows file absolute path strings to URI compliant 
+ * Utility that escapes illegal characters in URI strings according to RFC3986
+ * January 2005 as well as converting MS Windows file absolute path strings to URI compliant 
  * syntax.
  * 
  * @author Peter Firmstone.
  */
 public class UriString {
     
+    private static final char [] latin = new char[256];
+    private static final String [] latinEsc = new String[256];
+    
     // prevents instantiation.
     private UriString(){};
     
-    private final static Map<Character,String> escaped = new HashMap<Character,String>();
-    private final static Collection<Character> alpha;
+    private final static Map<String, Character> unreserved = new HashMap<String, Character>(66); // To be unescaped during normalisation.
+    
+    /* 2.1.  Percent-Encoding
+     * 
+     * A percent-encoding mechanism is used to represent a data octet in a
+     * component when that octet's corresponding character is outside the
+     * allowed set or is being used as a delimiter of, or within, the
+     * component.  A percent-encoded octet is encoded as a character
+     * triplet, consisting of the percent character "%" followed by the two
+     * hexadecimal digits representing that octet's numeric value.  For
+     * example, "%20" is the percent-encoding for the binary octet
+     * "00100000" (ABNF: %x20), which in US-ASCII corresponds to the space
+     * character (SP).  Section 2.4 describes when percent-encoding and
+     * decoding is applied.
+     * 
+     *    pct-encoded = "%" HEXDIG HEXDIG
+     * 
+     * The uppercase hexadecimal digits 'A' through 'F' are equivalent to
+     * the lowercase digits 'a' through 'f', respectively.  If two URIs
+     * differ only in the case of hexadecimal digits used in percent-encoded
+     * octets, they are equivalent.  For consistency, URI producers and
+     * normalizers should use uppercase hexadecimal digits for all percent-
+     * encodings.
+     */
+    // Any character that is not part of the reserved and unreserved sets must
+    // be encoded.
+    // Section 2.1 Percent encoding must be converted to upper case during normalisation.
+    private static final char escape = '%';
+     /* RFC3986 obsoletes RFC2396 and RFC2732
+     * 
+     * reserved    = gen-delims / sub-delims
+     * 
+     * gen-delims  = ":" / "/" / "?" / "#" / "[" / "]" / "@"
+     * 
+     * sub-delims  = "!" / "$" / "&" / "'" / "(" / ")"
+     *               / "*" / "+" / "," / ";" / "="
+     */
+    // Section 2.2 Reserved set is protected from normalisation.
+    private static final char [] gen_delims = {':', '/', '?', '#', '[', ']', '@'};
+    private static final char [] sub_delims = {'!', '$', '&', '\'', '(', ')', '*',
+                                                            '+', ',', ';', '='};
     
-        // Allowed
+    /*
+     * For consistency, percent-encoded octets in the ranges of ALPHA
+     * (%41-%5A and %61-%7A), DIGIT (%30-%39), hyphen (%2D), period (%2E),
+     * underscore (%5F), or tilde (%7E) should not be created by URI
+     * producers and, when found in a URI, should be decoded to their
+     * corresponding unreserved characters by URI normalizers.
+     */
+    // Section 2.3 Unreserved characters (Allowed) must be decoded during normalisation if % encoded.
     private static final char [] lowalpha = "abcdefghijklmnopqrstuvwxyz".toCharArray();
     private static final char [] upalpha = "ABCDEFGHIJKLMNOPQRSTUVWXYZ".toCharArray();
     private static final char [] numeric = "0123456789".toCharArray();
-    private static final char [] reserved = {';','/','?',':','@','&','=','+','$',','};
-    private static final char [] mark = "-_.!~*'()".toCharArray();
-    private static final char escape = '%';
-    private static final char fragment = '#';
-    // Excluded
-    private static final Character [] control = {
-        '\u0000','\u0001','\u0002','\u0003','\u0004','\u0005','\u0006','\u0007',
-        '\u0008','\u0009','\n',    '\u000B','\u000C','\r',    '\u000E','\u000F',
-        '\u0010','\u0011','\u0012','\u0013','\u0014','\u0015','\u0016','\u0017',
-        '\u0018','\u0019','\u001A','\u001B','\u001C','\u001D','\u001E','\u001F',
-        '\u007F'
-    };
-    private static final String [] contEsc = {
-        "%00", "%01", "%02", "%03", "%04", "%05", "%06", "%07", 
-        "%08", "%09", "%0A", "%0B", "%0C", "%0D", "%0E", "%0F", 
-        "%10", "%11", "%12", "%13", "%14", "%15", "%16", "%17", 
-        "%18", "%19", "%1A", "%1B", "%1C", "%1D", "%1E", "%1F",
-        "%7F"
-    };
-    private static final Character space = '\u0020';
-    private static final String spaceEsc = "%20";
-    // Excluded because they are often used as delimiters around URI
-    private static final Character [] delims = {'<','>','"'};
-    private static final String [] delimEsc = {"%3C", "%3E", "%22"};
-    // Excluded because gateways and other transport agents are known
-    // to sometimes modify such characters, or they are used as delimiters.
-    private static final Character [] unwise = {'{','}','|','\\','^','[',']','`'};
-    private static final String [] unwiseEsc = {"%7B", "%7D", "%7C", "%5C", 
-                                   "%53", "%5B", "%5D", "%60"};
-
-    
+    private static final char [] unres_punct =  {'-' , '.' , '_' , '~'};
+    private static final char [] schemeEx = "+-.".toCharArray(); // + ALPHA and numeric.
+     
     static {
-        escapes(control, contEsc);
-        escaped.put(space, spaceEsc);
-        escapes(delims, delimEsc);
-        escapes(unwise, unwiseEsc);
-        alpha = new ArrayList<Character>(lowalpha.length + upalpha.length);
-        addArrayToCollection(alpha, lowalpha);
-        addArrayToCollection(alpha, upalpha);
+        processLatin();
+        processUnreserved();
     }
     
-    private static void escapes(Character [] unicode, String[] escape){
-        int l = unicode.length;
-        if (l != escape.length) throw new IllegalArgumentException("unequal arrays");
+    private static void processUnreserved(){
+        int l = lowalpha.length;
         for (int i = 0; i < l; i++){
-            escaped.put(unicode[i], escape[i]);
+            int n = index(latin, lowalpha[i]);
+            unreserved.put(latinEsc[i], lowalpha[i]);
         }
-    }
-    
-    private static void addArrayToCollection(Collection<Character> col, char [] chars){
-        int l = chars.length;
-        for ( int i = 0; i < l; i++){
-            col.add(chars[i]);
+        l = upalpha.length;
+        for (int i = 0; i < l; i++){
+            int n = index(latin, upalpha[i]);
+            unreserved.put(latinEsc[i], upalpha[i]);
         }
-    }
+        l = numeric.length;
+        for (int i = 0; i < l; i++){
+            int n = index(latin, numeric[i]);
+            unreserved.put(latinEsc[i], numeric[i]);
+        }
+        l = unres_punct.length;
+        for (int i = 0; i < l; i++){
+            int n = index(latin, unres_punct[i]);
+            unreserved.put(latinEsc[i], unres_punct[i]);
+        }
+    } 
     
     /**
      * Finds a character in an array and returns the index at which it exists,
@@ -114,74 +139,1133 @@ public class UriString {
         return -1;
     }
     
-    public static String parse(String url) {
-        boolean isFile = url.startsWith("file:") || url.startsWith("FILE:");
-        char slash = reserved[1];
-        char [] u = url.toCharArray();
-        int l = u.length; 
-        StringBuilder sb = new StringBuilder();
-        for (int i=0; i<l; i++){
-            if (isFile){
+    /**
+     * Encodes illegal characters according to RFC3986, "%" characters are not 
+     * encoded, since they are legal and in case the string already
+     * contains escaped characters.  The percent character must be encoded
+     * manually prior to calling this method.
+     * 
+     * @param str
+     * @return
+     * @throws URISyntaxException  
+     */
+    public static String escapeIllegalCharacters(String str) throws URISyntaxException {
+        if (str == null) return null;
+        char [] chars = str.toCharArray();
+        int len = chars.length;
+        StringBuilder sb = new StringBuilder(len + 12);
+        boolean esc = false;
+        for (int i = 0; i < len; i++){
+            if (chars[i] == escape){
+                /*  Section 2.4
+                 * Because the percent ("%") character serves as the indicator for
+                 * percent-encoded octets, it must be percent-encoded as "%25" for that
+                 * octet to be used as data within a URI.  Implementations must not
+                 * percent-encode or decode the same string more than once, as decoding
+                 * an already decoded string might lead to misinterpreting a percent
+                 * data octet as the beginning of a percent-encoding, or vice versa in
+                 * the case of percent-encoding an already percent-encoded string.
+                 */
+                sb.append(chars[i]);
+            }else if ( index(gen_delims, chars[i]) != -1 
+                    || index(sub_delims, chars[i]) != -1
+                    || index(lowalpha, chars[i]) != -1
+                    || index(upalpha, chars[i]) != -1
+                    || index(numeric, chars[i]) != -1
+                    || index(unres_punct, chars[i]) != -1){
+                sb.append(chars[i]);
+            }else {
+                int n = index(latin, chars[i]);
+                if (n < 0) throw new URISyntaxException(str, "String contains unescapable character");
+                sb.append(latinEsc[n]);
+                esc = true;
+            }
+        }
+        if (!esc) return str;
+        return sb.toString();
+    }
+    
+    /* Fixes windows file URI path by converting back slashes to forward
+     * slashes and inserting a forward slash before the drive letter if it is
+     * missing.  No modification of case is performed.
+     */
+    public static String fixWindowsURI(String path) {
+        if (path == null) return null;
+        if ( path.startsWith("file:") || path.startsWith("FILE:")){
+            char [] u = path.toCharArray();
+            int l = u.length; 
+            StringBuilder sb = new StringBuilder();
+            for (int i=0; i<l; i++){
                 // Ensure we use forward slashes
                 if (u[i] == File.separatorChar) {
-                    u[i] = '/';
-                    sb.append(u[i]);
+                    sb.append('/');
                     continue;
                 }
-                if (i == 5 && url.startsWith(":", 6 )) {
+                if (i == 5 && path.startsWith(":", 6 )) {
                     // Windows drive letter without leading slashes doesn't comply
                     // with URI spec, fix it here
-                    // Ensure that drive letter is upper case only.
-                    int upcase = index(upalpha, u[i]);
-                    int lowcase = index(lowalpha, u[i]);
-                    if ( upcase > 0){
-                        sb.append("///");
-                        sb.append(u[i]);
+                    sb.append("/");
+                }
+                sb.append(u[i]);
+            }
+            return sb.toString();
+        }
+        return path;
+    }
+    
+    /**
+     * Normalises URIs to standard ones, eliminating pathname symbols, in addition
+     * to normalisation compliant with RFC3986 this method, uses the platform specific
+     * canonical file path for "file" scheme URIs and then normalises it using
+     * RFC3986 normalisation rules.
+     * 
+     * This minimises false negatives for URI equals and policy based URI
+     * comparison.
+     * 
+     * @param codebase - the original URI.
+     * @return - the normalised URI.
+     * @throws URISyntaxException  
+     */
+    public static URI normalise(URI codebase) throws URISyntaxException {
+        if (codebase == null) return null;
+        String scheme = codebase.getScheme();
+        if (scheme != null) scheme = scheme.toLowerCase();
+        if ("file".equals(scheme)) { //$NON-NLS-1$
+            if (codebase.getHost() == null || codebase.getHost().isEmpty()) {
+                String path = codebase.getPath();
+
+                if (path == null || path.length() == 0) {
+                    // codebase is "file:"
+                    path = "*";
+                }
+                return normalisation(filePathToURI(new File(path).getAbsolutePath()));
+            } else {
+                // codebase is "file://<smth>"
+                return normalisation(codebase);
+            }
+        }
+        return normalisation(codebase);
+    }
+
+    /**
+     * Converts a file path to URI without accessing file system
+     * (like {File#toURI()} does).
+     * 
+     * @param path -
+     *            file path.
+     * @return - the resulting URI.
+     * @throw URISyntaxException
+     */
+    private static URI filePathToURI(String path) throws URISyntaxException {
+        if (File.separatorChar == '\\') {
+            path = path.replace(File.separatorChar, '/');
+            path = path.toUpperCase(); // Windows path must be CAPITALISED during normalisation.
+        }
+//        path = filePathNormalise(path);
+        if (!path.startsWith("/")) { //$NON-NLS-1$
+            return new URI("file", null, //$NON-NLS-1$
+                    new StringBuilder(path.length() + 1).append('/')
+                            .append(path).toString(), null, null);
+        }
+        return new URI("file", null, path, null, null); //$NON-NLS-1$
+    }
+    
+    /**
+     * Normalisation of URI for comparison in compliance with RFC 3986 Section 6,
+     * without regard for platform specific dependencies.
+     * 
+     * @param uri  to be normalised.
+     * @return URI in normalised from for comparison.
+     * @throws URISyntaxException  
+     */
+    public static URI normalisation(URI uri) throws URISyntaxException {
+        if (uri == null) return null;
+        // Use URI normailze to remove path dot segments etc.
+        uri = uri.normalize();
+        String scheme = uri.getScheme();
+        char [] esc = new char[3]; // container for escape character string.
+        /* Section 3.1 Scheme
+         * Each URI begins with a scheme name that refers to a specification for
+         * assigning identifiers within that scheme.  As such, the URI syntax is
+         * a federated and extensible naming system wherein each scheme's
+         * specification may further restrict the syntax and semantics of
+         * identifiers using that scheme.
+         * 
+         * Scheme names consist of a sequence of characters beginning with a
+         * letter and followed by any combination of letters, digits, plus
+         * ("+"), period ("."), or hyphen ("-").  Although schemes are case-
+         * insensitive, the canonical form is lowercase and documents that
+         * specify schemes must do so with lowercase letters.  An implementation
+         * should accept uppercase letters as equivalent to lowercase in scheme
+         * names (e.g., allow "HTTP" as well as "http") for the sake of
+         * robustness but should only produce lowercase scheme names for
+         * consistency.
+         * 
+         *   scheme      = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." )
+         * 
+         * Individual schemes are not specified by this document.  The process
+         * for registration of new URI schemes is defined separately by [BCP35].
+         * The scheme registry maintains the mapping between scheme names and
+         * their specifications.  Advice for designers of new URI schemes can be
+         * found in [RFC2718].  URI scheme specifications must define their own
+         * syntax so that all strings matching their scheme-specific syntax will
+         * also match the <absolute-URI> grammar, as described in Section 4.3.
+         * 
+         * When presented with a URI that violates one or more scheme-specific
+         * restrictions, the scheme-specific resolution process should flag the
+         * reference as an error rather than ignore the unused parts; doing so
+         * reduces the number of equivalent URIs and helps detect abuses of the
+         * generic syntax, which might indicate that the URI has been
+         * constructed to mislead the user (Section 7.6).
+         * 
+         */
+        if (scheme != null){
+            scheme = scheme.toLowerCase();
+            char [] scm = scheme.toCharArray();
+            int l = scm.length;
+            for (int i = 0; i < l; i++){
+                if (index(lowalpha, scm[i]) < 0 &&
+                        index(numeric, scm[i]) < 0 &&
+                        index(schemeEx, scm[i]) < 0){
+                    throw new URISyntaxException(scheme, "Scheme contains illegal character at index " + i);
+                }
+            }
+        }
+        /* Section 3.2.2.  Host
+         * 
+         * The host subcomponent of authority is identified by an IP literal
+         * encapsulated within square brackets, an IPv4 address in dotted-
+         * decimal form, or a registered name.  The host subcomponent is case-
+         * insensitive.  The presence of a host subcomponent within a URI does
+         * not imply that the scheme requires access to the given host on the
+         * Internet.  In many cases, the host syntax is used only for the sake
+         * of reusing the existing registration process created and deployed for
+         * DNS, thus obtaining a globally unique name without the cost of
+         * deploying another registry.  However, such use comes with its own
+         * costs: domain name ownership may change over time for reasons not
+         * anticipated by the URI producer.  In other cases, the data within the
+         * host component identifies a registered name that has nothing to do
+         * with an Internet host.  We use the name "host" for the ABNF rule
+         * because that is its most common purpose, not its only purpose.
+         * 
+         *    host        = IP-literal / IPv4address / reg-name
+         * 
+         * The syntax rule for host is ambiguous because it does not completely
+         * distinguish between an IPv4address and a reg-name.  In order to
+         * disambiguate the syntax, we apply the "first-match-wins" algorithm:
+         * If host matches the rule for IPv4address, then it should be
+         * considered an IPv4 address literal and not a reg-name.  Although host
+         * is case-insensitive, producers and normalizers should use lowercase
+         * for registered names and hexadecimal addresses for the sake of
+         * uniformity, while only using uppercase letters for percent-encodings.
+         * 
+         * A host identified by an Internet Protocol literal address, version 6
+         * [RFC3513] or later, is distinguished by enclosing the IP literal
+         * within square brackets ("[" and "]").  This is the only place where
+         * square bracket characters are allowed in the URI syntax.
+         */
+        String host = uri.getHost();
+        if (host != null){
+            
+            char [] hos = host.toCharArray();
+            int l = hos.length;
+            StringBuilder sb = new StringBuilder(l + 12);
+            int escIndex = -1;
+            for (int i = 0; i < l; i++){
+                /* First check if escape is an unreserved character, if so
+                 * decode it, otherwise, change escape sequence to upper case.
+                 */
+                if (hos[i] == escape){
+                    // Although java.net.URI prohibits escape characters in
+                    // host, it may change in future if updated for compliance.
+                    esc[0] = hos[i];
+                    esc[1] = hos[i+1];
+                    esc[2] = hos[i+2];
+                    String e = new String(esc).toUpperCase();
+                    Character c = unreserved.get(e);
+                    if (c != null){
+                        sb.append(c);
+                        i = i+2;
                         continue;
-                    } else if ( lowcase > 0){
-                        sb.append("///");
-                        sb.append(upalpha[lowcase]);
+                    }
+                    sb.append(hos[i]);
+                    escIndex = i;
+                    continue;
+                } 
+                if (escIndex > 0 && i > escIndex && i < escIndex + 3 ){
+                    if (index(numeric, hos[i]) > 0) {
+                        sb.append(hos[i]);
                         continue;
                     }
-                }
-                if (i == 6 && u[5] == '/' && url.startsWith(":", 7) ){
-                    // Ensure drive letter is upper case only.
-                    int upcase = index(upalpha, u[i]);
-                    int lowcase = index(lowalpha, u[i]);
-                    if ( upcase > 0){
-                        sb.append("//");
-                        sb.append(u[i]);
+                    if (index(upalpha, hos[i]) > 0){
+                        sb.append(hos[i]);
                         continue;
-                    } else if ( lowcase > 0){
-                        sb.append("//");
-                        sb.append(upalpha[lowcase]);
+                    }
+                    int n = index(lowalpha, hos[i]);
+                    if (n > 0){
+                        sb.append(upalpha[n]);
                         continue;
                     }
+                    throw new URISyntaxException(host, "host contains escaped sequence that has an illegal character at index " + i);
+                }
+                int n = index(upalpha, hos[i]);
+                if (n > 0) {
+                    sb.append(lowalpha[n]);
+                    continue;
                 }
-                if (i == 8 && u[5] == slash && u[6] == slash 
-                        && u[7] == slash && url.startsWith(":", 9)){
-                    // Ensure drive letter is upper case only.
-                    int upcase = index(upalpha, u[i]);
-                    int lowcase = index(lowalpha, u[i]);
-                    if ( upcase > 0){
-                        sb.append(u[i]);
+                // Since this is normalisation of an existing URI, lets assume
+                // character is legal.
+                sb.append(hos[i]);
+            }
+            host = sb.toString();
+        }
+        /* Section 3.3.  Path
+         * 
+         * If a URI contains an authority component, then the path component
+         * must either be empty or begin with a slash ("/") character.  If a URI
+         * does not contain an authority component, then the path cannot begin
+         * with two slash characters ("//").  In addition, a URI reference
+         * (Section 4.1) may be a relative-path reference, in which case the
+         * first path segment cannot contain a colon (":") character.
+         */
+        String authority = uri.getAuthority();
+        String path = uri.getPath();
+        if (path != null || !path.equals("") ){
+            if (authority != null){
+                if ( !path.startsWith("/")) path = '/' + path;
+            } else {
+                if ( path.startsWith("//") && !path.startsWith("///")) 
+                    throw new URISyntaxException(path, "Path cannot start with two slash characters becuause Authority component is null");
+            }
+            // REMIND: Check for relative path and make sure the first path segment doesn't contain ":"
+            char [] pth = path.toCharArray();
+            int l = pth.length;
+            StringBuilder sb = new StringBuilder(l);
+            int escIndex = -1;
+            for (int i = 0; i < l; i++){
+                /* First check if escape is an unreserved character, if so
+                 * decode it, otherwise, change escape sequence to upper case.
+                 */
+                if (pth[i] == escape){
+                    // Although java.net.URI prohibits escape characters in
+                    // host, it may change in future if updated for compliance.
+                    esc[0] = pth[i];
+                    esc[1] = pth[i+1];
+                    esc[2] = pth[i+2];
+                    String e = new String(esc).toUpperCase();
+                    Character c = unreserved.get(e);
+                    if (c != null){
+                        sb.append(c);
+                        i = i+2;
+                        continue;
+                    }
+                    sb.append(pth[i]);
+                    escIndex = i;
+                    continue;
+                } 
+                if (escIndex > 0 && i > escIndex && i < escIndex + 3 ){
+                    if (index(numeric, pth[i]) > 0) {
+                        sb.append(pth[i]);
                         continue;
-                    } else if ( lowcase > 0){
-                        sb.append(upalpha[lowcase]);
+                    }
+                    if (index(upalpha, pth[i]) > 0){
+                        sb.append(pth[i]);
+                        continue;
+                    }
+                    int n = index(lowalpha, pth[i]);
+                    if (n > 0){
+                        sb.append(upalpha[n]);
                         continue;
                     }
+                    throw new URISyntaxException(path, "path contains escaped sequence that has an illegal character at index " + i);
                 }
-                    
-            }
-            Character c = Character.valueOf(u[i]);
-            if (escaped.keySet().contains(c)){
-                sb.append(escaped.get(c));
-            } else {
-                sb.append(c);
+                // Since this is normalisation of an existing URI, lets assume
+                // character is legal.
+                sb.append(pth[i]);
             }
+            path = sb.toString();
         }
-        return sb.toString();
+        // TODO: query and fragment normalisation.
+        //URI(String scheme, String userInfo, String host, int port, String path, String query, String fragment)
+        return new URI(scheme, uri.getRawUserInfo(), host, uri.getPort(), path, uri.getQuery(), uri.getFragment());
     }
     
+    
+    private static void processLatin(){
+        /*  Complete list of Unicode Latin possible to represent with percentage encoding.*/
+        //          Basic Latin 
+        //            Position Decimal Name Appearance 
+        //            0x0000 0 <control>: NULL 
+        latin[0] = '\u0000';
+        latinEsc[0] = "%00";
+        //            0x0001 1 <control>: START OF HEADING  
+        latin[1] = '\u0001';
+        latinEsc[1] = "%01";
+        //            0x0002 2 <control>: START OF TEXT 
+        latin[2] = '\u0002';
+        latinEsc[2] = "%02";
+        //            0x0003 3 <control>: END OF TEXT  
+        latin[3] = '\u0003';
+        latinEsc[3] = "%03";
+        //            0x0004 4 <control>: END OF TRANSMISSION 
+        latin[4] = '\u0004';
+        latinEsc[4] = "%04";
+        //            0x0005 5 <control>: ENQUIRY  
+        latin[5] = '\u0005';
+        latinEsc[5] = "%05";
+        //            0x0006 6 <control>: ACKNOWLEDGE  
+        latin[6] = '\u0006';
+        latinEsc[6] = "%06";
+        //            0x0007 7 <control>: BELL  
+        latin[7] = '\u0007';
+        latinEsc[7] = "%07";
+        //            0x0008 8 <control>: BACKSPACE 
+        latin[8] = '\u0008';
+        latinEsc[8] = "%08";
+        //            0x0009 9 <control>: HORIZONTAL TABULATION  
+        latin[9] = '\u0009';
+        latinEsc[9] = "%09";
+        //            0x000A 10 <control>: LINE FEED  
+        latin[10] = '\n';
+        latinEsc[10] = "%0A";
+        //            0x000B 11 <control>: VERTICAL TABULATION 
+        latin[11] = '\u000B';
+        latinEsc[11] = "%0B";
+        //            0x000C 12 <control>: FORM FEED  
+        latin[12] = '\u000C';
+        latinEsc[12] = "%0C";
+        //            0x000D 13 <control>: CARRIAGE RETURN 
+        latin[13] = '\r';
+        latinEsc[13] = "%0D";
+        //            0x000E 14 <control>: SHIFT OUT 
+        latin[14] = '\u000E';
+        latinEsc[14] = "%0E";
+        //            0x000F 15 <control>: SHIFT IN  
+        latin[15] = '\u000F';
+        latinEsc[15] = "%0F";
+        //            0x0010 16 <control>: DATA LINK ESCAPE 
+        latin[16] = '\u0010';
+        latinEsc[16] = "%10";
+        //            0x0011 17 <control>: DEVICE CONTROL ONE 
+        latin[17] = '\u0011';
+        latinEsc[17] = "%11";
+        //            0x0012 18 <control>: DEVICE CONTROL TWO 
+        latin[18] = '\u0012';
+        latinEsc[18] = "%12";
+        //            0x0013 19 <control>: DEVICE CONTROL THREE 
+        latin[19] = '\u0013';
+        latinEsc[19] = "%13";
+        //            0x0014 20 <control>: DEVICE CONTROL FOUR 
+        latin[20] = '\u0014';
+        latinEsc[20] = "%14";
+        //            0x0015 21 <control>: NEGATIVE ACKNOWLEDGE 
+        latin[21] = '\u0015';
+        latinEsc[21] = "%15";
+        //            0x0016 22 <control>: SYNCHRONOUS IDLE 
+        latin[22] = '\u0016';
+        latinEsc[22] = "%16"; 
+        //            0x0017 23 <control>: END OF TRANSMISSION BLOCK  
+        latin[23] = '\u0017';
+        latinEsc[23] = "%17";
+        //            0x0018 24 <control>: CANCEL  
+        latin[24] = '\u0018';
+        latinEsc[24] = "%18";
+        //            0x0019 25 <control>: END OF MEDIUM  
+        latin[25] = '\u0019';
+        latinEsc[25] = "%19";
+        //            0x001A 26 <control>: SUBSTITUTE  
+        latin[26] = '\u001A';
+        latinEsc[26] = "%1A";
+        //            0x001B 27 <control>: ESCAPE  
+        latin[27] = '\u001B';
+        latinEsc[27] = "%1B";
+        //            0x001C 28 <control>: FILE SEPARATOR  
+        latin[28] = '\u001C';
+        latinEsc[28] = "%1C";
+        //            0x001D 29 <control>: GROUP SEPARATOR  
+        latin[29] = '\u001D';
+        latinEsc[29] = "%1D";
+        //            0x001E 30 <control>: RECORD SEPARATOR  
+        latin[30] = '\u001E';
+        latinEsc[30] = "%1E";
+        //            0x001F 31 <control>: UNIT SEPARATOR  
+        latin[31] = '\u001F';
+        latinEsc[31] = "%1F";
+        //            0x0020 32 SPACE  
+        latin[32] = '\u0020';
+        latinEsc[32] = "%20";
+        //            0x0021 33 EXCLAMATION MARK ! 
+        latin[33] = '\u0021';
+        latinEsc[33] = "%21";
+        //            0x0022 34 QUOTATION MARK " 
+        latin[34] = '\u0022';
+        latinEsc[34] = "%22";
+        //            0x0023 35 NUMBER SIGN # 
+        latin[35] = '\u0023';
+        latinEsc[35] = "%23";
+        //            0x0024 36 DOLLAR SIGN $ 
+        latin[36] = '\u0024';
+        latinEsc[36] = "%24";
+        //            0x0025 37 PERCENT SIGN % 
+        latin[37] = '\u0025';
+        latinEsc[37] = "%25";
+        //            0x0026 38 AMPERSAND & 
+        latin[38] = '\u0026';
+        latinEsc[38] = "%26";
+        //            0x0027 39 APOSTROPHE ' 
+        latin[39] = '\'';
+        latinEsc[39] = "%27";
+        //            0x0028 40 LEFT PARENTHESIS ( 
+        latin[40] = '\u0028';
+        latinEsc[40] = "%28";
+        //            0x0029 41 RIGHT PARENTHESIS ) 
+        latin[41] = '\u0029';
+        latinEsc[41] = "%29";
+        //            0x002A 42 ASTERISK * 
+        latin[42] = '\u002A';
+        latinEsc[42] = "%2A";
+        //            0x002B 43 PLUS SIGN + 
+        latin[43] = '\u002B';
+        latinEsc[43] = "%2B";
+        //            0x002C 44 COMMA , 
+        latin[44] = '\u002C';
+        latinEsc[44] = "%2C";
+        //            0x002D 45 HYPHEN-MINUS - 
+        latin[45] = '\u002D';
+        latinEsc[0] = "%2D";
+        //            0x002E 46 FULL STOP . 
+        latin[46] = '\u002E';
+        latinEsc[46] = "%2E";
+        //            0x002F 47 SOLIDUS / 
+        latin[47] = '\u002F';
+        latinEsc[47] = "%2F";
+        //            0x0030 48 DIGIT ZERO 0 
+        latin[48] = '\u0030';
+        latinEsc[48] = "%30";
+        //            0x0031 49 DIGIT ONE 1 
+        latin[49] = '\u0031';
+        latinEsc[49] = "%31";
+        //            0x0032 50 DIGIT TWO 2 
+        latin[50] = '\u0032';
+        latinEsc[50] = "%32";
+        //            0x0033 51 DIGIT THREE 3 
+        latin[51] = '\u0033';
+        latinEsc[51] = "%33";
+        //            0x0034 52 DIGIT FOUR 4 
+        latin[52] = '\u0034';
+        latinEsc[52] = "%34";
+        //            0x0035 53 DIGIT FIVE 5 
+        latin[53] = '\u0035';
+        latinEsc[53] = "%35";
+        //            0x0036 54 DIGIT SIX 6 
+        latin[54] = '\u0036';
+        latinEsc[54] = "%36";
+        //            0x0037 55 DIGIT SEVEN 7 
+        latin[55] = '\u0037';
+        latinEsc[55] = "%37";
+        //            0x0038 56 DIGIT EIGHT 8 
+        latin[56] = '\u0038';
+        latinEsc[56] = "%38";
+        //            0x0039 57 DIGIT NINE 9 
+        latin[57] = '\u0039';
+        latinEsc[57] = "%39";
+        //            0x003A 58 COLON : 
+        latin[58] = '\u003A';
+        latinEsc[58] = "%3A";
+        //            0x003B 59 SEMICOLON ; 
+        latin[59] = '\u003B';
+        latinEsc[59] = "%3B";
+        //            0x003C 60 LESS-THAN SIGN < 
+        latin[60] = '\u003C';
+        latinEsc[60] = "%3C";
+        //            0x003D 61 EQUALS SIGN = 
+        latin[61] = '\u003D';
+        latinEsc[61] = "%3D";
+        //            0x003E 62 GREATER-THAN SIGN > 
+        latin[62] = '\u003E';
+        latinEsc[62] = "%3E";
+        //            0x003F 63 QUESTION MARK ? 
+        latin[63] = '\u003F';
+        latinEsc[63] = "%3F";
+        //            0x0040 64 COMMERCIAL AT @ 
+        latin[64] = '\u0040';
+        latinEsc[64] = "%40";
+        //            0x0041 65 LATIN CAPITAL LETTER A A 
+        latin[65] = '\u0041';
+        latinEsc[65] = "%41";
+        //            0x0042 66 LATIN CAPITAL LETTER B B 
+        latin[66] = '\u0042';
+        latinEsc[66] = "%42";
+        //            0x0043 67 LATIN CAPITAL LETTER C C 
+        latin[67] = '\u0043';
+        latinEsc[67] = "%43";
+        //            0x0044 68 LATIN CAPITAL LETTER D D 
+        latin[68] = '\u0044';
+        latinEsc[68] = "%44";
+        //            0x0045 69 LATIN CAPITAL LETTER E E 
+        latin[69] = '\u0045';
+        latinEsc[69] = "%45";
+        //            0x0046 70 LATIN CAPITAL LETTER F F 
+        latin[70] = '\u0046';
+        latinEsc[70] = "%46";
+        //            0x0047 71 LATIN CAPITAL LETTER G G 
+        latin[71] = '\u0047';
+        latinEsc[71] = "%47";
+        //            0x0048 72 LATIN CAPITAL LETTER H H 
+        latin[72] = '\u0048';
+        latinEsc[72] = "%48";
+        //            0x0049 73 LATIN CAPITAL LETTER I I 
+        latin[73] = '\u0049';
+        latinEsc[73] = "%49";
+        //            0x004A 74 LATIN CAPITAL LETTER J J 
+        latin[74] = '\u004A';
+        latinEsc[74] = "%4A";
+        //            0x004B 75 LATIN CAPITAL LETTER K K 
+        latin[75] = '\u004B';
+        latinEsc[75] = "%4B";
+        //            0x004C 76 LATIN CAPITAL LETTER L L 
+        latin[76] = '\u004C';
+        latinEsc[76] = "%4C";
+        //            0x004D 77 LATIN CAPITAL LETTER M M 
+        latin[77] = '\u004D';
+        latinEsc[77] = "%4D";
+        //            0x004E 78 LATIN CAPITAL LETTER N N 
+        latin[78] = '\u004E';
+        latinEsc[78] = "%4E";
+        //            0x004F 79 LATIN CAPITAL LETTER O O 
+        latin[79] = '\u004F';
+        latinEsc[79] = "%4F";
+        //            0x0050 80 LATIN CAPITAL LETTER P P 
+        latin[80] = '\u0050';
+        latinEsc[80] = "%50";
+        //            0x0051 81 LATIN CAPITAL LETTER Q Q 
+        latin[81] = '\u0051';
+        latinEsc[81] = "%51";
+        //            0x0052 82 LATIN CAPITAL LETTER R R 
+        latin[82] = '\u0052';
+        latinEsc[82] = "%52";
+        //            0x0053 83 LATIN CAPITAL LETTER S S 
+        latin[83] = '\u0053';
+        latinEsc[83] = "%53";
+        //            0x0054 84 LATIN CAPITAL LETTER T T 
+        latin[84] = '\u0054';
+        latinEsc[84] = "%54";
+        //            0x0055 85 LATIN CAPITAL LETTER U U 
+        latin[85] = '\u0055';
+        latinEsc[85] = "%55";
+        //            0x0056 86 LATIN CAPITAL LETTER V V 
+        latin[86] = '\u0056';
+        latinEsc[86] = "%56";
+        //            0x0057 87 LATIN CAPITAL LETTER W W 
+        latin[87] = '\u0057';
+        latinEsc[87] = "%57";
+        //            0x0058 88 LATIN CAPITAL LETTER X X 
+        latin[88] = '\u0058';
+        latinEsc[88] = "%58";
+        //            0x0059 89 LATIN CAPITAL LETTER Y Y 
+        latin[89] = '\u0059';
+        latinEsc[89] = "%59";
+        //            0x005A 90 LATIN CAPITAL LETTER Z Z 
+        latin[90] = '\u005A';
+        latinEsc[90] = "%5A";
+        //            0x005B 91 LEFT SQUARE BRACKET [ 
+        latin[91] = '\u005B';
+        latinEsc[91] = "%5B";
+        //            0x005C 92 REVERSE SOLIDUS \ 
+        latin[92] = '\\';
+        latinEsc[92] = "%5C";
+        //            0x005D 93 RIGHT SQUARE BRACKET ] 
+        latin[93] = '\u005D';
+        latinEsc[93] = "%5D";
+        //            0x005E 94 CIRCUMFLEX ACCENT ^ 
+        latin[94] = '\u005E';
+        latinEsc[94] = "%5E";
+        //            0x005F 95 LOW LINE _ 
+        latin[95] = '\u005F';
+        latinEsc[95] = "%5F";
+        //            0x0060 96 GRAVE ACCENT ` 
+        latin[96] = '\u0060';
+        latinEsc[96] = "%60";
+        //            0x0061 97 LATIN SMALL LETTER A a 
+        latin[97] = '\u0061';
+        latinEsc[97] = "%61";
+        //            0x0062 98 LATIN SMALL LETTER B b 
+        latin[98] = '\u0062';
+        latinEsc[98] = "%62";
+        //            0x0063 99 LATIN SMALL LETTER C c 
+        latin[99] = '\u0063';
+        latinEsc[99] = "%63";
+        //            0x0064 100 LATIN SMALL LETTER D d 
+        latin[100] = '\u0064';
+        latinEsc[100] = "%64";
+        //            0x0065 101 LATIN SMALL LETTER E e 
+        latin[101] = '\u0065';
+        latinEsc[101] = "%65";
+        //            0x0066 102 LATIN SMALL LETTER F f 
+        latin[102] = '\u0066';
+        latinEsc[102] = "%66";
+        //            0x0067 103 LATIN SMALL LETTER G g 
+        latin[103] = '\u0067';
+        latinEsc[103] = "%67";
+        //            0x0068 104 LATIN SMALL LETTER H h 
+        latin[104] = '\u0068';
+        latinEsc[104] = "%68";
+        //            0x0069 105 LATIN SMALL LETTER I i 
+        latin[105] = '\u0069';
+        latinEsc[105] = "%69";
+        //            0x006A 106 LATIN SMALL LETTER J j 
+        latin[106] = '\u006A';
+        latinEsc[106] = "%6A";
+        //            0x006B 107 LATIN SMALL LETTER K k 
+        latin[107] = '\u006B';
+        latinEsc[107] = "%6B";
+        //            0x006C 108 LATIN SMALL LETTER L l 
+        latin[108] = '\u006C';
+        latinEsc[108] = "%6C";
+        //            0x006D 109 LATIN SMALL LETTER M m 
+        latin[109] = '\u006D';
+        latinEsc[109] = "%6D";
+        //            0x006E 110 LATIN SMALL LETTER N n 
+        latin[110] = '\u006E';
+        latinEsc[110] = "%6E";
+        //            0x006F 111 LATIN SMALL LETTER O o 
+        latin[111] = '\u006F';
+        latinEsc[111] = "%6F";
+        //            0x0070 112 LATIN SMALL LETTER P p 
+        latin[112] = '\u0070';
+        latinEsc[112] = "%70";
+        //            0x0071 113 LATIN SMALL LETTER Q q 
+        latin[113] = '\u0071';
+        latinEsc[113] = "%71";
+        //            0x0072 114 LATIN SMALL LETTER R r 
+        latin[114] = '\u0072';
+        latinEsc[114] = "%72";
+        //            0x0073 115 LATIN SMALL LETTER S s 
+        latin[115] = '\u0073';
+        latinEsc[115] = "%73";
+        //            0x0074 116 LATIN SMALL LETTER T t 
+        latin[116] = '\u0074';
+        latinEsc[116] = "%74";
+        //            0x0075 117 LATIN SMALL LETTER U u 
+        latin[117] = '\u0075';
+        latinEsc[117] = "%75";
+        //            0x0076 118 LATIN SMALL LETTER V v 
+        latin[118] = '\u0076';
+        latinEsc[118] = "%76";
+        //            0x0077 119 LATIN SMALL LETTER W w 
+        latin[119] = '\u0077';
+        latinEsc[119] = "%77";
+        //            0x0078 120 LATIN SMALL LETTER X x 
+        latin[120] = '\u0078';
+        latinEsc[120] = "%78";
+        //            0x0079 121 LATIN SMALL LETTER Y y 
+        latin[121] = '\u0079';
+        latinEsc[121] = "%79";
+        //            0x007A 122 LATIN SMALL LETTER Z z 
+        latin[122] = '\u007A';
+        latinEsc[122] = "%7A";
+        //            0x007B 123 LEFT CURLY BRACKET { 
+        latin[123] = '\u007B';
+        latinEsc[123] = "%7B";
+        //            0x007C 124 VERTICAL LINE | 
+        latin[124] = '\u007C';
+        latinEsc[124] = "%7C";
+        //            0x007D 125 RIGHT CURLY BRACKET } 
+        latin[125] = '\u007D';
+        latinEsc[125] = "%7D";
+        //            0x007E 126 TILDE ~ 
+        latin[126] = '\u007E';
+        latinEsc[126] = "%7E";
+        //            0x007F 127 <control>: DELETE  
+        latin[127] = '\u007F';
+        latinEsc[127] = "%7F";
+        
+        //            Latin-1 Supplement 
+        //            Position Decimal Name Appearance 
+        //            0x0080 128 <control>:  € 
+        latin[128] = '\u0080';
+        latinEsc[128] = "%80";
+        //            0x0081 129 <control>:  ? 
+        latin[129] = '\u0081';
+        latinEsc[129] = "%81";
+        //            0x0082 130 <control>: BREAK PERMITTED HERE ‚ 
+        latin[130] = '\u0082';
+        latinEsc[130] = "%82";
+        //            0x0083 131 <control>: NO BREAK HERE ƒ 
+        latin[131] = '\u0083';
+        latinEsc[131] = "%83";
+        //            0x0084 132 <control>:  „ 
+        latin[132] = '\u0084';
+        latinEsc[132] = "%84";
+        //            0x0085 133 <control>: NEXT LINE … 
+        latin[133] = '\u0085';
+        latinEsc[133] = "%85";
+        //            0x0086 134 <control>: START OF SELECTED AREA † 
+        latin[134] = '\u0086';
+        latinEsc[134] = "%86";
+        //            0x0087 135 <control>: END OF SELECTED AREA ‡ 
+        latin[135] = '\u0087';
+        latinEsc[135] = "%87";
+        //            0x0088 136 <control>: CHARACTER TABULATION SET ˆ 
+        latin[136] = '\u0088';
+        latinEsc[136] = "%88";
+        //            0x0089 137 <control>: CHARACTER TABULATION WITH JUSTIFICATION ‰ 
+        latin[137] = '\u0089';
+        latinEsc[137] = "%89";
+        //            0x008A 138 <control>: LINE TABULATION SET Š 
+        latin[138] = '\u008A';
+        latinEsc[138] = "%8A";
+        //            0x008B 139 <control>: PARTIAL LINE DOWN ‹ 
+        latin[139] = '\u008B';
+        latinEsc[139] = "%8B";
+        //            0x008C 140 <control>: PARTIAL LINE UP Π
+        latin[140] = '\u008C';
+        latinEsc[140] = "%8C";
+        //            0x008D 141 <control>: REVERSE LINE FEED ? 
+        latin[141] = '\u008D';
+        latinEsc[141] = "%8D";
+        //            0x008E 142 <control>: SINGLE SHIFT TWO Ž 
+        latin[142] = '\u008E';
+        latinEsc[142] = "%8E";
+        //            0x008F 143 <control>: SINGLE SHIFT THREE ? 
+        latin[143] = '\u008F';
+        latinEsc[143] = "%8F";
+        //            0x0090 144 <control>: DEVICE CONTROL STRING ? 
+        latin[144] = '\u0090';
+        latinEsc[144] = "%90";
+        //            0x0091 145 <control>: PRIVATE USE ONE ‘ 
+        latin[145] = '\u0091';
+        latinEsc[145] = "%91";
+        //            0x0092 146 <control>: PRIVATE USE TWO ’ 
+        latin[146] = '\u0092';
+        latinEsc[146] = "%92";
+        //            0x0093 147 <control>: SET TRANSMIT STATE “ 
+        latin[147] = '\u0093';
+        latinEsc[147] = "%93";
+        //            0x0094 148 <control>: CANCEL CHARACTER ” 
+        latin[148] = '\u0094';
+        latinEsc[148] = "%94";
+        //            0x0095 149 <control>: MESSAGE WAITING • 
+        latin[149] = '\u0095';
+        latinEsc[149] = "%95";
+        //            0x0096 150 <control>: START OF GUARDED AREA – 
+        latin[150] = '\u0096';
+        latinEsc[150] = "%96";
+        //            0x0097 151 <control>: END OF GUARDED AREA — 
+        latin[151] = '\u0097';
+        latinEsc[151] = "%97";
+        //            0x0098 152 <control>: START OF STRING ˜ 
+        latin[152] = '\u0098';
+        latinEsc[152] = "%98";
+        //            0x0099 153 <control>:  ™ 
+        latin[153] = '\u0099';
+        latinEsc[153] = "%99";
+        //            0x009A 154 <control>: SINGLE CHARACTER INTRODUCER š 
+        latin[154] = '\u009A';
+        latinEsc[154] = "%9A";
+        //            0x009B 155 <control>: CONTROL SEQUENCE INTRODUCER › 
+        latin[155] = '\u009B';
+        latinEsc[155] = "%9B";
+        //            0x009C 156 <control>: STRING TERMINATOR œ 
+        latin[156] = '\u009C';
+        latinEsc[156] = "%9C";
+        //            0x009D 157 <control>: OPERATING SYSTEM COMMAND ? 
+        latin[157] = '\u009D';
+        latinEsc[157] = "%9D";
+        //            0x009E 158 <control>: PRIVACY MESSAGE ž 
+        latin[158] = '\u009E';
+        latinEsc[158] = "%9E";
+        //            0x009F 159 <control>: APPLICATION PROGRAM COMMAND Ÿ 
+        latin[159] = '\u009F';
+        latinEsc[159] = "%9F";
+        //            0x00A0 160 NO-BREAK SPACE   
+        latin[160] = '\u00A0';
+        latinEsc[160] = "%A0";
+        //            0x00A1 161 INVERTED EXCLAMATION MARK ¡ 
+        latin[161] = '\u00A1';
+        latinEsc[161] = "%A1";
+        //            0x00A2 162 CENT SIGN ¢ 
+        latin[162] = '\u00A2';
+        latinEsc[162] = "%A2";
+        //            0x00A3 163 POUND SIGN £ 
+        latin[163] = '\u00A3';
+        latinEsc[163] = "%A3";
+        //            0x00A4 164 CURRENCY SIGN ¤ 
+        latin[164] = '\u00A4';
+        latinEsc[164] = "%A4";
+        //            0x00A5 165 YEN SIGN ¥ 
+        latin[165] = '\u00A5';
+        latinEsc[165] = "%A5";
+        //            0x00A6 166 BROKEN BAR ¦ 
+        latin[166] = '\u00A6';
+        latinEsc[166] = "%A6";
+        //            0x00A7 167 SECTION SIGN § 
+        latin[167] = '\u00A7';
+        latinEsc[167] = "%A7";
+        //            0x00A8 168 DIAERESIS ¨ 
+        latin[168] = '\u00A8';
+        latinEsc[168] = "%A8";
+        //            0x00A9 169 COPYRIGHT SIGN © 
+        latin[169] = '\u00A9';
+        latinEsc[169] = "%A9";
+        //            0x00AA 170 FEMININE ORDINAL INDICATOR ª 
+        latin[170] = '\u00AA';
+        latinEsc[170] = "%AA";
+        //            0x00AB 171 LEFT-POINTING DOUBLE ANGLE QUOTATION MARK « 
+        latin[171] = '\u00AB';
+        latinEsc[171] = "%AB";
+        //            0x00AC 172 NOT SIGN ¬ 
+        latin[172] = '\u00AC';
+        latinEsc[172] = "%AC";
+        //            0x00AD 173 SOFT HYPHEN ­ 
+        latin[173] = '\u00AD';
+        latinEsc[173] = "%AD";
+        //            0x00AE 174 REGISTERED SIGN ® 
+        latin[174] = '\u00AE';
+        latinEsc[174] = "%AE";
+        //            0x00AF 175 MACRON ¯ 
+        latin[175] = '\u00AF';
+        latinEsc[175] = "%AF";
+        //            0x00B0 176 DEGREE SIGN ° 
+        latin[176] = '\u00B0';
+        latinEsc[176] = "%B0";
+        //            0x00B1 177 PLUS-MINUS SIGN ± 
+        latin[177] = '\u00B1';
+        latinEsc[177] = "%B1";
+        //            0x00B2 178 SUPERSCRIPT TWO ² 
+        latin[178] = '\u00B2';
+        latinEsc[178] = "%B2";
+        //            0x00B3 179 SUPERSCRIPT THREE ³ 
+        latin[179] = '\u00B3';
+        latinEsc[179] = "%B3";
+        //            0x00B4 180 ACUTE ACCENT ´ 
+        latin[180] = '\u00B4';
+        latinEsc[180] = "%B4";
+        //            0x00B5 181 MICRO SIGN µ 
+        latin[181] = '\u00B5';
+        latinEsc[181] = "%B5";
+        //            0x00B6 182 PILCROW SIGN ¶ 
+        latin[182] = '\u00B6';
+        latinEsc[182] = "%B6";
+        //            0x00B7 183 MIDDLE DOT · 
+        latin[183] = '\u00B7';
+        latinEsc[183] = "%B7";
+        //            0x00B8 184 CEDILLA ¸ 
+        latin[184] = '\u00B8';
+        latinEsc[184] = "%B8";
+        //            0x00B9 185 SUPERSCRIPT ONE ¹ 
+        latin[185] = '\u00B9';
+        latinEsc[185] = "%B9";
+        //            0x00BA 186 MASCULINE ORDINAL INDICATOR º 
+        latin[186] = '\u00BA';
+        latinEsc[186] = "%BA";
+        //            0x00BB 187 RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK » 
+        latin[187] = '\u00BB';
+        latinEsc[187] = "%BB";
+        //            0x00BC 188 VULGAR FRACTION ONE QUARTER ¼ 
+        latin[188] = '\u00BC';
+        latinEsc[188] = "%BC";
+        //            0x00BD 189 VULGAR FRACTION ONE HALF ½ 
+        latin[189] = '\u00BD';
+        latinEsc[189] = "%BD";
+        //            0x00BE 190 VULGAR FRACTION THREE QUARTERS ¾ 
+        latin[190] = '\u00BE';
+        latinEsc[190] = "%BE";
+        //            0x00BF 191 INVERTED QUESTION MARK ¿ 
+        latin[191] = '\u00BF';
+        latinEsc[191] = "%BF";
+        //            0x00C0 192 LATIN CAPITAL LETTER A WITH GRAVE À 
+        latin[192] = '\u00C0';
+        latinEsc[192] = "%C0";
+        //            0x00C1 193 LATIN CAPITAL LETTER A WITH ACUTE Á 
+        latin[193] = '\u00C1';
+        latinEsc[193] = "%C1";
+        //            0x00C2 194 LATIN CAPITAL LETTER A WITH CIRCUMFLEX Â 
+        latin[194] = '\u00C2';
+        latinEsc[194] = "%C2";
+        //            0x00C3 195 LATIN CAPITAL LETTER A WITH TILDE Ã 
+        latin[195] = '\u00C3';
+        latinEsc[195] = "%C3";
+        //            0x00C4 196 LATIN CAPITAL LETTER A WITH DIAERESIS Ä 
+        latin[196] = '\u00C4';
+        latinEsc[196] = "%C4";
+        //            0x00C5 197 LATIN CAPITAL LETTER A WITH RING ABOVE Å 
+        latin[197] = '\u00C5';
+        latinEsc[197] = "%C5";
+        //            0x00C6 198 LATIN CAPITAL LETTER AE Æ 
+        latin[198] = '\u00C6';
+        latinEsc[198] = "%C6";
+        //            0x00C7 199 LATIN CAPITAL LETTER C WITH CEDILLA Ç 
+        latin[199] = '\u00C7';
+        latinEsc[199] = "%C7";
+        //            0x00C8 200 LATIN CAPITAL LETTER E WITH GRAVE È 
+        latin[200] = '\u00C8';
+        latinEsc[200] = "%C8";
+        //            0x00C9 201 LATIN CAPITAL LETTER E WITH ACUTE É 
+        latin[201] = '\u00C9';
+        latinEsc[201] = "%C9";
+        //            0x00CA 202 LATIN CAPITAL LETTER E WITH CIRCUMFLEX Ê 
+        latin[202] = '\u00CA';
+        latinEsc[202] = "%CA";
+        //            0x00CB 203 LATIN CAPITAL LETTER E WITH DIAERESIS Ë 
+        latin[203] = '\u00CB';
+        latinEsc[203] = "%CB";
+        //            0x00CC 204 LATIN CAPITAL LETTER I WITH GRAVE Ì 
+        latin[204] = '\u00CC';
+        latinEsc[204] = "%CC";
+        //            0x00CD 205 LATIN CAPITAL LETTER I WITH ACUTE Í 
+        latin[205] = '\u00CD';
+        latinEsc[205] = "%CD";
+        //            0x00CE 206 LATIN CAPITAL LETTER I WITH CIRCUMFLEX Î 
+        latin[206] = '\u00CE';
+        latinEsc[206] = "%CE";
+        //            0x00CF 207 LATIN CAPITAL LETTER I WITH DIAERESIS Ï 
+        latin[207] = '\u00CF';
+        latinEsc[207] = "%CF";
+        //            0x00D0 208 LATIN CAPITAL LETTER ETH Ð 
+        latin[208] = '\u00D0';
+        latinEsc[208] = "%D0";
+        //            0x00D1 209 LATIN CAPITAL LETTER N WITH TILDE Ñ 
+        latin[209] = '\u00D1';
+        latinEsc[209] = "%D1";
+        //            0x00D2 210 LATIN CAPITAL LETTER O WITH GRAVE Ò 
+        latin[210] = '\u00D2';
+        latinEsc[210] = "%D2";
+        //            0x00D3 211 LATIN CAPITAL LETTER O WITH ACUTE Ó 
+        latin[211] = '\u00D3';
+        latinEsc[211] = "%D3";
+        //            0x00D4 212 LATIN CAPITAL LETTER O WITH CIRCUMFLEX Ô 
+        latin[212] = '\u00D4';
+        latinEsc[212] = "%D4";
+        //            0x00D5 213 LATIN CAPITAL LETTER O WITH TILDE Õ 
+        latin[213] = '\u00D5';
+        latinEsc[213] = "%D5";
+        //            0x00D6 214 LATIN CAPITAL LETTER O WITH DIAERESIS Ö 
+        latin[214] = '\u00D6';
+        latinEsc[214] = "%D6";
+        //            0x00D7 215 MULTIPLICATION SIGN × 
+        latin[215] = '\u00D7';
+        latinEsc[215] = "%D7";
+        //            0x00D8 216 LATIN CAPITAL LETTER O WITH STROKE Ø 
+        latin[216] = '\u00D8';
+        latinEsc[216] = "%D8";
+        //            0x00D9 217 LATIN CAPITAL LETTER U WITH GRAVE Ù 
+        latin[217] = '\u00D9';
+        latinEsc[217] = "%D9";
+        //            0x00DA 218 LATIN CAPITAL LETTER U WITH ACUTE Ú 
+        latin[218] = '\u00DA';
+        latinEsc[218] = "%DA";
+        //            0x00DB 219 LATIN CAPITAL LETTER U WITH CIRCUMFLEX Û 
+        latin[219] = '\u00DB';
+        latinEsc[219] = "%DB";
+        //            0x00DC 220 LATIN CAPITAL LETTER U WITH DIAERESIS Ü 
+        latin[220] = '\u00DC';
+        latinEsc[220] = "%DC";
+        //            0x00DD 221 LATIN CAPITAL LETTER Y WITH ACUTE Ý 
+        latin[221] = '\u00DD';
+        latinEsc[221] = "%DD";
+        //            0x00DE 222 LATIN CAPITAL LETTER THORN Þ 
+        latin[222] = '\u00DE';
+        latinEsc[222] = "%DE";
+        //            0x00DF 223 LATIN SMALL LETTER SHARP S ß 
+        latin[223] = '\u00DF';
+        latinEsc[223] = "%DF";
+        //            0x00E0 224 LATIN SMALL LETTER A WITH GRAVE à 
+        latin[224] = '\u00E0';
+        latinEsc[224] = "%E0";
+        //            0x00E1 225 LATIN SMALL LETTER A WITH ACUTE á 
+        latin[225] = '\u00E1';
+        latinEsc[225] = "%E1";
+        //            0x00E2 226 LATIN SMALL LETTER A WITH CIRCUMFLEX â 
+        latin[226] = '\u00E2';
+        latinEsc[226] = "%E2";
+        //            0x00E3 227 LATIN SMALL LETTER A WITH TILDE ã 
+        latin[227] = '\u00E3';
+        latinEsc[227] = "%E3";
+        //            0x00E4 228 LATIN SMALL LETTER A WITH DIAERESIS ä 
+        latin[228] = '\u00E4';
+        latinEsc[228] = "%E4";
+        //            0x00E5 229 LATIN SMALL LETTER A WITH RING ABOVE å 
+        latin[229] = '\u00E5';
+        latinEsc[229] = "%E5";
+        //            0x00E6 230 LATIN SMALL LETTER AE æ 
+        latin[230] = '\u00E6';
+        latinEsc[230] = "%E6";
+        //            0x00E7 231 LATIN SMALL LETTER C WITH CEDILLA ç 
+        latin[231] = '\u00E7';
+        latinEsc[231] = "%E7";
+        //            0x00E8 232 LATIN SMALL LETTER E WITH GRAVE è 
+        latin[232] = '\u00E8';
+        latinEsc[232] = "%E8";
+        //            0x00E9 233 LATIN SMALL LETTER E WITH ACUTE é 
+        latin[233] = '\u00E9';
+        latinEsc[233] = "%E9";
+        //            0x00EA 234 LATIN SMALL LETTER E WITH CIRCUMFLEX ê 
+        latin[234] = '\u00EA';
+        latinEsc[234] = "%EA";
+        //            0x00EB 235 LATIN SMALL LETTER E WITH DIAERESIS ë 
+        latin[235] = '\u00EB';
+        latinEsc[235] = "%EB";
+        //            0x00EC 236 LATIN SMALL LETTER I WITH GRAVE ì 
+        latin[236] = '\u00EC';
+        latinEsc[236] = "%EC";
+        //            0x00ED 237 LATIN SMALL LETTER I WITH ACUTE í 
+        latin[237] = '\u00ED';
+        latinEsc[237] = "%ED";
+        //            0x00EE 238 LATIN SMALL LETTER I WITH CIRCUMFLEX î 
+        latin[238] = '\u00EE';
+        latinEsc[238] = "%EE";
+        //            0x00EF 239 LATIN SMALL LETTER I WITH DIAERESIS ï 
+        latin[239] = '\u00EF';
+        latinEsc[239] = "%EF";
+        //            0x00F0 240 LATIN SMALL LETTER ETH ð 
+        latin[240] = '\u00F0';
+        latinEsc[240] = "%F0";
+        //            0x00F1 241 LATIN SMALL LETTER N WITH TILDE ñ 
+        latin[241] = '\u00F1';
+        latinEsc[241] = "%F1";
+        //            0x00F2 242 LATIN SMALL LETTER O WITH GRAVE ò 
+        latin[242] = '\u00F2';
+        latinEsc[242] = "%F2";
+        //            0x00F3 243 LATIN SMALL LETTER O WITH ACUTE ó 
+        latin[243] = '\u00F3';
+        latinEsc[243] = "%F3";
+        //            0x00F4 244 LATIN SMALL LETTER O WITH CIRCUMFLEX ô 
+        latin[244] = '\u00F4';
+        latinEsc[244] = "%F4";
+        //            0x00F5 245 LATIN SMALL LETTER O WITH TILDE õ 
+        latin[245] = '\u00F5';
+        latinEsc[245] = "%F5";
+        //            0x00F6 246 LATIN SMALL LETTER O WITH DIAERESIS ö 
+        latin[246] = '\u00F6';
+        latinEsc[246] = "%F6";
+        //            0x00F7 247 DIVISION SIGN ÷ 
+        latin[247] = '\u00F7';
+        latinEsc[247] = "%F7";
+        //            0x00F8 248 LATIN SMALL LETTER O WITH STROKE ø 
+        latin[248] = '\u00F8';
+        latinEsc[248] = "%F8";
+        //            0x00F9 249 LATIN SMALL LETTER U WITH GRAVE ù 
+        latin[249] = '\u00F9';
+        latinEsc[249] = "%F9";
+        //            0x00FA 250 LATIN SMALL LETTER U WITH ACUTE ú 
+        latin[250] = '\u00FA';
+        latinEsc[250] = "%FA";
+        //            0x00FB 251 LATIN SMALL LETTER U WITH CIRCUMFLEX û 
+        latin[251] = '\u00FB';
+        latinEsc[251] = "%FB";
+        //            0x00FC 252 LATIN SMALL LETTER U WITH DIAERESIS ü 
+        latin[252] = '\u00FC';
+        latinEsc[252] = "%FC";
+        //            0x00FD 253 LATIN SMALL LETTER Y WITH ACUTE ý 
+        latin[253] = '\u00FD';
+        latinEsc[253] = "%FD";
+        //            0x00FE 254 LATIN SMALL LETTER THORN þ 
+        latin[254] = '\u00FE';
+        latinEsc[254] = "%FE";
+        //            0x00FF 255 LATIN SMALL LETTER Y WITH DIAERESIS ÿ 
+        latin[255] = '\u00FF';
+        latinEsc[255] = "%FF";
+    }
 }
 

Modified: river/jtsk/trunk/test/src/org/apache/river/api/security/URIGrantTest.java
URL: http://svn.apache.org/viewvc/river/jtsk/trunk/test/src/org/apache/river/api/security/URIGrantTest.java?rev=1369328&r1=1369327&r2=1369328&view=diff
==============================================================================
--- river/jtsk/trunk/test/src/org/apache/river/api/security/URIGrantTest.java (original)
+++ river/jtsk/trunk/test/src/org/apache/river/api/security/URIGrantTest.java Sat Aug  4 14:06:20 2012
@@ -56,8 +56,8 @@ public class URIGrantTest {
     @Before
     public void setUp() throws URISyntaxException, MalformedURLException {
         URI [] u = new URI[2];
-        u[0] = new URI("file:///foo/*");
-        u[1] = new URI("file:///C:/foo/*");
+        u[0] = new URI("file:/foo/*");
+        u[1] = new URI("file:/C:/FOO/*");
         instance = new URIGrant(u, new Certificate[0], new Principal[0], new Permission[0]);
         pd = new ProtectionDomain( new CodeSource(new URL("file:/foo/bar"), (Certificate []) null), null);
     }
@@ -77,6 +77,13 @@ public class URIGrantTest {
         Assert.assertTrue(instance.implies(grant,alsoImplied));
         Assert.assertTrue(instance.implies(otherGrant, implied));
         Assert.assertTrue(instance.implies(otherGrant, alsoImplied));
+        grant = new URI("file:/C:/USERS/PETER/DOCUMENTS/NETBEANSPROJECTS/PETERCONCURRENTPOLICY/QA/-");
+        implied = new URI("file:/C:/USERS/PETER/DOCUMENTS/NETBEANSPROJECTS/PETERCONCURRENTPOLICY/QA/LIB/JINIHARNESS.JAR");
+        System.out.println(grant);
+        System.out.println(implied);
+        boolean result = instance.implies(grant, implied);
+        System.out.println(result);
+        Assert.assertTrue(result);
     }
     
     @Test

Modified: river/jtsk/trunk/test/src/org/apache/river/impl/net/UriStringTest.java
URL: http://svn.apache.org/viewvc/river/jtsk/trunk/test/src/org/apache/river/impl/net/UriStringTest.java?rev=1369328&r1=1369327&r2=1369328&view=diff
==============================================================================
--- river/jtsk/trunk/test/src/org/apache/river/impl/net/UriStringTest.java (original)
+++ river/jtsk/trunk/test/src/org/apache/river/impl/net/UriStringTest.java Sat Aug  4 14:06:20 2012
@@ -1,9 +1,24 @@
 /*
- * To change this template, choose Tools | Templates
- * and open the template in the editor.
+ *  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.impl.net;
 
+import java.net.URI;
+import java.net.URISyntaxException;
 import org.junit.After;
 import org.junit.AfterClass;
 import org.junit.Before;
@@ -40,28 +55,52 @@ public class UriStringTest {
      * Test of parse method, of class UriString.
      */
     @Test
-    public void testEscapeIllegalCharacters() {
+    public void testEscapeIllegalCharacters() throws URISyntaxException {
         System.out.println("escapeIllegalCharacters");
         String url = " ";
         String expResult = "%20";
-        String result = UriString.parse(url);
+        String result = UriString.escapeIllegalCharacters(url);
         assertEquals(expResult, result);
     }
     
     @Test
-    public void testWindowsDrives() {
-        System.out.println("windows drive letters");
-        String url = "file:c:/Program Files/java";
-        String expResult = "file:///C:/Program%20Files/java";
-        String result = UriString.parse(url);
+    public void testEscape() throws URISyntaxException {
+        System.out.println("test escape");
+        String url = "file:/c:/Program Files/java";
+        String expResult = "file:/c:/Program%20Files/java";
+        String result = UriString.escapeIllegalCharacters(url);
+        assertEquals(expResult, result);
+        url = "file:/c:/Program Files/java<|>lib";
+        expResult = "file:/c:/Program%20Files/java%3C%7C%3Elib";
+        result = UriString.escapeIllegalCharacters(url);
         assertEquals(expResult, result);
-        url = "file:/c:/Program Files/java lib";
-        expResult = "file:///C:/Program%20Files/java%20lib";
-        result = UriString.parse(url);
+    }
+    
+    @Test
+    public void testNormalise() throws URISyntaxException{
+        System.out.println("normalise test");
+        URI uri = new URI("FILE:/c:/Program%20Files/java");
+        String expResult = "file:/C:/PROGRAM%20FILES/JAVA";
+        String result = UriString.normalise(uri).toString();
         assertEquals(expResult, result);
-        url = "file:///c:/Program Files/java";
-        expResult = "file:///C:/Program%20Files/java";
-        result = UriString.parse(url);
+    }
+            
+    
+    @Test
+    public void testNormalisation() throws URISyntaxException {
+        System.out.println("URI Normalisation Test");
+        URI url = new URI("HTTP://river.apache.ORG/foo%7ebar/file%3clib");
+        URI expResult = new URI("http://river.apache.org/foo~bar/file%3Clib");
+        URI result = UriString.normalisation(url);
+        assertEquals(expResult.toString(), result.toString());
+    }
+    
+    @Test
+    public void testFixWindowsURI() {
+        System.out.println("Test fix Windows file URI string");
+        String uri = "file:C:\\home\\user";
+        String expResult = "file:/C:/home/user";
+        String result = UriString.fixWindowsURI(uri);
         assertEquals(expResult, result);
     }
 }