View Javadoc
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    *      http://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  import org.apache.commons.jexl3.JexlException;
21  
22  /**
23   * Specialized executor to set a property of an object.
24   * <p>Duck as in duck-typing for an interface like:
25   * <code>
26   * interface Setable {
27   *      Object set(Object property, Object value);
28   * }
29   * </code>
30   * or
31   * <code>
32   * interface Putable {
33   *      Object put(Object property, Object value);
34   * }
35   * </code>
36   * </p>
37   * @since 2.0
38   */
39  public final class DuckSetExecutor extends AbstractExecutor.Set {
40      /** The property, may be null. */
41      private final Object property;
42      /** The property value class. */
43      private final Class<?> valueClass;
44  
45      /**
46       * Discovers a DuckSetExecutor.
47       *
48       * @param is the introspector
49       * @param clazz the class to find the set method from
50       * @param key the key to use as 1st argument to the set method
51       * @param value the value to use as 2nd argument to the set method
52       * @return the executor if found, null otherwise
53       */
54      public static DuckSetExecutor discover(final Introspector is, final Class<?> clazz, final Object key, final Object value) {
55          java.lang.reflect.Method method = is.getMethod(clazz, "set", key, value);
56          if (method == null) {
57              method = is.getMethod(clazz, "put", makeArgs(key, value));
58          }
59          return method == null? null : new DuckSetExecutor(clazz, method, key, value);
60      }
61  
62      /**
63       * Creates an instance.
64       * @param clazz the class the set method applies to
65       * @param method the method called through this executor
66       * @param key the key to use as 1st argument to the set method
67       * @param value the value to use as 2nd argument to the set method
68       */
69      private DuckSetExecutor(final Class<?> clazz, final java.lang.reflect.Method method, final Object key, final Object value) {
70          super(clazz, method);
71          property = key;
72          valueClass = classOf(value);
73      }
74  
75      @Override
76      public Object getTargetProperty() {
77          return property;
78      }
79  
80      @Override
81      public Object invoke(final Object obj, final Object value) throws IllegalAccessException, InvocationTargetException {
82          final Object[] pargs = {property, value};
83          if (method != null) {
84                  method.invoke(obj, pargs);
85              }
86          return value;
87      }
88  
89      @Override
90      public Object tryInvoke(final Object obj, final Object key, final Object value) {
91          if (obj != null
92              && objectClass.equals(obj.getClass())
93              && method !=  null
94              && ((property != null && property.equals(key))
95                  || (property == null && key == null))
96              && valueClass.equals(classOf(value))) {
97              try {
98                  final Object[] args = {property, value};
99                  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 }