1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 *
9 * https://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17 package org.apache.commons.jexl3.internal.introspection;
18
19 import java.lang.reflect.InvocationTargetException;
20
21 import org.apache.commons.jexl3.JexlException;
22
23 /**
24 * Specialized executor to set a property of an object.
25 * <p>Duck as in duck-typing for an interface like:
26 * <code>
27 * interface Setable {
28 * Object set(Object property, Object value);
29 * }
30 * </code>
31 * or
32 * <code>
33 * interface Putable {
34 * Object put(Object property, Object value);
35 * }
36 * </code>
37 * </p>
38 *
39 * @since 2.0
40 */
41 public final class DuckSetExecutor extends AbstractExecutor.Set {
42
43 /**
44 * Discovers a DuckSetExecutor.
45 *
46 * @param is the introspector
47 * @param clazz the class to find the set method from
48 * @param key the key to use as 1st argument to the set method
49 * @param value the value to use as 2nd argument to the set method
50 * @return the executor if found, null otherwise
51 */
52 public static DuckSetExecutor discover(final Introspector is, final Class<?> clazz, final Object key, final Object value) {
53 java.lang.reflect.Method method = is.getMethod(clazz, "set", key, value);
54 if (method == null) {
55 method = is.getMethod(clazz, "put", makeArgs(key, value));
56 }
57 return method == null ? null : new DuckSetExecutor(clazz, method, key, value);
58 }
59
60 /** The property, may be null. */
61 private final Object property;
62
63 /** The property value class. */
64 private final Class<?> valueClass;
65
66 /**
67 * Creates an instance.
68 *
69 * @param clazz the class the set method applies to
70 * @param method the method called through this executor
71 * @param key the key to use as 1st argument to the set method
72 * @param value the value to use as 2nd argument to the set method
73 */
74 private DuckSetExecutor(final Class<?> clazz, final java.lang.reflect.Method method, final Object key, final Object value) {
75 super(clazz, method);
76 property = key;
77 valueClass = classOf(value);
78 }
79
80 @Override
81 public Object getTargetProperty() {
82 return property;
83 }
84
85 @Override
86 public Object invoke(final Object obj, final Object value) throws IllegalAccessException, InvocationTargetException {
87 final Object[] pargs = {property, value};
88 if (method != null) {
89 method.invoke(obj, pargs);
90 }
91 return value;
92 }
93
94 @Override
95 public Object tryInvoke(final Object obj, final Object key, final Object value) {
96 if (obj != null
97 && objectClass.equals(obj.getClass())
98 && method != null
99 && (property != null && property.equals(key)
100 || property == null && key == null)
101 && valueClass.equals(classOf(value))) {
102 try {
103 final Object[] args = {property, value};
104 method.invoke(obj, args);
105 return value;
106 } catch (IllegalAccessException | IllegalArgumentException xill) {
107 return TRY_FAILED; // fail
108 } catch (final InvocationTargetException xinvoke) {
109 throw JexlException.tryFailed(xinvoke); // throw
110 }
111 }
112 return TRY_FAILED;
113 }
114 }