001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.commons.jexl3.internal.introspection; 018 019import java.lang.reflect.InvocationTargetException; 020import org.apache.commons.jexl3.JexlException; 021 022/** 023 * Specialized executor to set a property of an object. 024 * <p>Duck as in duck-typing for an interface like: 025 * <code> 026 * interface Setable { 027 * Object set(Object property, Object value); 028 * } 029 * </code> 030 * or 031 * <code> 032 * interface Putable { 033 * Object put(Object property, Object value); 034 * } 035 * </code> 036 * </p> 037 * @since 2.0 038 */ 039public final class DuckSetExecutor extends AbstractExecutor.Set { 040 /** The property, may be null. */ 041 private final Object property; 042 /** The property value class. */ 043 private final Class<?> valueClass; 044 045 /** 046 * Discovers a DuckSetExecutor. 047 * 048 * @param is the introspector 049 * @param clazz the class to find the set method from 050 * @param key the key to use as 1st argument to the set method 051 * @param value the value to use as 2nd argument to the set method 052 * @return the executor if found, null otherwise 053 */ 054 public static DuckSetExecutor discover(final Introspector is, final Class<?> clazz, final Object key, final Object value) { 055 java.lang.reflect.Method method = is.getMethod(clazz, "set", makeArgs(key, value)); 056 if (method == null) { 057 method = is.getMethod(clazz, "put", makeArgs(key, value)); 058 } 059 return method == null? null : new DuckSetExecutor(clazz, method, key, value); 060 } 061 062 /** 063 * Creates an instance. 064 * @param clazz the class the set method applies to 065 * @param method the method called through this executor 066 * @param key the key to use as 1st argument to the set method 067 * @param value the value to use as 2nd argument to the set method 068 */ 069 private DuckSetExecutor(final Class<?> clazz, final java.lang.reflect.Method method, final Object key, final Object value) { 070 super(clazz, method); 071 property = key; 072 valueClass = classOf(value); 073 } 074 075 @Override 076 public Object getTargetProperty() { 077 return property; 078 } 079 080 @Override 081 public Object invoke(final Object obj, final Object value) throws IllegalAccessException, InvocationTargetException { 082 final Object[] pargs = {property, value}; 083 if (method != null) { 084 method.invoke(obj, pargs); 085 } 086 return value; 087 } 088 089 @Override 090 public Object tryInvoke(final Object obj, final Object key, final Object value) { 091 if (obj != null 092 && objectClass.equals(obj.getClass()) 093 && method != null 094 && ((property != null && property.equals(key)) 095 || (property == null && key == null)) 096 && valueClass.equals(classOf(value))) { 097 try { 098 final Object[] args = {property, value}; 099 method.invoke(obj, args); 100 return value; 101 } catch (IllegalAccessException | IllegalArgumentException xill) { 102 return TRY_FAILED;// fail 103 } catch (final InvocationTargetException xinvoke) { 104 throw JexlException.tryFailed(xinvoke); // throw 105 } 106 } 107 return TRY_FAILED; 108 } 109}