You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by he...@apache.org on 2017/03/14 14:05:16 UTC
svn commit: r1786904 - in /commons/proper/jexl/trunk/src:
main/java/org/apache/commons/jexl3/internal/introspection/IndexedType.java
test/java/org/apache/commons/jexl3/PropertyAccessTest.java
Author: henrib
Date: Tue Mar 14 14:05:16 2017
New Revision: 1786904
URL: http://svn.apache.org/viewvc?rev=1786904&view=rev
Log:
JEXL-222:
Improved IndexedType, allow caching of last set/get, expose container class & name
Modified:
commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/introspection/IndexedType.java
commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl3/PropertyAccessTest.java
Modified: commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/introspection/IndexedType.java
URL: http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/introspection/IndexedType.java?rev=1786904&r1=1786903&r2=1786904&view=diff
==============================================================================
--- commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/introspection/IndexedType.java (original)
+++ commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/introspection/IndexedType.java Tue Mar 14 14:05:16 2017
@@ -24,18 +24,23 @@ import java.beans.IntrospectionException
/**
* Abstract an indexed property container.
- * This stores the container name and owning class as well as the list of available getter and setter methods.
+ * <p>This allows getting properties from expressions like <code>var.container.property</code>.
+ * This stores the container name and class as well as the list of available getter and setter methods.
* It implements JexlPropertyGet since such a container can only be accessed from its owning instance (not set).
*/
public final class IndexedType implements JexlPropertyGet {
- /** The container name. */
+ /** The container name. */
private final String container;
- /** The owning class. */
+ /** The container class. */
private final Class<?> clazz;
/** The array of getter methods. */
private final Method[] getters;
+ /** Last get method used. */
+ private volatile Method get = null;
/** The array of setter methods. */
private final Method[] setters;
+ /** Last set method used. */
+ private volatile Method set = null;
/**
* Attempts to find an indexed-property getter in an object.
@@ -63,50 +68,66 @@ public final class IndexedType implement
}
/**
- * A generic indexed property container, exposes get(key) and set(key, value) and solves method call dynamically
- * based on arguments.
+ * A generic indexed property container, exposes get(key) and set(key, value)
+ * and solves method call dynamically based on arguments.
* <p>Must remain public for introspection purpose.</p>
*/
public static final class IndexedContainer {
- /** The instance owning the container. */
- private final Object object;
+ /** The container instance. */
+ private final Object container;
/** The container type instance. */
private final IndexedType type;
/**
* Creates a new duck container.
* @param theType the container type
- * @param theObject the instance owning the container
+ * @param theContainer the container instance
*/
- private IndexedContainer(IndexedType theType, Object theObject) {
+ private IndexedContainer(IndexedType theType, Object theContainer) {
this.type = theType;
- this.object = theObject;
+ this.container = theContainer;
+ }
+
+ /**
+ * Gets the property container name.
+ * @return the container name
+ */
+ public String getContainerName() {
+ return type.container;
}
/**
- * Gets a property from a container.
+ * Gets the property container class.
+ * @return the container class
+ */
+ public Class<?> getContainerClass() {
+ return type.clazz;
+ }
+
+ /**
+ * Gets a property from this indexed container.
* @param key the property key
* @return the property value
* @throws Exception if inner invocation fails
*/
public Object get(Object key) throws Exception {
- return type.invokeGet(object, key);
+ return type.invokeGet(container, key);
}
/**
- * Sets a property in a container.
+ * Sets a property in this indexed container.
* @param key the property key
* @param value the property value
* @return the invocation result (frequently null)
* @throws Exception if inner invocation fails
*/
public Object set(Object key, Object value) throws Exception {
- return type.invokeSet(object, key, value);
+ return type.invokeSet(container, key, value);
}
}
/**
- * Creates a new indexed type.
+ * Creates a new indexed property container type.
* @param name the container name
* @param c the owning class
* @param gets the array of getter methods
@@ -130,7 +151,9 @@ public final class IndexedType implement
@Override
public Object tryInvoke(Object obj, Object key) {
- if (obj != null && key != null && clazz.equals(obj.getClass()) && container.equals(key.toString())) {
+ if (obj != null && key != null
+ && clazz.equals(obj.getClass())
+ && container.equals(key.toString())) {
return new IndexedContainer(this, obj);
} else {
return Uberspect.TRY_FAILED;
@@ -149,49 +172,69 @@ public final class IndexedType implement
/**
* Gets the value of a property from a container.
- * @param object the instance owning the container (not null)
+ * @param object the container instance (not null)
* @param key the property key (not null)
* @return the property value
- * @throws Exception if invocation failed; IntrospectionException if a property getter could not be found
+ * @throws Exception if invocation failed;
+ * IntrospectionException if a property getter could not be found
*/
private Object invokeGet(Object object, Object key) throws Exception {
- if (getters != null) {
- final Object[] args = {key};
- final Method jm;
- if (getters.length == 1) {
- jm = getters[0];
- } else {
- jm = new MethodKey(getters[0].getName(), args).getMostSpecificMethod(getters);
+ if (getters != null && getters.length > 0) {
+ Method jm = get;
+ if (jm != null) {
+ final Class<?>[] ptypes = jm.getParameterTypes();
+ if (ptypes[0].isAssignableFrom(key.getClass())) {
+ return jm.invoke(object, key);
+ }
}
+ final Object[] args = {key};
+ final String mname = getters[0].getName();
+ final MethodKey km = new MethodKey(mname, args);
+ jm = km.getMostSpecificMethod(getters);
if (jm != null) {
- return jm.invoke(object, args);
+ Object invoked = jm.invoke(object, args);
+ get = jm;
+ return invoked;
}
}
- throw new IntrospectionException("property get error: " + object.getClass().toString() + "@" + key.toString());
+ throw new IntrospectionException("property get error: "
+ + object.getClass().toString()
+ + "@" + key.toString());
}
/**
* Sets the value of a property in a container.
- * @param object the instance owning the container (not null)
+ * @param object the container instance (not null)
* @param key the property key (not null)
* @param value the property value (not null)
* @return the result of the method invocation (frequently null)
- * @throws Exception if invocation failed; IntrospectionException if a property setter could not be found
+ * @throws Exception if invocation failed;
+ * IntrospectionException if a property setter could not be found
*/
private Object invokeSet(Object object, Object key, Object value) throws Exception {
- if (setters != null) {
- final Object[] args = {key, value};
- final Method jm;
- if (setters.length == 1) {
- jm = setters[0];
- } else {
- jm = new MethodKey(setters[0].getName(), args).getMostSpecificMethod(setters);
+ if (setters != null && setters.length > 0) {
+ Method jm = set;
+ if (jm != null) {
+ final Class<?>[] ptypes = jm.getParameterTypes();
+ if (ptypes[0].isAssignableFrom(key.getClass())
+ && (value == null
+ || ptypes[1].isAssignableFrom(value.getClass()))) {
+ return jm.invoke(object, key, value);
+ }
}
+ final Object[] args = {key, value};
+ final String mname = setters[0].getName();
+ final MethodKey km = new MethodKey(mname, args);
+ jm = km.getMostSpecificMethod(setters);
if (jm != null) {
- return jm.invoke(object, args);
+ Object invoked = jm.invoke(object, args);
+ set = jm;
+ return invoked;
}
}
- throw new IntrospectionException("property set error: " + object.getClass().toString() + "@" + key.toString());
+ throw new IntrospectionException("property set error: "
+ + object.getClass().toString()
+ + "@" + key.toString());
}
}
Modified: commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl3/PropertyAccessTest.java
URL: http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl3/PropertyAccessTest.java?rev=1786904&r1=1786903&r2=1786904&view=diff
==============================================================================
--- commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl3/PropertyAccessTest.java (original)
+++ commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl3/PropertyAccessTest.java Tue Mar 14 14:05:16 2017
@@ -20,6 +20,7 @@ import java.util.HashMap;
import java.util.Map;
import org.apache.commons.jexl3.internal.Debugger;
+import org.apache.commons.jexl3.internal.introspection.IndexedType;
import org.apache.commons.jexl3.junit.Asserter;
import org.junit.Assert;
import org.junit.Before;
@@ -45,7 +46,6 @@ public class PropertyAccessTest extends
asserter = new Asserter(JEXL);
}
-
@Test public void testPropertyProperty() throws Exception {
Integer i42 = Integer.valueOf(42);
Integer i43 = Integer.valueOf(43);
@@ -70,16 +70,19 @@ public class PropertyAccessTest extends
asserter.assertExpression("foo.0.'1'", i42);
}
}
-
- public static class Container {
+
+ /**
+ * A base property container; can only set from string.
+ */
+ public static class PropertyContainer {
String value0;
int value1;
-
- public Container(String name, int number) {
+
+ public PropertyContainer(String name, int number) {
value0 = name;
value1 = number;
}
-
+
public Object getProperty(String name) {
if ("name".equals(name)) {
return value0;
@@ -89,6 +92,91 @@ public class PropertyAccessTest extends
return null;
}
}
+
+ public void setProperty(String name, String value) {
+ if ("name".equals(name)) {
+ this.value0 = value.toUpperCase();
+ }
+ if ("number".equals(name)) {
+ this.value1 = Integer.parseInt(value) + 1000;
+ }
+ }
+ }
+
+
+ /**
+ * Overloads propertySet.
+ */
+ public static class PropertyArithmetic extends JexlArithmetic {
+ int ncalls = 0;
+
+ public PropertyArithmetic(boolean astrict) {
+ super(astrict);
+ }
+
+ public Object propertySet(IndexedType.IndexedContainer map, String key, Integer value) {
+ if (map.getContainerClass().equals(PropertyContainer.class)
+ && map.getContainerName().equals("property")) {
+ try {
+ map.set(key, value.toString());
+ ncalls += 1;
+ } catch (Exception xany) {
+ throw new JexlException.Operator(null, key + "." + value.toString(), xany);
+ }
+ return null;
+ }
+ return JexlEngine.TRY_FAILED;
+ }
+
+ public int getCalls() {
+ return ncalls;
+ }
+ }
+
+ @Test public void testInnerViaArithmetic() throws Exception {
+ PropertyArithmetic pa = new PropertyArithmetic(true);
+ JexlEngine jexl = new JexlBuilder().arithmetic(pa).debug(true).strict(true).cache(32).create();
+ PropertyContainer quux = new PropertyContainer("bar", 169);
+ Object result;
+
+ JexlScript getName = jexl.createScript("foo.property.name", "foo");
+ result = getName.execute(null, quux);
+ Assert.assertEquals("bar", result);
+ int calls = pa.getCalls();
+ JexlScript setName = jexl.createScript("foo.property.name = $0", "foo", "$0");
+ setName.execute(null, quux, 123);
+ result = getName.execute(null, quux);
+ Assert.assertEquals("123", result);
+ setName.execute(null, quux, 456);
+ result = getName.execute(null, quux);
+ Assert.assertEquals("456", result);
+ Assert.assertEquals(calls + 2, pa.getCalls());
+ setName.execute(null, quux, "quux");
+ result = getName.execute(null, quux);
+ Assert.assertEquals("QUUX", result);
+ Assert.assertEquals(calls + 2, pa.getCalls());
+
+ JexlScript getNumber = jexl.createScript("foo.property.number", "foo");
+ result = getNumber.execute(null, quux);
+ Assert.assertEquals(169, result);
+ JexlScript setNumber = jexl.createScript("foo.property.number = $0", "foo", "$0");
+ setNumber.execute(null, quux, 42);
+ result = getNumber.execute(null, quux);
+ Assert.assertEquals(1042, result);
+ setNumber.execute(null, quux, 24);
+ result = getNumber.execute(null, quux);
+ Assert.assertEquals(1024, result);
+ Assert.assertEquals(calls + 4, pa.getCalls());
+ setNumber.execute(null, quux, "42");
+ result = getNumber.execute(null, quux);
+ Assert.assertEquals(1042, result);
+ Assert.assertEquals(calls + 4, pa.getCalls());
+ }
+
+ public static class Container extends PropertyContainer {
+ public Container(String name, int number) {
+ super(name, number);
+ }
public Object getProperty(int ref) {
if (0 == ref) {
@@ -126,10 +214,13 @@ public class PropertyAccessTest extends
}
@Test public void testInnerProperty() throws Exception {
+ PropertyArithmetic pa = new PropertyArithmetic(true);
+ JexlEngine jexl = new JexlBuilder().arithmetic(pa).debug(true).strict(true).cache(32).create();
Container quux = new Container("quux", 42);
JexlScript get;
Object result;
+ int calls = pa.getCalls();
JexlScript getName = JEXL.createScript("foo.property.name", "foo");
result = getName.execute(null, quux);
Assert.assertEquals("quux", result);
@@ -173,6 +264,8 @@ public class PropertyAccessTest extends
Assert.assertEquals(24, result);
result = get1.execute(null, quux);
Assert.assertEquals(24, result);
+
+ Assert.assertEquals(calls, pa.getCalls());
}