1 // © 2016 and later: Unicode, Inc. and others. 2 // License & terms of use: http://www.unicode.org/copyright.html#License 3 /* 4 ******************************************************************************* 5 * Copyright (C) 2004-2014, International Business Machines Corporation and 6 * others. All Rights Reserved. 7 ******************************************************************************* 8 * 9 * Created on Feb 4, 2004 10 * 11 */ 12 package com.ibm.icu.impl; 13 14 import java.io.IOException; 15 import java.io.InputStream; 16 import java.net.URL; 17 import java.security.AccessController; 18 import java.security.PrivilegedAction; 19 import java.util.MissingResourceException; 20 import java.util.logging.Logger; 21 22 import com.ibm.icu.util.VersionInfo; 23 24 /** 25 * Provides access to ICU data files as InputStreams. Implements security checking. 26 */ 27 public final class ICUData { 28 /** 29 * The data path to be used with getBundleInstance API 30 */ 31 static final String ICU_DATA_PATH = "com/ibm/icu/impl/"; 32 /** 33 * The ICU data package name. 34 * This is normally the name of the .dat package, and the prefix (plus '/') 35 * of the package entry names. 36 */ 37 static final String PACKAGE_NAME = "icudt" + VersionInfo.ICU_DATA_VERSION_PATH; 38 /** 39 * The data path to be used with Class.getResourceAsStream(). 40 */ 41 public static final String ICU_BUNDLE = "data/" + PACKAGE_NAME; 42 43 /** 44 * The base name of ICU data to be used with ClassLoader.getResourceAsStream(), 45 * ICUResourceBundle.getBundleInstance() etc. 46 */ 47 public static final String ICU_BASE_NAME = ICU_DATA_PATH + ICU_BUNDLE; 48 49 /** 50 * The base name of collation data to be used with getBundleInstance API 51 */ 52 public static final String ICU_COLLATION_BASE_NAME = ICU_BASE_NAME + "/coll"; 53 54 /** 55 * The base name of rbbi data to be used with getData API 56 */ 57 public static final String ICU_BRKITR_NAME = "brkitr"; 58 59 /** 60 * The base name of rbbi data to be used with getBundleInstance API 61 */ 62 public static final String ICU_BRKITR_BASE_NAME = ICU_BASE_NAME + '/' + ICU_BRKITR_NAME; 63 64 /** 65 * The base name of rbnf data to be used with getBundleInstance API 66 */ 67 public static final String ICU_RBNF_BASE_NAME = ICU_BASE_NAME + "/rbnf"; 68 69 /** 70 * The base name of transliterator data to be used with getBundleInstance API 71 */ 72 public static final String ICU_TRANSLIT_BASE_NAME = ICU_BASE_NAME + "/translit"; 73 74 public static final String ICU_LANG_BASE_NAME = ICU_BASE_NAME + "/lang"; 75 public static final String ICU_CURR_BASE_NAME = ICU_BASE_NAME + "/curr"; 76 public static final String ICU_REGION_BASE_NAME = ICU_BASE_NAME + "/region"; 77 public static final String ICU_ZONE_BASE_NAME = ICU_BASE_NAME + "/zone"; 78 public static final String ICU_UNIT_BASE_NAME = ICU_BASE_NAME + "/unit"; 79 80 /** 81 * For testing (otherwise false): When reading an InputStream from a Class or ClassLoader 82 * (that is, not from a file), log when the stream contains ICU binary data. 83 * 84 * This cannot be ICUConfig'ured because ICUConfig calls ICUData.getStream() 85 * to read the properties file, so we would get a circular dependency 86 * in the class initialization. 87 */ 88 private static final boolean logBinaryDataFromInputStream = false; 89 private static final Logger logger = logBinaryDataFromInputStream ? 90 Logger.getLogger(ICUData.class.getName()) : null; 91 exists(final String resourceName)92 public static boolean exists(final String resourceName) { 93 URL i = null; 94 if (System.getSecurityManager() != null) { 95 i = AccessController.doPrivileged(new PrivilegedAction<URL>() { 96 @Override 97 public URL run() { 98 return ICUData.class.getResource(resourceName); 99 } 100 }); 101 } else { 102 i = ICUData.class.getResource(resourceName); 103 } 104 return i != null; 105 } 106 getStream(final Class<?> root, final String resourceName, boolean required)107 private static InputStream getStream(final Class<?> root, final String resourceName, boolean required) { 108 InputStream i = null; 109 if (System.getSecurityManager() != null) { 110 i = AccessController.doPrivileged(new PrivilegedAction<InputStream>() { 111 @Override 112 public InputStream run() { 113 return root.getResourceAsStream(resourceName); 114 } 115 }); 116 } else { 117 i = root.getResourceAsStream(resourceName); 118 } 119 120 if (i == null && required) { 121 throw new MissingResourceException("could not locate data " +resourceName, root.getPackage().getName(), resourceName); 122 } 123 checkStreamForBinaryData(i, resourceName); 124 return i; 125 } 126 127 /** 128 * Should be called only from ICUBinary.getData() or from convenience overloads here. 129 */ getStream(final ClassLoader loader, final String resourceName, boolean required)130 static InputStream getStream(final ClassLoader loader, final String resourceName, boolean required) { 131 InputStream i = null; 132 if (System.getSecurityManager() != null) { 133 i = AccessController.doPrivileged(new PrivilegedAction<InputStream>() { 134 @Override 135 public InputStream run() { 136 return loader.getResourceAsStream(resourceName); 137 } 138 }); 139 } else { 140 i = loader.getResourceAsStream(resourceName); 141 } 142 if (i == null && required) { 143 throw new MissingResourceException("could not locate data", loader.toString(), resourceName); 144 } 145 checkStreamForBinaryData(i, resourceName); 146 return i; 147 } 148 149 @SuppressWarnings("unused") // used if logBinaryDataFromInputStream == true checkStreamForBinaryData(InputStream is, String resourceName)150 private static void checkStreamForBinaryData(InputStream is, String resourceName) { 151 if (logBinaryDataFromInputStream && is != null && resourceName.indexOf(PACKAGE_NAME) >= 0) { 152 try { 153 is.mark(32); 154 byte[] b = new byte[32]; 155 int len = is.read(b); 156 if (len == 32 && b[2] == (byte)0xda && b[3] == 0x27) { 157 String msg = String.format( 158 "ICU binary data file loaded from Class/ClassLoader as InputStream " + 159 "from %s: MappedData %02x%02x%02x%02x dataFormat %02x%02x%02x%02x", 160 resourceName, 161 b[0], b[1], b[2], b[3], 162 b[12], b[13], b[14], b[15]); 163 logger.info(msg); 164 } 165 is.reset(); 166 } catch (IOException ignored) { 167 } 168 } 169 } 170 getStream(ClassLoader loader, String resourceName)171 public static InputStream getStream(ClassLoader loader, String resourceName){ 172 return getStream(loader,resourceName, false); 173 } 174 getRequiredStream(ClassLoader loader, String resourceName)175 public static InputStream getRequiredStream(ClassLoader loader, String resourceName){ 176 return getStream(loader, resourceName, true); 177 } 178 179 /** 180 * Convenience override that calls getStream(ICUData.class, resourceName, false); 181 * Returns null if the resource could not be found. 182 */ getStream(String resourceName)183 public static InputStream getStream(String resourceName) { 184 return getStream(ICUData.class, resourceName, false); 185 } 186 187 /** 188 * Convenience method that calls getStream(ICUData.class, resourceName, true). 189 * @throws MissingResourceException if the resource could not be found 190 */ getRequiredStream(String resourceName)191 public static InputStream getRequiredStream(String resourceName) { 192 return getStream(ICUData.class, resourceName, true); 193 } 194 195 /** 196 * Convenience override that calls getStream(root, resourceName, false); 197 * Returns null if the resource could not be found. 198 */ getStream(Class<?> root, String resourceName)199 public static InputStream getStream(Class<?> root, String resourceName) { 200 return getStream(root, resourceName, false); 201 } 202 203 /** 204 * Convenience method that calls getStream(root, resourceName, true). 205 * @throws MissingResourceException if the resource could not be found 206 */ getRequiredStream(Class<?> root, String resourceName)207 public static InputStream getRequiredStream(Class<?> root, String resourceName) { 208 return getStream(root, resourceName, true); 209 } 210 } 211