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 }