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 018package org.apache.commons.jexl3.introspection; 019 020import org.apache.commons.jexl3.JexlArithmetic; 021import org.apache.commons.jexl3.JexlOperator; 022 023import java.util.Arrays; 024import java.util.Collections; 025import java.util.Iterator; 026import java.util.List; 027import java.util.Map; 028 029/** 030 * 'Federated' introspection/reflection interface to allow JEXL introspection 031 * behavior to be customized. 032 * 033 * @since 1.0 034 */ 035public interface JexlUberspect { 036 /** 037 * Abstracts getting property setter and getter. 038 * <p> 039 * These are used through 'strategies' to solve properties; a strategy orders a list of resolver types, 040 * and each resolver type is tried in sequence; the first resolver that discovers a non-null {s,g}etter 041 * stops the search. 042 * 043 * @see JexlResolver 044 * @see JexlUberspect#getPropertyGet 045 * @see JexlUberspect#getPropertySet 046 * @since 3.0 047 */ 048 interface PropertyResolver { 049 050 /** 051 * Gets a property getter. 052 * 053 * @param uber the uberspect 054 * @param obj the object 055 * @param identifier the property identifier 056 * @return the property getter or null 057 */ 058 JexlPropertyGet getPropertyGet(JexlUberspect uber, Object obj, Object identifier); 059 060 /** 061 * Gets a property setter. 062 * 063 * @param uber the uberspect 064 * @param obj the object 065 * @param identifier the property identifier 066 * @param arg the property value 067 * @return the property setter or null 068 */ 069 JexlPropertySet getPropertySet(JexlUberspect uber, Object obj, Object identifier, Object arg); 070 } 071 072 /** 073 * The various builtin property resolvers. 074 * <p> 075 * Each resolver discovers how to set/get a property with different techniques; seeking 076 * method names or field names, etc. 077 * 078 * @since 3.0 079 */ 080 enum JexlResolver implements PropertyResolver { 081 /** Seeks methods named get{P,p}property and is{P,p}property. */ 082 PROPERTY, 083 084 /** Seeks map methods get/put. */ 085 MAP, 086 087 /** Seeks list methods get/set. */ 088 LIST, 089 090 /** Seeks any get/{set,put} method (quacking like a list or a map). */ 091 DUCK, 092 093 /** Seeks public instance members.*/ 094 FIELD, 095 096 /** Seeks a getContainer(property) and setContainer(property, value) as in <code>x.container.property</code>. */ 097 CONTAINER; 098 099 @Override 100 public final JexlPropertyGet getPropertyGet(final JexlUberspect uber, 101 final Object obj, 102 final Object identifier) { 103 return uber.getPropertyGet(Collections.singletonList(this), obj, identifier); 104 } 105 106 @Override 107 public final JexlPropertySet getPropertySet(final JexlUberspect uber, 108 final Object obj, 109 final Object identifier, 110 final Object arg) { 111 return uber.getPropertySet(Collections.singletonList(this), obj, identifier, arg); 112 } 113 } 114 115 /** 116 * A resolver types list tailored for POJOs, favors '.' over '[]'. 117 */ 118 List<PropertyResolver> POJO = Collections.unmodifiableList(Arrays.asList( 119 JexlResolver.PROPERTY, 120 JexlResolver.MAP, 121 JexlResolver.LIST, 122 JexlResolver.DUCK, 123 JexlResolver.FIELD, 124 JexlResolver.CONTAINER 125 )); 126 127 128 /** 129 * A resolver types list tailored for Maps, favors '[]' over '.'. 130 */ 131 List<PropertyResolver> MAP = Collections.unmodifiableList(Arrays.asList( 132 JexlResolver.MAP, 133 JexlResolver.LIST, 134 JexlResolver.DUCK, 135 JexlResolver.PROPERTY, 136 JexlResolver.FIELD, 137 JexlResolver.CONTAINER 138 )); 139 140 /** 141 * Determines property resolution strategy. 142 * 143 * <p>To use a strategy instance, you have to set it at engine creation using 144 * {@link org.apache.commons.jexl3.JexlBuilder#strategy(JexlUberspect.ResolverStrategy)} 145 * as in:</p> 146 * 147 * <code>JexlEngine jexl = new JexlBuilder().strategy(MY_STRATEGY).create();</code> 148 * 149 * @since 3.0 150 */ 151 interface ResolverStrategy { 152 /** 153 * Applies this strategy to a list of resolver types. 154 * 155 * @param operator the property access operator, may be null 156 * @param obj the instance we seek to obtain a property setter/getter from, can not be null 157 * @return the ordered list of resolvers types, must not be null 158 */ 159 List<PropertyResolver> apply(JexlOperator operator, Object obj); 160 } 161 162 /** 163 * The default strategy. 164 * <p> 165 * If the operator is '[]' or if the operator is null and the object is a map, use the MAP list of resolvers; 166 * Other cases use the POJO list of resolvers. 167 */ 168 ResolverStrategy JEXL_STRATEGY = (op, obj) -> { 169 if (op == JexlOperator.ARRAY_GET) { 170 return MAP; 171 } 172 if (op == JexlOperator.ARRAY_SET) { 173 return MAP; 174 } 175 if (op == null && obj instanceof Map) { 176 return MAP; 177 } 178 return POJO; 179 }; 180 181 /** 182 * The map strategy. 183 * 184 * <p>If the operator is '[]' or if the object is a map, use the MAP list of resolvers. 185 * Otherwise, use the POJO list of resolvers.</p> 186 */ 187 ResolverStrategy MAP_STRATEGY = (op, obj) -> { 188 if (op == JexlOperator.ARRAY_GET) { 189 return MAP; 190 } 191 if (op == JexlOperator.ARRAY_SET) { 192 return MAP; 193 } 194 if (obj instanceof Map) { 195 return MAP; 196 } 197 return POJO; 198 }; 199 200 /** 201 * Applies this uberspect property resolver strategy. 202 * 203 * @param op the operator 204 * @param obj the object 205 * @return the applied strategy resolver list 206 */ 207 List<PropertyResolver> getResolvers(JexlOperator op, Object obj); 208 209 /** 210 * Sets the class loader to use. 211 * 212 * <p>This increments the version.</p> 213 * 214 * @param loader the class loader 215 */ 216 void setClassLoader(ClassLoader loader); 217 218 /** 219 * Gets the current class loader. 220 * @return the class loader 221 */ 222 ClassLoader getClassLoader(); 223 224 /** 225 * Gets this uberspect version. 226 * 227 * @return the class loader modification count 228 */ 229 int getVersion(); 230 231 /** 232 * Seeks a class by name using this uberspect class-loader. 233 * @param className the class name 234 * @return the class instance or null if the class cannot be located by this uberspect class loader or if 235 * permissions deny access to the class 236 */ 237 default Class<?> getClassByName(final String className) { 238 try { 239 return Class.forName(className, false, getClassLoader()); 240 } catch (final ClassNotFoundException xignore) { 241 return null; 242 } 243 } 244 245 /** 246 * Returns a class constructor. 247 * 248 * @param ctorHandle a class or class name 249 * @param args constructor arguments 250 * @return a {@link JexlMethod} 251 * @since 3.0 252 */ 253 JexlMethod getConstructor(Object ctorHandle, Object... args); 254 255 /** 256 * Returns a JexlMethod. 257 * 258 * @param obj the object 259 * @param method the method name 260 * @param args method arguments 261 * @return a {@link JexlMethod} 262 */ 263 JexlMethod getMethod(Object obj, String method, Object... args); 264 265 /** 266 * Property getter. 267 * 268 * <p>returns a JelPropertySet apropos to an expression like <code>bar.woogie</code>.</p> 269 * 270 * @param obj the object to get the property from 271 * @param identifier property name 272 * @return a {@link JexlPropertyGet} or null 273 */ 274 JexlPropertyGet getPropertyGet(Object obj, Object identifier); 275 276 /** 277 * Property getter. 278 * <p> 279 * Seeks a JexlPropertyGet apropos to an expression like <code>bar.woogie</code>.</p> 280 * See {@link ResolverStrategy#apply(JexlOperator, java.lang.Object) } 281 * 282 * @param resolvers the list of property resolvers to try 283 * @param obj the object to get the property from 284 * @param identifier property name 285 * @return a {@link JexlPropertyGet} or null 286 * @since 3.0 287 */ 288 JexlPropertyGet getPropertyGet(List<PropertyResolver> resolvers, Object obj, Object identifier); 289 290 /** 291 * Property setter. 292 * <p> 293 * Seeks a JelPropertySet apropos to an expression like <code>foo.bar = "geir"</code>.</p> 294 * 295 * @param obj the object to get the property from. 296 * @param identifier property name 297 * @param arg value to set 298 * @return a {@link JexlPropertySet} or null 299 */ 300 JexlPropertySet getPropertySet(Object obj, Object identifier, Object arg); 301 302 /** 303 * Property setter. 304 * <p> 305 * Seeks a JelPropertySet apropos to an expression like <code>foo.bar = "geir"</code>.</p> 306 * See {@link ResolverStrategy#apply(JexlOperator, java.lang.Object) } 307 * 308 * @param resolvers the list of property resolvers to try, 309 * @param obj the object to get the property from 310 * @param identifier property name 311 * @param arg value to set 312 * @return a {@link JexlPropertySet} or null 313 * @since 3.0 314 */ 315 JexlPropertySet getPropertySet(List<PropertyResolver> resolvers, Object obj, Object identifier, Object arg); 316 317 /** 318 * Gets an iterator from an object. 319 * 320 * @param obj to get the iterator from 321 * @return an iterator over obj or null 322 */ 323 Iterator<?> getIterator(Object obj); 324 325 /** 326 * Gets an arithmetic operator resolver for a given arithmetic instance. 327 * 328 * @param arithmetic the arithmetic instance 329 * @return the arithmetic uberspect or null if no operator method were overridden 330 * @since 3.0 331 */ 332 JexlArithmetic.Uberspect getArithmetic(JexlArithmetic arithmetic); 333 334}