All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.hazelcast.shaded.org.apache.calcite.runtime.Resources Maven / Gradle / Ivy

There is a newer version: 5.5.0
Show newest version
/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to you under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.hazelcast.shaded.org.apache.calcite.runtime;

import com.hazelcast.shaded.org.checkerframework.checker.initialization.qual.UnderInitialization;
import com.hazelcast.shaded.org.checkerframework.checker.nullness.qual.Nullable;
import com.hazelcast.shaded.org.checkerframework.checker.nullness.qual.PolyNull;
import com.hazelcast.shaded.org.checkerframework.checker.nullness.qual.RequiresNonNull;

import java.io.IOException;
import java.io.InputStream;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Proxy;
import java.lang.reflect.Type;
import java.text.DateFormat;
import java.text.Format;
import java.text.MessageFormat;
import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.EnumSet;
import java.util.Enumeration;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.MissingResourceException;
import java.util.Properties;
import java.util.PropertyResourceBundle;
import java.util.ResourceBundle;
import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentHashMap;

import static com.hazelcast.shaded.org.apache.calcite.linq4j.Nullness.castNonNull;

import static java.util.Objects.requireNonNull;

/**
 * Defining wrapper classes around resources that allow the compiler to check
 * whether the resources exist, and that uses of resources have the appropriate
 * number and types of arguments to match the message.
 */
public class Resources {
  private static final ThreadLocal<@Nullable Locale> MAP_THREAD_TO_LOCALE =
      new ThreadLocal<>();

  private Resources() {}

  /** Returns the preferred locale of the current thread, or
   * the default locale if the current thread has not called
   * {@link #setThreadLocale}.
   *
   * @return Locale */
  protected static Locale getThreadOrDefaultLocale() {
    Locale locale = getThreadLocale();
    if (locale == null) {
      return Locale.getDefault();
    } else {
      return locale;
    }
  }

  /** Sets the locale for the current thread.
   *
   * @param locale Locale */
  public static void setThreadLocale(Locale locale) {
    MAP_THREAD_TO_LOCALE.set(locale);
  }

  /** Returns the preferred locale of the current thread, or null if the
   * thread has not called {@link #setThreadLocale}.
   *
   * @return Locale */
  public static @Nullable Locale getThreadLocale() {
    return MAP_THREAD_TO_LOCALE.get();
  }

  /** Creates an instance of the resource object, using the class's name as
   * the name of the resource file.
   *
   * @see #create(String, Class)
   *
   * @param  Resource type
   * @param clazz Interface that contains a method for each resource
   * @return Instance of the interface that can be used to instantiate
   * resources
   */
  public static  T create(Class clazz) {
    return create(clazz.getCanonicalName(), clazz);
  }

  /** Creates an instance of the resource object.
   *
   * 

The resource interface has methods that return {@link Inst} and * {@link ExInst} values. Each of those methods is basically a factory method. * *

This method creates an instance of that interface backed by a resource * bundle, using a dynamic proxy ({@link Proxy}). * *

Suppose that base = "com.example.MyResource" and the current locale is * "en_US". A method * *


   *     @BaseMessage("Illegal binary string {0}")
   *     ExInst<IllegalArgumentException> illegalBinaryString(String a0);
   * 
* *

will look up a resource "IllegalBinaryString" from the resource file * "com/example/MyResource_en_US.properties", and substitute in the parameter * value {@code a0}. * *

The resource in the properties file may or may not be equal to the * base message "Illegal binary string {0}". But in the base locale, it * probably should be. * * @param Resource type * @param base Base name of the resource.properties file * @param clazz Interface that contains a method for each resource * @return Instance of the interface that can be used to instantiate * resources */ public static T create(@Nullable String base, Class clazz) { return create(base, EmptyPropertyAccessor.INSTANCE, clazz); } /** Creates an instance of the resource object that can access properties * but not resources. */ public static T create(PropertyAccessor accessor, Class clazz) { return create(null, accessor, clazz); } /** Creates an instance of the resource object that can access properties * but not resources. */ public static T create(final Properties properties, Class clazz) { return create(null, new PropertiesAccessor(properties), clazz); } private static T create(final @Nullable String base, final PropertyAccessor accessor, Class clazz) { //noinspection unchecked return (T) Proxy.newProxyInstance(clazz.getClassLoader(), new Class[] {clazz}, new InvocationHandler() { final Map cache = new ConcurrentHashMap<>(); @Override public Object invoke(Object proxy, Method method, @Nullable Object @Nullable [] args) throws Throwable { if (args == null || args.length == 0) { Object o = cache.get(method.getName()); if (o == null) { o = create(method, args); cache.put(method.getName(), o); } return o; } return create(method, args); } private Object create(Method method, @Nullable Object @Nullable [] args) throws NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException { if (method.equals(BuiltinMethod.OBJECT_TO_STRING.method)) { return toString(); } final Class returnType = method.getReturnType(); try { if (Inst.class.isAssignableFrom(returnType)) { final Constructor constructor = returnType.getConstructor(String.class, Locale.class, Method.class, Object[].class); final Locale locale = Resources.getThreadOrDefaultLocale(); return constructor.newInstance(base, locale, method, args != null ? args : new Object[0]); } else { final Constructor constructor = returnType.getConstructor(PropertyAccessor.class, Method.class); return constructor.newInstance(accessor, method); } } catch (InvocationTargetException e) { final Throwable e2 = e.getTargetException(); if (e2 instanceof RuntimeException) { throw (RuntimeException) e2; } if (e2 instanceof Error) { throw (Error) e2; } throw e; } } }); } /** Applies all validations to all resource methods in the given * resource object. * * @param o Resource object to validate */ public static void validate(Object o) { validate(o, EnumSet.allOf(Validation.class)); } /** Applies the given validations to all resource methods in the given * resource object. * * @param o Resource object to validate * @param validations Validations to perform */ public static void validate(Object o, EnumSet validations) { int count = 0; for (Method method : o.getClass().getMethods()) { if (!Modifier.isStatic(method.getModifiers()) && Inst.class.isAssignableFrom(method.getReturnType())) { ++count; final Class[] parameterTypes = method.getParameterTypes(); @Nullable Object[] args = new Object[parameterTypes.length]; for (int i = 0; i < parameterTypes.length; i++) { args[i] = zero(parameterTypes[i]); } try { Inst inst = (Inst) method.invoke(o, args); assert inst != null : "got null from " + method; inst.validate(validations); } catch (IllegalAccessException e) { throw new RuntimeException("in " + method, e); } catch (InvocationTargetException e) { throw new RuntimeException("in " + method, e.getCause()); } } } if (count == 0 && validations.contains(Validation.AT_LEAST_ONE)) { throw new AssertionError("resource object " + o + " contains no resources"); } } private static @Nullable Object zero(Class clazz) { return clazz == String.class ? "" : clazz == byte.class ? (byte) 0 : clazz == char.class ? (char) 0 : clazz == short.class ? (short) 0 : clazz == int.class ? 0 : clazz == long.class ? 0L : clazz == float.class ? 0F : clazz == double.class ? 0D : clazz == boolean.class ? false : null; } /** Returns whether two objects are equal or are both null. */ private static boolean equal(@Nullable Object o0, @Nullable Object o1) { return o0 == o1 || o0 != null && o0.equals(o1); } /** Element in a resource (either a resource or a property). */ public static class Element { protected final Method method; protected final String key; @SuppressWarnings("method.invocation.invalid") public Element(Method method) { this.method = method; this.key = deriveKey(); } protected String deriveKey() { final Resource resource = method.getAnnotation(Resource.class); if (resource != null) { return resource.value(); } else { final String name = method.getName(); return Character.toUpperCase(name.charAt(0)) + name.substring(1); } } } /** Resource instance. It contains the resource method (which * serves to identify the resource), the locale with which we * expect to render the resource, and any arguments. */ public static class Inst extends Element { private final Locale locale; protected final String base; protected final @Nullable Object[] args; public Inst(String base, Locale locale, Method method, @Nullable Object... args) { super(method); this.base = base; this.locale = locale; this.args = args; } @Override public boolean equals(@Nullable Object obj) { return this == obj || obj != null && obj.getClass() == this.getClass() && locale == ((Inst) obj).locale && method == ((Inst) obj).method && Arrays.equals(args, ((Inst) obj).args); } @Override public int hashCode() { return Arrays.asList(locale, method, Arrays.asList(args)).hashCode(); } public ResourceBundle bundle() { return ResourceBundle.getBundle(base, locale); } public Inst localize(Locale locale) { return new Inst(base, locale, method, args); } public void validate(EnumSet validations) { final ResourceBundle bundle = bundle(); for (Validation validation : validations) { switch (validation) { case BUNDLE_HAS_RESOURCE: if (!bundle.containsKey(key)) { String suggested = null; final BaseMessage annotation = method.getAnnotation(BaseMessage.class); if (annotation != null) { final String message = annotation.value(); suggested = "; add the following line to " + bundle.getBaseBundleName() + ".properties:\n" + key + '=' + message + "\n"; } throw new AssertionError("key '" + key + "' not found for resource '" + method.getName() + "' in bundle '" + bundle + "'" + (suggested == null ? "" : suggested)); } break; case MESSAGE_SPECIFIED: final BaseMessage annotation1 = method.getAnnotation(BaseMessage.class); if (annotation1 == null) { throw new AssertionError("resource '" + method.getName() + "' must specify BaseMessage"); } break; case EVEN_QUOTES: String message = requireNonNull( method.getAnnotation(BaseMessage.class), () -> "@BaseMessage is missing for resource '" + method.getName() + "'").value(); if (countQuotesIn(message) % 2 == 1) { throw new AssertionError("resource '" + method.getName() + "' should have even number of quotes"); } break; case MESSAGE_MATCH: final BaseMessage annotation2 = method.getAnnotation(BaseMessage.class); if (annotation2 != null) { final String value = annotation2.value(); final String value2 = bundle.containsKey(key) ? bundle.getString(key) : null; if (!equal(value, value2)) { throw new AssertionError("message for resource '" + method.getName() + "' is different between class and resource file"); } } break; case ARGUMENT_MATCH: String raw = raw(); MessageFormat format = new MessageFormat(raw); final @Nullable Format[] formats = format.getFormatsByArgumentIndex(); final List types = new ArrayList<>(); final Class[] parameterTypes = method.getParameterTypes(); for (int i = 0; i < formats.length; i++) { Format format1 = formats[i]; Class parameterType = parameterTypes[i]; final Class e; if (format1 instanceof NumberFormat) { e = parameterType == short.class || parameterType == int.class || parameterType == long.class || parameterType == float.class || parameterType == double.class || Number.class.isAssignableFrom(parameterType) ? parameterType : Number.class; } else if (format1 instanceof DateFormat) { e = Date.class; } else { e = String.class; } types.add(e); } final List> parameterTypeList = Arrays.asList(parameterTypes); if (!types.equals(parameterTypeList)) { throw new AssertionError("type mismatch in method '" + method.getName() + "' between message format elements " + types + " and method parameters " + parameterTypeList); } break; default: break; } } } private static int countQuotesIn(String message) { int count = 0; for (int i = 0, n = message.length(); i < n; i++) { if (message.charAt(i) == '\'') { ++count; } } return count; } public String str() { String message = raw(); MessageFormat format = new MessageFormat(message); format.setLocale(locale); return format.format(args); } public String raw() { try { return bundle().getString(key); } catch (MissingResourceException e) { // Resource is not in the bundle. (It is probably missing from the // .properties file.) Fall back to the base message. return requireNonNull( method.getAnnotation(BaseMessage.class), () -> "@BaseMessage is missing for resource '" + method.getName() + "'").value(); } } public Map getProperties() { // At present, annotations allow at most one property per resource. We // could design new annotations if any resource needed more. final Property property = method.getAnnotation(Property.class); if (property == null) { return Collections.emptyMap(); } else { return Collections.singletonMap(property.name(), property.value()); } } } /** Sub-class of {@link Inst} that can throw an exception. Requires caused * by exception.*/ public static class ExInstWithCause extends Inst { public ExInstWithCause(String base, Locale locale, Method method, @Nullable Object... args) { super(base, locale, method, args); } @Override public Inst localize(Locale locale) { return new ExInstWithCause(base, locale, method, args); } public T ex(@Nullable Throwable cause) { try { //noinspection unchecked final Class exceptionClass = getExceptionClass(method.getGenericReturnType()); Constructor constructor; final String str = str(); boolean causeInConstructor = false; try { constructor = exceptionClass.getConstructor(String.class, Throwable.class); causeInConstructor = true; } catch (NoSuchMethodException nsmStringThrowable) { try { constructor = exceptionClass.getConstructor(String.class); } catch (NoSuchMethodException nsmString) { // Ignore nsmString to encourage users to have (String, // Throwable) constructors. throw nsmStringThrowable; } } if (causeInConstructor) { return constructor.newInstance(str, cause); } T ex = constructor.newInstance(str); if (cause != null) { try { ex.initCause(cause); } catch (IllegalStateException iae) { // Sorry, unable to add cause via constructor and via initCause } } return ex; } catch (InstantiationException | IllegalAccessException | NoSuchMethodException e) { throw new RuntimeException(e); } catch (InvocationTargetException e) { if (e.getCause() instanceof Error) { throw (Error) e.getCause(); } else if (e.getCause() instanceof RuntimeException) { throw (RuntimeException) e.getCause(); } else { throw new RuntimeException(e); } } } public static Class getExceptionClass(Type type) { // Get exception type from ExInstWithCause type parameter. // ExInstWithCause might be one of super classes. // And, this class may be a parameter-less sub-class of a generic base. // // NOTE: We used to use // com.fasterxml.jackson.databind.type.TypeFactory.findTypeParameters. // More powerful, but we can't afford an extra dependency. final Type type0 = type; for (;;) { if (type instanceof ParameterizedType) { final Type[] types = ((ParameterizedType) type).getActualTypeArguments(); if (types.length >= 1 && types[0] instanceof Class && Throwable.class.isAssignableFrom((Class) types[0])) { return (Class) types[0]; } throw new IllegalStateException( "Unable to find superclass ExInstWithCause for " + type); } if (type instanceof Class) { Type superclass = ((Class) type).getGenericSuperclass(); if (superclass == null) { throw new IllegalStateException( "Unable to find superclass ExInstWithCause for " + type0); } type = superclass; } } } protected void validateException(Callable exSupplier) { Throwable cause = null; try { //noinspection ThrowableResultOfMethodCallIgnored final Exception ex = exSupplier.call(); if (ex == null) { cause = new NullPointerException(); } } catch (AssertionError | Exception e) { // catch(Exception) is required since Callable#call throws Exception. // But in practice, e will always be AssertionError or RuntimeException, // since exSupplier should be just a ex() call. cause = e; } if (cause != null) { throw new AssertionError("error instantiating exception for resource '" + method.getName() + "'", cause); } } @Override public void validate(EnumSet validations) { super.validate(validations); if (validations.contains(Validation.CREATE_EXCEPTION)) { validateException(() -> ex(new NullPointerException("test"))); } } } /** Sub-class of {@link Inst} that can throw an exception without caused * by. */ public static class ExInst extends ExInstWithCause { public ExInst(String base, Locale locale, Method method, Object... args) { super(base, locale, method, args); } public T ex() { return ex(null); } @Override public void validate(EnumSet validations) { super.validate(validations); if (validations.contains(Validation.CREATE_EXCEPTION)) { validateException(this::ex); } } } /** Property instance. */ public abstract static class Prop extends Element { protected final PropertyAccessor accessor; protected final boolean hasDefault; protected Prop(PropertyAccessor accessor, Method method) { super(method); this.accessor = accessor; final Default resource = method.getAnnotation(Default.class); this.hasDefault = resource != null; } @RequiresNonNull("method") protected final @Nullable Default getDefault( @UnderInitialization Prop this ) { if (hasDefault) { return castNonNull(method.getAnnotation(Default.class)); } else { return null; } } public boolean isSet() { return accessor.isSet(this); } public boolean hasDefault() { return hasDefault; } void checkDefault() { if (!hasDefault) { throw new NoDefaultValueException("Property " + key + " has no default value"); } } void checkDefault2() { if (!hasDefault) { throw new NoDefaultValueException("Property " + key + " is not set and has no default value"); } } } /** Integer property instance. */ public static class IntProp extends Prop { private final int defaultValue; public IntProp(PropertyAccessor accessor, Method method) { super(accessor, method); final Default resource = getDefault(); if (resource != null) { defaultValue = Integer.parseInt(resource.value(), 10); } else { defaultValue = 0; } } /** Returns the value of this integer property. */ public int get() { return accessor.intValue(this); } /** Returns the value of this integer property, returning the given default * value if the property is not set. */ public int get(int defaultValue) { return accessor.intValue(this, defaultValue); } public int defaultValue() { checkDefault(); return defaultValue; } } /** Boolean property instance. */ public static class BooleanProp extends Prop { private final boolean defaultValue; public BooleanProp(PropertyAccessor accessor, Method method) { super(accessor, method); final Default resource = getDefault(); if (resource != null) { defaultValue = Boolean.parseBoolean(resource.value()); } else { defaultValue = false; } } /** Returns the value of this boolean property. */ public boolean get() { return accessor.booleanValue(this); } /** Returns the value of this boolean property, returning the given default * value if the property is not set. */ public boolean get(boolean defaultValue) { return accessor.booleanValue(this, defaultValue); } public boolean defaultValue() { checkDefault(); return defaultValue; } } /** Double property instance. */ public static class DoubleProp extends Prop { private final double defaultValue; public DoubleProp(PropertyAccessor accessor, Method method) { super(accessor, method); final Default resource = getDefault(); if (resource != null) { defaultValue = Double.parseDouble(resource.value()); } else { defaultValue = 0d; } } /** Returns the value of this double property. */ public double get() { return accessor.doubleValue(this); } /** Returns the value of this double property, returning the given default * value if the property is not set. */ public double get(double defaultValue) { return accessor.doubleValue(this, defaultValue); } public double defaultValue() { checkDefault(); return defaultValue; } } /** String property instance. */ public static class StringProp extends Prop { private final @Nullable String defaultValue; public StringProp(PropertyAccessor accessor, Method method) { super(accessor, method); final Default resource = getDefault(); if (resource != null) { defaultValue = resource.value(); } else { defaultValue = null; } } /** Returns the value of this String property. */ public @Nullable String get() { return accessor.stringValue(this); } /** Returns the value of this String property, returning the given default * value if the property is not set. * *

If {@code defaultValue} is not null, never returns null. */ public @PolyNull String get(@PolyNull String defaultValue) { return accessor.stringValue(this, defaultValue); } public @Nullable String defaultValue() { checkDefault(); return defaultValue; } } /** Thrown when a default value is needed but a property does not have * one. */ public static class NoDefaultValueException extends RuntimeException { NoDefaultValueException(String message) { super(message); } } /** Means by which a resource can get values of properties, given their * name. */ public interface PropertyAccessor { boolean isSet(Prop p); int intValue(IntProp p); int intValue(IntProp p, int defaultValue); @Nullable String stringValue(StringProp p); @PolyNull String stringValue(StringProp p, @PolyNull String defaultValue); boolean booleanValue(BooleanProp p); boolean booleanValue(BooleanProp p, boolean defaultValue); double doubleValue(DoubleProp p); double doubleValue(DoubleProp p, double defaultValue); } enum EmptyPropertyAccessor implements PropertyAccessor { INSTANCE; @Override public boolean isSet(Prop p) { return false; } @Override public int intValue(IntProp p) { return p.defaultValue(); } @Override public int intValue(IntProp p, int defaultValue) { return defaultValue; } @Override public @Nullable String stringValue(StringProp p) { return p.defaultValue(); } @Override public @PolyNull String stringValue(StringProp p, @PolyNull String defaultValue) { return defaultValue; } @Override public boolean booleanValue(BooleanProp p) { return p.defaultValue(); } @Override public boolean booleanValue(BooleanProp p, boolean defaultValue) { return defaultValue; } @Override public double doubleValue(DoubleProp p) { return p.defaultValue(); } @Override public double doubleValue(DoubleProp p, double defaultValue) { return defaultValue; } } /** Types of validation that can be performed on a resource. */ public enum Validation { /** Checks that each method's resource key corresponds to a resource in the * bundle. */ BUNDLE_HAS_RESOURCE, /** Checks that there is at least one resource in the bundle. */ AT_LEAST_ONE, /** Checks that the base message annotation is on every resource. */ MESSAGE_SPECIFIED, /** Checks that every message contains even number of quotes. */ EVEN_QUOTES, /** Checks that the base message matches the message in the bundle. */ MESSAGE_MATCH, /** Checks that it is possible to create an exception. */ CREATE_EXCEPTION, /** Checks that the parameters of the method are consistent with the * format elements in the base message. */ ARGUMENT_MATCH, } /** The message in the default locale. */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface BaseMessage { String value(); } /** The name of the property in the resource file. */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface Resource { String value(); } /** Property of a resource. */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface Property { String name(); String value(); } /** Default value of a property. */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface Default { String value(); } /** * ShadowResourceBundle is an abstract base class for * {@link ResourceBundle} classes which are backed by a properties file. When * the class is created, it loads a properties file with the same name as the * class. * *

In the standard scheme (see {@link ResourceBundle}), if * you call {@link ResourceBundle#getBundle}("foo.MyResource"), * it first looks for a class called foo.MyResource, then * looks for a file called foo/MyResource.properties. If it finds * the file, it creates a {@link PropertyResourceBundle} and loads the class. * The problem is if you want to load the .properties file * into a dedicated class; ShadowResourceBundle helps with this * case. * *

You should create a class as follows: * *


   * package foo;
   *
   * class MyResource extends ShadowResourceBundle {
   *   public MyResource() throws java.io.IOException {
   *   }
   * }
   * 
* *

Then when you call * {@link ResourceBundle#getBundle ResourceBundle.getBundle("foo.MyResource")}, * it will find the class before the properties file, but still automatically * load the properties file based upon the name of the class. */ public abstract static class ShadowResourceBundle extends ResourceBundle { private final PropertyResourceBundle bundle; /** * Creates a ShadowResourceBundle, and reads resources from * a .properties file with the same name as the current class. * For example, if the class is called foo.MyResource_en_US, * reads from foo/MyResource_en_US.properties, then * foo/MyResource_en.properties, then * foo/MyResource.properties. * * @throws IOException on error */ protected ShadowResourceBundle() throws IOException { super(); Class clazz = getClass(); InputStream stream = openPropertiesFile(clazz); if (stream == null) { throw new IOException("could not open properties file for " + getClass()); } MyPropertyResourceBundle previousBundle = new MyPropertyResourceBundle(stream); bundle = previousBundle; stream.close(); // Now load properties files for parent locales, which we deduce from // the names of our super-class, and its super-class. while (true) { clazz = clazz.getSuperclass(); if (clazz == null || clazz == ShadowResourceBundle.class || !ResourceBundle.class.isAssignableFrom(clazz)) { break; } stream = openPropertiesFile(clazz); if (stream == null) { continue; } MyPropertyResourceBundle newBundle = new MyPropertyResourceBundle(stream); stream.close(); previousBundle.setParentTrojan(newBundle); previousBundle = newBundle; } } /** * Opens the properties file corresponding to a given class. The code is * copied from {@link ResourceBundle}. */ @SuppressWarnings("removal") private static @Nullable InputStream openPropertiesFile(Class clazz) { final ClassLoader loader = clazz.getClassLoader(); final String resName = clazz.getName().replace('.', '/') + ".properties"; return java.security.AccessController.doPrivileged( (java.security.PrivilegedAction<@Nullable InputStream>) () -> { if (loader != null) { return loader.getResourceAsStream(resName); } else { return ClassLoader.getSystemResourceAsStream(resName); } }); } @Override public Enumeration getKeys() { return bundle.getKeys(); } @Override protected Object handleGetObject(String key) { return bundle.getObject(key); } /** * Returns the instance of the baseName resource bundle * for the given locale. * *

This method should be called from a derived class, with the proper * casting: * *


     * class MyResource extends ShadowResourceBundle {
     *    ...
     *
     *    /**
     *      * Retrieves the instance of {@link MyResource} appropriate
     *      * to the given locale.
     *      **/
     *    public static MyResource instance(Locale locale) {
     *       return (MyResource) instance(
     *           MyResource.class.getName(), locale,
     *           ResourceBundle.getBundle(MyResource.class.getName(), locale));
     *    }
     *    ...
     * }
     * 
* * @param baseName Base name * @param locale Locale * @param bundle Resource bundle * @return Resource bundle */ protected static ShadowResourceBundle instance( String baseName, Locale locale, ResourceBundle bundle) { if (bundle instanceof PropertyResourceBundle) { throw new ClassCastException( "ShadowResourceBundle.instance('" + baseName + "','" + locale + "') found " + baseName + "_" + locale + ".properties but not " + baseName + "_" + locale + ".class"); } return (ShadowResourceBundle) bundle; } } /** Resource bundle based on properties. */ static class MyPropertyResourceBundle extends PropertyResourceBundle { public MyPropertyResourceBundle(InputStream stream) throws IOException { super(stream); } void setParentTrojan(ResourceBundle parent) { super.setParent(parent); } } enum BuiltinMethod { OBJECT_TO_STRING(Object.class, "toString"); @SuppressWarnings("ImmutableEnumChecker") public final Method method; BuiltinMethod(Class clazz, String methodName, Class... argumentTypes) { this.method = lookupMethod(clazz, methodName, argumentTypes); } /** * Finds a method of a given name that accepts a given set of arguments. * Includes in its search inherited methods and methods with wider argument * types. * * @param clazz Class against which method is invoked * @param methodName Name of method * @param argumentTypes Types of arguments * * @return A method with the given name that matches the arguments given * @throws RuntimeException if method not found */ public static Method lookupMethod(Class clazz, String methodName, Class... argumentTypes) { try { //noinspection unchecked return clazz.getMethod(methodName, argumentTypes); } catch (NoSuchMethodException e) { throw new RuntimeException( "while resolving method '" + methodName + Arrays.toString(argumentTypes) + "' in class " + clazz, e); } } } /** Implementation of {@link PropertyAccessor} that reads from a * {@link Properties}. */ private static class PropertiesAccessor implements PropertyAccessor { private final Properties properties; PropertiesAccessor(Properties properties) { this.properties = properties; } @Override public boolean isSet(Prop p) { return properties.containsKey(p.key); } @Override public int intValue(IntProp p) { final String s = properties.getProperty(p.key); if (s != null) { return Integer.parseInt(s, 10); } p.checkDefault2(); return p.defaultValue; } @Override public int intValue(IntProp p, int defaultValue) { final String s = properties.getProperty(p.key); return s == null ? defaultValue : Integer.parseInt(s, 10); } @Override public @Nullable String stringValue(StringProp p) { final String s = properties.getProperty(p.key); if (s != null) { return s; } p.checkDefault2(); return p.defaultValue; } @Override public @PolyNull String stringValue(StringProp p, @PolyNull String defaultValue) { final String s = properties.getProperty(p.key); return s == null ? defaultValue : s; } @Override public boolean booleanValue(BooleanProp p) { final String s = properties.getProperty(p.key); if (s != null) { return Boolean.parseBoolean(s); } p.checkDefault2(); return p.defaultValue; } @Override public boolean booleanValue(BooleanProp p, boolean defaultValue) { final String s = properties.getProperty(p.key); return s == null ? defaultValue : Boolean.parseBoolean(s); } @Override public double doubleValue(DoubleProp p) { final String s = properties.getProperty(p.key); if (s != null) { return Double.parseDouble(s); } p.checkDefault2(); return p.defaultValue; } @Override public double doubleValue(DoubleProp p, double defaultValue) { final String s = properties.getProperty(p.key); return s == null ? defaultValue : Double.parseDouble(s); } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy