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