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:
+ *
+ * - as a valid URL:
new URL(path)
, use sub-protocol if asset URL
+ * - via ClassLoader: {@link #getClassLoader()}.{@link ClassLoader#getResource(String) getResource(path)}, use sub-protocol if asset URL
+ * - 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