From 4376174ad35fdaf76f59430328582e913f468674 Mon Sep 17 00:00:00 2001
From: Sven Gothel <sgothel@jausoft.com>
Date: Wed, 19 Jun 2013 04:44:14 +0200
Subject: Fix Bug 757: Regression of URL to URI conversion (Encoded path not
 compatible w/ file scheme.
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Regression of (Bug 683, Commit b98825eb7cfb61aead4a7dff57471cd2d2c26823).

The URI encoded path cannot be read by File I/O (if file scheme), since the latter
requests an UTF8/16 name, not an URI encoded name (i.e. %20 for space).

The encoded URL is produced if calling 'uri.toURL()' and hence
the new 'IOUtil.toURL(URI)' provides a custom conversion recovering the UTF name via 'new File(uri).getPath()'.

Tested w/
  - synthetic URI/URL coposition (unit test)
  - manual w/ moving 'build' to 'build öä lala' for gluegen, joal and jogl.

+++

Misc.:

- 'URI JarUtil.getURIDirname(URI)'  -> 'URI IOUtil.getDirname(URI)'

++
---
 .../com/jogamp/common/jvm/JNILibLoaderBase.java    |   3 +-
 src/java/com/jogamp/common/os/Platform.java        |   3 +-
 src/java/com/jogamp/common/util/IOUtil.java        | 109 +++++++++++++++++++--
 src/java/com/jogamp/common/util/JarUtil.java       |  87 ++++++++--------
 .../com/jogamp/common/util/cache/TempJarCache.java |  20 +++-
 5 files changed, 157 insertions(+), 65 deletions(-)

(limited to 'src/java/com')

diff --git a/src/java/com/jogamp/common/jvm/JNILibLoaderBase.java b/src/java/com/jogamp/common/jvm/JNILibLoaderBase.java
index 456c35a..2023162 100644
--- a/src/java/com/jogamp/common/jvm/JNILibLoaderBase.java
+++ b/src/java/com/jogamp/common/jvm/JNILibLoaderBase.java
@@ -52,6 +52,7 @@ import java.util.Iterator;
 import java.util.List;
 
 import com.jogamp.common.os.NativeLibrary;
+import com.jogamp.common.util.IOUtil;
 import com.jogamp.common.util.JarUtil;
 import com.jogamp.common.util.PropertyAccess;
 import com.jogamp.common.util.cache.TempJarCache;
@@ -155,7 +156,7 @@ public class JNILibLoaderBase {
     if(TempJarCache.isInitialized()) {        
         final String nativeJarName = nativeJarBasename+"-natives-"+PlatformPropsImpl.os_and_arch+".jar";
         msg.append(nativeJarName);
-        final URI jarUriRoot = JarUtil.getURIDirname( JarUtil.getJarSubURI( classJarURI ) );
+        final URI jarUriRoot = IOUtil.getDirname( JarUtil.getJarSubURI( classJarURI ) );
         msg.append(" + ").append(jarUriRoot);
         final URI nativeJarURI = JarUtil.getJarFileURI(jarUriRoot, nativeJarName);
         msg.append(" -> ").append(nativeJarURI);
diff --git a/src/java/com/jogamp/common/os/Platform.java b/src/java/com/jogamp/common/os/Platform.java
index 0ae3cbb..9971cf4 100644
--- a/src/java/com/jogamp/common/os/Platform.java
+++ b/src/java/com/jogamp/common/os/Platform.java
@@ -33,6 +33,7 @@ import java.security.AccessController;
 import java.security.PrivilegedAction;
 import java.util.concurrent.TimeUnit;
 
+import com.jogamp.common.util.IOUtil;
 import com.jogamp.common.util.JarUtil;
 import com.jogamp.common.util.ReflectionUtil;
 import com.jogamp.common.util.VersionNumber;
@@ -198,7 +199,7 @@ public class Platform extends PlatformPropsImpl {
                         final String jarName = JarUtil.getJarBasename( platformClassJarURI );
                         final String nativeJarBasename = jarName.substring(0, jarName.indexOf(".jar")); // ".jar" already validated w/ JarUtil.getJarBasename(..)
                         nativeJarName = nativeJarBasename+"-natives-"+PlatformPropsImpl.os_and_arch+".jar";                    
-                        jarUriRoot = JarUtil.getURIDirname( JarUtil.getJarSubURI( platformClassJarURI ) );
+                        jarUriRoot = IOUtil.getDirname( JarUtil.getJarSubURI( platformClassJarURI ) );
                         nativeJarURI = JarUtil.getJarFileURI(jarUriRoot, nativeJarName);
                         TempJarCache.bootstrapNativeLib(Platform.class, libBaseName, nativeJarURI);
                     } catch (Exception e0) {
diff --git a/src/java/com/jogamp/common/util/IOUtil.java b/src/java/com/jogamp/common/util/IOUtil.java
index 2f0c77f..2c3c756 100644
--- a/src/java/com/jogamp/common/util/IOUtil.java
+++ b/src/java/com/jogamp/common/util/IOUtil.java
@@ -56,14 +56,22 @@ import com.jogamp.common.os.Platform;
 public class IOUtil {
     public static final boolean DEBUG = Debug.debug("IOUtil");
     
-    public static final String JAR_SCHEME = "jar";
+    /** {@value} */
+    public static final String SCHEME_SEPARATOR = ":";
+    /** {@value} */
     public static final String FILE_SCHEME = "file";
+    /** {@value} */
     public static final String HTTP_SCHEME = "http";
+    /** {@value} */
     public static final String HTTPS_SCHEME = "https";
+    /** {@value} */
+    public static final String JAR_SCHEME = "jar";
+    /** A JAR subprotocol is separeted from the JAR entry w/ this separator {@value}. Even if no class is specified '!/' must follow!. */
+    public static final String JAR_SCHEME_SEPARATOR = "!";
         
-    /** Std. temporary directory property key <code>java.io.tmpdir</code> */
-    public static final String java_io_tmpdir_propkey = "java.io.tmpdir";
-    public static final String user_home_propkey = "user.home";
+    /** Std. temporary directory property key <code>java.io.tmpdir</code>. */
+    private static final String java_io_tmpdir_propkey = "java.io.tmpdir";    
+    private static final String user_home_propkey = "user.home";
     private static final String XDG_CACHE_HOME_envkey = "XDG_CACHE_HOME";
 
     /** Subdirectory within platform's temporary root directory where all JogAmp related temp files are being stored: {@code jogamp} */ 
@@ -300,7 +308,7 @@ public class IOUtil {
     public static URI toURISimple(String protocol, String file, boolean isDirectory) throws URISyntaxException {
         return new URI(protocol, null, slashify(file, true, isDirectory), null);        
     }
-    
+
     /**
      * Returns the lowercase suffix of the given file name (the text
      * after the last '.' in the file name). Returns null if the file
@@ -333,7 +341,14 @@ public class IOUtil {
         }
         return toLowerCase(filename.substring(lastDot + 1));
     }
+    private static String toLowerCase(String arg) {
+        if (arg == null) {
+            return null;
+        }
 
+        return arg.toLowerCase();
+    }
+    
     /***
      * @param file
      * @param allowOverwrite
@@ -402,14 +417,88 @@ public class IOUtil {
         return fname;
     }
     
-    private static String toLowerCase(String arg) {
-        if (arg == null) {
-            return null;
+    /**
+     * The URI's <code><i>protocol</i>:/some/path/gluegen-rt.jar</code>
+     * parent dirname URI <code><i>protocol</i>:/some/path/</code> will be returned.
+     * <p>
+     * <i>protocol</i> may be "file", "http", etc..
+     * </p>
+     * 
+     * @param uri "<i>protocol</i>:/some/path/gluegen-rt.jar"
+     * @return "<i>protocol</i>:/some/path/"
+     * @throws IllegalArgumentException if the URI doesn't match the expected formatting, or is null
+     * @throws URISyntaxException 
+     */
+    public static URI getDirname(URI uri) throws IllegalArgumentException, URISyntaxException {
+        if(null == uri) {
+            throw new IllegalArgumentException("URI is null");            
         }
-
-        return arg.toLowerCase();
+        String uriS = uri.toString();
+        if( DEBUG ) {
+            System.out.println("getURIDirname "+uri+", extForm: "+uriS);
+        }
+        // from 
+        //   file:/some/path/gluegen-rt.jar  _or_ rsrc:gluegen-rt.jar
+        // to
+        //   file:/some/path/                _or_ rsrc:
+        int idx = uriS.lastIndexOf('/');
+        if(0 > idx) {
+            // no abs-path, check for protocol terminator ':'
+            idx = uriS.lastIndexOf(':');
+            if(0 > idx) {
+                throw new IllegalArgumentException("URI does not contain protocol terminator ':', in <"+uri+">");
+            }
+        }
+        uriS = uriS.substring(0, idx+1); // exclude jar name, include terminal '/' or ':'        
+        
+        if( DEBUG ) {
+            System.out.println("getJarURIDirname res: "+uriS);
+        }        
+        return new URI(uriS);
     }
     
+    /** 
+     * Converts an {@link URI} to an {@link URL} while using a non encoded path
+     * for <i>file scheme</i>, i.e. <code>file:/</code>.
+     * Otherwise the default {@link URL} translation {@link URI#toURL()} is being used.
+     * <p>
+     * The folloing cases are considered:
+     * <ul>
+     *   <li><i>file schema</i> is converted via <code>new File(uri).getPath()</code>.</li>
+     *   <li><i>jar scheme</i>
+     *   <ul>
+     *     <li>subprotocol is being converted as above, if <i>file scheme</i>.</li>
+     *     <li>JAR entry is not converted but preserved.</li>
+     *   </ul></li>
+     * </ul>
+     * </p>
+     * @param uri
+     * @return
+     * @throws IOException
+     * @throws IllegalArgumentException
+     * @throws URISyntaxException
+     */
+    public static URL toURL(URI uri) throws IOException, IllegalArgumentException, URISyntaxException {
+        final URL url;
+        final String uriSchema = uri.getScheme();
+        final boolean isJAR = IOUtil.JAR_SCHEME.equals(uriSchema);
+        final URI specificURI = isJAR ? JarUtil.getJarSubURI(uri) : uri; 
+        if( IOUtil.FILE_SCHEME.equals( specificURI.getScheme() ) ) {
+            final File f = new File(specificURI);
+            if( specificURI == uri ) {
+                url = new URL(IOUtil.FILE_SCHEME+IOUtil.SCHEME_SEPARATOR+f.getPath());
+                // url = f.toURI().toURL(); // Doesn't work, since it uses encoded path!
+            } else {
+                final String post = isJAR ? IOUtil.JAR_SCHEME_SEPARATOR + JarUtil.getJarEntry(uri) : "";
+                final String urlS = uriSchema+IOUtil.SCHEME_SEPARATOR+IOUtil.FILE_SCHEME+IOUtil.SCHEME_SEPARATOR+f.getPath()+post;
+                url = new URL(urlS);
+            }
+        } else {
+            url = uri.toURL();
+        }
+        return url;
+    }
+        
     /***
      * 
      * RESOURCE LOCATION STUFF
diff --git a/src/java/com/jogamp/common/util/JarUtil.java b/src/java/com/jogamp/common/util/JarUtil.java
index 4045cfa..9ea6f70 100644
--- a/src/java/com/jogamp/common/util/JarUtil.java
+++ b/src/java/com/jogamp/common/util/JarUtil.java
@@ -142,10 +142,10 @@ public class JarUtil {
             final URL url = IOUtil.getClassURL(clazzBinName, cl);
             final String scheme = url.getProtocol();
             if( null != resolver &&
-                !scheme.startsWith( IOUtil.JAR_SCHEME ) &&
-                !scheme.startsWith( IOUtil.FILE_SCHEME ) &&
-                !scheme.startsWith( IOUtil.HTTP_SCHEME ) &&
-                !scheme.startsWith( IOUtil.HTTPS_SCHEME ) ) 
+                !scheme.equals( IOUtil.JAR_SCHEME ) &&
+                !scheme.equals( IOUtil.FILE_SCHEME ) &&
+                !scheme.equals( IOUtil.HTTP_SCHEME ) &&
+                !scheme.equals( IOUtil.HTTPS_SCHEME ) ) 
             {
                 final URL _url = resolver.resolve( url );
                 uri = _url.toURI();
@@ -252,7 +252,6 @@ public class JarUtil {
      * 
      * @param classJarURI as retrieved w/ {@link #getJarURI(String, ClassLoader) getJarURI("com.jogamp.common.GlueGenVersion", cl).toURI()}, 
      *                    i.e. <code>jar:<i>sub_protocol</i>:/some/path/gluegen-rt.jar!/com/jogamp/common/GlueGenVersion.class</code>
-     * @param cl
      * @return <code><i>sub_protocol</i>:/some/path/gluegen-rt.jar</code>
      * @throws IllegalArgumentException if the URI doesn't match the expected formatting or is null 
      * @throws URISyntaxException if the URI could not be translated into a RFC 2396 URI
@@ -286,6 +285,36 @@ public class JarUtil {
         }
         return new URI(uriS);
     }
+    
+    /**
+     * The Class's Jar URI <code>jar:<i>sub_protocol</i>:/some/path/gluegen-rt.jar!/com/jogamp/common/GlueGenVersion.class</code>
+     * Jar file's entry <code>/com/jogamp/common/GlueGenVersion.class</code> will be returned.
+     * 
+     * @param classJarURI as retrieved w/ {@link #getJarURI(String, ClassLoader) getJarURI("com.jogamp.common.GlueGenVersion", cl).toURI()}, 
+     *                    i.e. <code>jar:<i>sub_protocol</i>:/some/path/gluegen-rt.jar!/com/jogamp/common/GlueGenVersion.class</code>
+     * @return <code>/com/jogamp/common/GlueGenVersion.class</code>
+     * @see {@link IOUtil#getClassURL(String, ClassLoader)}
+     */
+    public static String getJarEntry(URI classJarURI) {
+        if(null == classJarURI) {
+            throw new IllegalArgumentException("URI is null");            
+        }
+        if( !classJarURI.getScheme().equals(IOUtil.JAR_SCHEME) ) {
+            throw new IllegalArgumentException("URI is not a using scheme "+IOUtil.JAR_SCHEME+": <"+classJarURI+">");
+        }
+        String uriS = classJarURI.getRawSchemeSpecificPart();
+        
+        // from 
+        //   file:/some/path/gluegen-rt.jar!/com/jogamp/common/GlueGenVersion.class
+        // to
+        //   file:/some/path/gluegen-rt.jar
+        int idx = uriS.lastIndexOf('!');
+        if (0 <= idx) {
+            return uriS.substring(idx+1); // right of '!'
+        } else {
+            throw new IllegalArgumentException("JAR URI does not contain jar uri terminator '!', uri <"+classJarURI+">");
+        }
+    }
 
     /**
      * The Class's <code>com.jogamp.common.GlueGenVersion</code> 
@@ -334,46 +363,6 @@ public class JarUtil {
         return uri;
     }
 
-    /**
-     * The URI's <code><i>protocol</i>:/some/path/gluegen-rt.jar</code>
-     * parent dirname URI <code><i>protocol</i>:/some/path/</code> will be returned.
-     * <p>
-     * <i>protocol</i> may be "file", "http", etc..
-     * </p>
-     * 
-     * @param aURI "<i>protocol</i>:/some/path/gluegen-rt.jar"
-     * @return "<i>protocol</i>:/some/path/"
-     * @throws IllegalArgumentException if the URI doesn't match the expected formatting, or is null
-     * @throws URISyntaxException 
-     */
-    public static URI getURIDirname(URI aURI) throws IllegalArgumentException, URISyntaxException {
-        if(null == aURI) {
-            throw new IllegalArgumentException("URI is null");            
-        }
-        String uriS = aURI.toString();
-        if(DEBUG) {
-            System.out.println("getURIDirname "+aURI+", extForm: "+uriS);
-        }
-        // from 
-        //   file:/some/path/gluegen-rt.jar  _or_ rsrc:gluegen-rt.jar
-        // to
-        //   file:/some/path/                _or_ rsrc:
-        int idx = uriS.lastIndexOf('/');
-        if(0 > idx) {
-            // no abs-path, check for protocol terminator ':'
-            idx = uriS.lastIndexOf(':');
-            if(0 > idx) {
-                throw new IllegalArgumentException("URI does not contain protocol terminator ':', in <"+aURI+">");
-            }
-        }
-        uriS = uriS.substring(0, idx+1); // exclude jar name, include terminal '/' or ':'        
-        
-        if(DEBUG) {
-            System.out.println("getJarURIDirname res: "+uriS);
-        }        
-        return new URI(uriS);
-    }
-            
     /**
      * @param baseUri file:/some/path/
      * @param jarFileName gluegen-rt.jar
@@ -433,15 +422,17 @@ public class JarUtil {
      * @return JarFile as named by URI within the given ClassLoader
      * @throws IllegalArgumentException null arguments
      * @throws IOException if the Jar file could not been found 
+     * @throws URISyntaxException 
      */
-    public static JarFile getJarFile(URI jarFileURI) throws IOException, IllegalArgumentException {
+    public static JarFile getJarFile(URI jarFileURI) throws IOException, IllegalArgumentException, URISyntaxException {
         if(null == jarFileURI) {
             throw new IllegalArgumentException("null jarFileURI");
         }
         if(DEBUG) {
-            System.out.println("getJarFile: "+jarFileURI);
+            System.out.println("getJarFile: "+jarFileURI.toString());
         }
-        final URL jarFileURL = jarFileURI.toURL();
+        final URL jarFileURL = IOUtil.toURL(jarFileURI);
+        // final URL jarFileURL = jarFileURI.toURL(); // doesn't work due to encoded path even w/ file schema! 
         final URLConnection urlc = jarFileURL.openConnection();
         if(urlc instanceof JarURLConnection) {
             JarURLConnection jarConnection = (JarURLConnection)jarFileURL.openConnection();
diff --git a/src/java/com/jogamp/common/util/cache/TempJarCache.java b/src/java/com/jogamp/common/util/cache/TempJarCache.java
index b17dd52..4c505f9 100644
--- a/src/java/com/jogamp/common/util/cache/TempJarCache.java
+++ b/src/java/com/jogamp/common/util/cache/TempJarCache.java
@@ -206,8 +206,10 @@ public class TempJarCache {
      * @param jarURI
      * @throws IOException if the <code>jarURI</code> could not be loaded or a previous load attempt failed
      * @throws SecurityException
+     * @throws URISyntaxException 
+     * @throws IllegalArgumentException 
      */
-    public synchronized static final void addNativeLibs(Class<?> certClass, URI jarURI) throws IOException, SecurityException {        
+    public synchronized static final void addNativeLibs(Class<?> certClass, URI jarURI) throws IOException, SecurityException, IllegalArgumentException, URISyntaxException {        
         final LoadState nativeLibJarsLS = nativeLibJars.get(jarURI);
         if( !testLoadState(nativeLibJarsLS, LoadState.LOOKED_UP) ) { 
             nativeLibJars.put(jarURI, LoadState.LOOKED_UP);
@@ -233,8 +235,10 @@ public class TempJarCache {
      * @param jarURI
      * @throws IOException if the <code>jarURI</code> could not be loaded or a previous load attempt failed
      * @throws SecurityException
+     * @throws URISyntaxException 
+     * @throws IllegalArgumentException 
      */
-    public synchronized static final void addClasses(Class<?> certClass, URI jarURI) throws IOException, SecurityException {
+    public synchronized static final void addClasses(Class<?> certClass, URI jarURI) throws IOException, SecurityException, IllegalArgumentException, URISyntaxException {
         final LoadState classFileJarsLS = classFileJars.get(jarURI);
         if( !testLoadState(classFileJarsLS, LoadState.LOOKED_UP) ) { 
             classFileJars.put(jarURI, LoadState.LOOKED_UP);
@@ -259,8 +263,10 @@ public class TempJarCache {
      * @return
      * @throws IOException if the <code>jarURI</code> could not be loaded or a previous load attempt failed
      * @throws SecurityException
+     * @throws URISyntaxException 
+     * @throws IllegalArgumentException 
      */
-    public synchronized static final void addResources(Class<?> certClass, URI jarURI) throws IOException, SecurityException {        
+    public synchronized static final void addResources(Class<?> certClass, URI jarURI) throws IOException, SecurityException, IllegalArgumentException, URISyntaxException {        
         final LoadState resourceFileJarsLS = resourceFileJars.get(jarURI);
         if( !testLoadState(resourceFileJarsLS, LoadState.LOOKED_UP) ) { 
             resourceFileJars.put(jarURI, LoadState.LOOKED_UP);
@@ -288,8 +294,10 @@ public class TempJarCache {
      * @param jarURI
      * @throws IOException if the <code>jarURI</code> could not be loaded or a previous load attempt failed
      * @throws SecurityException
+     * @throws URISyntaxException 
+     * @throws IllegalArgumentException 
      */
-    public synchronized static final void addAll(Class<?> certClass, URI jarURI) throws IOException, SecurityException {
+    public synchronized static final void addAll(Class<?> certClass, URI jarURI) throws IOException, SecurityException, IllegalArgumentException, URISyntaxException {
         checkInitialized();
         if(null == jarURI) {
             throw new IllegalArgumentException("jarURI is null");
@@ -400,9 +408,11 @@ public class TempJarCache {
      *  
      * @throws IOException
      * @throws SecurityException
+     * @throws URISyntaxException 
+     * @throws IllegalArgumentException 
      */
     public synchronized static final void bootstrapNativeLib(Class<?> certClass, String libBaseName, URI jarURI) 
-            throws IOException, SecurityException {
+            throws IOException, SecurityException, IllegalArgumentException, URISyntaxException {
         checkInitialized();
         boolean ok = false;
         int countEntries = 0;
-- 
cgit v1.2.3