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 18 package org.apache.commons.jexl3.introspection; 19 20 import org.apache.commons.jexl3.JexlArithmetic; 21 import org.apache.commons.jexl3.JexlOperator; 22 23 import java.util.Arrays; 24 import java.util.Collections; 25 import java.util.Iterator; 26 import java.util.List; 27 import java.util.Map; 28 29 /** 30 * 'Federated' introspection/reflection interface to allow JEXL introspection 31 * behavior to be customized. 32 * 33 * @since 1.0 34 */ 35 public interface JexlUberspect { 36 /** 37 * Abstracts getting property setter and getter. 38 * <p> 39 * These are used through 'strategies' to solve properties; a strategy orders a list of resolver types, 40 * and each resolver type is tried in sequence; the first resolver that discovers a non-null {s,g}etter 41 * stops the search. 42 * 43 * @see JexlResolver 44 * @see JexlUberspect#getPropertyGet 45 * @see JexlUberspect#getPropertySet 46 * @since 3.0 47 */ 48 interface PropertyResolver { 49 50 /** 51 * Gets a property getter. 52 * 53 * @param uber the uberspect 54 * @param obj the object 55 * @param identifier the property identifier 56 * @return the property getter or null 57 */ 58 JexlPropertyGet getPropertyGet(JexlUberspect uber, Object obj, Object identifier); 59 60 /** 61 * Gets a property setter. 62 * 63 * @param uber the uberspect 64 * @param obj the object 65 * @param identifier the property identifier 66 * @param arg the property value 67 * @return the property setter or null 68 */ 69 JexlPropertySet getPropertySet(JexlUberspect uber, Object obj, Object identifier, Object arg); 70 } 71 72 /** 73 * The various builtin property resolvers. 74 * <p> 75 * Each resolver discovers how to set/get a property with different techniques; seeking 76 * method names or field names, etc. 77 * 78 * @since 3.0 79 */ 80 enum JexlResolver implements PropertyResolver { 81 /** Seeks methods named get{P,p}property and is{P,p}property. */ 82 PROPERTY, 83 84 /** Seeks map methods get/put. */ 85 MAP, 86 87 /** Seeks list methods get/set. */ 88 LIST, 89 90 /** Seeks any get/{set,put} method (quacking like a list or a map). */ 91 DUCK, 92 93 /** Seeks public instance members.*/ 94 FIELD, 95 96 /** Seeks a getContainer(property) and setContainer(property, value) as in <code>x.container.property</code>. */ 97 CONTAINER; 98 99 @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 }