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. --- .../com/jogamp/common/net/AssetURLContext.java | 212 +++++++++++++++++++++ 1 file changed, 212 insertions(+) create mode 100644 src/java/com/jogamp/common/net/AssetURLContext.java (limited to 'src/java/com/jogamp/common/net/AssetURLContext.java') diff --git a/src/java/com/jogamp/common/net/AssetURLContext.java b/src/java/com/jogamp/common/net/AssetURLContext.java new file mode 100644 index 0000000..00c7df7 --- /dev/null +++ b/src/java/com/jogamp/common/net/AssetURLContext.java @@ -0,0 +1,212 @@ +package com.jogamp.common.net; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLConnection; +import java.net.URLStreamHandler; + +import jogamp.common.Debug; + +import com.jogamp.common.os.AndroidVersion; +import com.jogamp.common.util.IOUtil; + +/** + * See {@link PiggybackURLConnection} for description and examples. + */ +public abstract class AssetURLContext implements PiggybackURLContext { + private static final boolean DEBUG = Debug.isPropertyDefined("jogamp.debug.IOUtil", true); + + /** The asset URL protocol name asset */ + public static final String asset_protocol = "asset"; + + /** The asset URL protocol prefix asset: */ + public static final String asset_protocol_prefix = "asset:"; + + /** + * The optional asset folder name with ending slash assets/. + *

+ * Note that the asset folder is not used on all platforms using the asset protocol + * and you should not rely on it, use {@link AssetURLConnection#getEntryName()}. + *

+ **/ + public static final String assets_folder = "assets/"; + + public static AssetURLContext create(final ClassLoader cl) { + return new AssetURLContext() { + @Override + public ClassLoader getClassLoader() { + return cl; + } + }; + } + + public static AssetURLStreamHandler createHandler(final ClassLoader cl) { + return new AssetURLStreamHandler(create(cl)); + } + + /** + * Create an asset URL, suitable even w/o the registered asset URLStreamHandler. + *

+ * This is equivalent with: + *

+     *   return new URL(null, path.startsWith("asset:") ? path : "asset:" + path, new AssetURLStreamHandler(cl));
+     * 
+ *

+ * @param path resource path, with or w/o asset: prefix + * @param cl the ClassLoader used to resolve the location, see {@link #getClassLoader()}. + * @return + * @throws MalformedURLException + */ + public static URL createURL(String path, ClassLoader cl) throws MalformedURLException { + return new URL(null, path.startsWith(asset_protocol_prefix) ? path : asset_protocol_prefix + path, createHandler(cl)); + } + + /** + * Create an asset URL, suitable only with the registered asset URLStreamHandler. + *

+ * This is equivalent with: + *

+     *   return new URL(path.startsWith("asset:") ? path : "asset:" + path);
+     * 
+ *

+ * @param path resource path, with or w/o asset: prefix + * @return + * @throws MalformedURLException + */ + public static URL createURL(String path) throws MalformedURLException { + return new URL(path.startsWith(asset_protocol_prefix) ? path : asset_protocol_prefix + path); + } + + /** + * Returns the asset handler previously set via {@link #registerHandler(ClassLoader)}, + * or null if none was set. + */ + public static URLStreamHandler getRegisteredHandler() { + final GenericURLStreamHandlerFactory f = GenericURLStreamHandlerFactory.register(); + return ( null != f ) ? f.getHandler(asset_protocol) : null; + } + + /** + * Registers the generic URLStreamHandlerFactory via {@link GenericURLStreamHandlerFactory#register()} + * and if successful sets the asset handler for the given ClassLoader cl. + * + * @return true if successful, otherwise false + */ + public static boolean registerHandler(ClassLoader cl) { + final GenericURLStreamHandlerFactory f = GenericURLStreamHandlerFactory.register(); + if( null != f ) { + f.setHandler(asset_protocol, createHandler(cl)); + return true; + } else { + return false; + } + } + + /** + * Returns an asset aware ClassLoader. + *

+ * The ClassLoader is required to find the asset resource + * via it's URL findResource(String) implementation. + *

+ *

+ * It's URL findResource(String) implementation shall return either + * an asset URL asset:sub-protocol or just the sub-protocol URL. + *

+ *

+ * For example, on Android, we redirect all path request to assets/path. + *

+ */ + public abstract ClassLoader getClassLoader(); + + @Override + public String getImplementedProtocol() { + return asset_protocol; + } + + /** + * {@inheritDoc} + *

+ * This implementation attempts to resolve path in the following order: + *

    + *
  1. as a valid URL: new URL(path), use sub-protocol if asset URL
  2. + *
  3. via ClassLoader: {@link #getClassLoader()}.{@link ClassLoader#getResource(String) getResource(path)}, use sub-protocol if asset URL
  4. + *
  5. as a File: new File(path).toURI().toURL() + *
+ *

+ *

+ * In case of using the ClassLoader (2) and if running on Android, + * the {@link #assets_folder} is being prepended to path if missing. + *

+ **/ + @Override + public URLConnection resolve(String path) throws IOException { + return resolve(path, getClassLoader()); + } + + public static URLConnection resolve(String path, ClassLoader cl) throws IOException { + URL url = null; + URLConnection conn = null; + int type = -1; + + if(DEBUG) { + System.err.println("AssetURLContext.resolve: <"+path+">"); + } + + try { + // lookup as valid sub-protocol + url = new URL(path); + conn = open(url); + type = null != conn ? 1 : -1; + } catch(MalformedURLException e1) { if(DEBUG) { System.err.println("ERR: "+e1.getMessage()); } } + + if(null == conn && null != cl) { + // lookup via ClassLoader .. cleanup leading '/' + String cpath = path; + while(cpath.startsWith("/")) { + cpath = cpath.substring(1); + } + if(AndroidVersion.isAvailable) { + cpath = cpath.startsWith(assets_folder) ? cpath : assets_folder + cpath; + } + url = cl.getResource(cpath); + conn = open(url); + type = null != conn ? 2 : -1; + } + + if(null == conn) { + // lookup as File + try { + File file = new File(path); + if(file.exists()) { + url = IOUtil.toURLSimple(file); + conn = open(url); + type = null != conn ? 3 : -1; + } + } catch (Throwable e) { if(DEBUG) { System.err.println("ERR: "+e.getMessage()); } } + } + + if(DEBUG) { + System.err.println("AssetURLContext.resolve: type "+type+": url <"+url+">, conn <"+conn+">, connURL <"+(null!=conn?conn.getURL():null)+">"); + } + if(null == conn) { + throw new FileNotFoundException("Could not look-up: "+path+" as URL, w/ ClassLoader or as File"); + } + return conn; + } + + private static URLConnection open(URL url) { + if(null==url) { + return null; + } + try { + final URLConnection c = url.openConnection(); + c.connect(); // redundant + return c; + } catch (IOException ioe) { if(DEBUG) { System.err.println("ERR: "+ioe.getMessage()); } } + return null; + } + +} -- cgit v1.2.3