From 235f8b1cbff8ed13071d5c19c0be492c0b25cb78 Mon Sep 17 00:00:00 2001
From: Sven Gothel
Date: Sat, 17 Mar 2012 21:15:49 +0100
Subject: Add 'asset' URLConnection; IOUtil uses URLConnection / incr.
effeciency; Android ClassLoaderUtil cleanup;
- Add 'asset' URLConnection
- Please read API doc 'PiggybackURLConnection' and 'AssetURLConnection'
- Solves generic resource handling where platform locations may differ,
ie ClassLoader lookup on Android in the 'assets/' subfolder.
- New Android 'AssetDexClassLoader' uses 'assets/' folder for findResource(..)
- aapt.signed (our APK ant task)
- uses 'assets/' folder
- adds the 'assetsdir' attribute allowing to copy other assets into the APK
- IOUtil uses URLConnection / incr. effeciency
- using URLConnection on all getResource(..) since URL
is connected anyways for validation and URLConnection can be used by caller right away
- String getRelativeOf(URL, String) -> URL getRelativeOf(URL, String)
- preserves scheme, authority, etc
- simple parentOf handling, more efficient
- reusing new 'asset' protocol impl.
- Android ClassLoaderUtil cleanup;
- Use createClassLoader(..) impl for build-in static jogamp and user APKs,
which removes code redundancy
Tests: New code path, especially 'assets' are covered by new unit tests, no regressions on Linux.
---
src/java/com/jogamp/common/util/IOUtil.java | 250 ++++++++++++++++++----------
1 file changed, 159 insertions(+), 91 deletions(-)
(limited to 'src/java/com/jogamp/common/util')
diff --git a/src/java/com/jogamp/common/util/IOUtil.java b/src/java/com/jogamp/common/util/IOUtil.java
index a05302a..891453e 100644
--- a/src/java/com/jogamp/common/util/IOUtil.java
+++ b/src/java/com/jogamp/common/util/IOUtil.java
@@ -37,7 +37,6 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.security.AccessControlContext;
-import java.net.JarURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
@@ -48,6 +47,7 @@ import jogamp.common.os.android.StaticContext;
import android.content.Context;
+import com.jogamp.common.net.AssetURLContext;
import com.jogamp.common.nio.Buffers;
import com.jogamp.common.os.AndroidVersion;
import com.jogamp.common.os.MachineDescription;
@@ -68,28 +68,47 @@ public class IOUtil {
*/
/**
- * Copy the specified input stream to the specified output file. The total
+ * Copy the specified URL resource to the specified output file. The total
* number of bytes written is returned. Both streams are closed upon completion.
+ *
+ * @param conn the open URLConnection
+ * @param outFile the destination
+ * @return
+ * @throws IOException
*/
- public static int copyURL2File(URL url, File outFile) throws IOException {
- URLConnection conn = url.openConnection();
- conn.connect();
+ public static int copyURLConn2File(URLConnection conn, File outFile) throws IOException {
+ conn.connect(); // redundant
int totalNumBytes = 0;
InputStream in = new BufferedInputStream(conn.getInputStream());
try {
- OutputStream out = new BufferedOutputStream(new FileOutputStream(outFile));
- try {
- totalNumBytes = copyStream2Stream(in, out, conn.getContentLength());
- } finally {
- out.close();
- }
+ totalNumBytes = copyStream2File(in, outFile, conn.getContentLength());
} finally {
in.close();
}
return totalNumBytes;
}
+ /**
+ * Copy the specified input stream to the specified output file. The total
+ * number of bytes written is returned. Both streams are closed upon completion.
+ *
+ * @param in the source
+ * @param outFile the destination
+ * @param totalNumBytes informal number of expected bytes, maybe used for user feedback while processing. -1 if unknown
+ * @return
+ * @throws IOException
+ */
+ public static int copyStream2File(InputStream in, File outFile, int totalNumBytes) throws IOException {
+ OutputStream out = new BufferedOutputStream(new FileOutputStream(outFile));
+ try {
+ totalNumBytes = copyStream2Stream(in, out, totalNumBytes);
+ } finally {
+ out.close();
+ }
+ return totalNumBytes;
+ }
+
/**
* Copy the specified input stream to the specified output stream. The total
* number of bytes written is returned.
@@ -301,103 +320,88 @@ public class IOUtil {
*/
/**
- * Locating a resource using 'getResource(String path, ClassLoader cl)',
- * with the
+ * Locating a resource using {@link #getResource(String, ClassLoader)}:
*
- * - context's package name-path plus the resourcePath (incl. JAR/Applets)
- * - context's ClassLoader and the resourcePath as is (filesystem)
+ * - relative:
context
's package name-path plus resourcePath
via context
's ClassLoader. This allows locations relative to JAR- and other URLs.
+ * - absolute:
context
's ClassLoader and the resourcePath
as is (filesystem)
*
*
+ *
+ * Returns the resolved and open URLConnection or null if not found.
+ *
+ *
* @see #getResource(String, ClassLoader)
+ * @see ClassLoader#getResource(String)
+ * @see ClassLoader#getSystemResource(String)
*/
- public static URL getResource(Class> context, String resourcePath) {
+ public static URLConnection getResource(Class> context, String resourcePath) {
if(null == resourcePath) {
return null;
}
- ClassLoader contextCL = (null!=context)?context.getClassLoader():null;
- URL url = null;
+ ClassLoader contextCL = (null!=context)?context.getClassLoader():IOUtil.class.getClassLoader();
+ URLConnection conn = null;
if(null != context) {
// scoping the path within the class's package
String className = context.getName().replace('.', '/');
int lastSlash = className.lastIndexOf('/');
if (lastSlash >= 0) {
String tmpPath = className.substring(0, lastSlash + 1) + resourcePath;
- url = getResource(tmpPath, contextCL);
+ conn = getResource(tmpPath, contextCL);
}
if(DEBUG) {
- System.err.println("IOUtil: found <"+resourcePath+"> within class package: "+(null!=url));
+ System.err.println("IOUtil: found <"+resourcePath+"> within class package: "+(null!=conn));
}
} else if(DEBUG) {
System.err.println("IOUtil: null context");
}
- if(null == url) {
- url = getResource(resourcePath, contextCL);
+ if(null == conn) {
+ conn = getResource(resourcePath, contextCL);
if(DEBUG) {
- System.err.println("IOUtil: found <"+resourcePath+"> by classloader: "+(null!=url));
+ System.err.println("IOUtil: found <"+resourcePath+"> by classloader: "+(null!=conn));
}
}
- return url;
+ return conn;
}
/**
- * Locating a resource using the ClassLoader's facility if not null,
- * the absolute URL and absolute file.
+ * Locating a resource using the ClassLoader's facilities.
+ *
+ * Returns the resolved and connected URLConnection or null if not found.
+ *
*
* @see ClassLoader#getResource(String)
* @see ClassLoader#getSystemResource(String)
* @see URL#URL(String)
* @see File#File(String)
*/
- public static URL getResource(String resourcePath, ClassLoader cl) {
+ public static URLConnection getResource(String resourcePath, ClassLoader cl) {
if(null == resourcePath) {
return null;
}
if(DEBUG) {
System.err.println("IOUtil: locating <"+resourcePath+">, has cl: "+(null!=cl));
}
- URL url = null;
- if (cl != null) {
- url = cl.getResource(resourcePath);
- if(!urlExists(url, "cl.getResource()")) {
- url = null;
- }
- }
- if(null == url) {
- url = ClassLoader.getSystemResource(resourcePath);
- if(!urlExists(url, "cl.getSystemResource()")) {
- url = null;
- }
- }
- if(null == url) {
+ if(resourcePath.startsWith(AssetURLContext.asset_protocol_prefix)) {
try {
- url = new URL(resourcePath);
- if(!urlExists(url, "new URL()")) {
- url = null;
- }
- } catch (Throwable e) {
+ return AssetURLContext.createURL(resourcePath, cl).openConnection();
+ } catch (IOException ioe) {
if(DEBUG) {
System.err.println("IOUtil: Catched Exception:");
- e.printStackTrace();
- }
+ ioe.printStackTrace();
+ }
+ return null;
}
- }
- if(null == url) {
+ } else {
try {
- File file = new File(resourcePath);
- if(file.exists()) {
- url = toURLSimple(file);
- }
- } catch (Throwable e) {
+ return AssetURLContext.resolve(resourcePath, cl);
+ } catch (IOException ioe) {
if(DEBUG) {
System.err.println("IOUtil: Catched Exception:");
- e.printStackTrace();
+ ioe.printStackTrace();
}
}
- if(DEBUG) {
- System.err.println("IOUtil: file.exists("+resourcePath+") - "+(null!=url));
- }
}
- return url;
+ return null;
}
/**
@@ -411,11 +415,11 @@ public class IOUtil {
return null;
}
- while (baseLocation != null && relativeFile.startsWith("../")) {
- baseLocation = baseLocation.getParentFile();
- relativeFile = relativeFile.substring(3);
- }
if (baseLocation != null) {
+ while (relativeFile.startsWith("../")) {
+ baseLocation = baseLocation.getParentFile();
+ relativeFile = relativeFile.substring(3);
+ }
final File file = new File(baseLocation, relativeFile);
// Handle things on Windows
return slashify(file.getPath(), false, false);
@@ -424,55 +428,119 @@ public class IOUtil {
}
/**
- * Generates a path for the 'relativeFile' relative to the 'baseLocation'.
+ * @param path assuming a slashified path beginning with "/" as it's root directory, either denotes a file or directory.
+ * @return parent of path
+ * @throws MalformedURLException if path is empty or has parent directory available
+ */
+ public static String getParentOf(String path) throws MalformedURLException {
+ final int pl = null!=path ? path.length() : 0;
+ if(pl == 0) {
+ throw new MalformedURLException("path is empty <"+path+">");
+ }
+
+ final int e = path.lastIndexOf("/");
+ if( e < 0 ) {
+ throw new MalformedURLException("path contains no '/' <"+path+">");
+ }
+ if( e == 0 ) {
+ // path is root directory
+ throw new MalformedURLException("path has no parents <"+path+">");
+ }
+ if( e < pl - 1 ) {
+ // path is file, return it's parent directory
+ return path.substring(0, e+1);
+ }
+ final int j = path.lastIndexOf("!") + 1; // '!' Separates JARFile entry -> local start of path
+ // path is a directory ..
+ final int p = path.lastIndexOf("/", e-1);
+ if( p >= j) {
+ return path.substring(0, p+1);
+ }
+ throw new MalformedURLException("parent of path contains no '/' <"+path+">");
+ }
+
+ /**
+ * Generates a path for the 'relativeFile' relative to the 'baseLocation',
+ * hence the result is a absolute location.
*
- * @param baseLocation denotes a URL to a file
+ * @param baseLocation denotes a URL to a directory if ending w/ '/', otherwise we assume a file
* @param relativeFile denotes a relative file to the baseLocation's parent directory
+ * @throws MalformedURLException
*/
- public static String getRelativeOf(URL baseLocation, String relativeFile) {
- String urlPath = baseLocation.getPath();
+ public static URL getRelativeOf(URL baseLocation, String relativeFile) throws MalformedURLException {
+ final String scheme = baseLocation.getProtocol();
+ final String auth = baseLocation.getAuthority();
+ String path = baseLocation.getPath();
+ String query = baseLocation.getQuery();
+ String fragment = baseLocation.getRef();
- if ( baseLocation.toString().startsWith("jar") ) {
- JarURLConnection jarConnection;
- try {
- jarConnection = (JarURLConnection) baseLocation.openConnection();
- urlPath = jarConnection.getEntryName();
- } catch (IOException e) {
- e.printStackTrace();
- return null;
- }
+ if(!path.endsWith("/")) {
+ path = getParentOf(path);
}
-
- // Try relative path first
- return getRelativeOf(new File(urlPath).getParentFile(), relativeFile);
+ while (relativeFile.startsWith("../")) {
+ path = getParentOf(path);
+ relativeFile = relativeFile.substring(3);
+ }
+ return compose(scheme, auth, path, relativeFile, query, fragment);
}
+ public static URL compose(String scheme, String auth, String path1, String path2, String query, String fragment) throws MalformedURLException {
+ StringBuffer sb = new StringBuffer();
+ if(null!=scheme) {
+ sb.append(scheme);
+ sb.append(":");
+ }
+ if(null!=auth) {
+ sb.append("//");
+ sb.append(auth);
+ }
+ if(null!=path1) {
+ sb.append(path1);
+ }
+ if(null!=path2) {
+ sb.append(path2);
+ }
+ if(null!=query) {
+ sb.append("?");
+ sb.append(query);
+ }
+ if(null!=fragment) {
+ sb.append("#");
+ sb.append(fragment);
+ }
+ return new URL(sb.toString());
+ }
+
/**
- * Returns true, if the URL exists and a connection could be opened.
+ * Returns the connected URLConnection, or null if not url is not available
*/
- public static boolean urlExists(URL url) {
- return urlExists(url, ".");
+ public static URLConnection openURL(URL url) {
+ return openURL(url, ".");
}
- public static boolean urlExists(URL url, String dbgmsg) {
- boolean v = false;
+ /**
+ * Returns the connected URLConnection, or null if not url is not available
+ */
+ public static URLConnection openURL(URL url, String dbgmsg) {
if(null!=url) {
try {
- url.openConnection();
- v = true;
+ final URLConnection c = url.openConnection();
+ c.connect(); // redundant
if(DEBUG) {
System.err.println("IOUtil: urlExists("+url+") ["+dbgmsg+"] - true");
}
+ return c;
} catch (IOException ioe) {
if(DEBUG) {
- System.err.println("IOUtil: urlExists("+url+") ["+dbgmsg+"] - false: "+ioe.getMessage());
+ System.err.println("IOUtil: urlExists("+url+") ["+dbgmsg+"] - false - "+ioe.getClass().getSimpleName()+": "+ioe.getMessage());
+ ioe.printStackTrace();
}
}
} else if(DEBUG) {
System.err.println("IOUtil: no url - urlExists(null) ["+dbgmsg+"]");
}
- return v;
+ return null;
}
/**
@@ -507,7 +575,7 @@ public class IOUtil {
* On Android a temp
folder relative to the applications local folder
* (see {@link Context#getDir(String, int)}) is returned, if
* the Android application/activity has registered it's Application Context
- * via {@link StaticContext#setContext(Context)}.
+ * via {@link StaticContext#init(Context, ClassLoader)}.
* This allows using the temp folder w/o the need for sdcard
* access, which would be the java.io.tempdir
location on Android!
*
@@ -517,7 +585,7 @@ public class IOUtil {
* @throws RuntimeException is the property java.io.tmpdir
or the resulting temp directory is invalid
*
* @see PropertyAccess#getProperty(String, boolean, java.security.AccessControlContext)
- * @see StaticContext#setContext(Context)
+ * @see StaticContext#init(Context, ClassLoader)
* @see Context#getDir(String, int)
*/
public static File getTempRoot(AccessControlContext acc)
--
cgit v1.2.3