001 /*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements. See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License. You may obtain a copy of the License at
008 *
009 * http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017 package org.apache.commons.jexl2.introspection;
018
019 import org.apache.commons.jexl2.internal.Introspector;
020 import java.lang.reflect.Constructor;
021 import java.lang.reflect.Field;
022 import java.lang.reflect.Modifier;
023 import java.util.Enumeration;
024 import java.util.Iterator;
025
026 import java.util.Map;
027 import org.apache.commons.jexl2.JexlInfo;
028 import org.apache.commons.jexl2.JexlException;
029 import org.apache.commons.jexl2.internal.AbstractExecutor;
030 import org.apache.commons.jexl2.internal.ArrayIterator;
031 import org.apache.commons.jexl2.internal.EnumerationIterator;
032 import org.apache.commons.jexl2.internal.introspection.MethodKey;
033 import org.apache.commons.logging.Log;
034
035 /**
036 * Implementation of Uberspect to provide the default introspective
037 * functionality of JEXL.
038 * <p>This is the class to derive to customize introspection.</p>
039 *
040 * @since 1.0
041 * @author <a href="mailto:geirm@optonline.net">Geir Magnusson Jr.</a>
042 * @author <a href="mailto:henning@apache.org">Henning P. Schmiedehausen</a>
043 * @version $Id: UberspectImpl.java 896952 2010-01-07 18:21:29Z henrib $
044 */
045 public class UberspectImpl extends Introspector implements Uberspect {
046 /**
047 * Publicly exposed special failure object returned by tryInvoke.
048 */
049 public static final Object TRY_FAILED = AbstractExecutor.TRY_FAILED;
050
051 /**
052 * Creates a new UberspectImpl.
053 * @param runtimeLogger the logger used for all logging needs
054 */
055 public UberspectImpl(Log runtimeLogger) {
056 super(runtimeLogger);
057 }
058
059 /**
060 * {@inheritDoc}
061 */
062 @SuppressWarnings("unchecked")
063 public Iterator<?> getIterator(Object obj, JexlInfo info) {
064 if (obj instanceof Iterator<?>) {
065 return ((Iterator<?>) obj);
066 }
067 if (obj.getClass().isArray()) {
068 return new ArrayIterator(obj);
069 }
070 if (obj instanceof Map<?,?>) {
071 return ((Map<?,?>) obj).values().iterator();
072 }
073 if (obj instanceof Enumeration<?>) {
074 return new EnumerationIterator<Object>((Enumeration<Object>) obj);
075 }
076 if (obj instanceof Iterable<?>) {
077 return ((Iterable<?>) obj).iterator();
078 }
079 try {
080 // look for an iterator() method to support the JDK5 Iterable
081 // interface or any user tools/DTOs that want to work in
082 // foreach without implementing the Collection interface
083 AbstractExecutor.Method it = getMethodExecutor(obj, "iterator", null);
084 if (it != null && Iterator.class.isAssignableFrom(it.getReturnType())) {
085 return (Iterator<Object>) it.execute(obj, null);
086 }
087 } catch(Exception xany) {
088 throw new JexlException(info, "unable to generate iterator()", xany);
089 }
090 return null;
091 }
092
093 /**
094 * Returns a class field.
095 * @param obj the object
096 * @param name the field name
097 * @param info debug info
098 * @return a {@link Field}.
099 */
100 public Field getField(Object obj, String name, JexlInfo info) {
101 final Class<?> clazz = obj instanceof Class<?>? (Class<?>) obj : obj.getClass();
102 return getField(clazz, name);
103 }
104
105 /**
106 * {@inheritDoc}
107 */
108 public Constructor<?> getConstructor(Object ctorHandle, Object[] args, JexlInfo info) {
109 return getConstructor(ctorHandle, args);
110 }
111
112 /**
113 * {@inheritDoc}
114 */
115 public JexlMethod getMethod(Object obj, String method, Object[] args, JexlInfo info) {
116 return getMethodExecutor(obj, method, args);
117 }
118
119 /**
120 * A JexlPropertyGet for public fields.
121 */
122 public static final class FieldPropertyGet implements JexlPropertyGet {
123 /**
124 * The public field.
125 */
126 private final Field field;
127
128 /**
129 * Creates a new instance of FieldPropertyGet.
130 * @param theField the class public field
131 */
132 public FieldPropertyGet(Field theField) {
133 field = theField;
134 }
135
136 /**
137 * {@inheritDoc}
138 */
139 public Object invoke(Object obj) throws Exception {
140 return field.get(obj);
141 }
142
143 /**
144 * {@inheritDoc}
145 */
146 public Object tryInvoke(Object obj, Object key) {
147 if (obj.getClass().equals(field.getDeclaringClass()) && key.equals(field.getName())) {
148 try {
149 return field.get(obj);
150 } catch (IllegalAccessException xill) {
151 return TRY_FAILED;
152 }
153 }
154 return TRY_FAILED;
155 }
156
157 /**
158 * {@inheritDoc}
159 */
160 public boolean tryFailed(Object rval) {
161 return rval == TRY_FAILED;
162 }
163
164 /**
165 * {@inheritDoc}
166 */
167 public boolean isCacheable() {
168 return true;
169 }
170 }
171
172 /**
173 * {@inheritDoc}
174 */
175 public JexlPropertyGet getPropertyGet(Object obj, Object identifier, JexlInfo info) {
176 JexlPropertyGet get = getGetExecutor(obj, identifier);
177 if (get == null && obj != null && identifier != null) {
178 Field field = getField(obj, identifier.toString(), info);
179 if (field != null) {
180 return new FieldPropertyGet(field);
181 }
182 }
183 return get;
184 }
185
186 /**
187 * A JexlPropertySet for public fields.
188 */
189 public static final class FieldPropertySet implements JexlPropertySet {
190 /**
191 * The public field.
192 */
193 private final Field field;
194
195 /**
196 * Creates a new instance of FieldPropertySet.
197 * @param theField the class public field
198 */
199 public FieldPropertySet(Field theField) {
200 field = theField;
201 }
202
203 /**
204 * {@inheritDoc}
205 */
206 public Object invoke(Object obj, Object arg) throws Exception {
207 field.set(obj, arg);
208 return arg;
209 }
210
211 /**
212 * {@inheritDoc}
213 */
214 public Object tryInvoke(Object obj, Object key, Object value) {
215 if (obj.getClass().equals(field.getDeclaringClass())
216 && key.equals(field.getName())
217 && (value == null || MethodKey.isInvocationConvertible(field.getType(), value.getClass(), false))) {
218 try {
219 field.set(obj, value);
220 return value;
221 } catch (IllegalAccessException xill) {
222 return TRY_FAILED;
223 }
224 }
225 return TRY_FAILED;
226 }
227
228 /**
229 * {@inheritDoc}
230 */
231 public boolean tryFailed(Object rval) {
232 return rval == TRY_FAILED;
233 }
234
235 /**
236 * {@inheritDoc}
237 */
238 public boolean isCacheable() {
239 return true;
240 }
241 }
242
243 /**
244 * {@inheritDoc}
245 */
246 public JexlPropertySet getPropertySet(final Object obj, final Object identifier, Object arg, JexlInfo info) {
247 JexlPropertySet set = getSetExecutor(obj, identifier, arg);
248 if (set == null && obj != null && identifier != null) {
249 Field field = getField(obj, identifier.toString(), info);
250 if (field != null
251 && !Modifier.isFinal(field.getModifiers())
252 && (arg == null || MethodKey.isInvocationConvertible(field.getType(), arg.getClass(), false))) {
253 return new FieldPropertySet(field);
254 }
255 }
256 return set;
257 }
258 }