JexlUberspect.java
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.commons.jexl3.introspection;
import java.util.Arrays;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.apache.commons.jexl3.JexlArithmetic;
import org.apache.commons.jexl3.JexlOperator;
/**
* 'Federated' introspection/reflection interface to allow JEXL introspection
* behavior to be customized.
*
* @since 1.0
*/
public interface JexlUberspect {
/**
* The various builtin property resolvers.
* <p>
* Each resolver discovers how to set/get a property with different techniques; seeking
* method names or field names, etc.
*
* @since 3.0
*/
enum JexlResolver implements PropertyResolver {
/** Seeks methods named get{P,p}property and is{P,p}property. */
PROPERTY,
/** Seeks map methods get/put. */
MAP,
/** Seeks list methods get/set. */
LIST,
/** Seeks any get/{set,put} method (quacking like a list or a map). */
DUCK,
/** Seeks public instance members.*/
FIELD,
/** Seeks a getContainer(property) and setContainer(property, value) as in <code>x.container.property</code>. */
CONTAINER;
@Override
public final JexlPropertyGet getPropertyGet(final JexlUberspect uber,
final Object obj,
final Object identifier) {
return uber.getPropertyGet(Collections.singletonList(this), obj, identifier);
}
@Override
public final JexlPropertySet getPropertySet(final JexlUberspect uber,
final Object obj,
final Object identifier,
final Object arg) {
return uber.getPropertySet(Collections.singletonList(this), obj, identifier, arg);
}
}
/**
* Abstracts getting property setter and getter.
* <p>
* These are used through 'strategies' to solve properties; a strategy orders a list of resolver types,
* and each resolver type is tried in sequence; the first resolver that discovers a non-null {s,g}etter
* stops the search.
*
* @see JexlResolver
* @see JexlUberspect#getPropertyGet
* @see JexlUberspect#getPropertySet
* @since 3.0
*/
interface PropertyResolver {
/**
* Gets a property getter.
*
* @param uber the uberspect
* @param obj the object
* @param identifier the property identifier
* @return the property getter or null
*/
JexlPropertyGet getPropertyGet(JexlUberspect uber, Object obj, Object identifier);
/**
* Gets a property setter.
*
* @param uber the uberspect
* @param obj the object
* @param identifier the property identifier
* @param arg the property value
* @return the property setter or null
*/
JexlPropertySet getPropertySet(JexlUberspect uber, Object obj, Object identifier, Object arg);
}
/**
* Determines property resolution strategy.
*
* <p>To use a strategy instance, you have to set it at engine creation using
* {@link org.apache.commons.jexl3.JexlBuilder#strategy(JexlUberspect.ResolverStrategy)}
* as in:</p>
*
* <code>JexlEngine jexl = new JexlBuilder().strategy(MY_STRATEGY).create();</code>
*
* @since 3.0
*/
interface ResolverStrategy {
/**
* Applies this strategy to a list of resolver types.
*
* @param operator the property access operator, may be null
* @param obj the instance we seek to obtain a property setter/getter from, can not be null
* @return the ordered list of resolvers types, must not be null
*/
List<PropertyResolver> apply(JexlOperator operator, Object obj);
}
/**
* A resolver types list tailored for POJOs, favors '.' over '[]'.
*/
List<PropertyResolver> POJO = Collections.unmodifiableList(Arrays.asList(
JexlResolver.PROPERTY,
JexlResolver.MAP,
JexlResolver.LIST,
JexlResolver.DUCK,
JexlResolver.FIELD,
JexlResolver.CONTAINER
));
/**
* A resolver types list tailored for Maps, favors '[]' over '.'.
*/
List<PropertyResolver> MAP = Collections.unmodifiableList(Arrays.asList(
JexlResolver.MAP,
JexlResolver.LIST,
JexlResolver.DUCK,
JexlResolver.PROPERTY,
JexlResolver.FIELD,
JexlResolver.CONTAINER
));
/**
* The default strategy.
* <p>
* If the operator is '[]' or if the operator is null and the object is a map, use the MAP list of resolvers;
* Other cases use the POJO list of resolvers.
*/
ResolverStrategy JEXL_STRATEGY = (op, obj) -> {
if (op == JexlOperator.ARRAY_GET) {
return MAP;
}
if (op == JexlOperator.ARRAY_SET) {
return MAP;
}
if (op == null && obj instanceof Map) {
return MAP;
}
return POJO;
};
/**
* The map strategy.
*
* <p>If the operator is '[]' or if the object is a map, use the MAP list of resolvers.
* Otherwise, use the POJO list of resolvers.</p>
*/
ResolverStrategy MAP_STRATEGY = (op, obj) -> {
if (op == JexlOperator.ARRAY_GET) {
return MAP;
}
if (op == JexlOperator.ARRAY_SET) {
return MAP;
}
if (obj instanceof Map) {
return MAP;
}
return POJO;
};
/**
* Gets an arithmetic operator resolver for a given arithmetic instance.
*
* @param arithmetic the arithmetic instance
* @return the arithmetic uberspect or null if no operator method were overridden
* @since 3.0
*/
JexlArithmetic.Uberspect getArithmetic(JexlArithmetic arithmetic);
/**
* Seeks a class by name using this uberspect class-loader.
* @param className the class name
* @return the class instance or null if the class cannot be located by this uberspect class loader or if
* permissions deny access to the class
*/
default Class<?> getClassByName(final String className) {
try {
return Class.forName(className, false, getClassLoader());
} catch (final ClassNotFoundException xignore) {
return null;
}
}
/**
* Gets the current class loader.
* @return the class loader
*/
ClassLoader getClassLoader();
/**
* Returns a class constructor.
*
* @param ctorHandle a class or class name
* @param args constructor arguments
* @return a {@link JexlMethod}
* @since 3.0
*/
JexlMethod getConstructor(Object ctorHandle, Object... args);
/**
* Gets an iterator from an object.
*
* @param obj to get the iterator from
* @return an iterator over obj or null
*/
Iterator<?> getIterator(Object obj);
/**
* Returns a JexlMethod.
*
* @param obj the object
* @param method the method name
* @param args method arguments
* @return a {@link JexlMethod}
*/
JexlMethod getMethod(Object obj, String method, Object... args);
/**
* Property getter.
* <p>
* Seeks a JexlPropertyGet apropos to an expression like <code>bar.woogie</code>.</p>
* See {@link ResolverStrategy#apply(JexlOperator, java.lang.Object) }
*
* @param resolvers the list of property resolvers to try
* @param obj the object to get the property from
* @param identifier property name
* @return a {@link JexlPropertyGet} or null
* @since 3.0
*/
JexlPropertyGet getPropertyGet(List<PropertyResolver> resolvers, Object obj, Object identifier);
/**
* Property getter.
*
* <p>returns a JelPropertySet apropos to an expression like <code>bar.woogie</code>.</p>
*
* @param obj the object to get the property from
* @param identifier property name
* @return a {@link JexlPropertyGet} or null
*/
JexlPropertyGet getPropertyGet(Object obj, Object identifier);
/**
* Property setter.
* <p>
* Seeks a JelPropertySet apropos to an expression like <code>foo.bar = "geir"</code>.</p>
* See {@link ResolverStrategy#apply(JexlOperator, java.lang.Object) }
*
* @param resolvers the list of property resolvers to try,
* @param obj the object to get the property from
* @param identifier property name
* @param arg value to set
* @return a {@link JexlPropertySet} or null
* @since 3.0
*/
JexlPropertySet getPropertySet(List<PropertyResolver> resolvers, Object obj, Object identifier, Object arg);
/**
* Property setter.
* <p>
* Seeks a JelPropertySet apropos to an expression like <code>foo.bar = "geir"</code>.</p>
*
* @param obj the object to get the property from.
* @param identifier property name
* @param arg value to set
* @return a {@link JexlPropertySet} or null
*/
JexlPropertySet getPropertySet(Object obj, Object identifier, Object arg);
/**
* Applies this uberspect property resolver strategy.
*
* @param op the operator
* @param obj the object
* @return the applied strategy resolver list
*/
List<PropertyResolver> getResolvers(JexlOperator op, Object obj);
/**
* Gets this uberspect version.
*
* @return the class loader modification count
*/
int getVersion();
/**
* Sets the class loader to use.
*
* <p>This increments the version.</p>
*
* @param loader the class loader
*/
void setClassLoader(ClassLoader loader);
}