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 }