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-2016, International Business Machines Corporation and 6 * others. All Rights Reserved. 7 ****************************************************************************** 8 */ 9 10 package com.ibm.icu.impl; 11 12 import java.io.InputStream; 13 import java.util.ArrayList; 14 import java.util.Collections; 15 import java.util.Enumeration; 16 import java.util.List; 17 import java.util.MissingResourceException; 18 import java.util.PropertyResourceBundle; 19 import java.util.ResourceBundle; 20 21 import com.ibm.icu.util.ULocale; 22 import com.ibm.icu.util.UResourceBundle; 23 24 /** 25 * just a wrapper for Java ListResourceBundles and 26 * @author ram 27 * 28 */ 29 public final class ResourceBundleWrapper extends UResourceBundle { 30 private ResourceBundle bundle = null; 31 private String localeID = null; 32 private String baseName = null; 33 private List<String> keys = null; 34 35 /** Loader for bundle instances, for caching. */ 36 private static abstract class Loader { load()37 abstract ResourceBundleWrapper load(); 38 } 39 40 private static CacheBase<String, ResourceBundleWrapper, Loader> BUNDLE_CACHE = 41 new SoftCache<String, ResourceBundleWrapper, Loader>() { 42 @Override 43 protected ResourceBundleWrapper createInstance(String unusedKey, Loader loader) { 44 return loader.load(); 45 } 46 }; 47 ResourceBundleWrapper(ResourceBundle bundle)48 private ResourceBundleWrapper(ResourceBundle bundle){ 49 this.bundle=bundle; 50 } 51 52 @Override handleGetObject(String aKey)53 protected Object handleGetObject(String aKey){ 54 ResourceBundleWrapper current = this; 55 Object obj = null; 56 while(current!=null){ 57 try{ 58 obj = current.bundle.getObject(aKey); 59 break; 60 }catch(MissingResourceException ex){ 61 current = (ResourceBundleWrapper)current.getParent(); 62 } 63 } 64 if (obj == null){ 65 throw new MissingResourceException("Can't find resource for bundle " 66 +baseName 67 +", key "+aKey, 68 this.getClass().getName(), 69 aKey); 70 } 71 return obj; 72 } 73 74 @Override getKeys()75 public Enumeration<String> getKeys(){ 76 return Collections.enumeration(keys); 77 } 78 initKeysVector()79 private void initKeysVector(){ 80 ResourceBundleWrapper current = this; 81 keys = new ArrayList<String>(); 82 while(current!=null){ 83 Enumeration<String> e = current.bundle.getKeys(); 84 while(e.hasMoreElements()){ 85 String elem = e.nextElement(); 86 if(!keys.contains(elem)){ 87 keys.add(elem); 88 } 89 } 90 current = (ResourceBundleWrapper)current.getParent(); 91 } 92 } 93 @Override getLocaleID()94 protected String getLocaleID(){ 95 return localeID; 96 } 97 98 @Override getBaseName()99 protected String getBaseName(){ 100 return bundle.getClass().getName().replace('.','/'); 101 } 102 103 @Override getULocale()104 public ULocale getULocale(){ 105 return new ULocale(localeID); 106 } 107 108 @Override getParent()109 public UResourceBundle getParent(){ 110 return (UResourceBundle)parent; 111 } 112 113 // Flag for enabling/disabling debugging code 114 private static final boolean DEBUG = ICUDebug.enabled("resourceBundleWrapper"); 115 116 // This method is for super class's instantiateBundle method getBundleInstance(String baseName, String localeID, ClassLoader root, boolean disableFallback)117 public static ResourceBundleWrapper getBundleInstance(String baseName, String localeID, 118 ClassLoader root, boolean disableFallback) { 119 if (root == null) { 120 root = ClassLoaderUtil.getClassLoader(); 121 } 122 ResourceBundleWrapper b; 123 if (disableFallback) { 124 b = instantiateBundle(baseName, localeID, null, root, disableFallback); 125 } else { 126 b = instantiateBundle(baseName, localeID, ULocale.getDefault().getBaseName(), 127 root, disableFallback); 128 } 129 if(b==null){ 130 String separator ="_"; 131 if(baseName.indexOf('/')>=0){ 132 separator = "/"; 133 } 134 throw new MissingResourceException("Could not find the bundle "+ baseName+separator+ localeID,"",""); 135 } 136 return b; 137 } 138 localeIDStartsWithLangSubtag(String localeID, String lang)139 private static boolean localeIDStartsWithLangSubtag(String localeID, String lang) { 140 return localeID.startsWith(lang) && 141 (localeID.length() == lang.length() || localeID.charAt(lang.length()) == '_'); 142 } 143 instantiateBundle( final String baseName, final String localeID, final String defaultID, final ClassLoader root, final boolean disableFallback)144 private static ResourceBundleWrapper instantiateBundle( 145 final String baseName, final String localeID, final String defaultID, 146 final ClassLoader root, final boolean disableFallback) { 147 final String name = localeID.isEmpty() ? baseName : baseName + '_' + localeID; 148 String cacheKey = disableFallback ? name : name + '#' + defaultID; 149 return BUNDLE_CACHE.getInstance(cacheKey, new Loader() { 150 @Override 151 public ResourceBundleWrapper load() { 152 ResourceBundleWrapper parent = null; 153 int i = localeID.lastIndexOf('_'); 154 155 boolean loadFromProperties = false; 156 boolean parentIsRoot = false; 157 if (i != -1) { 158 String locName = localeID.substring(0, i); 159 parent = instantiateBundle(baseName, locName, defaultID, root, disableFallback); 160 }else if(!localeID.isEmpty()){ 161 parent = instantiateBundle(baseName, "", defaultID, root, disableFallback); 162 parentIsRoot = true; 163 } 164 ResourceBundleWrapper b = null; 165 try { 166 Class<? extends ResourceBundle> cls = 167 root.loadClass(name).asSubclass(ResourceBundle.class); 168 ResourceBundle bx = cls.newInstance(); 169 b = new ResourceBundleWrapper(bx); 170 if (parent != null) { 171 b.setParent(parent); 172 } 173 b.baseName=baseName; 174 b.localeID = localeID; 175 } catch (ClassNotFoundException e) { 176 loadFromProperties = true; 177 } catch (NoClassDefFoundError e) { 178 loadFromProperties = true; 179 } catch (Exception e) { 180 if (DEBUG) 181 System.out.println("failure"); 182 if (DEBUG) 183 System.out.println(e); 184 } 185 186 if (loadFromProperties) { 187 try { 188 final String resName = name.replace('.', '/') + ".properties"; 189 InputStream stream = java.security.AccessController.doPrivileged( 190 new java.security.PrivilegedAction<InputStream>() { 191 @Override 192 public InputStream run() { 193 return root.getResourceAsStream(resName); 194 } 195 } 196 ); 197 if (stream != null) { 198 // make sure it is buffered 199 stream = new java.io.BufferedInputStream(stream); 200 try { 201 b = new ResourceBundleWrapper(new PropertyResourceBundle(stream)); 202 if (parent != null) { 203 b.setParent(parent); 204 } 205 b.baseName=baseName; 206 b.localeID=localeID; 207 } catch (Exception ex) { 208 // throw away exception 209 } finally { 210 try { 211 stream.close(); 212 } catch (Exception ex) { 213 // throw away exception 214 } 215 } 216 } 217 218 // if a bogus locale is passed then the parent should be 219 // the default locale not the root locale! 220 if (b == null && !disableFallback && 221 !localeID.isEmpty() && localeID.indexOf('_') < 0 && 222 !localeIDStartsWithLangSubtag(defaultID, localeID)) { 223 // localeID is only a language subtag, different from the default language. 224 b = instantiateBundle(baseName, defaultID, defaultID, root, disableFallback); 225 } 226 // if still could not find the bundle then return the parent 227 if(b==null && (!parentIsRoot || !disableFallback)){ 228 b=parent; 229 } 230 } catch (Exception e) { 231 if (DEBUG) 232 System.out.println("failure"); 233 if (DEBUG) 234 System.out.println(e); 235 } 236 } 237 if(b!=null){ 238 b.initKeysVector(); 239 }else{ 240 if(DEBUG)System.out.println("Returning null for "+baseName+"_"+localeID); 241 } 242 return b; 243 }}); 244 } 245 } 246