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