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 java.util.Arrays; 21 import java.util.Collections; 22 import java.util.Iterator; 23 import java.util.List; 24 import java.util.Map; 25 26 import org.apache.commons.jexl3.JexlArithmetic; 27 import org.apache.commons.jexl3.JexlOperator; 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 * The various builtin property resolvers. 38 * <p> 39 * Each resolver discovers how to set/get a property with different techniques; seeking 40 * method names or field names, etc. 41 * 42 * @since 3.0 43 */ 44 enum JexlResolver implements PropertyResolver { 45 /** Seeks methods named get{P,p}property and is{P,p}property. */ 46 PROPERTY, 47 48 /** Seeks map methods get/put. */ 49 MAP, 50 51 /** Seeks list methods get/set. */ 52 LIST, 53 54 /** Seeks any get/{set,put} method (quacking like a list or a map). */ 55 DUCK, 56 57 /** Seeks public instance members.*/ 58 FIELD, 59 60 /** Seeks a getContainer(property) and setContainer(property, value) as in {@code x.container.property}. */ 61 CONTAINER; 62 63 @Override 64 public final JexlPropertyGet getPropertyGet(final JexlUberspect uber, 65 final Object obj, 66 final Object identifier) { 67 return uber.getPropertyGet(Collections.singletonList(this), obj, identifier); 68 } 69 70 @Override 71 public final JexlPropertySet getPropertySet(final JexlUberspect uber, 72 final Object obj, 73 final Object identifier, 74 final Object arg) { 75 return uber.getPropertySet(Collections.singletonList(this), obj, identifier, arg); 76 } 77 } 78 79 /** 80 * Abstracts getting property setter and getter. 81 * <p> 82 * These are used through 'strategies' to solve properties; a strategy orders a list of resolver types, 83 * and each resolver type is tried in sequence; the first resolver that discovers a non-null {s,g}etter 84 * stops the search. 85 * 86 * @see JexlResolver 87 * @see JexlUberspect#getPropertyGet 88 * @see JexlUberspect#getPropertySet 89 * @since 3.0 90 */ 91 interface PropertyResolver { 92 93 /** 94 * Gets a property getter. 95 * 96 * @param uber the uberspect 97 * @param obj the object 98 * @param identifier the property identifier 99 * @return the property getter or null 100 */ 101 JexlPropertyGet getPropertyGet(JexlUberspect uber, Object obj, Object identifier); 102 103 /** 104 * Gets a property setter. 105 * 106 * @param uber the uberspect 107 * @param obj the object 108 * @param identifier the property identifier 109 * @param arg the property value 110 * @return the property setter or null 111 */ 112 JexlPropertySet getPropertySet(JexlUberspect uber, Object obj, Object identifier, Object arg); 113 } 114 115 /** 116 * Determines property resolution strategy. 117 * 118 * <p>To use a strategy instance, you have to set it at engine creation using 119 * {@link org.apache.commons.jexl3.JexlBuilder#strategy(JexlUberspect.ResolverStrategy)} 120 * as in:</p> 121 * 122 * {@code JexlEngine jexl = new JexlBuilder().strategy(MY_STRATEGY).create();} 123 * 124 * @since 3.0 125 */ 126 interface ResolverStrategy { 127 /** 128 * Applies this strategy to a list of resolver types. 129 * 130 * @param operator the property access operator, may be null 131 * @param obj the instance we seek to obtain a property setter/getter from, cannot be null 132 * @return the ordered list of resolvers types, must not be null 133 */ 134 List<PropertyResolver> apply(JexlOperator operator, Object obj); 135 } 136 137 /** 138 * A resolver types list tailored for POJOs, favors '.' over '[]'. 139 */ 140 List<PropertyResolver> POJO = Collections.unmodifiableList(Arrays.asList( 141 JexlResolver.PROPERTY, 142 JexlResolver.MAP, 143 JexlResolver.LIST, 144 JexlResolver.DUCK, 145 JexlResolver.FIELD, 146 JexlResolver.CONTAINER 147 )); 148 149 /** 150 * A resolver types list tailored for Maps, favors '[]' over '.'. 151 */ 152 List<PropertyResolver> MAP = Collections.unmodifiableList(Arrays.asList( 153 JexlResolver.MAP, 154 JexlResolver.LIST, 155 JexlResolver.DUCK, 156 JexlResolver.PROPERTY, 157 JexlResolver.FIELD, 158 JexlResolver.CONTAINER 159 )); 160 161 /** 162 * The default strategy. 163 * <p> 164 * If the operator is '[]' or if the operator is null and the object is a map, use the MAP list of resolvers; 165 * Other cases use the POJO list of resolvers. 166 */ 167 ResolverStrategy JEXL_STRATEGY = (op, obj) -> { 168 if (op == JexlOperator.ARRAY_GET) { 169 return MAP; 170 } 171 if (op == JexlOperator.ARRAY_SET) { 172 return MAP; 173 } 174 if (op == null && obj instanceof Map) { 175 return MAP; 176 } 177 return POJO; 178 }; 179 180 /** 181 * The map strategy. 182 * 183 * <p>If the operator is '[]' or if the object is a map, use the MAP list of resolvers. 184 * Otherwise, use the POJO list of resolvers.</p> 185 */ 186 ResolverStrategy MAP_STRATEGY = (op, obj) -> { 187 if (op == JexlOperator.ARRAY_GET) { 188 return MAP; 189 } 190 if (op == JexlOperator.ARRAY_SET) { 191 return MAP; 192 } 193 if (obj instanceof Map) { 194 return MAP; 195 } 196 return POJO; 197 }; 198 199 /** 200 * Gets an arithmetic operator resolver for a given arithmetic instance. 201 * 202 * @param arithmetic the arithmetic instance 203 * @return the arithmetic uberspect or null if no operator method were overridden 204 * @since 3.0 205 * @see #getOperator(JexlArithmetic) 206 */ 207 JexlArithmetic.Uberspect getArithmetic(JexlArithmetic arithmetic); 208 209 /** 210 * Gets an arithmetic operator executor for a given arithmetic instance. 211 * 212 * @param arithmetic the arithmetic instance 213 * @return an operator uberspect instance 214 * @since 3.5.0 215 */ 216 default JexlOperator.Uberspect getOperator(final JexlArithmetic arithmetic) { 217 return null; 218 } 219 220 /** 221 * Seeks a class by name using this uberspect class-loader. 222 * @param className the class name 223 * @return the class instance or null if the class cannot be located by this uberspect class loader or if 224 * permissions deny access to the class 225 */ 226 default Class<?> getClassByName(final String className) { 227 try { 228 return Class.forName(className, false, getClassLoader()); 229 } catch (final ClassNotFoundException xignore) { 230 return null; 231 } 232 } 233 234 /** 235 * Gets the current class loader. 236 * @return the class loader 237 */ 238 ClassLoader getClassLoader(); 239 240 /** 241 * Returns a class constructor. 242 * 243 * @param ctorHandle a class or class name 244 * @param args constructor arguments 245 * @return a {@link JexlMethod} 246 * @since 3.0 247 */ 248 JexlMethod getConstructor(Object ctorHandle, Object... args); 249 250 /** 251 * Gets an iterator from an object. 252 * 253 * @param obj to get the iterator from 254 * @return an iterator over obj or null 255 */ 256 Iterator<?> getIterator(Object obj); 257 258 /** 259 * Returns a JexlMethod. 260 * 261 * @param obj the object 262 * @param method the method name 263 * @param args method arguments 264 * @return a {@link JexlMethod} 265 */ 266 JexlMethod getMethod(Object obj, String method, Object... args); 267 268 /** 269 * Property getter. 270 * <p> 271 * Seeks a JexlPropertyGet apropos to an expression like {@code bar.woogie}.</p> 272 * See {@link ResolverStrategy#apply(JexlOperator, java.lang.Object)} 273 * 274 * @param resolvers the list of property resolvers to try 275 * @param obj the object to get the property from 276 * @param identifier property name 277 * @return a {@link JexlPropertyGet} or null 278 * @since 3.0 279 */ 280 JexlPropertyGet getPropertyGet(List<PropertyResolver> resolvers, Object obj, Object identifier); 281 282 /** 283 * Property getter. 284 * 285 * <p>returns a JelPropertySet apropos to an expression like {@code bar.woogie}.</p> 286 * 287 * @param obj the object to get the property from 288 * @param identifier property name 289 * @return a {@link JexlPropertyGet} or null 290 */ 291 JexlPropertyGet getPropertyGet(Object obj, Object identifier); 292 293 /** 294 * Property setter. 295 * <p> 296 * Seeks a JelPropertySet apropos to an expression like {@code foo.bar = "geir"}.</p> 297 * See {@link ResolverStrategy#apply(JexlOperator, java.lang.Object)} 298 * 299 * @param resolvers the list of property resolvers to try, 300 * @param obj the object to get the property from 301 * @param identifier property name 302 * @param arg value to set 303 * @return a {@link JexlPropertySet} or null 304 * @since 3.0 305 */ 306 JexlPropertySet getPropertySet(List<PropertyResolver> resolvers, Object obj, Object identifier, Object arg); 307 308 /** 309 * Property setter. 310 * <p> 311 * Seeks a JelPropertySet apropos to an expression like {@code foo.bar = "geir"}.</p> 312 * 313 * @param obj the object to get the property from. 314 * @param identifier property name 315 * @param arg value to set 316 * @return a {@link JexlPropertySet} or null 317 */ 318 JexlPropertySet getPropertySet(Object obj, Object identifier, Object arg); 319 320 /** 321 * Applies this uberspect property resolver strategy. 322 * 323 * @param op the operator 324 * @param obj the object 325 * @return the applied strategy resolver list 326 */ 327 List<PropertyResolver> getResolvers(JexlOperator op, Object obj); 328 329 /** 330 * Gets this uberspect version. 331 * 332 * @return the class loader modification count 333 */ 334 int getVersion(); 335 336 /** 337 * Sets the class loader to use. 338 * 339 * <p>This increments the version.</p> 340 * 341 * @param loader the class loader 342 */ 343 void setClassLoader(ClassLoader loader); 344 345 }