michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this file, michael@0: * You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: package org.mozilla.gecko.util; michael@0: michael@0: import org.mozilla.gecko.mozglue.NativeZip; michael@0: michael@0: import android.content.res.Resources; michael@0: import android.graphics.Bitmap; michael@0: import android.graphics.drawable.BitmapDrawable; michael@0: import android.util.Log; michael@0: import org.mozilla.gecko.mozglue.RobocopTarget; michael@0: michael@0: import java.io.BufferedReader; michael@0: import java.io.IOException; michael@0: import java.io.InputStream; michael@0: import java.io.InputStreamReader; michael@0: import java.net.URI; michael@0: import java.net.URISyntaxException; michael@0: import java.util.Stack; michael@0: michael@0: /* Reads out of a multiple level deep jar file such as michael@0: * jar:jar:file:///data/app/org.mozilla.fennec.apk!/omni.ja!/chrome/chrome/content/branding/favicon32.png michael@0: */ michael@0: public final class GeckoJarReader { michael@0: private static final String LOGTAG = "GeckoJarReader"; michael@0: michael@0: private GeckoJarReader() {} michael@0: michael@0: public static Bitmap getBitmap(Resources resources, String url) { michael@0: BitmapDrawable drawable = getBitmapDrawable(resources, url); michael@0: return (drawable != null) ? drawable.getBitmap() : null; michael@0: } michael@0: michael@0: public static BitmapDrawable getBitmapDrawable(Resources resources, String url) { michael@0: Stack jarUrls = parseUrl(url); michael@0: InputStream inputStream = null; michael@0: BitmapDrawable bitmap = null; michael@0: michael@0: NativeZip zip = null; michael@0: try { michael@0: // Load the initial jar file as a zip michael@0: zip = getZipFile(jarUrls.pop()); michael@0: inputStream = getStream(zip, jarUrls, url); michael@0: if (inputStream != null) { michael@0: bitmap = new BitmapDrawable(resources, inputStream); michael@0: } michael@0: } catch (IOException ex) { michael@0: Log.e(LOGTAG, "Exception ", ex); michael@0: } catch (URISyntaxException ex) { michael@0: Log.e(LOGTAG, "Exception ", ex); michael@0: } finally { michael@0: if (inputStream != null) { michael@0: try { michael@0: inputStream.close(); michael@0: } catch(IOException ex) { michael@0: Log.e(LOGTAG, "Error closing stream", ex); michael@0: } michael@0: } michael@0: if (zip != null) { michael@0: zip.close(); michael@0: } michael@0: } michael@0: michael@0: return bitmap; michael@0: } michael@0: michael@0: public static String getText(String url) { michael@0: Stack jarUrls = parseUrl(url); michael@0: michael@0: NativeZip zip = null; michael@0: BufferedReader reader = null; michael@0: String text = null; michael@0: try { michael@0: zip = getZipFile(jarUrls.pop()); michael@0: InputStream input = getStream(zip, jarUrls, url); michael@0: if (input != null) { michael@0: reader = new BufferedReader(new InputStreamReader(input)); michael@0: text = reader.readLine(); michael@0: } michael@0: } catch (IOException ex) { michael@0: Log.e(LOGTAG, "Exception ", ex); michael@0: } catch (URISyntaxException ex) { michael@0: Log.e(LOGTAG, "Exception ", ex); michael@0: } finally { michael@0: if (reader != null) { michael@0: try { michael@0: reader.close(); michael@0: } catch(IOException ex) { michael@0: Log.e(LOGTAG, "Error closing reader", ex); michael@0: } michael@0: } michael@0: if (zip != null) { michael@0: zip.close(); michael@0: } michael@0: } michael@0: michael@0: return text; michael@0: } michael@0: michael@0: private static NativeZip getZipFile(String url) throws IOException, URISyntaxException { michael@0: URI fileUrl = new URI(url); michael@0: return new NativeZip(fileUrl.getPath()); michael@0: } michael@0: michael@0: @RobocopTarget michael@0: public static InputStream getStream(String url) { michael@0: Stack jarUrls = parseUrl(url); michael@0: try { michael@0: NativeZip zip = getZipFile(jarUrls.pop()); michael@0: return getStream(zip, jarUrls, url); michael@0: } catch (Exception ex) { michael@0: // Some JNI code throws IllegalArgumentException on a bad file name; michael@0: // swallow the error and return null. We could also see legitimate michael@0: // IOExceptions here. michael@0: return null; michael@0: } michael@0: } michael@0: michael@0: private static InputStream getStream(NativeZip zip, Stack jarUrls, String origUrl) { michael@0: InputStream inputStream = null; michael@0: michael@0: // loop through children jar files until we reach the innermost one michael@0: while (!jarUrls.empty()) { michael@0: String fileName = jarUrls.pop(); michael@0: michael@0: if (inputStream != null) { michael@0: // intermediate NativeZips and InputStreams will be garbage collected. michael@0: try { michael@0: zip = new NativeZip(inputStream); michael@0: } catch (IllegalArgumentException e) { michael@0: String description = "!!! BUG 849589 !!! origUrl=" + origUrl; michael@0: Log.e(LOGTAG, description, e); michael@0: throw new IllegalArgumentException(description); michael@0: } michael@0: } michael@0: michael@0: inputStream = zip.getInputStream(fileName); michael@0: if (inputStream == null) { michael@0: Log.d(LOGTAG, "No Entry for " + fileName); michael@0: return null; michael@0: } michael@0: } michael@0: michael@0: return inputStream; michael@0: } michael@0: michael@0: /* Returns a stack of strings breaking the url up into pieces. Each piece michael@0: * is assumed to point to a jar file except for the final one. Callers should michael@0: * pass in the url to parse, and null for the parent parameter (used for recursion) michael@0: * For example, jar:jar:file:///data/app/org.mozilla.fennec.apk!/omni.ja!/chrome/chrome/content/branding/favicon32.png michael@0: * will return: michael@0: * file:///data/app/org.mozilla.fennec.apk michael@0: * omni.ja michael@0: * chrome/chrome/content/branding/favicon32.png michael@0: */ michael@0: private static Stack parseUrl(String url) { michael@0: return parseUrl(url, null); michael@0: } michael@0: michael@0: private static Stack parseUrl(String url, Stack results) { michael@0: if (results == null) { michael@0: results = new Stack(); michael@0: } michael@0: michael@0: if (url.startsWith("jar:")) { michael@0: int jarEnd = url.lastIndexOf("!"); michael@0: String subStr = url.substring(4, jarEnd); michael@0: results.push(url.substring(jarEnd+2)); // remove the !/ characters michael@0: return parseUrl(subStr, results); michael@0: } else { michael@0: results.push(url); michael@0: return results; michael@0: } michael@0: } michael@0: }