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      * A helper to initialize the marker methods (array.get, list.get, etc...).
130      *
131      * @param clazz the class to introspect
132      * @param name the name of the method
133      * @param parms the parameters
134      * @return the method
135      */
136     static java.lang.reflect.Method initMarker(final Class<?> clazz, final String name, final Class<?>... parms) {
137         try {
138             return clazz.getMethod(name, parms);
139         } catch (final Exception e) {
140             throw new IllegalArgumentException(e);
141         }
142     }
143 
144     /**
145      * Creates an arguments array.
146      *
147      * @param args the list of arguments
148      * @return the arguments array
149      */
150     static Object[] makeArgs(final Object... args) {
151         return args;
152     }
153 
154     /** The class this executor applies to. */
155     protected final Class<?> objectClass;
156 
157     /** Method to be executed. */
158     protected final java.lang.reflect.Method method;
159 
160     /**
161      * Default and sole constructor.
162      *
163      * @param theClass the class this executor applies to
164      * @param theMethod the method held by this executor
165      */
166     protected AbstractExecutor(final Class<?> theClass, final java.lang.reflect.Method theMethod) {
167         objectClass = theClass;
168         method = theMethod;
169     }
170 
171     /**
172      * Indicates whether some other executor is equivalent to this one.
173      *
174      * @param arg the other executor to check
175      * @return true if both executors are equivalent, false otherwise
176      */
177     public boolean equals(final AbstractExecutor arg) {
178         // common equality check
179         if (!this.getClass().equals(arg.getClass())) {
180             return false;
181         }
182         if (!getMethod().equals(arg.getMethod())) {
183             return false;
184         }
185         if (!getTargetClass().equals(arg.getTargetClass())) {
186             return false;
187         }
188         // specific equality check
189         final Object lhsp = getTargetProperty();
190         final Object rhsp = arg.getTargetProperty();
191         if (lhsp == null && rhsp == null) {
192             return true;
193         }
194         if (lhsp != null && rhsp != null) {
195             return lhsp.equals(rhsp);
196         }
197         return false;
198     }
199 
200     @Override
201     public boolean equals(final Object obj) {
202         return this == obj || obj instanceof AbstractExecutor && equals((AbstractExecutor) obj);
203     }
204 
205     /**
206      * Gets the method to be executed or used as a marker.
207      *
208      * @return Method The method used by execute in derived classes.
209      */
210     public final java.lang.reflect.Method getMethod() {
211         return method;
212     }
213 
214     /**
215      * Gets the method name used.
216      *
217      * @return method name
218      */
219     public final String getMethodName() {
220         return method.getName();
221     }
222 
223     /**
224      * Gets the object class targeted by this executor.
225      *
226      * @return the target object class
227      */
228     public final Class<?> getTargetClass() {
229         return objectClass;
230     }
231 
232     /**
233      * Gets the property targeted by this executor.
234      *
235      * @return the target property
236      */
237     public Object getTargetProperty() {
238         return null;
239     }
240 
241     @Override
242     public int hashCode() {
243         return method.hashCode();
244     }
245 
246     /**
247      * Tell whether the executor is alive by looking
248      * at the value of the method.
249      *
250      * @return boolean Whether the executor is alive.
251      */
252     public final boolean isAlive() {
253         return method != null;
254     }
255 
256     /**
257      * Specifies if this executor is cacheable and able to be reused for this
258      * class of object it was returned for.
259      *
260      * @return true if can be reused for this class, false if not
261      */
262     public boolean isCacheable() {
263         return method != null;
264     }
265 
266     /**
267      * Checks whether a tryExecute failed or not.
268      *
269      * @param exec the value returned by tryExecute
270      * @return true if tryExecute failed, false otherwise
271      */
272     public final boolean tryFailed(final Object exec) {
273         return exec == JexlEngine.TRY_FAILED;
274     }
275 }