18cdfc1007818eec034fd6f547425cfda7369ed49Neil Fuller/* 28cdfc1007818eec034fd6f547425cfda7369ed49Neil Fuller * Licensed to the Apache Software Foundation (ASF) under one or more 38cdfc1007818eec034fd6f547425cfda7369ed49Neil Fuller * contributor license agreements. See the NOTICE file distributed with 48cdfc1007818eec034fd6f547425cfda7369ed49Neil Fuller * this work for additional information regarding copyright ownership. 58cdfc1007818eec034fd6f547425cfda7369ed49Neil Fuller * The ASF licenses this file to You under the Apache License, Version 2.0 68cdfc1007818eec034fd6f547425cfda7369ed49Neil Fuller * (the "License"); you may not use this file except in compliance with 78cdfc1007818eec034fd6f547425cfda7369ed49Neil Fuller * the License. You may obtain a copy of the License at 88cdfc1007818eec034fd6f547425cfda7369ed49Neil Fuller * 98cdfc1007818eec034fd6f547425cfda7369ed49Neil Fuller * http://www.apache.org/licenses/LICENSE-2.0 108cdfc1007818eec034fd6f547425cfda7369ed49Neil Fuller * 118cdfc1007818eec034fd6f547425cfda7369ed49Neil Fuller * Unless required by applicable law or agreed to in writing, software 128cdfc1007818eec034fd6f547425cfda7369ed49Neil Fuller * distributed under the License is distributed on an "AS IS" BASIS, 138cdfc1007818eec034fd6f547425cfda7369ed49Neil Fuller * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 148cdfc1007818eec034fd6f547425cfda7369ed49Neil Fuller * See the License for the specific language governing permissions and 158cdfc1007818eec034fd6f547425cfda7369ed49Neil Fuller * limitations under the License. 168cdfc1007818eec034fd6f547425cfda7369ed49Neil Fuller */ 178cdfc1007818eec034fd6f547425cfda7369ed49Neil Fuller 188cdfc1007818eec034fd6f547425cfda7369ed49Neil Fullerpackage com.squareup.okhttp.internal; 198cdfc1007818eec034fd6f547425cfda7369ed49Neil Fuller 208cdfc1007818eec034fd6f547425cfda7369ed49Neil Fullerimport java.lang.reflect.InvocationTargetException; 218cdfc1007818eec034fd6f547425cfda7369ed49Neil Fullerimport java.lang.reflect.Method; 228cdfc1007818eec034fd6f547425cfda7369ed49Neil Fullerimport java.lang.reflect.Modifier; 238cdfc1007818eec034fd6f547425cfda7369ed49Neil Fuller 248cdfc1007818eec034fd6f547425cfda7369ed49Neil Fuller/** 258cdfc1007818eec034fd6f547425cfda7369ed49Neil Fuller * Duck-typing for methods: Represents a method that may or may not be present on an object. 268cdfc1007818eec034fd6f547425cfda7369ed49Neil Fuller * 278cdfc1007818eec034fd6f547425cfda7369ed49Neil Fuller * @param <T> the type of the object the method might be on, typically an interface or base class 288cdfc1007818eec034fd6f547425cfda7369ed49Neil Fuller */ 298cdfc1007818eec034fd6f547425cfda7369ed49Neil Fullerclass OptionalMethod<T> { 308cdfc1007818eec034fd6f547425cfda7369ed49Neil Fuller 318cdfc1007818eec034fd6f547425cfda7369ed49Neil Fuller /** The return type of the method. null means "don't care". */ 328cdfc1007818eec034fd6f547425cfda7369ed49Neil Fuller private final Class<?> returnType; 338cdfc1007818eec034fd6f547425cfda7369ed49Neil Fuller 348cdfc1007818eec034fd6f547425cfda7369ed49Neil Fuller private final String methodName; 358cdfc1007818eec034fd6f547425cfda7369ed49Neil Fuller 368cdfc1007818eec034fd6f547425cfda7369ed49Neil Fuller private final Class[] methodParams; 378cdfc1007818eec034fd6f547425cfda7369ed49Neil Fuller 388cdfc1007818eec034fd6f547425cfda7369ed49Neil Fuller /** 398cdfc1007818eec034fd6f547425cfda7369ed49Neil Fuller * Creates an optional method. 408cdfc1007818eec034fd6f547425cfda7369ed49Neil Fuller * 418cdfc1007818eec034fd6f547425cfda7369ed49Neil Fuller * @param returnType the return type to required, null if it does not matter 428cdfc1007818eec034fd6f547425cfda7369ed49Neil Fuller * @param methodName the name of the method 438cdfc1007818eec034fd6f547425cfda7369ed49Neil Fuller * @param methodParams the method parameter types 448cdfc1007818eec034fd6f547425cfda7369ed49Neil Fuller */ 458cdfc1007818eec034fd6f547425cfda7369ed49Neil Fuller public OptionalMethod(Class<?> returnType, String methodName, Class... methodParams) { 468cdfc1007818eec034fd6f547425cfda7369ed49Neil Fuller this.returnType = returnType; 478cdfc1007818eec034fd6f547425cfda7369ed49Neil Fuller this.methodName = methodName; 488cdfc1007818eec034fd6f547425cfda7369ed49Neil Fuller this.methodParams = methodParams; 498cdfc1007818eec034fd6f547425cfda7369ed49Neil Fuller } 508cdfc1007818eec034fd6f547425cfda7369ed49Neil Fuller 518cdfc1007818eec034fd6f547425cfda7369ed49Neil Fuller /** 528cdfc1007818eec034fd6f547425cfda7369ed49Neil Fuller * Returns true if the method exists on the supplied {@code target}. 538cdfc1007818eec034fd6f547425cfda7369ed49Neil Fuller */ 548cdfc1007818eec034fd6f547425cfda7369ed49Neil Fuller public boolean isSupported(T target) { 558cdfc1007818eec034fd6f547425cfda7369ed49Neil Fuller return getMethod(target.getClass()) != null; 568cdfc1007818eec034fd6f547425cfda7369ed49Neil Fuller } 578cdfc1007818eec034fd6f547425cfda7369ed49Neil Fuller 588cdfc1007818eec034fd6f547425cfda7369ed49Neil Fuller /** 598cdfc1007818eec034fd6f547425cfda7369ed49Neil Fuller * Invokes the method on {@code target} with {@code args}. If the method does not exist or is not 608cdfc1007818eec034fd6f547425cfda7369ed49Neil Fuller * public then {@code null} is returned. See also 618cdfc1007818eec034fd6f547425cfda7369ed49Neil Fuller * {@link #invokeOptionalWithoutCheckedException(Object, Object...)}. 628cdfc1007818eec034fd6f547425cfda7369ed49Neil Fuller * 638cdfc1007818eec034fd6f547425cfda7369ed49Neil Fuller * @throws IllegalArgumentException if the arguments are invalid 648cdfc1007818eec034fd6f547425cfda7369ed49Neil Fuller * @throws InvocationTargetException if the invocation throws an exception 658cdfc1007818eec034fd6f547425cfda7369ed49Neil Fuller */ 668cdfc1007818eec034fd6f547425cfda7369ed49Neil Fuller public Object invokeOptional(T target, Object... args) throws InvocationTargetException { 678cdfc1007818eec034fd6f547425cfda7369ed49Neil Fuller Method m = getMethod(target.getClass()); 688cdfc1007818eec034fd6f547425cfda7369ed49Neil Fuller if (m == null) { 698cdfc1007818eec034fd6f547425cfda7369ed49Neil Fuller return null; 708cdfc1007818eec034fd6f547425cfda7369ed49Neil Fuller } 718cdfc1007818eec034fd6f547425cfda7369ed49Neil Fuller try { 728cdfc1007818eec034fd6f547425cfda7369ed49Neil Fuller return m.invoke(target, args); 738cdfc1007818eec034fd6f547425cfda7369ed49Neil Fuller } catch (IllegalAccessException e) { 748cdfc1007818eec034fd6f547425cfda7369ed49Neil Fuller return null; 758cdfc1007818eec034fd6f547425cfda7369ed49Neil Fuller } 768cdfc1007818eec034fd6f547425cfda7369ed49Neil Fuller } 778cdfc1007818eec034fd6f547425cfda7369ed49Neil Fuller 788cdfc1007818eec034fd6f547425cfda7369ed49Neil Fuller /** 798cdfc1007818eec034fd6f547425cfda7369ed49Neil Fuller * Invokes the method on {@code target}. If the method does not exist or is not 808cdfc1007818eec034fd6f547425cfda7369ed49Neil Fuller * public then {@code null} is returned. Any RuntimeException thrown by the method is thrown, 818cdfc1007818eec034fd6f547425cfda7369ed49Neil Fuller * checked exceptions are wrapped in an {@link AssertionError}. 828cdfc1007818eec034fd6f547425cfda7369ed49Neil Fuller * 838cdfc1007818eec034fd6f547425cfda7369ed49Neil Fuller * @throws IllegalArgumentException if the arguments are invalid 848cdfc1007818eec034fd6f547425cfda7369ed49Neil Fuller */ 858cdfc1007818eec034fd6f547425cfda7369ed49Neil Fuller public Object invokeOptionalWithoutCheckedException(T target, Object... args) { 868cdfc1007818eec034fd6f547425cfda7369ed49Neil Fuller try { 878cdfc1007818eec034fd6f547425cfda7369ed49Neil Fuller return invokeOptional(target, args); 888cdfc1007818eec034fd6f547425cfda7369ed49Neil Fuller } catch (InvocationTargetException e) { 898cdfc1007818eec034fd6f547425cfda7369ed49Neil Fuller Throwable targetException = e.getTargetException(); 908cdfc1007818eec034fd6f547425cfda7369ed49Neil Fuller if (targetException instanceof RuntimeException) { 918cdfc1007818eec034fd6f547425cfda7369ed49Neil Fuller throw (RuntimeException) targetException; 928cdfc1007818eec034fd6f547425cfda7369ed49Neil Fuller } 938cdfc1007818eec034fd6f547425cfda7369ed49Neil Fuller throw new AssertionError("Unexpected exception", targetException); 948cdfc1007818eec034fd6f547425cfda7369ed49Neil Fuller } 958cdfc1007818eec034fd6f547425cfda7369ed49Neil Fuller } 968cdfc1007818eec034fd6f547425cfda7369ed49Neil Fuller 978cdfc1007818eec034fd6f547425cfda7369ed49Neil Fuller /** 988cdfc1007818eec034fd6f547425cfda7369ed49Neil Fuller * Invokes the method on {@code target} with {@code args}. Throws an error if the method is not 998cdfc1007818eec034fd6f547425cfda7369ed49Neil Fuller * supported. See also {@link #invokeWithoutCheckedException(Object, Object...)}. 1008cdfc1007818eec034fd6f547425cfda7369ed49Neil Fuller * 1018cdfc1007818eec034fd6f547425cfda7369ed49Neil Fuller * @throws IllegalArgumentException if the arguments are invalid 1028cdfc1007818eec034fd6f547425cfda7369ed49Neil Fuller * @throws InvocationTargetException if the invocation throws an exception 1038cdfc1007818eec034fd6f547425cfda7369ed49Neil Fuller */ 1048cdfc1007818eec034fd6f547425cfda7369ed49Neil Fuller public Object invoke(T target, Object... args) throws InvocationTargetException { 1058cdfc1007818eec034fd6f547425cfda7369ed49Neil Fuller Method m = getMethod(target.getClass()); 1068cdfc1007818eec034fd6f547425cfda7369ed49Neil Fuller if (m == null) { 1078cdfc1007818eec034fd6f547425cfda7369ed49Neil Fuller throw new AssertionError("Method " + methodName + " not supported for object " + target); 1088cdfc1007818eec034fd6f547425cfda7369ed49Neil Fuller } 1098cdfc1007818eec034fd6f547425cfda7369ed49Neil Fuller try { 1108cdfc1007818eec034fd6f547425cfda7369ed49Neil Fuller return m.invoke(target, args); 1118cdfc1007818eec034fd6f547425cfda7369ed49Neil Fuller } catch (IllegalAccessException e) { 1128cdfc1007818eec034fd6f547425cfda7369ed49Neil Fuller // Method should be public: we checked. 1138cdfc1007818eec034fd6f547425cfda7369ed49Neil Fuller throw new AssertionError("Unexpectedly could not call: " + m, e); 1148cdfc1007818eec034fd6f547425cfda7369ed49Neil Fuller } 1158cdfc1007818eec034fd6f547425cfda7369ed49Neil Fuller } 1168cdfc1007818eec034fd6f547425cfda7369ed49Neil Fuller 1178cdfc1007818eec034fd6f547425cfda7369ed49Neil Fuller /** 1188cdfc1007818eec034fd6f547425cfda7369ed49Neil Fuller * Invokes the method on {@code target}. Throws an error if the method is not supported. Any 1198cdfc1007818eec034fd6f547425cfda7369ed49Neil Fuller * RuntimeException thrown by the method is thrown, checked exceptions are wrapped in 1208cdfc1007818eec034fd6f547425cfda7369ed49Neil Fuller * an {@link AssertionError}. 1218cdfc1007818eec034fd6f547425cfda7369ed49Neil Fuller * 1228cdfc1007818eec034fd6f547425cfda7369ed49Neil Fuller * @throws IllegalArgumentException if the arguments are invalid 1238cdfc1007818eec034fd6f547425cfda7369ed49Neil Fuller */ 1248cdfc1007818eec034fd6f547425cfda7369ed49Neil Fuller public Object invokeWithoutCheckedException(T target, Object... args) { 1258cdfc1007818eec034fd6f547425cfda7369ed49Neil Fuller try { 1268cdfc1007818eec034fd6f547425cfda7369ed49Neil Fuller return invoke(target, args); 1278cdfc1007818eec034fd6f547425cfda7369ed49Neil Fuller } catch (InvocationTargetException e) { 1288cdfc1007818eec034fd6f547425cfda7369ed49Neil Fuller Throwable targetException = e.getTargetException(); 1298cdfc1007818eec034fd6f547425cfda7369ed49Neil Fuller if (targetException instanceof RuntimeException) { 1308cdfc1007818eec034fd6f547425cfda7369ed49Neil Fuller throw (RuntimeException) targetException; 1318cdfc1007818eec034fd6f547425cfda7369ed49Neil Fuller } 1328cdfc1007818eec034fd6f547425cfda7369ed49Neil Fuller throw new AssertionError("Unexpected exception", targetException); 1338cdfc1007818eec034fd6f547425cfda7369ed49Neil Fuller } 1348cdfc1007818eec034fd6f547425cfda7369ed49Neil Fuller } 1358cdfc1007818eec034fd6f547425cfda7369ed49Neil Fuller 1368cdfc1007818eec034fd6f547425cfda7369ed49Neil Fuller /** 1378cdfc1007818eec034fd6f547425cfda7369ed49Neil Fuller * Perform a lookup for the method. No caching. 1388cdfc1007818eec034fd6f547425cfda7369ed49Neil Fuller * In order to return a method the method name and arguments must match those specified when 1398cdfc1007818eec034fd6f547425cfda7369ed49Neil Fuller * the {@link OptionalMethod} was created. If the return type is specified (i.e. non-null) it 1408cdfc1007818eec034fd6f547425cfda7369ed49Neil Fuller * must also be compatible. The method must also be public. 1418cdfc1007818eec034fd6f547425cfda7369ed49Neil Fuller */ 1428cdfc1007818eec034fd6f547425cfda7369ed49Neil Fuller private Method getMethod(Class<?> clazz) { 1438cdfc1007818eec034fd6f547425cfda7369ed49Neil Fuller Method method = null; 1448cdfc1007818eec034fd6f547425cfda7369ed49Neil Fuller if (methodName != null) { 1458cdfc1007818eec034fd6f547425cfda7369ed49Neil Fuller method = getPublicMethod(clazz, methodName, methodParams); 1468cdfc1007818eec034fd6f547425cfda7369ed49Neil Fuller if (method != null 1478cdfc1007818eec034fd6f547425cfda7369ed49Neil Fuller && returnType != null 1488cdfc1007818eec034fd6f547425cfda7369ed49Neil Fuller && !returnType.isAssignableFrom(method.getReturnType())) { 1498cdfc1007818eec034fd6f547425cfda7369ed49Neil Fuller 1508cdfc1007818eec034fd6f547425cfda7369ed49Neil Fuller // If the return type is non-null it must be compatible. 1518cdfc1007818eec034fd6f547425cfda7369ed49Neil Fuller method = null; 1528cdfc1007818eec034fd6f547425cfda7369ed49Neil Fuller } 1538cdfc1007818eec034fd6f547425cfda7369ed49Neil Fuller } 1548cdfc1007818eec034fd6f547425cfda7369ed49Neil Fuller return method; 1558cdfc1007818eec034fd6f547425cfda7369ed49Neil Fuller } 1568cdfc1007818eec034fd6f547425cfda7369ed49Neil Fuller 1578cdfc1007818eec034fd6f547425cfda7369ed49Neil Fuller private static Method getPublicMethod(Class<?> clazz, String methodName, Class[] parameterTypes) { 1588cdfc1007818eec034fd6f547425cfda7369ed49Neil Fuller Method method = null; 1598cdfc1007818eec034fd6f547425cfda7369ed49Neil Fuller try { 1608cdfc1007818eec034fd6f547425cfda7369ed49Neil Fuller method = clazz.getMethod(methodName, parameterTypes); 1618cdfc1007818eec034fd6f547425cfda7369ed49Neil Fuller if ((method.getModifiers() & Modifier.PUBLIC) == 0) { 1628cdfc1007818eec034fd6f547425cfda7369ed49Neil Fuller method = null; 1638cdfc1007818eec034fd6f547425cfda7369ed49Neil Fuller } 1648cdfc1007818eec034fd6f547425cfda7369ed49Neil Fuller } catch (NoSuchMethodException e) { 1658cdfc1007818eec034fd6f547425cfda7369ed49Neil Fuller // None. 1668cdfc1007818eec034fd6f547425cfda7369ed49Neil Fuller } 1678cdfc1007818eec034fd6f547425cfda7369ed49Neil Fuller return method; 1688cdfc1007818eec034fd6f547425cfda7369ed49Neil Fuller } 1698cdfc1007818eec034fd6f547425cfda7369ed49Neil Fuller} 170