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 }