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    *      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 org.apache.commons.jexl3.JexlEngine;
20  import org.apache.commons.jexl3.introspection.JexlMethod;
21  import org.apache.commons.jexl3.introspection.JexlPropertyGet;
22  import org.apache.commons.jexl3.introspection.JexlPropertySet;
23  
24  /**
25   * Abstract class that is used to execute an arbitrary
26   * method that is introspected. This is the superclass
27   * for all other AbstractExecutor classes.
28   *
29   * @since 1.0
30   */
31  abstract class AbstractExecutor {
32  
33      /**
34       * Abstract class that is used to execute an arbitrary 'get' method.
35       */
36      public abstract static class Get extends AbstractExecutor implements JexlPropertyGet {
37  
38          /**
39           * Default and sole constructor.
40           *
41           * @param theClass the class this executor applies to
42           * @param theMethod the method held by this executor
43           */
44          protected Get(final Class<?> theClass, final java.lang.reflect.Method theMethod) {
45              super(theClass, theMethod);
46          }
47      }
48  
49      /**
50       * Abstract class that is used to execute an arbitrary method.
51       */
52      public abstract static class Method extends AbstractExecutor implements JexlMethod {
53  
54          /** The method key discovered from the arguments. */
55          protected final MethodKey key;
56  
57          /**
58           * Creates a new instance.
59           *
60           * @param c the class this executor applies to
61           * @param m the method
62           * @param k the MethodKey
63           */
64          protected Method(final Class<?> c, final java.lang.reflect.Method m, final MethodKey k) {
65              super(c, m);
66              key = k;
67          }
68  
69          @Override
70          public final Class<?> getReturnType() {
71              return method.getReturnType();
72          }
73  
74          @Override
75          public Object getTargetProperty() {
76              return key;
77          }
78      }
79  
80      /**
81       * Abstract class that is used to execute an arbitrary 'set' method.
82       */
83      public abstract static class Set extends AbstractExecutor implements JexlPropertySet {
84  
85          /**
86           * Default and sole constructor.
87           *
88           * @param theClass the class this executor applies to
89           * @param theMethod the method held by this executor
90           */
91          protected Set(final Class<?> theClass, final java.lang.reflect.Method theMethod) {
92              super(theClass, theMethod);
93          }
94      }
95  
96      /** A marker for invocation failures in tryInvoke. */
97      public static final Object TRY_FAILED = JexlEngine.TRY_FAILED;
98  
99      /**
100      * Coerce an Object which must be a number to an Integer.
101      *
102      * @param arg the Object to coerce
103      * @return an Integer if it can be converted, null otherwise
104      */
105     static Integer castInteger(final Object arg) {
106         return arg instanceof Number? ((Number) arg).intValue() : null;
107     }
108 
109     /**
110      * Coerce an Object to a String.
111      *
112      * @param arg the Object to coerce
113      * @return a String if it can be converted, null otherwise
114      */
115     static String castString(final Object arg) {
116         return arg instanceof CharSequence || arg instanceof Integer ? arg.toString() : null;
117     }
118 
119     /**
120      * Gets the class of an object or Object if null.
121      *
122      * @param instance the instance
123      * @return the class
124      */
125     static Class<?> classOf(final Object instance) {
126         return instance == null ? Object.class : instance.getClass();
127     }
128 
129     /**
130      * A helper to initialize the marker methods (array.get, list.get, etc...).
131      *
132      * @param clazz the class to introspect
133      * @param name the name of the method
134      * @param parms the parameters
135      * @return the method
136      */
137     static java.lang.reflect.Method initMarker(final Class<?> clazz, final String name, final Class<?>... parms) {
138         try {
139             return clazz.getMethod(name, parms);
140         } catch (final Exception e) {
141             throw new IllegalArgumentException(e);
142         }
143     }
144 
145     /**
146      * Creates an arguments array.
147      *
148      * @param args the list of arguments
149      * @return the arguments array
150      */
151     static Object[] makeArgs(final Object... args) {
152         return args;
153     }
154 
155     /** The class this executor applies to. */
156     protected final Class<?> objectClass;
157 
158     /** Method to be executed. */
159     protected final java.lang.reflect.Method method;
160 
161     /**
162      * Default and sole constructor.
163      *
164      * @param theClass the class this executor applies to
165      * @param theMethod the method held by this executor
166      */
167     protected AbstractExecutor(final Class<?> theClass, final java.lang.reflect.Method theMethod) {
168         objectClass = theClass;
169         method = theMethod;
170     }
171 
172     /**
173      * Indicates whether some other executor is equivalent to this one.
174      *
175      * @param arg the other executor to check
176      * @return true if both executors are equivalent, false otherwise
177      */
178     public boolean equals(final AbstractExecutor arg) {
179         // common equality check
180         if (!this.getClass().equals(arg.getClass())) {
181             return false;
182         }
183         if (!getMethod().equals(arg.getMethod())) {
184             return false;
185         }
186         if (!getTargetClass().equals(arg.getTargetClass())) {
187             return false;
188         }
189         // specific equality check
190         final Object lhsp = getTargetProperty();
191         final Object rhsp = arg.getTargetProperty();
192         if (lhsp == null && rhsp == null) {
193             return true;
194         }
195         if (lhsp != null && rhsp != null) {
196             return lhsp.equals(rhsp);
197         }
198         return false;
199     }
200 
201     @Override
202     public boolean equals(final Object obj) {
203         return this == obj || obj instanceof AbstractExecutor && equals((AbstractExecutor) obj);
204     }
205 
206     /**
207      * Gets the method to be executed or used as a marker.
208      *
209      * @return Method The method used by execute in derived classes.
210      */
211     public final java.lang.reflect.Method getMethod() {
212         return method;
213     }
214 
215     /**
216      * Gets the method name used.
217      *
218      * @return method name
219      */
220     public final String getMethodName() {
221         return method.getName();
222     }
223 
224     /**
225      * Gets the object class targeted by this executor.
226      *
227      * @return the target object class
228      */
229     public final Class<?> getTargetClass() {
230         return objectClass;
231     }
232 
233     /**
234      * Gets the property targeted by this executor.
235      *
236      * @return the target property
237      */
238     public Object getTargetProperty() {
239         return null;
240     }
241 
242     @Override
243     public int hashCode() {
244         return method.hashCode();
245     }
246 
247     /**
248      * Tell whether the executor is alive by looking
249      * at the value of the method.
250      *
251      * @return boolean Whether the executor is alive.
252      */
253     public final boolean isAlive() {
254         return method != null;
255     }
256 
257     /**
258      * Specifies if this executor is cacheable and able to be reused for this
259      * class of object it was returned for.
260      *
261      * @return true if can be reused for this class, false if not
262      */
263     public boolean isCacheable() {
264         return method != null;
265     }
266 
267     /**
268      * Checks whether a tryExecute failed or not.
269      *
270      * @param exec the value returned by tryExecute
271      * @return true if tryExecute failed, false otherwise
272      */
273     public final boolean tryFailed(final Object exec) {
274         return exec == JexlEngine.TRY_FAILED;
275     }
276 }