/*
 *  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 okhttp3.internal.platform;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;

Duck-typing for methods: Represents a method that may or may not be present on an object.
Type parameters:
  • <T> – the type of the object the method might be on, typically an interface or base class
/** * Duck-typing for methods: Represents a method that may or may not be present on an object. * * @param <T> the type of the object the method might be on, typically an interface or base class */
class OptionalMethod<T> {
The return type of the method. null means "don't care".
/** The return type of the method. null means "don't care". */
private final Class<?> returnType; private final String methodName; private final Class[] methodParams;
Creates an optional method.
Params:
  • returnType – the return type to required, null if it does not matter
  • methodName – the name of the method
  • methodParams – the method parameter types
/** * Creates an optional method. * * @param returnType the return type to required, null if it does not matter * @param methodName the name of the method * @param methodParams the method parameter types */
OptionalMethod(Class<?> returnType, String methodName, Class... methodParams) { this.returnType = returnType; this.methodName = methodName; this.methodParams = methodParams; }
Returns true if the method exists on the supplied target.
/** * Returns true if the method exists on the supplied {@code target}. */
public boolean isSupported(T target) { return getMethod(target.getClass()) != null; }
Invokes the method on target with args. If the method does not exist or is not public then null is returned. See also invokeOptionalWithoutCheckedException.
Throws:
/** * Invokes the method on {@code target} with {@code args}. If the method does not exist or is not * public then {@code null} is returned. See also {@link #invokeOptionalWithoutCheckedException}. * * @throws IllegalArgumentException if the arguments are invalid * @throws InvocationTargetException if the invocation throws an exception */
public Object invokeOptional(T target, Object... args) throws InvocationTargetException { Method m = getMethod(target.getClass()); if (m == null) { return null; } try { return m.invoke(target, args); } catch (IllegalAccessException e) { return null; } }
Invokes the method on target. If the method does not exist or is not public then null is returned. Any RuntimeException thrown by the method is thrown, checked exceptions are wrapped in an AssertionError.
Throws:
/** * Invokes the method on {@code target}. If the method does not exist or is not public then * {@code null} is returned. Any RuntimeException thrown by the method is thrown, checked * exceptions are wrapped in an {@link AssertionError}. * * @throws IllegalArgumentException if the arguments are invalid */
public Object invokeOptionalWithoutCheckedException(T target, Object... args) { try { return invokeOptional(target, args); } catch (InvocationTargetException e) { Throwable targetException = e.getTargetException(); if (targetException instanceof RuntimeException) { throw (RuntimeException) targetException; } AssertionError error = new AssertionError("Unexpected exception"); error.initCause(targetException); throw error; } }
Invokes the method on target with args. Throws an error if the method is not supported. See also invokeWithoutCheckedException(Object, Object...).
Throws:
/** * Invokes the method on {@code target} with {@code args}. Throws an error if the method is not * supported. See also {@link #invokeWithoutCheckedException(Object, Object...)}. * * @throws IllegalArgumentException if the arguments are invalid * @throws InvocationTargetException if the invocation throws an exception */
public Object invoke(T target, Object... args) throws InvocationTargetException { Method m = getMethod(target.getClass()); if (m == null) { throw new AssertionError("Method " + methodName + " not supported for object " + target); } try { return m.invoke(target, args); } catch (IllegalAccessException e) { // Method should be public: we checked. AssertionError error = new AssertionError("Unexpectedly could not call: " + m); error.initCause(e); throw error; } }
Invokes the method on target. Throws an error if the method is not supported. Any RuntimeException thrown by the method is thrown, checked exceptions are wrapped in an AssertionError.
Throws:
/** * Invokes the method on {@code target}. Throws an error if the method is not supported. Any * RuntimeException thrown by the method is thrown, checked exceptions are wrapped in an {@link * AssertionError}. * * @throws IllegalArgumentException if the arguments are invalid */
public Object invokeWithoutCheckedException(T target, Object... args) { try { return invoke(target, args); } catch (InvocationTargetException e) { Throwable targetException = e.getTargetException(); if (targetException instanceof RuntimeException) { throw (RuntimeException) targetException; } AssertionError error = new AssertionError("Unexpected exception"); error.initCause(targetException); throw error; } }
Perform a lookup for the method. No caching. In order to return a method the method name and arguments must match those specified when the OptionalMethod was created. If the return type is specified (i.e. non-null) it must also be compatible. The method must also be public.
/** * Perform a lookup for the method. No caching. In order to return a method the method name and * arguments must match those specified when the {@link OptionalMethod} was created. If the return * type is specified (i.e. non-null) it must also be compatible. The method must also be public. */
private Method getMethod(Class<?> clazz) { Method method = null; if (methodName != null) { method = getPublicMethod(clazz, methodName, methodParams); if (method != null && returnType != null && !returnType.isAssignableFrom(method.getReturnType())) { // If the return type is non-null it must be compatible. method = null; } } return method; } private static Method getPublicMethod(Class<?> clazz, String methodName, Class[] parameterTypes) { Method method = null; try { method = clazz.getMethod(methodName, parameterTypes); if ((method.getModifiers() & Modifier.PUBLIC) == 0) { method = null; } } catch (NoSuchMethodException e) { // None. } return method; } }