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