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