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