/* gnu.java.net.protocol.jar.Handler - jar protocol handler for java.net Copyright (C) 1999, 2002, 2003, 2005, 2006 Free Software Foundation, Inc. This file is part of GNU Classpath. GNU Classpath is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. GNU Classpath is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with GNU Classpath; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. Linking this library statically or dynamically with other modules is making a combined work based on this library. Thus, the terms and conditions of the GNU General Public License cover the whole combination. As a special exception, the copyright holders of this library give you permission to link this library with independent modules to produce an executable, regardless of the license terms of these independent modules, and to copy and distribute the resulting executable under terms of your choice, provided that you also meet, for each linked independent module, the terms and conditions of the license of that module. An independent module is a module which is not derived from or based on this library. If you modify this library, you may extend this exception to your version of the library, but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version. */ package gnu.java.net.protocol.jar; import gnu.java.lang.CPStringBuilder; import gnu.java.net.URLParseError; import java.io.IOException; import java.net.MalformedURLException; import java.net.URL; import java.net.URLConnection; import java.net.URLStreamHandler; import java.util.ArrayList; import java.util.Iterator; import java.util.StringTokenizer; /** * @author Kresten Krab Thorup (krab@gnu.org) */ public class Handler extends URLStreamHandler { /** * A do nothing constructor */ public Handler() { } /** * This method returs a new JarURLConnection for the specified URL * * @param url The URL to return a connection for * * @return The URLConnection * * @exception IOException If an error occurs */ protected URLConnection openConnection(URL url) throws IOException { return new Connection(url); } /** * This method overrides URLStreamHandler's for parsing url of protocol "jar" * * @param url The URL object in which to store the results * @param url_string The String-ized URL to parse * @param start The position in the string to start scanning from * @param end The position in the string to stop scanning */ protected void parseURL (URL url, String url_string, int start, int end) { // This method does not throw an exception or return a value. Thus our // strategy when we encounter an error in parsing is to return without // doing anything. String file = url.getFile(); if (!file.equals("")) { //has context url url_string = url_string.substring (start, end); if (url_string.startsWith("/")) { //url string is an absolute path int idx = file.lastIndexOf ("!/"); if (idx < 0) throw new URLParseError("no !/ in spec"); file = file.substring (0, idx + 1) + url_string; } else if (url_string.length() > 0) { int idx = file.lastIndexOf ("/"); if (idx == -1) //context path is weird file = "/" + url_string; else if (idx == (file.length() - 1)) //just concatenate two parts file = file + url_string; else // according to Java API Documentation, here is a little different // with URLStreamHandler.parseURL // but JDK seems doesn't handle it well file = file.substring(0, idx + 1) + url_string; } setURL (url, "jar", url.getHost(), url.getPort(), flat(file), null); return; } // Bunches of things should be true. Make sure. if (end < start) return; if (end - start < 2) return; if (start > url_string.length()) return; // Skip remains of protocol url_string = url_string.substring (start, end); int jar_stop; if ((jar_stop = url_string.indexOf("!/")) < 0) throw new URLParseError("no !/ in spec"); try { new URL(url_string.substring (0, jar_stop)); } catch (MalformedURLException e) { throw new URLParseError("invalid inner URL: " + e.getMessage()); } if (!url.getProtocol().equals ("jar") ) throw new URLParseError("unexpected protocol " + url.getProtocol()); setURL (url, "jar", url.getHost(), url.getPort(), url_string, null); } /** * Makes the given jar url string 'flat' by removing any . and .. from * jar file path because ZipFile entries can only handle flat paths. * Inside jar files '/' is always the path separator. */ private static String flat(String url_string) { int jar_stop = url_string.indexOf("!/"); String jar_path = url_string.substring(jar_stop + 1, url_string.length()); if (jar_path.indexOf("/.") < 0) return url_string; ArrayList<String> tokens = new ArrayList<String>(); StringTokenizer st = new StringTokenizer(jar_path, "/"); while (st.hasMoreTokens()) { String token = st.nextToken(); if (token.equals(".")) continue; else if (token.equals("..")) { if (! tokens.isEmpty()) tokens.remove(tokens.size() - 1); } else tokens.add(token); } CPStringBuilder path = new CPStringBuilder(url_string.length()); path.append(url_string.substring(0, jar_stop + 1)); Iterator<String> it = tokens.iterator(); while (it.hasNext()) path.append('/').append(it.next()); return path.toString(); } /** * This method converts a Jar URL object into a String. * * @param url The URL object to convert */ protected String toExternalForm (URL url) { String file = url.getFile(); String ref = url.getRef(); // return "jar:" + file; // Performance!!: // Do the concatenation manually to avoid resize StringBuffer's // internal buffer. The length of ref is not taken into consideration // as it's a rare path. CPStringBuilder sb = new CPStringBuilder (file.length() + 5); sb.append ("jar:"); sb.append (file); if (ref != null) sb.append('#').append(ref); return sb.toString(); } }