summaryrefslogtreecommitdiffstats
path: root/src/java/com/jogamp/common/net/AssetURLContext.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/java/com/jogamp/common/net/AssetURLContext.java')
-rw-r--r--src/java/com/jogamp/common/net/AssetURLContext.java212
1 files changed, 212 insertions, 0 deletions
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 <i>asset URL</i> protocol name <code>asset</code> */
+ public static final String asset_protocol = "asset";
+
+ /** The <i>asset URL</i> protocol prefix <code>asset:</code> */
+ public static final String asset_protocol_prefix = "asset:";
+
+ /**
+ * The <i>optional</i> <i>asset</i> folder name with ending slash <code>assets/</code>.
+ * <p>
+ * Note that the <i>asset</i> folder is not used on all platforms using the <i>asset</i> protocol
+ * and you should not rely on it, use {@link AssetURLConnection#getEntryName()}.
+ * </p>
+ **/
+ 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 <i>asset</i> URL, suitable even w/o the registered <i>asset</i> URLStreamHandler.
+ * <p>
+ * This is equivalent with:
+ * <pre>
+ * return new URL(null, path.startsWith("asset:") ? path : "asset:" + path, new AssetURLStreamHandler(cl));
+ * </pre>
+ * </p>
+ * @param path resource path, with or w/o <code>asset:</code> 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 <i>asset</i> URL, suitable only with the registered <i>asset</i> URLStreamHandler.
+ * <p>
+ * This is equivalent with:
+ * <pre>
+ * return new URL(path.startsWith("asset:") ? path : "asset:" + path);
+ * </pre>
+ * </p>
+ * @param path resource path, with or w/o <code>asset:</code> 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 <i>asset</i> 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 <i>asset</i> <code>handler</code> for the given ClassLoader <code>cl</code>.
+ *
+ * @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 <i>asset</i> aware ClassLoader.
+ * <p>
+ * The ClassLoader is required to find the <i>asset</i> resource
+ * via it's <code>URL findResource(String)</code> implementation.
+ * </p>
+ * <p>
+ * It's <code>URL findResource(String)</code> implementation shall return either
+ * an <i>asset</i> URL <code>asset:sub-protocol</code> or just the sub-protocol URL.
+ * </p>
+ * <p>
+ * For example, on Android, we <i>redirect</i> all <code>path</code> request to <i>assets/</i><code>path</code>.
+ * </p>
+ */
+ public abstract ClassLoader getClassLoader();
+
+ @Override
+ public String getImplementedProtocol() {
+ return asset_protocol;
+ }
+
+ /**
+ * {@inheritDoc}
+ * <p>
+ * This implementation attempts to resolve <code>path</code> in the following order:
+ * <ol>
+ * <li> as a valid URL: <code>new URL(path)</code>, use sub-protocol if <i>asset</i> URL</li>
+ * <li> via ClassLoader: {@link #getClassLoader()}.{@link ClassLoader#getResource(String) getResource(path)}, use sub-protocol if <i>asset</i> URL </li>
+ * <li> as a File: <code>new File(path).toURI().toURL()</code>
+ * </ol>
+ * </p>
+ * <p>
+ * In case of using the ClassLoader (2) <b>and</b> if running on Android,
+ * the {@link #assets_folder} is being prepended to <code>path</code> if missing.
+ * </p>
+ **/
+ @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;
+ }
+
+}