12be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka/* 22be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka * Copyright (C) 2012 The Android Open Source Project 32be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka * 42be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka * Licensed under the Apache License, Version 2.0 (the "License"); 52be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka * you may not use this file except in compliance with the License. 62be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka * You may obtain a copy of the License at 72be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka * 82be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka * http://www.apache.org/licenses/LICENSE-2.0 92be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka * 102be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka * Unless required by applicable law or agreed to in writing, software 112be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka * distributed under the License is distributed on an "AS IS" BASIS, 122be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 132be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka * See the License for the specific language governing permissions and 142be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka * limitations under the License. 152be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka */ 162be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka 17ca6acfdd6b3400ad6e29d45c29b0ec40ea92a968Tadashi G. Takaokapackage com.android.inputmethod.keyboard.tools; 182be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka 19465a2ac534bf5c0dea38608dbe713ed3a9dc84ebTadashi G. Takaokaimport java.io.Closeable; 202be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaokaimport java.io.IOException; 212be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaokaimport java.io.InputStream; 222be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaokaimport java.io.UnsupportedEncodingException; 232be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaokaimport java.net.URL; 242be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaokaimport java.net.URLDecoder; 252be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaokaimport java.util.ArrayList; 262be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaokaimport java.util.Enumeration; 27d317796207d9c9443669ff94aac63c4193ec0e6fTadashi G. Takaokaimport java.util.Locale; 282be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaokaimport java.util.jar.JarEntry; 292be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaokaimport java.util.jar.JarFile; 302be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka 311e11c44d1b5f9ddf593c5407cb14c458be0056f2Tadashi G. Takaokapublic final class JarUtils { 322be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka private JarUtils() { 332be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka // This utility class is not publicly instantiable. 342be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka } 352be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka 369c9802e6473e5e2e2b792d5f14502139feb49637Tadashi G. Takaoka public static JarFile getJarFile(final Class<?> mainClass) { 379c9802e6473e5e2e2b792d5f14502139feb49637Tadashi G. Takaoka final String mainClassPath = "/" + mainClass.getName().replace('.', '/') + ".class"; 389c9802e6473e5e2e2b792d5f14502139feb49637Tadashi G. Takaoka final URL resUrl = mainClass.getResource(mainClassPath); 392be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka if (!resUrl.getProtocol().equals("jar")) { 402be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka throw new RuntimeException("Should run as jar"); 412be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka } 422be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka final String path = resUrl.getPath(); 432be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka if (!path.startsWith("file:")) { 442be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka throw new RuntimeException("Unknown jar path: " + path); 452be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka } 462be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka final String jarPath = path.substring("file:".length(), path.indexOf('!')); 472be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka try { 482be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka return new JarFile(URLDecoder.decode(jarPath, "UTF-8")); 492be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka } catch (UnsupportedEncodingException e) { 502be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka } catch (IOException e) { 512be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka } 522be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka return null; 532be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka } 542be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka 552be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka public static InputStream openResource(final String name) { 562be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka return JarUtils.class.getResourceAsStream("/" + name); 572be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka } 582be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka 592be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka public interface JarFilter { 602be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka public boolean accept(String dirName, String name); 612be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka } 622be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka 63465a2ac534bf5c0dea38608dbe713ed3a9dc84ebTadashi G. Takaoka public static ArrayList<String> getEntryNameListing(final JarFile jar, final JarFilter filter) { 64a91561aa58db1c43092c1caecc051a11fa5391c7Tadashi G. Takaoka final ArrayList<String> result = new ArrayList<>(); 652be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka final Enumeration<JarEntry> entries = jar.entries(); 662be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka while (entries.hasMoreElements()) { 672be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka final JarEntry entry = entries.nextElement(); 682be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka final String path = entry.getName(); 692be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka final int pos = path.lastIndexOf('/'); 702be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka final String dirName = (pos >= 0) ? path.substring(0, pos) : ""; 712be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka final String name = (pos >= 0) ? path.substring(pos + 1) : path; 722be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka if (filter.accept(dirName, name)) { 732be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka result.add(path); 742be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka } 752be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka } 762be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka return result; 772be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka } 782be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka 79465a2ac534bf5c0dea38608dbe713ed3a9dc84ebTadashi G. Takaoka public static ArrayList<String> getEntryNameListing(final JarFile jar, 80465a2ac534bf5c0dea38608dbe713ed3a9dc84ebTadashi G. Takaoka final String filterName) { 81465a2ac534bf5c0dea38608dbe713ed3a9dc84ebTadashi G. Takaoka return getEntryNameListing(jar, new JarFilter() { 822be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka @Override 832be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka public boolean accept(final String dirName, final String name) { 842be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka return name.equals(filterName); 852be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka } 862be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka }); 872be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka } 88465a2ac534bf5c0dea38608dbe713ed3a9dc84ebTadashi G. Takaoka 896bb3556ff7b24a5a38d7cc4276017bda3a9a4bbaTadashi G. Takaoka // The locale is taken from string resource jar entry name (values-<locale>/) 90d317796207d9c9443669ff94aac63c4193ec0e6fTadashi G. Takaoka // or {@link LocaleUtils#DEFAULT_LOCALE} for the default string resource 91465a2ac534bf5c0dea38608dbe713ed3a9dc84ebTadashi G. Takaoka // directory (values/). 92d317796207d9c9443669ff94aac63c4193ec0e6fTadashi G. Takaoka public static Locale getLocaleFromEntryName(final String jarEntryName) { 93465a2ac534bf5c0dea38608dbe713ed3a9dc84ebTadashi G. Takaoka final String dirName = jarEntryName.substring(0, jarEntryName.lastIndexOf('/')); 94465a2ac534bf5c0dea38608dbe713ed3a9dc84ebTadashi G. Takaoka final int pos = dirName.lastIndexOf('/'); 95465a2ac534bf5c0dea38608dbe713ed3a9dc84ebTadashi G. Takaoka final String parentName = (pos >= 0) ? dirName.substring(pos + 1) : dirName; 966bb3556ff7b24a5a38d7cc4276017bda3a9a4bbaTadashi G. Takaoka final int localePos = parentName.indexOf('-'); 976bb3556ff7b24a5a38d7cc4276017bda3a9a4bbaTadashi G. Takaoka if (localePos < 0) { 98465a2ac534bf5c0dea38608dbe713ed3a9dc84ebTadashi G. Takaoka // Default resource name. 99d317796207d9c9443669ff94aac63c4193ec0e6fTadashi G. Takaoka return LocaleUtils.DEFAULT_LOCALE; 100465a2ac534bf5c0dea38608dbe713ed3a9dc84ebTadashi G. Takaoka } 101d317796207d9c9443669ff94aac63c4193ec0e6fTadashi G. Takaoka final String localeStr = parentName.substring(localePos + 1); 102d317796207d9c9443669ff94aac63c4193ec0e6fTadashi G. Takaoka final int regionPos = localeStr.indexOf("-r"); 1036bb3556ff7b24a5a38d7cc4276017bda3a9a4bbaTadashi G. Takaoka if (regionPos < 0) { 104d317796207d9c9443669ff94aac63c4193ec0e6fTadashi G. Takaoka return LocaleUtils.constructLocaleFromString(localeStr); 105465a2ac534bf5c0dea38608dbe713ed3a9dc84ebTadashi G. Takaoka } 106d317796207d9c9443669ff94aac63c4193ec0e6fTadashi G. Takaoka return LocaleUtils.constructLocaleFromString(localeStr.replace("-r", "_")); 107465a2ac534bf5c0dea38608dbe713ed3a9dc84ebTadashi G. Takaoka } 108465a2ac534bf5c0dea38608dbe713ed3a9dc84ebTadashi G. Takaoka 109465a2ac534bf5c0dea38608dbe713ed3a9dc84ebTadashi G. Takaoka public static void close(final Closeable stream) { 110465a2ac534bf5c0dea38608dbe713ed3a9dc84ebTadashi G. Takaoka try { 111465a2ac534bf5c0dea38608dbe713ed3a9dc84ebTadashi G. Takaoka if (stream != null) { 112465a2ac534bf5c0dea38608dbe713ed3a9dc84ebTadashi G. Takaoka stream.close(); 113465a2ac534bf5c0dea38608dbe713ed3a9dc84ebTadashi G. Takaoka } 114465a2ac534bf5c0dea38608dbe713ed3a9dc84ebTadashi G. Takaoka } catch (IOException e) { 115465a2ac534bf5c0dea38608dbe713ed3a9dc84ebTadashi G. Takaoka } 116465a2ac534bf5c0dea38608dbe713ed3a9dc84ebTadashi G. Takaoka } 1172be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka} 118