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</code>. */ 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();</code> 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, can not 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 */ 206 JexlArithmetic.Uberspect getArithmetic(JexlArithmetic arithmetic); 207 208 /** 209 * Seeks a class by name using this uberspect class-loader. 210 * @param className the class name 211 * @return the class instance or null if the class cannot be located by this uberspect class loader or if 212 * permissions deny access to the class 213 */ 214 default Class<?> getClassByName(final String className) { 215 try { 216 return Class.forName(className, false, getClassLoader()); 217 } catch (final ClassNotFoundException xignore) { 218 return null; 219 } 220 } 221 222 /** 223 * Gets the current class loader. 224 * @return the class loader 225 */ 226 ClassLoader getClassLoader(); 227 228 /** 229 * Returns a class constructor. 230 * 231 * @param ctorHandle a class or class name 232 * @param args constructor arguments 233 * @return a {@link JexlMethod} 234 * @since 3.0 235 */ 236 JexlMethod getConstructor(Object ctorHandle, Object... args); 237 238 /** 239 * Gets an iterator from an object. 240 * 241 * @param obj to get the iterator from 242 * @return an iterator over obj or null 243 */ 244 Iterator<?> getIterator(Object obj); 245 246 /** 247 * Returns a JexlMethod. 248 * 249 * @param obj the object 250 * @param method the method name 251 * @param args method arguments 252 * @return a {@link JexlMethod} 253 */ 254 JexlMethod getMethod(Object obj, String method, Object... args); 255 256 /** 257 * Property getter. 258 * <p> 259 * Seeks a JexlPropertyGet apropos to an expression like <code>bar.woogie</code>.</p> 260 * See {@link ResolverStrategy#apply(JexlOperator, java.lang.Object) } 261 * 262 * @param resolvers the list of property resolvers to try 263 * @param obj the object to get the property from 264 * @param identifier property name 265 * @return a {@link JexlPropertyGet} or null 266 * @since 3.0 267 */ 268 JexlPropertyGet getPropertyGet(List<PropertyResolver> resolvers, Object obj, Object identifier); 269 270 /** 271 * Property getter. 272 * 273 * <p>returns a JelPropertySet apropos to an expression like <code>bar.woogie</code>.</p> 274 * 275 * @param obj the object to get the property from 276 * @param identifier property name 277 * @return a {@link JexlPropertyGet} or null 278 */ 279 JexlPropertyGet getPropertyGet(Object obj, Object identifier); 280 281 /** 282 * Property setter. 283 * <p> 284 * Seeks a JelPropertySet apropos to an expression like <code>foo.bar = "geir"</code>.</p> 285 * See {@link ResolverStrategy#apply(JexlOperator, java.lang.Object) } 286 * 287 * @param resolvers the list of property resolvers to try, 288 * @param obj the object to get the property from 289 * @param identifier property name 290 * @param arg value to set 291 * @return a {@link JexlPropertySet} or null 292 * @since 3.0 293 */ 294 JexlPropertySet getPropertySet(List<PropertyResolver> resolvers, Object obj, Object identifier, Object arg); 295 296 /** 297 * Property setter. 298 * <p> 299 * Seeks a JelPropertySet apropos to an expression like <code>foo.bar = "geir"</code>.</p> 300 * 301 * @param obj the object to get the property from. 302 * @param identifier property name 303 * @param arg value to set 304 * @return a {@link JexlPropertySet} or null 305 */ 306 JexlPropertySet getPropertySet(Object obj, Object identifier, Object arg); 307 308 /** 309 * Applies this uberspect property resolver strategy. 310 * 311 * @param op the operator 312 * @param obj the object 313 * @return the applied strategy resolver list 314 */ 315 List<PropertyResolver> getResolvers(JexlOperator op, Object obj); 316 317 /** 318 * Gets this uberspect version. 319 * 320 * @return the class loader modification count 321 */ 322 int getVersion(); 323 324 /** 325 * Sets the class loader to use. 326 * 327 * <p>This increments the version.</p> 328 * 329 * @param loader the class loader 330 */ 331 void setClassLoader(ClassLoader loader); 332 333 }