1adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project/*
2adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project *  Licensed to the Apache Software Foundation (ASF) under one or more
3adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project *  contributor license agreements.  See the NOTICE file distributed with
4adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project *  this work for additional information regarding copyright ownership.
5adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project *  The ASF licenses this file to You under the Apache License, Version 2.0
6adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project *  (the "License"); you may not use this file except in compliance with
7adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project *  the License.  You may obtain a copy of the License at
8adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project *
9adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project *     http://www.apache.org/licenses/LICENSE-2.0
10adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project *
11adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project *  Unless required by applicable law or agreed to in writing, software
12adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project *  distributed under the License is distributed on an "AS IS" BASIS,
13adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project *  See the License for the specific language governing permissions and
15adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project *  limitations under the License.
16adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project */
17adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
185aafac4db69e6d087c512cdfa5c7c0e2f1611681Jesse Wilsonpackage libcore.net.url;
19adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
20adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Projectimport java.io.File;
21adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Projectimport java.io.FileNotFoundException;
22adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Projectimport java.io.FileOutputStream;
23adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Projectimport java.io.FilterInputStream;
24adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Projectimport java.io.IOException;
25adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Projectimport java.io.InputStream;
26adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Projectimport java.net.ContentHandler;
27adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Projectimport java.net.ContentHandlerFactory;
285839b909d9528b7726e678a4b696ed37df15d897Jesse Wilsonimport java.net.JarURLConnection;
29adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Projectimport java.net.MalformedURLException;
30adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Projectimport java.net.URL;
31adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Projectimport java.security.Permission;
32f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilsonimport java.util.HashMap;
33f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilsonimport java.util.Iterator;
34f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilsonimport java.util.Map;
35f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilsonimport java.util.Set;
36adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Projectimport java.util.jar.JarEntry;
37adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Projectimport java.util.jar.JarFile;
38adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Projectimport java.util.zip.ZipFile;
395aafac4db69e6d087c512cdfa5c7c0e2f1611681Jesse Wilsonimport libcore.net.UriCodec;
40adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
41adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project/**
425839b909d9528b7726e678a4b696ed37df15d897Jesse Wilson * This subclass extends {@code URLConnection}.
43adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * <p>
44f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson *
45adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * This class is responsible for connecting and retrieving resources from a Jar
465839b909d9528b7726e678a4b696ed37df15d897Jesse Wilson * file which can be anywhere that can be referred to by an URL.
47adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project */
485839b909d9528b7726e678a4b696ed37df15d897Jesse Wilsonpublic class JarURLConnectionImpl extends JarURLConnection {
49adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
5059675dbb837c2a92352032e2ef0c8fc3305da9c8Elliott Hughes    private static final HashMap<URL, JarFile> jarCache = new HashMap<URL, JarFile>();
51f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson
52f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson    private URL jarFileURL;
53adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
54f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson    private InputStream jarInput;
55adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
56adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    private JarFile jarFile;
57adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
58adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    private JarEntry jarEntry;
59adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
60f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson    private boolean closed;
61adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
62adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    /**
63adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @param url
64adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     *            the URL of the JAR
65adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @throws MalformedURLException
66adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     *             if the URL is malformed
675839b909d9528b7726e678a4b696ed37df15d897Jesse Wilson     * @throws IOException
685839b909d9528b7726e678a4b696ed37df15d897Jesse Wilson     *             if there is a problem opening the connection.
69adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     */
7059675dbb837c2a92352032e2ef0c8fc3305da9c8Elliott Hughes    public JarURLConnectionImpl(URL url) throws MalformedURLException, IOException {
71adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        super(url);
72f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson        jarFileURL = getJarFileURL();
73f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson        jarFileURLConnection = jarFileURL.openConnection();
74adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
75adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
76adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    /**
77adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @see java.net.URLConnection#connect()
78adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     */
79adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    @Override
80adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    public void connect() throws IOException {
81f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson        if (!connected) {
82f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson            findJarFile(); // ensure the file can be found
83f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson            findJarEntry(); // ensure the entry, if any, can be found
84f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson            connected = true;
85f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson        }
86adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
87adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
88adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    /**
895839b909d9528b7726e678a4b696ed37df15d897Jesse Wilson     * Returns the Jar file referred by this {@code URLConnection}.
90f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes     *
91adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @throws IOException
92adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     *             thrown if an IO error occurs while connecting to the
93adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     *             resource.
94adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     */
95adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    @Override
96adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    public JarFile getJarFile() throws IOException {
97f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson        connect();
98adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        return jarFile;
99adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
100adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
101adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    /**
1025839b909d9528b7726e678a4b696ed37df15d897Jesse Wilson     * Returns the Jar file referred by this {@code URLConnection}
103f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes     *
104adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @throws IOException
105adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     *             if an IO error occurs while connecting to the resource.
106adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     */
107adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    private void findJarFile() throws IOException {
108f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson        if (getUseCaches()) {
1095839b909d9528b7726e678a4b696ed37df15d897Jesse Wilson            synchronized (jarCache) {
110f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                jarFile = jarCache.get(jarFileURL);
111adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            }
112f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson            if (jarFile == null) {
11359675dbb837c2a92352032e2ef0c8fc3305da9c8Elliott Hughes                JarFile jar = openJarFile();
1145839b909d9528b7726e678a4b696ed37df15d897Jesse Wilson                synchronized (jarCache) {
115f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                    jarFile = jarCache.get(jarFileURL);
1165839b909d9528b7726e678a4b696ed37df15d897Jesse Wilson                    if (jarFile == null) {
117f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                        jarCache.put(jarFileURL, jar);
118f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                        jarFile = jar;
1195839b909d9528b7726e678a4b696ed37df15d897Jesse Wilson                    } else {
120f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                        jar.close();
121f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                    }
122f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                }
123adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            }
1245839b909d9528b7726e678a4b696ed37df15d897Jesse Wilson        } else {
125f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson            jarFile = openJarFile();
126adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
127adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
128f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson        if (jarFile == null) {
129f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson            throw new IOException();
130adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
131f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson    }
132adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
13359675dbb837c2a92352032e2ef0c8fc3305da9c8Elliott Hughes    private JarFile openJarFile() throws IOException {
1345839b909d9528b7726e678a4b696ed37df15d897Jesse Wilson        if (jarFileURL.getProtocol().equals("file")) {
13556099d23fcb002b164bff8fb7f14d6ec0453509eJesse Wilson            String decodedFile = UriCodec.decode(jarFileURL.getFile());
13656099d23fcb002b164bff8fb7f14d6ec0453509eJesse Wilson            return new JarFile(new File(decodedFile), true, ZipFile.OPEN_READ);
137a389b4a499f40379b0b204d7ba1c2057663d95c0Jesse Wilson        } else {
138f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson            final InputStream is = jarFileURL.openConnection().getInputStream();
139f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson            try {
140ad41624e761bcf1af9c8008eb45187fc13983717Elliott Hughes                FileOutputStream fos = null;
141ad41624e761bcf1af9c8008eb45187fc13983717Elliott Hughes                JarFile result = null;
142ad41624e761bcf1af9c8008eb45187fc13983717Elliott Hughes                try {
143ad41624e761bcf1af9c8008eb45187fc13983717Elliott Hughes                    File tempJar = File.createTempFile("hyjar_", ".tmp", null);
144ad41624e761bcf1af9c8008eb45187fc13983717Elliott Hughes                    tempJar.deleteOnExit();
145ad41624e761bcf1af9c8008eb45187fc13983717Elliott Hughes                    fos = new FileOutputStream(tempJar);
146ad41624e761bcf1af9c8008eb45187fc13983717Elliott Hughes                    byte[] buf = new byte[4096];
147ad41624e761bcf1af9c8008eb45187fc13983717Elliott Hughes                    int nbytes = 0;
148ad41624e761bcf1af9c8008eb45187fc13983717Elliott Hughes                    while ((nbytes = is.read(buf)) > -1) {
149ad41624e761bcf1af9c8008eb45187fc13983717Elliott Hughes                        fos.write(buf, 0, nbytes);
150ad41624e761bcf1af9c8008eb45187fc13983717Elliott Hughes                    }
151ad41624e761bcf1af9c8008eb45187fc13983717Elliott Hughes                    fos.close();
152ad41624e761bcf1af9c8008eb45187fc13983717Elliott Hughes                    return new JarFile(tempJar, true, ZipFile.OPEN_READ | ZipFile.OPEN_DELETE);
153ad41624e761bcf1af9c8008eb45187fc13983717Elliott Hughes                } catch (IOException e) {
154ad41624e761bcf1af9c8008eb45187fc13983717Elliott Hughes                    return null;
155ad41624e761bcf1af9c8008eb45187fc13983717Elliott Hughes                } finally {
156ad41624e761bcf1af9c8008eb45187fc13983717Elliott Hughes                    if (fos != null) {
157ad41624e761bcf1af9c8008eb45187fc13983717Elliott Hughes                        try {
158ad41624e761bcf1af9c8008eb45187fc13983717Elliott Hughes                            fos.close();
159ad41624e761bcf1af9c8008eb45187fc13983717Elliott Hughes                        } catch (IOException ex) {
160ad41624e761bcf1af9c8008eb45187fc13983717Elliott Hughes                            return null;
161ad41624e761bcf1af9c8008eb45187fc13983717Elliott Hughes                        }
162ad41624e761bcf1af9c8008eb45187fc13983717Elliott Hughes                    }
163ad41624e761bcf1af9c8008eb45187fc13983717Elliott Hughes                }
164f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson            } finally {
1655839b909d9528b7726e678a4b696ed37df15d897Jesse Wilson                if (is != null) {
1665839b909d9528b7726e678a4b696ed37df15d897Jesse Wilson                    is.close();
1675839b909d9528b7726e678a4b696ed37df15d897Jesse Wilson                }
168adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            }
169adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
170adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
171adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
172adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    /**
1735839b909d9528b7726e678a4b696ed37df15d897Jesse Wilson     * Returns the JarEntry of the entry referenced by this {@code
1745839b909d9528b7726e678a4b696ed37df15d897Jesse Wilson     * URLConnection}.
175f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes     *
1765839b909d9528b7726e678a4b696ed37df15d897Jesse Wilson     * @return the JarEntry referenced
177f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes     *
178adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @throws IOException
179adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     *             if an IO error occurs while getting the entry
180adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     */
181adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    @Override
182adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    public JarEntry getJarEntry() throws IOException {
183f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson        connect();
184adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        return jarEntry;
185adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
186adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
187adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
188adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    /**
1895839b909d9528b7726e678a4b696ed37df15d897Jesse Wilson     * Look up the JarEntry of the entry referenced by this {@code
1905839b909d9528b7726e678a4b696ed37df15d897Jesse Wilson     * URLConnection}.
191adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     */
192adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    private void findJarEntry() throws IOException {
193adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        if (getEntryName() == null) {
194adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            return;
195adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
196adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        jarEntry = jarFile.getJarEntry(getEntryName());
197adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        if (jarEntry == null) {
198adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            throw new FileNotFoundException(getEntryName());
199adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
200adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
201adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
202adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    /**
203adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * Creates an input stream for reading from this URL Connection.
204f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson     *
205adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @return the input stream
206f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson     *
207adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @throws IOException
208adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     *             if an IO error occurs while connecting to the resource.
209adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     */
210adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    @Override
211adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    public InputStream getInputStream() throws IOException {
212adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        if (closed) {
213b1396870f92135aa140bd2b86221768dea5bc11dElliott Hughes            throw new IllegalStateException("JarURLConnection InputStream has been closed");
214adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
215f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson        connect();
216adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        if (jarInput != null) {
217adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            return jarInput;
218adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
219adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        if (jarEntry == null) {
220b1396870f92135aa140bd2b86221768dea5bc11dElliott Hughes            throw new IOException("Jar entry not specified");
221adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
222adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        return jarInput = new JarURLConnectionInputStream(jarFile
223adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                .getInputStream(jarEntry), jarFile);
224adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
225adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
226adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    /**
2275839b909d9528b7726e678a4b696ed37df15d897Jesse Wilson     * Returns the content type of the resource. For jar file itself
2285839b909d9528b7726e678a4b696ed37df15d897Jesse Wilson     * "x-java/jar" should be returned, for jar entries the content type of the
2295839b909d9528b7726e678a4b696ed37df15d897Jesse Wilson     * entry should be returned. Returns non-null results ("content/unknown" for
2305839b909d9528b7726e678a4b696ed37df15d897Jesse Wilson     * unknown types).
231f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes     *
232adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @return the content type
233adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     */
234adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    @Override
235adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    public String getContentType() {
236f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes        if (url.getFile().endsWith("!/")) {
237f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson            // the type for jar file itself is always "x-java/jar"
238f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes            return "x-java/jar";
2395839b909d9528b7726e678a4b696ed37df15d897Jesse Wilson        }
2405839b909d9528b7726e678a4b696ed37df15d897Jesse Wilson        String cType = null;
2415839b909d9528b7726e678a4b696ed37df15d897Jesse Wilson        String entryName = getEntryName();
242f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson
2435839b909d9528b7726e678a4b696ed37df15d897Jesse Wilson        if (entryName != null) {
2445839b909d9528b7726e678a4b696ed37df15d897Jesse Wilson            // if there is an Jar Entry, get the content type from the name
2455839b909d9528b7726e678a4b696ed37df15d897Jesse Wilson            cType = guessContentTypeFromName(entryName);
2465839b909d9528b7726e678a4b696ed37df15d897Jesse Wilson        } else {
2475839b909d9528b7726e678a4b696ed37df15d897Jesse Wilson            try {
2485839b909d9528b7726e678a4b696ed37df15d897Jesse Wilson                connect();
2495839b909d9528b7726e678a4b696ed37df15d897Jesse Wilson                cType = jarFileURLConnection.getContentType();
2505839b909d9528b7726e678a4b696ed37df15d897Jesse Wilson            } catch (IOException ioe) {
2515839b909d9528b7726e678a4b696ed37df15d897Jesse Wilson                // Ignore
252f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson            }
253adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
2545839b909d9528b7726e678a4b696ed37df15d897Jesse Wilson        if (cType == null) {
255f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes            cType = "content/unknown";
2565839b909d9528b7726e678a4b696ed37df15d897Jesse Wilson        }
2575839b909d9528b7726e678a4b696ed37df15d897Jesse Wilson        return cType;
258adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
259adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
260adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    /**
261adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * Returns the content length of the resource. Test cases reveal that if the
2625839b909d9528b7726e678a4b696ed37df15d897Jesse Wilson     * URL is referring to a Jar file, this method answers a content-length
263f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson     * returned by URLConnection. For jar entry it should return it's size.
264f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson     * Otherwise, it will return -1.
265f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson     *
266adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @return the content length
267adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     */
268adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    @Override
269adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    public int getContentLength() {
270adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        try {
271f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson            connect();
272f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson            if (jarEntry == null) {
273f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                return jarFileURLConnection.getContentLength();
274adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            }
2755839b909d9528b7726e678a4b696ed37df15d897Jesse Wilson            return (int) getJarEntry().getSize();
276adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        } catch (IOException e) {
2775839b909d9528b7726e678a4b696ed37df15d897Jesse Wilson            // Ignored
278adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
279adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        return -1;
280adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
281adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
282adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    /**
2835839b909d9528b7726e678a4b696ed37df15d897Jesse Wilson     * Returns the object pointed by this {@code URL}. If this URLConnection is
2845839b909d9528b7726e678a4b696ed37df15d897Jesse Wilson     * pointing to a Jar File (no Jar Entry), this method will return a {@code
2855839b909d9528b7726e678a4b696ed37df15d897Jesse Wilson     * JarFile} If there is a Jar Entry, it will return the object corresponding
2865839b909d9528b7726e678a4b696ed37df15d897Jesse Wilson     * to the Jar entry content type.
287f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes     *
288adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @return a non-null object
289f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson     *
290adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @throws IOException
2915839b909d9528b7726e678a4b696ed37df15d897Jesse Wilson     *             if an IO error occurred
292f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes     *
293adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @see ContentHandler
294adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @see ContentHandlerFactory
295adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @see java.io.IOException
296adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @see #setContentHandlerFactory(ContentHandlerFactory)
297adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     */
298adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    @Override
299adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    public Object getContent() throws IOException {
300f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson        connect();
301adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        // if there is no Jar Entry, return a JarFile
302adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        if (jarEntry == null) {
303adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            return jarFile;
304adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
305adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        return super.getContent();
306adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
307adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
308adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    /**
309adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * Returns the permission, in this case the subclass, FilePermission object
310adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * which represents the permission necessary for this URLConnection to
311adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * establish the connection.
312f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson     *
313adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @return the permission required for this URLConnection.
314f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson     *
315adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @throws IOException
316adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     *             thrown when an IO exception occurs while creating the
317adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     *             permission.
318adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     */
319f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson
320adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    @Override
321adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    public Permission getPermission() throws IOException {
322f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson        return jarFileURLConnection.getPermission();
323f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson    }
324f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson
325f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson    @Override
326f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson    public boolean getUseCaches() {
327f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson        return jarFileURLConnection.getUseCaches();
328f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson    }
329f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson
330f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson    @Override
331f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson    public void setUseCaches(boolean usecaches) {
332f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson        jarFileURLConnection.setUseCaches(usecaches);
333f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson    }
334f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson
335f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson    @Override
336f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson    public boolean getDefaultUseCaches() {
337f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson        return jarFileURLConnection.getDefaultUseCaches();
338f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson    }
339f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson
340f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson    @Override
341f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson    public void setDefaultUseCaches(boolean defaultusecaches) {
342f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson        jarFileURLConnection.setDefaultUseCaches(defaultusecaches);
343adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
344adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
345adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    private class JarURLConnectionInputStream extends FilterInputStream {
34680b486724ca19b3c1c3c36334d06856330362f83Jesse Wilson        final JarFile jarFile;
347adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
348adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        protected JarURLConnectionInputStream(InputStream in, JarFile file) {
349adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            super(in);
350adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            jarFile = file;
351adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
352adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
353adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        @Override
354adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        public void close() throws IOException {
355adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            super.close();
356f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson            if (!getUseCaches()) {
357adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                closed = true;
358adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                jarFile.close();
359adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            }
360adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
361adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
362adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project}
363