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 }