1 /*
   2  * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package sun.util.locale.provider;
  27 
  28 import java.io.IOException;
  29 import java.io.InputStream;
  30 import java.io.UncheckedIOException;
  31 import java.lang.reflect.Constructor;
  32 import java.lang.reflect.InvocationTargetException;
  33 import java.lang.reflect.Modifier;
  34 import java.lang.reflect.Module;
  35 import java.security.AccessController;
  36 import java.security.PrivilegedAction;
  37 import java.util.PropertyResourceBundle;
  38 import java.util.ResourceBundle;
  39 
  40 import jdk.internal.misc.BootLoader;
  41 import sun.misc.Unsafe;
  42 
  43 
  44 /**
  45  * ResourceBundleProviderSupport provides convenience methods for loading
  46  * resource bundles.
  47  */
  48 public class ResourceBundleProviderSupport {
  49     /**
  50      * Loads a {@code ResourceBundle} of the given {@code bundleName} local to
  51      * the given {@code module}.
  52      *
  53      * @param module     the module from which the {@code ResourceBundle} is loaded
  54      * @param bundleName the bundle name for the {@code ResourceBundle} class,
  55      *                   such as "com.example.app.MyResources_fr"
  56      * @return the {@code ResourceBundle}, or null if no {@code ResourceBundle} is found
  57      */
  58     public static ResourceBundle loadResourceBundle(Module module, String bundleName)
  59     {
  60         // TODO: security permission check to access a bundle in another module?
  61         PrivilegedAction<Class<?>> pa = () -> Class.forName(module, bundleName);
  62         Class<?> c = AccessController.doPrivileged(pa);
  63         if (c != null && ResourceBundle.class.isAssignableFrom(c)) {
  64             try {
  65                 @SuppressWarnings("unchecked")
  66                 Class<ResourceBundle> bundleClass = (Class<ResourceBundle>) c;
  67                 Constructor<ResourceBundle> ctor = bundleClass.getConstructor();
  68                 if (!Modifier.isPublic(ctor.getModifiers())) {
  69                     return null;
  70                 }
  71 
  72                 // java.base may not be able to read the bundleClass's module.
  73                 PrivilegedAction<Void> pa1 = () -> { ctor.setAccessible(true); return null; };
  74                 AccessController.doPrivileged(pa1);
  75                 try {
  76                     return ctor.newInstance((Object[]) null);
  77                 } catch (InvocationTargetException e) {
  78                     Unsafe.getUnsafe().throwException(e.getTargetException());
  79                 } catch (InstantiationException | IllegalAccessException e) {
  80                     throw new InternalError(e);
  81                 }
  82             } catch (NoSuchMethodException e) {
  83             }
  84         }
  85         return null;
  86     }
  87 
  88     /**
  89      * Loads properties of the given {@code bundleName} local to the given
  90      * {@code module} and returns a {@code ResourceBundle} produced from the
  91      * loaded properties.
  92      *
  93      * @apiNote This method is intended for internal use.  Need to refactor.
  94      *
  95      * @param module     the module from which the properties are loaded
  96      * @param bundleName the bundle name of the properties,
  97      *                   such as "com.example.app.MyResources_de"
  98      * @return the {@code ResourceBundle} produced from the loaded properties,
  99      *         or null if no properties are found
 100      * @see PropertiesResourceBundle
 101      */
 102     public static ResourceBundle loadPropertyResourceBundle(Module module, String bundleName)
 103             throws IOException
 104     {
 105         String resourceName = toResourceName(bundleName, "properties");
 106         if (resourceName == null) {
 107             return null;
 108         }
 109 
 110         PrivilegedAction<InputStream> pa = () -> {
 111             try {
 112                 return module.getResourceAsStream(resourceName);
 113             } catch (IOException e) {
 114                 throw new UncheckedIOException(e);
 115             }
 116         };
 117         try (InputStream stream = AccessController.doPrivileged(pa)) {
 118             if (stream != null) {
 119                 return new PropertyResourceBundle(stream);
 120             } else {
 121                 return null;
 122             }
 123         } catch (UncheckedIOException e) {
 124             throw e.getCause();
 125         }
 126     }
 127 
 128     private static String toResourceName(String bundleName, String suffix) {
 129         if (bundleName.contains("://")) {
 130             return null;
 131         }
 132         StringBuilder sb = new StringBuilder(bundleName.length() + 1 + suffix.length());
 133         sb.append(bundleName.replace('.', '/')).append('.').append(suffix);
 134         return sb.toString();
 135     }
 136 }