/* * Copyright 2003-2012 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package jetbrains.mps.util; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.io.FileNotFoundException; import java.io.FilterInputStream; import java.io.IOException; import java.io.InputStream; import java.io.UnsupportedEncodingException; import java.net.URL; import java.util.ArrayList; import java.util.List; import java.util.zip.ZipEntry; import java.util.zip.ZipFile; /** * Partially copied from IDEA * * @since 1/13/12 */ public class URLUtil { public static final String SCHEME_SEPARATOR = "://"; public static final String FILE_PROTOCOL = "file"; public static final String FILE_PROTOCOL_PREFIX = FILE_PROTOCOL + ":"; public static final String JAR_PROTOCOL = "jar"; public static final String JAR_PROTOCOL_PREFIX = JAR_PROTOCOL + ":"; public static final String JAR_SEPARATOR = "!/"; private URLUtil() { } /** * Opens a url stream. The semantics is the sames as {@link java.net.URL#openStream()}. The * separate method is needed, since jar URLs open jars via JarFactory and thus keep them * mapped into memory. */ @NotNull public static InputStream openStream(final URL url) throws IOException { @NonNls final String protocol = url.getProtocol(); if (protocol.equals(JAR_PROTOCOL)) { return openJarStream(url); } return url.openStream(); } @NotNull private static InputStream openJarStream(final URL url) throws IOException { String file = url.getFile(); assert file.startsWith(FILE_PROTOCOL_PREFIX); file = file.substring(FILE_PROTOCOL_PREFIX.length()); assert file.indexOf(JAR_SEPARATOR) > 0; String resource = file.substring(file.indexOf(JAR_SEPARATOR) + 2); file = file.substring(0, file.indexOf("!")); final ZipFile zipFile = new ZipFile(FileUtil.unquote(file)); final ZipEntry zipEntry = zipFile.getEntry(resource); if (zipEntry == null) throw new FileNotFoundException("Entry " + resource + " not found in " + file); return new FilterInputStream(zipFile.getInputStream(zipEntry)) { @Override public void close() throws IOException { super.close(); zipFile.close(); } }; } @NotNull public static String unescapePercentSequences(@NotNull String s) { if (s.indexOf('%') == -1) { return s; } StringBuilder decoded = new StringBuilder(); final int len = s.length(); int i = 0; while (i < len) { char c = s.charAt(i); if (c == '%') { List<Integer> bytes = new ArrayList<Integer>(); while (i + 2 < len && s.charAt(i) == '%') { final int d1 = decode(s.charAt(i + 1)); final int d2 = decode(s.charAt(i + 2)); if (d1 != -1 && d2 != -1) { bytes.add(((d1 & 0xf) << 4 | d2 & 0xf)); i += 3; } else { break; } } if (!bytes.isEmpty()) { final byte[] bytesArray = new byte[bytes.size()]; for (int j = 0; j < bytes.size(); j++) { bytesArray[j] = (byte) bytes.get(j).intValue(); } try { decoded.append(new String(bytesArray, "UTF-8")); continue; } catch (UnsupportedEncodingException ignored) { } } } decoded.append(c); i++; } return decoded.toString(); } /** * Splits .jar URL along a separator and strips "jar" and "file" prefixes if any. * Returns a pair of path to a .jar file and entry name inside a .jar, or null if the URL does not contain a separator. * <p/> * E.g. "jar:file:///path/to/jar.jar!/resource.xml" is converted into ["/path/to/jar.jar", "resource.xml"]. */ @Nullable public static Pair<String, String> splitJarUrl(@NotNull String url) { int pivot = url.indexOf(JAR_SEPARATOR); if (pivot < 0) return null; String resourcePath = url.substring(pivot + JAR_SEPARATOR.length()); String jarPath = url.substring(0, pivot); if (jarPath.startsWith(JAR_PROTOCOL_PREFIX)) { jarPath = jarPath.substring(JAR_PROTOCOL_PREFIX.length()); } if (jarPath.startsWith(FILE_PROTOCOL)) { jarPath = jarPath.substring(FILE_PROTOCOL.length()); if (jarPath.startsWith(SCHEME_SEPARATOR)) { jarPath = jarPath.substring(SCHEME_SEPARATOR.length()); } else if (StringUtil.startsWithChar(jarPath, ':')) { jarPath = jarPath.substring(1); } } return new Pair<String, String>(jarPath, resourcePath); } private static int decode(char c) { if ((c >= '0') && (c <= '9')) return c - '0'; if ((c >= 'a') && (c <= 'f')) return c - 'a' + 10; if ((c >= 'A') && (c <= 'F')) return c - 'A' + 10; return -1; } }