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

org.apache.parquet.util.DynMethods Maven / Gradle / Ivy

/*
 * 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 org.apache.parquet.util;

import org.apache.parquet.Preconditions;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.Arrays;

import static org.apache.parquet.Exceptions.throwIfInstance;

public class DynMethods {

  /**
   * Convenience wrapper class around {@link java.lang.reflect.Method}.
   *
   * Allows callers to invoke the wrapped method with all Exceptions wrapped by
   * RuntimeException, or with a single Exception catch block.
   */
  public static class UnboundMethod {

    private final Method method;
    private final String name;
    private final int argLength;

    UnboundMethod(Method method, String name) {
      this.method = method;
      this.name = name;
      this.argLength = (method == null || method.isVarArgs()) ? -1 :
          method.getParameterTypes().length;
    }

    @SuppressWarnings("unchecked")
    public  R invokeChecked(Object target, Object... args) throws Exception {
      try {
        if (argLength < 0) {
          return (R) method.invoke(target, args);
        } else {
          return (R) method.invoke(target, Arrays.copyOfRange(args, 0, argLength));
        }

      } catch (InvocationTargetException e) {
        throwIfInstance(e.getCause(), Exception.class);
        throwIfInstance(e.getCause(), RuntimeException.class);
        throw new RuntimeException(e.getCause());
      }
    }

    public  R invoke(Object target, Object... args) {
      try {
        return this.invokeChecked(target, args);
      } catch (Exception e) {
        throwIfInstance(e, RuntimeException.class);
        throw new RuntimeException(e);
      }
    }

    /**
     * Returns this method as a BoundMethod for the given receiver.
     *
     * @param receiver an Object to receive the method invocation
     * @return a {@link BoundMethod} for this method and the receiver
     * @throws IllegalStateException if the method is static
     * @throws IllegalArgumentException if the receiver's class is incompatible
     */
    public BoundMethod bind(Object receiver) {
      Preconditions.checkState(!isStatic(),
          "Cannot bind static method %s", method.toGenericString());
      Preconditions.checkArgument(
          method.getDeclaringClass().isAssignableFrom(receiver.getClass()),
          "Cannot bind %s to instance of %s", method.toGenericString(), 
          receiver.getClass());

      return new BoundMethod(this, receiver);
    }

    /**
     * @return whether the method is a static method
     */
    public boolean isStatic() {
      return Modifier.isStatic(method.getModifiers());
    }

    /**
     * @return whether the method is a noop
     */
    public boolean isNoop() {
      return this == NOOP;
    }

    /**
     * Returns this method as a StaticMethod.
     *
     * @return a {@link StaticMethod} for this method
     * @throws IllegalStateException if the method is not static
     */
    public StaticMethod asStatic() {
      Preconditions.checkState(isStatic(), "Method is not static");
      return new StaticMethod(this);
    }

    public String toString() {
      return "DynMethods.UnboundMethod(name=" + name +" method=" +
          method.toGenericString() + ")";
    }

    /**
     * Singleton {@link UnboundMethod}, performs no operation and returns null.
     */
    private static UnboundMethod NOOP = new UnboundMethod(null, "NOOP") {
      @Override
      public  R invokeChecked(Object target, Object... args) throws Exception {
        return null;
      }

      @Override
      public BoundMethod bind(Object receiver) {
        return new BoundMethod(this, receiver);
      }

      @Override
      public StaticMethod asStatic() {
        return new StaticMethod(this);
      }

      @Override
      public boolean isStatic() {
        return true;
      }

      @Override
      public String toString() {
        return "DynMethods.UnboundMethod(NOOP)";
      }
    };
  }

  public static class BoundMethod {
    private final UnboundMethod method;
    private final Object receiver;

    private BoundMethod(UnboundMethod method, Object receiver) {
      this.method = method;
      this.receiver = receiver;
    }

    public  R invokeChecked(Object... args) throws Exception {
      return method.invokeChecked(receiver, args);
    }

    public  R invoke(Object... args) {
      return method.invoke(receiver, args);
    }
  }

  public static class StaticMethod {
    private final UnboundMethod method;

    private StaticMethod(UnboundMethod method) {
      this.method = method;
    }

    public  R invokeChecked(Object... args) throws Exception {
      return method.invokeChecked(null, args);
    }

    public  R invoke(Object... args) {
      return method.invoke(null, args);
    }
  }

  public static class Builder {
    private final String name;
    private ClassLoader loader = Thread.currentThread().getContextClassLoader();
    private UnboundMethod method = null;

    public Builder(String methodName) {
      this.name = methodName;
    }

    /**
     * Set the {@link ClassLoader} used to lookup classes by name.
     * 

* If not set, the current thread's ClassLoader is used. * * @param loader a ClassLoader * @return this Builder for method chaining */ public Builder loader(ClassLoader loader) { this.loader = loader; return this; } /** * If no implementation has been found, adds a NOOP method. * * Note: calls to impl will not match after this method is called! * * @return this Builder for method chaining */ public Builder orNoop() { if (method == null) { this.method = UnboundMethod.NOOP; } return this; } /** * Checks for an implementation, first finding the given class by name. * * @param className name of a class * @param methodName name of a method (different from constructor) * @param argClasses argument classes for the method * @return this Builder for method chaining */ public Builder impl(String className, String methodName, Class... argClasses) { // don't do any work if an implementation has been found if (method != null) { return this; } try { Class targetClass = Class.forName(className, true, loader); impl(targetClass, methodName, argClasses); } catch (ClassNotFoundException e) { // not the right implementation } return this; } /** * Checks for an implementation, first finding the given class by name. * * The name passed to the constructor is the method name used. * * @param className name of a class * @param argClasses argument classes for the method * @return this Builder for method chaining */ public Builder impl(String className, Class... argClasses) { impl(className, name, argClasses); return this; } /** * Checks for a method implementation. * * @param targetClass the class to check for an implementation * @param methodName name of a method (different from constructor) * @param argClasses argument classes for the method * @return this Builder for method chaining */ public Builder impl(Class targetClass, String methodName, Class... argClasses) { // don't do any work if an implementation has been found if (method != null) { return this; } try { this.method = new UnboundMethod( targetClass.getMethod(methodName, argClasses), name); } catch (NoSuchMethodException e) { // not the right implementation } return this; } /** * Checks for a method implementation. * * The name passed to the constructor is the method name used. * * @param targetClass the class to check for an implementation * @param argClasses argument classes for the method * @return this Builder for method chaining */ public Builder impl(Class targetClass, Class... argClasses) { impl(targetClass, name, argClasses); return this; } public Builder ctorImpl(Class targetClass, Class... argClasses) { // don't do any work if an implementation has been found if (method != null) { return this; } try { this.method = new DynConstructors.Builder() .impl(targetClass, argClasses) .buildChecked(); } catch (NoSuchMethodException e) { // not the right implementation } return this; } public Builder ctorImpl(String className, Class... argClasses) { // don't do any work if an implementation has been found if (method != null) { return this; } try { this.method = new DynConstructors.Builder() .impl(className, argClasses) .buildChecked(); } catch (NoSuchMethodException e) { // not the right implementation } return this; } /** * Checks for an implementation, first finding the given class by name. * * @param className name of a class * @param methodName name of a method (different from constructor) * @param argClasses argument classes for the method * @return this Builder for method chaining */ public Builder hiddenImpl(String className, String methodName, Class... argClasses) { // don't do any work if an implementation has been found if (method != null) { return this; } try { Class targetClass = Class.forName(className, true, loader); hiddenImpl(targetClass, methodName, argClasses); } catch (ClassNotFoundException e) { // not the right implementation } return this; } /** * Checks for an implementation, first finding the given class by name. * * The name passed to the constructor is the method name used. * * @param className name of a class * @param argClasses argument classes for the method * @return this Builder for method chaining */ public Builder hiddenImpl(String className, Class... argClasses) { hiddenImpl(className, name, argClasses); return this; } /** * Checks for a method implementation. * * @param targetClass the class to check for an implementation * @param methodName name of a method (different from constructor) * @param argClasses argument classes for the method * @return this Builder for method chaining */ public Builder hiddenImpl(Class targetClass, String methodName, Class... argClasses) { // don't do any work if an implementation has been found if (method != null) { return this; } try { Method hidden = targetClass.getDeclaredMethod(methodName, argClasses); AccessController.doPrivileged(new MakeAccessible(hidden)); this.method = new UnboundMethod(hidden, name); } catch (SecurityException | NoSuchMethodException e) { // unusable or not the right implementation } return this; } /** * Checks for a method implementation. * * The name passed to the constructor is the method name used. * * @param targetClass the class to check for an implementation * @param argClasses argument classes for the method * @return this Builder for method chaining */ public Builder hiddenImpl(Class targetClass, Class... argClasses) { hiddenImpl(targetClass, name, argClasses); return this; } /** * Returns the first valid implementation as a UnboundMethod or throws a * NoSuchMethodException if there is none. * * @return a {@link UnboundMethod} with a valid implementation * @throws NoSuchMethodException if no implementation was found */ public UnboundMethod buildChecked() throws NoSuchMethodException { if (method != null) { return method; } else { throw new NoSuchMethodException("Cannot find method: " + name); } } /** * Returns the first valid implementation as a UnboundMethod or throws a * RuntimeError if there is none. * * @return a {@link UnboundMethod} with a valid implementation * @throws RuntimeException if no implementation was found */ public UnboundMethod build() { if (method != null) { return method; } else { throw new RuntimeException("Cannot find method: " + name); } } /** * Returns the first valid implementation as a BoundMethod or throws a * NoSuchMethodException if there is none. * * @param receiver an Object to receive the method invocation * @return a {@link BoundMethod} with a valid implementation and receiver * @throws IllegalStateException if the method is static * @throws IllegalArgumentException if the receiver's class is incompatible * @throws NoSuchMethodException if no implementation was found */ public BoundMethod buildChecked(Object receiver) throws NoSuchMethodException { return buildChecked().bind(receiver); } /** * Returns the first valid implementation as a BoundMethod or throws a * RuntimeError if there is none. * * @param receiver an Object to receive the method invocation * @return a {@link BoundMethod} with a valid implementation and receiver * @throws IllegalStateException if the method is static * @throws IllegalArgumentException if the receiver's class is incompatible * @throws RuntimeException if no implementation was found */ public BoundMethod build(Object receiver) { return build().bind(receiver); } /** * Returns the first valid implementation as a StaticMethod or throws a * NoSuchMethodException if there is none. * * @return a {@link StaticMethod} with a valid implementation * @throws IllegalStateException if the method is not static * @throws NoSuchMethodException if no implementation was found */ public StaticMethod buildStaticChecked() throws NoSuchMethodException { return buildChecked().asStatic(); } /** * Returns the first valid implementation as a StaticMethod or throws a * RuntimeException if there is none. * * @return a {@link StaticMethod} with a valid implementation * @throws IllegalStateException if the method is not static * @throws RuntimeException if no implementation was found */ public StaticMethod buildStatic() { return build().asStatic(); } } private static class MakeAccessible implements PrivilegedAction { private Method hidden; public MakeAccessible(Method hidden) { this.hidden = hidden; } @Override public Void run() { hidden.setAccessible(true); return null; } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy