WICKET-6318 making property expression resolver implementation a configurable option in the application
Project: http://git-wip-us.apache.org/repos/asf/wicket/repo Commit: http://git-wip-us.apache.org/repos/asf/wicket/commit/3a6db1bf Tree: http://git-wip-us.apache.org/repos/asf/wicket/tree/3a6db1bf Diff: http://git-wip-us.apache.org/repos/asf/wicket/diff/3a6db1bf Branch: refs/heads/WICKET-6318-configurable-property-expression-resolver Commit: 3a6db1bf7bd5de41299532cc1a6b73b3ff582516 Parents: a4a2ac6 Author: Pedro Henrique Oliveira dos Santos <[email protected]> Authored: Thu Feb 9 23:54:48 2017 -0200 Committer: Pedro Henrique Oliveira dos Santos <[email protected]> Committed: Thu Feb 9 23:54:48 2017 -0200 ---------------------------------------------------------------------- .../java/org/apache/wicket/Application.java | 2 +- .../core/util/lang/DefaultPropertyLocator.java | 160 ++ .../util/lang/IPropertyExpressionResolver.java | 12 + .../core/util/lang/IPropertyResolver.java | 10 + .../lang/OGNLPropertyExpressionResolver.java | 393 +++++ .../wicket/core/util/lang/PropertyResolver.java | 1563 +----------------- .../util/parser/PropertyExpressionResolver.java | 28 + .../core/util/reflection/AbstractGetAndSet.java | 43 + .../core/util/reflection/ArrayGetAndSet.java | 72 + .../util/reflection/ArrayLengthGetAndSet.java | 51 + .../util/reflection/CachingPropertyLocator.java | 70 + .../util/reflection/ClassMetadataIndex.java | 22 + .../core/util/reflection/FieldGetAndSet.java | 103 ++ .../wicket/core/util/reflection/IGetAndSet.java | 58 + .../reflection/IndexedPropertyGetAndSet.java | 149 ++ .../core/util/reflection/ListGetAndSet.java | 67 + .../core/util/reflection/MapGetAndSet.java | 46 + .../core/util/reflection/MethodGetAndSet.java | 243 +++ .../util/reflection/ObjectWithGetAndSet.java | 75 + .../core/util/reflection/ReflectionUtility.java | 92 ++ .../wicket/markup/html/form/FormComponent.java | 1 - .../wicket/model/AbstractPropertyModel.java | 74 +- .../model/IPropertyReflectionAwareModel.java | 54 - .../wicket/settings/ApplicationSettings.java | 15 + .../AbstractPropertyModelObjectClassTest.java | 5 +- .../wicket/model/PropertyModelWithListTest.java | 4 +- .../OGNLPropertyExpressionResolverTest.java | 842 ++++++++++ .../wicket/util/lang/PropertyResolverTest.java | 809 --------- .../PropertyVariableInterpolatorTest.java | 4 +- 29 files changed, 2574 insertions(+), 2493 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/wicket/blob/3a6db1bf/wicket-core/src/main/java/org/apache/wicket/Application.java ---------------------------------------------------------------------- diff --git a/wicket-core/src/main/java/org/apache/wicket/Application.java b/wicket-core/src/main/java/org/apache/wicket/Application.java index d6d4bb5..c879700 100644 --- a/wicket-core/src/main/java/org/apache/wicket/Application.java +++ b/wicket-core/src/main/java/org/apache/wicket/Application.java @@ -609,7 +609,7 @@ public abstract class Application implements UnboundListener, IEventSink // Clear caches of Class keys so the classloader can be garbage // collected (WICKET-625) - PropertyResolver.destroy(this); +// getApplicationSettings()..destroy(this); MarkupFactory markupFactory = getMarkupSettings().getMarkupFactory(); if (markupFactory.hasMarkupCache()) http://git-wip-us.apache.org/repos/asf/wicket/blob/3a6db1bf/wicket-core/src/main/java/org/apache/wicket/core/util/lang/DefaultPropertyLocator.java ---------------------------------------------------------------------- diff --git a/wicket-core/src/main/java/org/apache/wicket/core/util/lang/DefaultPropertyLocator.java b/wicket-core/src/main/java/org/apache/wicket/core/util/lang/DefaultPropertyLocator.java new file mode 100644 index 0000000..c8803c7 --- /dev/null +++ b/wicket-core/src/main/java/org/apache/wicket/core/util/lang/DefaultPropertyLocator.java @@ -0,0 +1,160 @@ +package org.apache.wicket.core.util.lang; + +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.util.List; +import java.util.Map; + +import org.apache.wicket.WicketRuntimeException; +import org.apache.wicket.core.util.reflection.ArrayGetAndSet; +import org.apache.wicket.core.util.reflection.ArrayLengthGetAndSet; +import org.apache.wicket.core.util.reflection.FieldGetAndSet; +import org.apache.wicket.core.util.reflection.IGetAndSet; +import org.apache.wicket.core.util.reflection.IndexedPropertyGetAndSet; +import org.apache.wicket.core.util.reflection.ListGetAndSet; +import org.apache.wicket.core.util.reflection.MapGetAndSet; +import org.apache.wicket.core.util.reflection.MethodGetAndSet; +import org.apache.wicket.core.util.reflection.ReflectionUtility; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Default locator supporting <em>Java Beans</em> properties, maps, lists and method invocations. + */ +public class DefaultPropertyLocator implements IPropertyResolver +{ + public static final Logger log = LoggerFactory.getLogger(DefaultPropertyLocator.class); + + public IGetAndSet get(Class<?> clz, String exp) + { + IGetAndSet getAndSet = null; + + Method method = null; + Field field; + if (exp.startsWith("[")) + { + // if expression begins with [ skip method finding and use it as + // a key/index lookup on a map. + exp = exp.substring(1, exp.length() - 1); + } + else if (exp.endsWith("()")) + { + // if expression ends with (), don't test for setters just skip + // directly to method finding. + method = ReflectionUtility.findMethod(clz, exp); + } + else + { + method = ReflectionUtility.findGetter(clz, exp); + } + if (method == null) + { + if (List.class.isAssignableFrom(clz)) + { + try + { + int index = Integer.parseInt(exp); + getAndSet = new ListGetAndSet(index); + } + catch (NumberFormatException ex) + { + // can't parse the exp as an index, maybe the exp was a + // method. + method = ReflectionUtility.findMethod(clz, exp); + if (method != null) + { + getAndSet = new MethodGetAndSet(method, + MethodGetAndSet.findSetter(method, clz), null); + } + else + { + field = ReflectionUtility.findField(clz, exp); + if (field != null) + { + getAndSet = new FieldGetAndSet(field); + } + else + { + throw new WicketRuntimeException("The expression '" + exp + + "' is neither an index nor is it a method or field for the list " + + clz); + } + } + } + } + else if (Map.class.isAssignableFrom(clz)) + { + getAndSet = new MapGetAndSet(exp); + } + else if (clz.isArray()) + { + try + { + int index = Integer.parseInt(exp); + getAndSet = new ArrayGetAndSet(clz.getComponentType(), index); + } + catch (NumberFormatException ex) + { + if (exp.equals("length") || exp.equals("size")) + { + getAndSet = new ArrayLengthGetAndSet(); + } + else + { + throw new WicketRuntimeException("Can't parse the expression '" + exp + + "' as an index for an array lookup"); + } + } + } + else + { + field = ReflectionUtility.findField(clz, exp); + if (field == null) + { + method = ReflectionUtility.findMethod(clz, exp); + if (method == null) + { + int index = exp.indexOf('.'); + if (index != -1) + { + String propertyName = exp.substring(0, index); + String propertyIndex = exp.substring(index + 1); + try + { + int parsedIndex = Integer.parseInt(propertyIndex); + // if so then it could be a getPropertyIndex(int) + // and setPropertyIndex(int, object) + String name = Character.toUpperCase(propertyName.charAt(0)) + + propertyName.substring(1); + method = clz.getMethod(MethodGetAndSet.GET + name, + new Class[] { int.class }); + getAndSet = new IndexedPropertyGetAndSet(method, parsedIndex); + } + catch (Exception e) + { + throw new WicketRuntimeException("No get method defined for class: " + + clz + " expression: " + propertyName); + } + } + } + else + { + getAndSet = new MethodGetAndSet(method, + MethodGetAndSet.findSetter(method, clz), null); + } + } + else + { + getAndSet = new FieldGetAndSet(field); + } + } + } + else + { + field = ReflectionUtility.findField(clz, exp); + getAndSet = new MethodGetAndSet(method, MethodGetAndSet.findSetter(method, clz), field); + } + + return getAndSet; + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/wicket/blob/3a6db1bf/wicket-core/src/main/java/org/apache/wicket/core/util/lang/IPropertyExpressionResolver.java ---------------------------------------------------------------------- diff --git a/wicket-core/src/main/java/org/apache/wicket/core/util/lang/IPropertyExpressionResolver.java b/wicket-core/src/main/java/org/apache/wicket/core/util/lang/IPropertyExpressionResolver.java new file mode 100644 index 0000000..18c8141 --- /dev/null +++ b/wicket-core/src/main/java/org/apache/wicket/core/util/lang/IPropertyExpressionResolver.java @@ -0,0 +1,12 @@ +package org.apache.wicket.core.util.lang; + +public interface IPropertyExpressionResolver +{ + + <T> T getValue(String expression, T object); + + <T> Class<T> getPropertyClass(String expression, Object object, Class<?> targetClass); + + void setValue(String expression, Object object, Object value, PropertyResolverConverter prc); + +} http://git-wip-us.apache.org/repos/asf/wicket/blob/3a6db1bf/wicket-core/src/main/java/org/apache/wicket/core/util/lang/IPropertyResolver.java ---------------------------------------------------------------------- diff --git a/wicket-core/src/main/java/org/apache/wicket/core/util/lang/IPropertyResolver.java b/wicket-core/src/main/java/org/apache/wicket/core/util/lang/IPropertyResolver.java new file mode 100644 index 0000000..e72a29e --- /dev/null +++ b/wicket-core/src/main/java/org/apache/wicket/core/util/lang/IPropertyResolver.java @@ -0,0 +1,10 @@ +package org.apache.wicket.core.util.lang; + +import org.apache.wicket.core.util.reflection.IGetAndSet; + +public interface IPropertyResolver +{ + + IGetAndSet get(Class<?> clz, String exp); + +} http://git-wip-us.apache.org/repos/asf/wicket/blob/3a6db1bf/wicket-core/src/main/java/org/apache/wicket/core/util/lang/OGNLPropertyExpressionResolver.java ---------------------------------------------------------------------- diff --git a/wicket-core/src/main/java/org/apache/wicket/core/util/lang/OGNLPropertyExpressionResolver.java b/wicket-core/src/main/java/org/apache/wicket/core/util/lang/OGNLPropertyExpressionResolver.java new file mode 100644 index 0000000..76f5814 --- /dev/null +++ b/wicket-core/src/main/java/org/apache/wicket/core/util/lang/OGNLPropertyExpressionResolver.java @@ -0,0 +1,393 @@ +/* + * 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.wicket.core.util.lang; + +import java.lang.reflect.Field; +import java.lang.reflect.Method; + +import org.apache.wicket.Application; +import org.apache.wicket.Session; +import org.apache.wicket.WicketRuntimeException; +import org.apache.wicket.core.util.reflection.CachingPropertyLocator; +import org.apache.wicket.core.util.reflection.IGetAndSet; +import org.apache.wicket.core.util.reflection.ObjectWithGetAndSet; +import org.apache.wicket.util.string.Strings; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * This class parses expressions to lookup or set a value on the object that is given. <br/> + * The supported expressions are: + * <dl> + * <dt>"property"</dt> + * <dd>This could be a bean property with getter and setter. Or if a map is given as an object it + * will be lookup with the expression as a key when there is not getter for that property.</dd> + * <dt>"property1.property2"</dt> + * <dd>Both properties are looked up as described above. If property1 evaluates to null then if + * there is a setter (or if it is a map) and the Class of the property has a default constructor + * then the object will be constructed and set on the object.</dd> + * <dt>"method()"</dt> + * <dd>The corresponding method is invoked.</dd> + * <dt>"property.index" or "property[index]"</dt> + * <dd>If the property is a List or Array then the following expression can be a index on that list + * like: 'mylist.0'. The list will grow automatically if the index is greater than the size. + * <p> + * This expression will also map on indexed properties, i.e. {@code getProperty(index)} and + * {@code setProperty(index,value)} methods.</dd> + * <dt>"property.key" or "property[key]"</dt> + * <dd>If the property is a Map then the following expression can be a key in that map like: + * 'myMap.key'.</dd> + * </dl> + * <strong>Note that the {@link DefaultPropertyLocator} by default provides access to private + * members and methods. If guaranteeing encapsulation of the target objects is a big concern, you + * should consider using an alternative implementation.</strong> + * <p> + * <strong>Note: If a property evaluates to an instance of {@link org.apache.wicket.model.IModel} + * then the expression should use '.object' to work with its value.</strong> + * + * @author jcompagner + * @author svenmeier + */ + +public class OGNLPropertyExpressionResolver implements IPropertyExpressionResolver +{ + + /** Log. */ + private static final Logger log = LoggerFactory.getLogger(PropertyResolver.class); + + private final static int RETURN_NULL = 0; + private final static int CREATE_NEW_VALUE = 1; + private final static int RESOLVE_CLASS = 2; + + private static IPropertyResolver locator = new CachingPropertyLocator( new DefaultPropertyLocator()); + + + /** + * Looks up the value from the object with the given expression. If the expression, the object + * itself or one property evaluates to null then a null will be returned. + * + * @param expression + * The expression string with the property to be lookup. + * @param object + * The object which is evaluated. + * @return The value that is evaluated. Null something in the expression evaluated to null. + */ + @Override + public Object getValue(final String expression, final Object object) + { + if (expression == null || expression.equals("") || object == null) + { + return object; + } + + ObjectWithGetAndSet objectWithGetAndSet = getObjectWithGetAndSet(expression, object, + RETURN_NULL); + if (objectWithGetAndSet == null) + { + return null; + } + + return objectWithGetAndSet.getValue(); + } + + /** + * Set the value on the object with the given expression. If the expression can't be evaluated + * then a WicketRuntimeException will be thrown. If a null object is encountered then it will + * try to generate it by calling the default constructor and set it on the object. + * + * The value will be tried to convert to the right type with the given converter. + * + * @param expression + * The expression string with the property to be set. + * @param object + * The object which is evaluated to set the value on. + * @param value + * The value to set. + * @param converter + * The converter to convert the value if needed to the right type. + * @throws WicketRuntimeException + */ + @Override + public void setValue(final String expression, final Object object, final Object value, + final PropertyResolverConverter converter) + { + if (Strings.isEmpty(expression)) + { + throw new WicketRuntimeException( + "Empty expression setting value: " + value + " on object: " + object); + } + if (object == null) + { + throw new WicketRuntimeException( + "Attempted to set property value on a null object. Property expression: " + + expression + " Value: " + value); + } + + ObjectWithGetAndSet objectWithGetAndSet = getObjectWithGetAndSet(expression, object, + CREATE_NEW_VALUE); + if (objectWithGetAndSet == null) + { + throw new WicketRuntimeException("Null object returned for expression: " + expression + + " for setting value: " + value + " on: " + object); + } + objectWithGetAndSet.setValue(value, + converter == null + ? new PropertyResolverConverter(Application.get().getConverterLocator(), + Session.get().getLocale()) + : converter); + } + + /** + * @param <T> + * @param expression + * @param clz + * @return class of the target Class property expression + * @throws WicketRuntimeException + * if class cannot be resolved + */ + @Override + @SuppressWarnings("unchecked") + public <T> Class<T> getPropertyClass(final String expression, Object object, final Class<?> clz) + { + ObjectWithGetAndSet objectWithGetAndSet = getObjectWithGetAndSet(expression, object, + RESOLVE_CLASS, clz); + if (objectWithGetAndSet == null) + { + throw new WicketRuntimeException("No Class returned for expression: " + expression + + " for getting the target class of: " + clz); + } + return (Class<T>)objectWithGetAndSet.getTargetClass(); + } + + /** + * @param expression + * @param object + * @return Field for the property expression + * @throws WicketRuntimeException + * if there is no such field + */ + public static Field getPropertyField(final String expression, final Object object) + { + ObjectWithGetAndSet objectWithGetAndSet = getObjectWithGetAndSet(expression, object, + RESOLVE_CLASS); + if (objectWithGetAndSet == null) + { + throw new WicketRuntimeException("Null object returned for expression: " + expression + + " for getting the target class of: " + object); + } + return objectWithGetAndSet.getField(); + } + + /** + * @param expression + * @param object + * @return Getter method for the property expression + * @throws WicketRuntimeException + * if there is no getter method + */ + public static Method getPropertyGetter(final String expression, final Object object) + { + ObjectWithGetAndSet objectWithGetAndSet = getObjectWithGetAndSet(expression, object, + RESOLVE_CLASS); + if (objectWithGetAndSet == null) + { + throw new WicketRuntimeException("Null object returned for expression: " + expression + + " for getting the target class of: " + object); + } + return objectWithGetAndSet.getGetter(); + } + + /** + * @param expression + * @param object + * @return Setter method for the property expression + * @throws WicketRuntimeException + * if there is no setter method + */ + public static Method getPropertySetter(final String expression, final Object object) + { + ObjectWithGetAndSet objectWithGetAndSet = getObjectWithGetAndSet(expression, object, + RESOLVE_CLASS); + if (objectWithGetAndSet == null) + { + throw new WicketRuntimeException("Null object returned for expression: " + expression + + " for getting the target class of: " + object); + } + return objectWithGetAndSet.getSetter(); + } + + /** + * Just delegating the call to the original getObjectAndGetSetter passing the object type as + * parameter. + * + * @param expression + * @param object + * @param tryToCreateNull + * @return {@link ObjectWithGetAndSet} + */ + private static ObjectWithGetAndSet getObjectWithGetAndSet(final String expression, + final Object object, int tryToCreateNull) + { + return getObjectWithGetAndSet(expression, object, tryToCreateNull, object.getClass()); + } + + /** + * Receives the class parameter also, since this method can resolve the type for some + * expression, only knowing the target class. + * + * @param expression + * property expression + * @param object + * root object + * @param tryToCreateNull + * how should null values be handled + * @param clz + * owning clazz + * @return final getAndSet and the target to apply it on, or {@code null} if expression results + * in an intermediate null + */ + private static ObjectWithGetAndSet getObjectWithGetAndSet(final String expression, + final Object object, final int tryToCreateNull, Class<?> clz) + { + String expressionBracketsSeperated = Strings.replaceAll(expression, "[", ".[").toString(); + int index = getNextDotIndex(expressionBracketsSeperated, 0); + while (index == 0 && expressionBracketsSeperated.startsWith(".")) + { + // eat dots at the beginning of the expression since they will confuse + // later steps + expressionBracketsSeperated = expressionBracketsSeperated.substring(1); + index = getNextDotIndex(expressionBracketsSeperated, 0); + } + int lastIndex = 0; + Object value = object; + String exp = expressionBracketsSeperated; + while (index != -1) + { + exp = expressionBracketsSeperated.substring(lastIndex, index); + if (exp.length() == 0) + { + exp = expressionBracketsSeperated.substring(index + 1); + break; + } + + IGetAndSet getAndSet = null; + try + { + getAndSet = getGetAndSet(exp, clz); + } + catch (WicketRuntimeException ex) + { + // expression by itself can't be found. try combined with the following + // expression (e.g. for a indexed property); + int temp = getNextDotIndex(expressionBracketsSeperated, index + 1); + if (temp == -1) + { + exp = expressionBracketsSeperated.substring(lastIndex); + break; + } + else + { + index = temp; + continue; + } + } + Object nextValue = null; + if (value != null) + { + nextValue = getAndSet.getValue(value); + } + if (nextValue == null) + { + if (tryToCreateNull == CREATE_NEW_VALUE) + { + nextValue = getAndSet.newValue(value); + if (nextValue == null) + { + return null; + } + } + else if (tryToCreateNull == RESOLVE_CLASS) + { + clz = getAndSet.getTargetClass(); + } + else + { + return null; + } + } + value = nextValue; + if (value != null) + { + // value can be null if we are in the RESOLVE_CLASS + clz = value.getClass(); + } + + lastIndex = index + 1; + index = getNextDotIndex(expressionBracketsSeperated, lastIndex); + if (index == -1) + { + exp = expressionBracketsSeperated.substring(lastIndex); + break; + } + } + IGetAndSet getAndSet = getGetAndSet(exp, clz); + return new ObjectWithGetAndSet(getAndSet, value); + } + + /** + * + * @param expression + * @param start + * @return next dot index + */ + private static int getNextDotIndex(final String expression, final int start) + { + boolean insideBracket = false; + for (int i = start; i < expression.length(); i++) + { + char ch = expression.charAt(i); + if (ch == '.' && !insideBracket) + { + return i; + } + else if (ch == '[') + { + insideBracket = true; + } + else if (ch == ']') + { + insideBracket = false; + } + } + return -1; + } + + private static IGetAndSet getGetAndSet(String exp, final Class<?> clz) + { + + IGetAndSet getAndSet = locator.get(clz, exp); + if (getAndSet == null) + { + throw new WicketRuntimeException( + "Property could not be resolved for class: " + clz + " expression: " + exp); + } + + return getAndSet; + } + + +} http://git-wip-us.apache.org/repos/asf/wicket/blob/3a6db1bf/wicket-core/src/main/java/org/apache/wicket/core/util/lang/PropertyResolver.java ---------------------------------------------------------------------- diff --git a/wicket-core/src/main/java/org/apache/wicket/core/util/lang/PropertyResolver.java b/wicket-core/src/main/java/org/apache/wicket/core/util/lang/PropertyResolver.java index ba5c48b..ad4063f 100644 --- a/wicket-core/src/main/java/org/apache/wicket/core/util/lang/PropertyResolver.java +++ b/wicket-core/src/main/java/org/apache/wicket/core/util/lang/PropertyResolver.java @@ -16,1570 +16,33 @@ */ package org.apache.wicket.core.util.lang; -import java.lang.reflect.Array; -import java.lang.reflect.Field; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.util.List; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; - import org.apache.wicket.Application; -import org.apache.wicket.Session; -import org.apache.wicket.WicketRuntimeException; -import org.apache.wicket.core.util.lang.PropertyResolverConverter; -import org.apache.wicket.util.convert.ConversionException; -import org.apache.wicket.util.lang.Generics; -import org.apache.wicket.util.string.Strings; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; /** - * This class parses expressions to lookup or set a value on the object that is given. <br/> - * The supported expressions are: - * <dl> - * <dt>"property"</dt> - * <dd> - * This could be a bean property with getter and setter. Or if a map is given as - * an object it will be lookup with the expression as a key when there is not getter for that - * property. - * </dd> - * <dt>"property1.property2"</dt> - * <dd> - * Both properties are looked up as described above. If property1 evaluates to - * null then if there is a setter (or if it is a map) and the Class of the property has a default - * constructor then the object will be constructed and set on the object. - * </dd> - * <dt>"method()"</dt> - * <dd> - * The corresponding method is invoked. - * </dd> - * <dt>"property.index" or "property[index]"</dt> - * <dd> - * If the property is a List or Array then the following expression can be a index on - * that list like: 'mylist.0'. The list will grow automatically if the index is greater than the size.<p> - * This expression will also map on indexed properties, i.e. {@code getProperty(index)} and {@code setProperty(index,value)} - * methods. - * </dd> - * <dt>"property.key" or "property[key]"</dt> - * <dd> - * If the property is a Map then the following expression can be a key in that map like: 'myMap.key'. - * </dd> - * </dl> - * <strong>Note that the {@link DefaultPropertyLocator} by default provides access to private members - * and methods. If guaranteeing encapsulation of the target objects is a big concern, you should consider - * using an alternative implementation.</strong> - * <p> - * <strong>Note: If a property evaluates to an instance of {@link org.apache.wicket.model.IModel} then - * the expression should use '.object' to work with its value.</strong> - * - * @author jcompagner - * @author svenmeier + * Old {@link PropertyResolver} kept just as a facade for the current implementation */ +@Deprecated public final class PropertyResolver { - /** Log. */ - private static final Logger log = LoggerFactory.getLogger(PropertyResolver.class); - - private final static int RETURN_NULL = 0; - private final static int CREATE_NEW_VALUE = 1; - private final static int RESOLVE_CLASS = 2; - - private final static ConcurrentHashMap<Object, IPropertyLocator> applicationToLocators = Generics.newConcurrentHashMap(2); - - private static final String GET = "get"; - private static final String IS = "is"; - private static final String SET = "set"; - - /** - * Looks up the value from the object with the given expression. If the expression, the object - * itself or one property evaluates to null then a null will be returned. - * - * @param expression - * The expression string with the property to be lookup. - * @param object - * The object which is evaluated. - * @return The value that is evaluated. Null something in the expression evaluated to null. - */ - public static Object getValue(final String expression, final Object object) - { - if (expression == null || expression.equals("") || object == null) - { - return object; - } - - ObjectWithGetAndSet objectWithGetAndSet = getObjectWithGetAndSet(expression, object, RETURN_NULL); - if (objectWithGetAndSet == null) - { - return null; - } - - return objectWithGetAndSet.getValue(); - } - - /** - * Set the value on the object with the given expression. If the expression can't be evaluated - * then a WicketRuntimeException will be thrown. If a null object is encountered then it will - * try to generate it by calling the default constructor and set it on the object. - * - * The value will be tried to convert to the right type with the given converter. - * - * @param expression - * The expression string with the property to be set. - * @param object - * The object which is evaluated to set the value on. - * @param value - * The value to set. - * @param converter - * The converter to convert the value if needed to the right type. - * @throws WicketRuntimeException - */ - public static void setValue(final String expression, final Object object, - final Object value, final PropertyResolverConverter converter) - { - if (Strings.isEmpty(expression)) - { - throw new WicketRuntimeException("Empty expression setting value: " + value + - " on object: " + object); - } - if (object == null) - { - throw new WicketRuntimeException( - "Attempted to set property value on a null object. Property expression: " + - expression + " Value: " + value); - } - - ObjectWithGetAndSet objectWithGetAndSet = getObjectWithGetAndSet(expression, object, CREATE_NEW_VALUE); - if (objectWithGetAndSet == null) - { - throw new WicketRuntimeException("Null object returned for expression: " + expression + - " for setting value: " + value + " on: " + object); - } - objectWithGetAndSet.setValue(value, converter == null ? new PropertyResolverConverter(Application.get() - .getConverterLocator(), Session.get().getLocale()) : converter); - } - - /** - * @param expression - * @param object - * @return class of the target property object - * @throws WicketRuntimeException if the cannot be resolved - */ - public static Class<?> getPropertyClass(final String expression, final Object object) - { - ObjectWithGetAndSet objectWithGetAndSet = getObjectWithGetAndSet(expression, object, RESOLVE_CLASS); - if (objectWithGetAndSet == null) - { - throw new WicketRuntimeException("Null object returned for expression: " + expression + - " for getting the target class of: " + object); - } - return objectWithGetAndSet.getTargetClass(); - } - - /** - * @param <T> - * @param expression - * @param clz - * @return class of the target Class property expression - * @throws WicketRuntimeException if class cannot be resolved - */ - @SuppressWarnings("unchecked") - public static <T> Class<T> getPropertyClass(final String expression, final Class<?> clz) - { - ObjectWithGetAndSet objectWithGetAndSet = getObjectWithGetAndSet(expression, null, RESOLVE_CLASS, clz); - if (objectWithGetAndSet == null) - { - throw new WicketRuntimeException("No Class returned for expression: " + expression + - " for getting the target class of: " + clz); - } - return (Class<T>)objectWithGetAndSet.getTargetClass(); - } - - /** - * @param expression - * @param object - * @return Field for the property expression - * @throws WicketRuntimeException if there is no such field - */ - public static Field getPropertyField(final String expression, final Object object) - { - ObjectWithGetAndSet objectWithGetAndSet = getObjectWithGetAndSet(expression, object, RESOLVE_CLASS); - if (objectWithGetAndSet == null) - { - throw new WicketRuntimeException("Null object returned for expression: " + expression + - " for getting the target class of: " + object); - } - return objectWithGetAndSet.getField(); - } - - /** - * @param expression - * @param object - * @return Getter method for the property expression - * @throws WicketRuntimeException if there is no getter method - */ - public static Method getPropertyGetter(final String expression, final Object object) - { - ObjectWithGetAndSet objectWithGetAndSet = getObjectWithGetAndSet(expression, object, RESOLVE_CLASS); - if (objectWithGetAndSet == null) - { - throw new WicketRuntimeException("Null object returned for expression: " + expression + - " for getting the target class of: " + object); - } - return objectWithGetAndSet.getGetter(); - } - - /** - * @param expression - * @param object - * @return Setter method for the property expression - * @throws WicketRuntimeException if there is no setter method - */ - public static Method getPropertySetter(final String expression, final Object object) - { - ObjectWithGetAndSet objectWithGetAndSet = getObjectWithGetAndSet(expression, object, RESOLVE_CLASS); - if (objectWithGetAndSet == null) - { - throw new WicketRuntimeException("Null object returned for expression: " + expression + - " for getting the target class of: " + object); - } - return objectWithGetAndSet.getSetter(); - } - - /** - * Just delegating the call to the original getObjectAndGetSetter passing the object type as - * parameter. - * - * @param expression - * @param object - * @param tryToCreateNull - * @return {@link ObjectWithGetAndSet} - */ - private static ObjectWithGetAndSet getObjectWithGetAndSet(final String expression, - final Object object, int tryToCreateNull) - { - return getObjectWithGetAndSet(expression, object, tryToCreateNull, object.getClass()); - } - - /** - * Receives the class parameter also, since this method can resolve the type for some - * expression, only knowing the target class. - * - * @param expression property expression - * @param object root object - * @param tryToCreateNull how should null values be handled - * @param clz owning clazz - * @return final getAndSet and the target to apply it on, or {@code null} if expression results in an intermediate null - */ - private static ObjectWithGetAndSet getObjectWithGetAndSet(final String expression, final Object object, final int tryToCreateNull, Class<?> clz) - { - String expressionBracketsSeperated = Strings.replaceAll(expression, "[", ".[").toString(); - int index = getNextDotIndex(expressionBracketsSeperated, 0); - while (index == 0 && expressionBracketsSeperated.startsWith(".")) - { - // eat dots at the beginning of the expression since they will confuse - // later steps - expressionBracketsSeperated = expressionBracketsSeperated.substring(1); - index = getNextDotIndex(expressionBracketsSeperated, 0); - } - int lastIndex = 0; - Object value = object; - String exp = expressionBracketsSeperated; - while (index != -1) - { - exp = expressionBracketsSeperated.substring(lastIndex, index); - if (exp.length() == 0) - { - exp = expressionBracketsSeperated.substring(index + 1); - break; - } - - IGetAndSet getAndSet = null; - try - { - getAndSet = getGetAndSet(exp, clz); - } - catch (WicketRuntimeException ex) - { - // expression by itself can't be found. try combined with the following - // expression (e.g. for a indexed property); - int temp = getNextDotIndex(expressionBracketsSeperated, index + 1); - if (temp == -1) - { - exp = expressionBracketsSeperated.substring(lastIndex); - break; - } else { - index = temp; - continue; - } - } - Object nextValue = null; - if (value != null) - { - nextValue = getAndSet.getValue(value); - } - if (nextValue == null) - { - if (tryToCreateNull == CREATE_NEW_VALUE) - { - nextValue = getAndSet.newValue(value); - if (nextValue == null) - { - return null; - } - } - else if (tryToCreateNull == RESOLVE_CLASS) - { - clz = getAndSet.getTargetClass(); - } - else - { - return null; - } - } - value = nextValue; - if (value != null) - { - // value can be null if we are in the RESOLVE_CLASS - clz = value.getClass(); - } - - lastIndex = index + 1; - index = getNextDotIndex(expressionBracketsSeperated, lastIndex); - if (index == -1) - { - exp = expressionBracketsSeperated.substring(lastIndex); - break; - } - } - IGetAndSet getAndSet = getGetAndSet(exp, clz); - return new ObjectWithGetAndSet(getAndSet, value); - } - - /** - * - * @param expression - * @param start - * @return next dot index - */ - private static int getNextDotIndex(final String expression, final int start) - { - boolean insideBracket = false; - for (int i = start; i < expression.length(); i++) - { - char ch = expression.charAt(i); - if (ch == '.' && !insideBracket) - { - return i; - } - else if (ch == '[') - { - insideBracket = true; - } - else if (ch == ']') - { - insideBracket = false; - } - } - return -1; - } - - private static IGetAndSet getGetAndSet(String exp, final Class<?> clz) - { - IPropertyLocator locator = getLocator(); - - IGetAndSet getAndSet = locator.get(clz, exp); - if (getAndSet == null) { - throw new WicketRuntimeException( - "Property could not be resolved for class: " + clz + " expression: " + exp); - } - - return getAndSet; - } - - /** - * Utility class: instantiation not allowed. - */ - private PropertyResolver() - { - } - - /** - * @author jcompagner - * - */ - private final static class ObjectWithGetAndSet - { - private final IGetAndSet getAndSet; - private final Object value; - - /** - * @param getAndSet - * @param value - */ - public ObjectWithGetAndSet(IGetAndSet getAndSet, Object value) - { - this.getAndSet = getAndSet; - this.value = value; - } - - /** - * @param value - * @param converter - */ - public void setValue(Object value, PropertyResolverConverter converter) - { - getAndSet.setValue(this.value, value, converter); - } - - /** - * @return The value - */ - public Object getValue() - { - return getAndSet.getValue(value); - } - - /** - * @return class of property value - */ - public Class<?> getTargetClass() - { - return getAndSet.getTargetClass(); - } - - /** - * @return Field or null if no field exists for expression - */ - public Field getField() - { - return getAndSet.getField(); - } - - /** - * @return Getter method or null if no getter exists for expression - */ - public Method getGetter() - { - return getAndSet.getGetter(); - } - - /** - * @return Setter method or null if no setter exists for expression - */ - public Method getSetter() - { - return getAndSet.getSetter(); - } - } - - /** - * A property to get and set. - * - * @author jcompagner - */ - public interface IGetAndSet - { - /** - * @param object - * The object where the value must be taken from. - * - * @return The value of this property - */ - public Object getValue(final Object object); - - /** - * @return The target class of the object that as to be set. - */ - public Class<?> getTargetClass(); - - /** - * @param object - * The object where the new value must be set on. - * - * @return The new value for the property that is set back on that object. - */ - public Object newValue(Object object); - - /** - * @param object - * @param value - * @param converter - */ - public void setValue(final Object object, final Object value, - PropertyResolverConverter converter); - - /** - * @return Field or null if there is no field - */ - public Field getField(); - - /** - * @return Getter method or null if there is no getter - */ - public Method getGetter(); - - /** - * @return Setter of null if there is no setter - */ - public Method getSetter(); - } - - public static abstract class AbstractGetAndSet implements IGetAndSet - { - /** - * {@inheritDoc} - */ - @Override - public Field getField() - { - return null; - } - - /** - * {@inheritDoc} - */ - @Override - public Method getGetter() - { - return null; - } - - /** - * {@inheritDoc} - */ - @Override - public Method getSetter() - { - return null; - } - - /** - * {@inheritDoc} - */ - @Override - public Class<?> getTargetClass() - { - return null; - } - } - - private static final class MapGetAndSet extends AbstractGetAndSet - { - private final String key; - - MapGetAndSet(String key) - { - this.key = key; - } - - /** - * {@inheritDoc} - */ - @Override - public Object getValue(final Object object) - { - return ((Map<?, ?>)object).get(key); - } - - /** - * {@inheritDoc} - */ - @Override - @SuppressWarnings("unchecked") - public void setValue(final Object object, final Object value, - final PropertyResolverConverter converter) - { - ((Map<String, Object>)object).put(key, value); - } - /** - * {@inheritDoc} - */ - @Override - public Object newValue(final Object object) - { - // Map can't make a newValue or should it look what is more in the - // map and try to make one of the class if finds? - return null; - } - } - - private static final class ListGetAndSet extends AbstractGetAndSet - { - final private int index; - - ListGetAndSet(int index) - { - this.index = index; - } - - /** - * {@inheritDoc} - */ - @Override - public Object getValue(final Object object) - { - if (((List<?>)object).size() <= index) - { - return null; - } - return ((List<?>)object).get(index); - } - - /** - * {@inheritDoc} - */ - @Override - @SuppressWarnings("unchecked") - public void setValue(final Object object, final Object value, - final PropertyResolverConverter converter) - { - List<Object> lst = (List<Object>)object; - - if (lst.size() > index) - { - lst.set(index, value); - } - else if (lst.size() == index) - { - lst.add(value); - } - else - { - while (lst.size() < index) - { - lst.add(null); - } - lst.add(value); - } - } - - /** - * {@inheritDoc} - */ - @Override - public Object newValue(Object object) - { - // List can't make a newValue or should it look what is more in the - // list and try to make one of the class if finds? - return null; - } - } - - private static final class ArrayGetAndSet extends AbstractGetAndSet - { - private final int index; - private final Class<?> clzComponentType; - - ArrayGetAndSet(Class<?> clzComponentType, int index) - { - this.clzComponentType = clzComponentType; - this.index = index; - } - - /** - * {@inheritDoc} - */ - @Override - public Object getValue(Object object) - { - if (Array.getLength(object) > index) - { - return Array.get(object, index); - } - return null; - } - - /** - * {@inheritDoc} - */ - @Override - public void setValue(Object object, Object value, PropertyResolverConverter converter) - { - value = converter.convert(value, clzComponentType); - Array.set(object, index, value); - } - - /** - * {@inheritDoc} - */ - @Override - public Object newValue(Object object) - { - Object value = null; - try - { - value = clzComponentType.newInstance(); - Array.set(object, index, value); - } - catch (Exception e) - { - log.warn("Cannot set new value " + value + " at index " + index + - " for array holding elements of class " + clzComponentType, e); - } - return value; - } - - /** - * {@inheritDoc} - */ - @Override - public Class<?> getTargetClass() - { - return clzComponentType; - } - } - - private static final class ArrayLengthGetAndSet extends AbstractGetAndSet - { - ArrayLengthGetAndSet() - { - } - - /** - * {@inheritDoc} - */ - @Override - public Object getValue(final Object object) - { - return Array.getLength(object); - } - - /** - * {@inheritDoc} - */ - @Override - public void setValue(final Object object, final Object value, - final PropertyResolverConverter converter) - { - throw new WicketRuntimeException("You can't set the length on an array:" + object); - } - - /** - * {@inheritDoc} - */ - @Override - public Object newValue(final Object object) - { - throw new WicketRuntimeException("Can't get a new value from a length of an array: " + - object); - } - - /** - * {@inheritDoc} - */ - @Override - public Class<?> getTargetClass() - { - return int.class; - } - } - - private static final class IndexedPropertyGetAndSet extends AbstractGetAndSet - { - final private Integer index; - final private Method getMethod; - private Method setMethod; - - IndexedPropertyGetAndSet(final Method method, final int index) - { - this.index = index; - getMethod = method; - getMethod.setAccessible(true); - } - - private static Method findSetter(final Method getMethod, final Class<?> clz) - { - String name = getMethod.getName(); - name = SET + name.substring(3); - try - { - return clz.getMethod(name, new Class[] { int.class, getMethod.getReturnType() }); - } - catch (Exception e) - { - log.debug("Can't find setter method corresponding to " + getMethod); - } - return null; - } - - /** - * {@inheritDoc} - */ - @Override - public Object getValue(Object object) - { - Object ret; - try - { - ret = getMethod.invoke(object, index); - } - catch (InvocationTargetException ex) - { - throw new WicketRuntimeException("Error calling index property method: " + - getMethod + " on object: " + object, ex.getCause()); - } - catch (Exception ex) - { - throw new WicketRuntimeException("Error calling index property method: " + - getMethod + " on object: " + object, ex); - } - return ret; - } - - /** - * {@inheritDoc} - */ - @Override - public void setValue(final Object object, final Object value, - final PropertyResolverConverter converter) - { - if (setMethod == null) - { - setMethod = findSetter(getMethod, object.getClass()); - } - if (setMethod != null) - { - setMethod.setAccessible(true); - Object converted = converter.convert(value, getMethod.getReturnType()); - if (converted == null && value != null) - { - throw new ConversionException("Can't convert value: " + value + " to class: " + - getMethod.getReturnType() + " for setting it on " + object); - } - try - { - setMethod.invoke(object, index, converted); - } - catch (InvocationTargetException ex) - { - throw new WicketRuntimeException("Error index property calling method: " + - setMethod + " on object: " + object, ex.getCause()); - } - catch (Exception ex) - { - throw new WicketRuntimeException("Error index property calling method: " + - setMethod + " on object: " + object, ex); - } - } - else - { - throw new WicketRuntimeException("No set method defined for value: " + value + - " on object: " + object); - } - } - - /** - * {@inheritDoc} - */ - @Override - public Class<?> getTargetClass() - { - return getMethod.getReturnType(); - } - - /** - * {@inheritDoc} - */ - @Override - public Object newValue(Object object) - { - if (setMethod == null) - { - setMethod = findSetter(getMethod, object.getClass()); - } - - if (setMethod == null) - { - log.warn("Null setMethod"); - return null; - } - - Class<?> clz = getMethod.getReturnType(); - Object value = null; - try - { - value = clz.newInstance(); - setMethod.invoke(object, index, value); - } - catch (Exception e) - { - log.warn("Cannot set new value " + value + " at index " + index, e); - } - return value; - } - } - - private static final class MethodGetAndSet extends AbstractGetAndSet - { - private final Method getMethod; - private final Method setMethod; - private final Field field; - - MethodGetAndSet(Method getMethod, Method setMethod, Field field) - { - this.getMethod = getMethod; - this.getMethod.setAccessible(true); - this.field = field; - this.setMethod = setMethod; - } - - /** - * {@inheritDoc} - */ - @Override - public final Object getValue(final Object object) - { - Object ret; - try - { - ret = getMethod.invoke(object, (Object[])null); - } - catch (InvocationTargetException ex) - { - throw new WicketRuntimeException("Error calling method: " + getMethod + - " on object: " + object, ex.getCause()); - } - catch (Exception ex) - { - throw new WicketRuntimeException("Error calling method: " + getMethod + - " on object: " + object, ex); - } - return ret; - } - - /** - * @param object - * @param value - * @param converter - */ - @Override - public final void setValue(final Object object, final Object value, - PropertyResolverConverter converter) - { - Class<?> type = null; - if (setMethod != null) - { - // getMethod is always there and if the value will be set through a setMethod then - // the getMethod return type will be its type. Else we have to look at the - // parameters if the setter but getting the return type is quicker - type = getMethod.getReturnType(); - } - else if (field != null) - { - type = field.getType(); - } - - Object converted = null; - if (type != null) - { - converted = converter.convert(value, type); - if (converted == null) - { - if (value != null) - { - throw new ConversionException("Method [" + getMethod + - "]. Can't convert value: " + value + " to class: " + - getMethod.getReturnType() + " for setting it on " + object); - } - else if (getMethod.getReturnType().isPrimitive()) - { - throw new ConversionException("Method [" + getMethod + - "]. Can't convert null value to a primitive class: " + - getMethod.getReturnType() + " for setting it on " + object); - } - } - } - - if (setMethod != null) - { - try - { - setMethod.invoke(object, converted); - } - catch (InvocationTargetException ex) - { - throw new WicketRuntimeException("Error calling method: " + setMethod + - " on object: " + object, ex.getCause()); - } - catch (Exception ex) - { - throw new WicketRuntimeException("Error calling method: " + setMethod + - " on object: " + object, ex); - } - } - else if (field != null) - { - try - { - field.set(object, converted); - } - catch (Exception ex) - { - throw new WicketRuntimeException("Error setting field: " + field + - " on object: " + object, ex); - } - } - else - { - throw new WicketRuntimeException("no set method defined for value: " + value + - " on object: " + object + " while respective getMethod being " + - getMethod.getName()); - } - } - - private static Method findSetter(Method getMethod, Class<?> clz) - { - String name = getMethod.getName(); - if (name.startsWith(GET)) - { - name = SET + name.substring(3); - } - else - { - name = SET + name.substring(2); - } - try - { - Method method = clz.getMethod(name, new Class[] { getMethod.getReturnType() }); - if (method != null) - { - method.setAccessible(true); - } - return method; - } - catch (NoSuchMethodException e) - { - Method[] methods = clz.getMethods(); - for (Method method : methods) - { - if (method.getName().equals(name)) - { - Class<?>[] parameterTypes = method.getParameterTypes(); - if (parameterTypes.length == 1) - { - if (parameterTypes[0].isAssignableFrom(getMethod.getReturnType())) - { - return method; - } - } - } - } - log.debug("Cannot find setter corresponding to " + getMethod); - } - catch (Exception e) - { - log.debug("Cannot find setter corresponding to " + getMethod); - } - return null; - } - - /** - * {@inheritDoc} - */ - @Override - public Object newValue(Object object) - { - if (setMethod == null) - { - log.warn("Null setMethod"); - return null; - } - - Class<?> clz = getMethod.getReturnType(); - Object value = null; - try - { - value = clz.newInstance(); - setMethod.invoke(object, value); - } - catch (Exception e) - { - log.warn("Cannot set new value " + value, e); - } - return value; - } - - /** - * {@inheritDoc} - */ - @Override - public Class<?> getTargetClass() - { - return getMethod.getReturnType(); - } - - /** - * {@inheritDoc} - */ - @Override - public Method getGetter() - { - return getMethod; - } - - /** - * {@inheritDoc} - */ - @Override - public Method getSetter() - { - return setMethod; - } - - /** - * {@inheritDoc} - */ - @Override - public Field getField() - { - return field; - } - } - - /** - * @author jcompagner - */ - private static class FieldGetAndSet extends AbstractGetAndSet - { - private final Field field; - - /** - * Construct. - * - * @param field - */ - public FieldGetAndSet(final Field field) - { - super(); - this.field = field; - this.field.setAccessible(true); - } - - /** - * {@inheritDoc} - */ - @Override - public Object getValue(final Object object) - { - try - { - return field.get(object); - } - catch (Exception ex) - { - throw new WicketRuntimeException("Error getting field value of field " + field + - " from object " + object, ex); - } - } - - /** - * {@inheritDoc} - */ - @Override - public Object newValue(final Object object) - { - Class<?> clz = field.getType(); - Object value = null; - try - { - value = clz.newInstance(); - field.set(object, value); - } - catch (Exception e) - { - log.warn("Cannot set field " + field + " to " + value, e); - } - return value; - } - - /** - * {@inheritDoc} - */ - @Override - public void setValue(final Object object, Object value, - final PropertyResolverConverter converter) - { - value = converter.convert(value, field.getType()); - try - { - field.set(object, value); - } - catch (Exception ex) - { - throw new WicketRuntimeException("Error setting field value of field " + field + - " on object " + object + ", value " + value, ex); - } - } - - /** - * {@inheritDoc} - */ - @Override - public Class<?> getTargetClass() - { - return field.getType(); - } - - /** - * {@inheritDoc} - */ - @Override - public Field getField() - { - return field; - } - } - - /** - * Clean up cache for this app. - * - * @param application - */ - public static void destroy(Application application) - { - applicationToLocators.remove(application); - } - - /** - * Get the current {@link IPropertyLocator}. - * - * @return locator for the current {@link Application} or a general one if no current application is present - * @see Application#get() - */ - public static IPropertyLocator getLocator() + public static <T> T getValue(String expression, T object) { - Object key; - if (Application.exists()) - { - key = Application.get(); - } - else - { - key = PropertyResolver.class; - } - IPropertyLocator result = applicationToLocators.get(key); - if (result == null) - { - IPropertyLocator tmpResult = applicationToLocators.putIfAbsent(key, result = new CachingPropertyLocator(new DefaultPropertyLocator())); - if (tmpResult != null) - { - result = tmpResult; - } - } - return result; + return Application.get().getApplicationSettings().getPropertyExpressionResolver() + .getValue(expression, object); } - /** - * Set a locator for the given application. - * - * @param application application, may be {@code null} - * @param locator locator - */ - public static void setLocator(final Application application, final IPropertyLocator locator) + public static <T> Class<T> getPropertyClass(String expression, Object object, + Class<?> targetClass) { - if (application == null) - { - applicationToLocators.put(PropertyResolver.class, locator); - } - else - { - applicationToLocators.put(application, locator); - } + return Application.get().getApplicationSettings().getPropertyExpressionResolver() + .getPropertyClass(expression, object, targetClass); } - /** - * A locator of properties. - * - * @see https://issues.apache.org/jira/browse/WICKET-5623 - */ - public static interface IPropertyLocator + public static void setValue(String expression, Object object, Object value, + PropertyResolverConverter prc) { - /** - * Get {@link IGetAndSet} for a property. - * - * @param clz owning class - * @param exp identifying the property - * @return getAndSet or {@code null} if non located - */ - IGetAndSet get(Class<?> clz, String exp); + Application.get().getApplicationSettings().getPropertyExpressionResolver() + .setValue(expression, object, value, prc); } - /** - * A wrapper for another {@link IPropertyLocator} that caches results of {@link #get(Class, String)}. - */ - public static class CachingPropertyLocator implements IPropertyLocator - { - private final ConcurrentHashMap<String, IGetAndSet> map = Generics.newConcurrentHashMap(16); - - /** - * Special token to put into the cache representing no located {@link IGetAndSet}. - */ - private IGetAndSet NONE = new AbstractGetAndSet() { - - @Override - public Object getValue(Object object) { - return null; - } - - @Override - public Object newValue(Object object) { - return null; - } - - @Override - public void setValue(Object object, Object value, PropertyResolverConverter converter) { - } - }; - - private IPropertyLocator locator; - - public CachingPropertyLocator(IPropertyLocator locator) { - this.locator = locator; - } - - @Override - public IGetAndSet get(Class<?> clz, String exp) { - String key = clz.getName() + "#" + exp; - - IGetAndSet located = map.get(key); - if (located == null) { - located = locator.get(clz, exp); - if (located == null) { - located = NONE; - } - map.put(key, located); - } - - if (located == NONE) { - located = null; - } - - return located; - } - } - - /** - * Default locator supporting <em>Java Beans</em> properties, maps, lists and method invocations. - */ - public static class DefaultPropertyLocator implements IPropertyLocator - { - @Override - public IGetAndSet get(Class<?> clz, String exp) { - IGetAndSet getAndSet = null; - - Method method = null; - Field field; - if (exp.startsWith("[")) - { - // if expression begins with [ skip method finding and use it as - // a key/index lookup on a map. - exp = exp.substring(1, exp.length() - 1); - } - else if (exp.endsWith("()")) - { - // if expression ends with (), don't test for setters just skip - // directly to method finding. - method = findMethod(clz, exp); - } - else - { - method = findGetter(clz, exp); - } - if (method == null) - { - if (List.class.isAssignableFrom(clz)) - { - try - { - int index = Integer.parseInt(exp); - getAndSet = new ListGetAndSet(index); - } - catch (NumberFormatException ex) - { - // can't parse the exp as an index, maybe the exp was a - // method. - method = findMethod(clz, exp); - if (method != null) - { - getAndSet = new MethodGetAndSet(method, MethodGetAndSet.findSetter( - method, clz), null); - } - else - { - field = findField(clz, exp); - if (field != null) - { - getAndSet = new FieldGetAndSet(field); - } - else - { - throw new WicketRuntimeException( - "The expression '" + - exp + - "' is neither an index nor is it a method or field for the list " + - clz); - } - } - } - } - else if (Map.class.isAssignableFrom(clz)) - { - getAndSet = new MapGetAndSet(exp); - } - else if (clz.isArray()) - { - try - { - int index = Integer.parseInt(exp); - getAndSet = new ArrayGetAndSet(clz.getComponentType(), index); - } - catch (NumberFormatException ex) - { - if (exp.equals("length") || exp.equals("size")) - { - getAndSet = new ArrayLengthGetAndSet(); - } - else - { - throw new WicketRuntimeException("Can't parse the expression '" + exp + - "' as an index for an array lookup"); - } - } - } - else - { - field = findField(clz, exp); - if (field == null) - { - method = findMethod(clz, exp); - if (method == null) - { - int index = exp.indexOf('.'); - if (index != -1) - { - String propertyName = exp.substring(0, index); - String propertyIndex = exp.substring(index + 1); - try - { - int parsedIndex = Integer.parseInt(propertyIndex); - // if so then it could be a getPropertyIndex(int) - // and setPropertyIndex(int, object) - String name = Character.toUpperCase(propertyName.charAt(0)) + - propertyName.substring(1); - method = clz.getMethod(GET + name, new Class[] { int.class }); - getAndSet = new IndexedPropertyGetAndSet(method, parsedIndex); - } - catch (Exception e) - { - throw new WicketRuntimeException( - "No get method defined for class: " + clz + - " expression: " + propertyName); - } - } - } - else - { - getAndSet = new MethodGetAndSet(method, MethodGetAndSet.findSetter( - method, clz), null); - } - } - else - { - getAndSet = new FieldGetAndSet(field); - } - } - } - else - { - field = findField(clz, exp); - getAndSet = new MethodGetAndSet(method, MethodGetAndSet.findSetter(method, clz), - field); - } - - return getAndSet; - } - - /** - * @param clz - * @param expression - * @return introspected field - */ - private Field findField(final Class<?> clz, final String expression) - { - Field field = null; - try - { - field = clz.getField(expression); - } - catch (Exception e) - { - Class<?> tmp = clz; - while (tmp != null && tmp != Object.class) - { - Field[] fields = tmp.getDeclaredFields(); - for (Field aField : fields) - { - if (aField.getName().equals(expression)) - { - aField.setAccessible(true); - return aField; - } - } - tmp = tmp.getSuperclass(); - } - log.debug("Cannot find field " + clz + "." + expression); - } - return field; - } - - /** - * @param clz - * @param expression - * @return The method for the expression null if not found - */ - private Method findGetter(final Class<?> clz, final String expression) - { - String name = Character.toUpperCase(expression.charAt(0)) + expression.substring(1); - Method method = null; - try - { - method = clz.getMethod(GET + name, (Class[])null); - } - catch (Exception ignored) - { - } - if (method == null) - { - try - { - method = clz.getMethod(IS + name, (Class[])null); - } - catch (Exception e) - { - log.debug("Cannot find getter " + clz + "." + expression); - } - } - return method; - } - - private Method findMethod(final Class<?> clz, String expression) - { - if (expression.endsWith("()")) - { - expression = expression.substring(0, expression.length() - 2); - } - Method method = null; - try - { - method = clz.getMethod(expression, (Class[])null); - } - catch (Exception e) - { - log.debug("Cannot find method " + clz + "." + expression); - } - return method; - } - } } \ No newline at end of file http://git-wip-us.apache.org/repos/asf/wicket/blob/3a6db1bf/wicket-core/src/main/java/org/apache/wicket/core/util/parser/PropertyExpressionResolver.java ---------------------------------------------------------------------- diff --git a/wicket-core/src/main/java/org/apache/wicket/core/util/parser/PropertyExpressionResolver.java b/wicket-core/src/main/java/org/apache/wicket/core/util/parser/PropertyExpressionResolver.java new file mode 100644 index 0000000..22049d2 --- /dev/null +++ b/wicket-core/src/main/java/org/apache/wicket/core/util/parser/PropertyExpressionResolver.java @@ -0,0 +1,28 @@ +package org.apache.wicket.core.util.parser; + +import org.apache.wicket.core.util.lang.IPropertyExpressionResolver; +import org.apache.wicket.core.util.lang.PropertyResolverConverter; + +public class PropertyExpressionResolver implements IPropertyExpressionResolver +{ + + @Override + public <T> T getValue(String expression, T object) + { + return null; + } + + @Override + public <T> Class<T> getPropertyClass(String expression, Object object, Class<?> targetClass) + { + return null; + } + + @Override + public void setValue(String expression, Object object, Object value, + PropertyResolverConverter prc) + { + + } + +} http://git-wip-us.apache.org/repos/asf/wicket/blob/3a6db1bf/wicket-core/src/main/java/org/apache/wicket/core/util/reflection/AbstractGetAndSet.java ---------------------------------------------------------------------- diff --git a/wicket-core/src/main/java/org/apache/wicket/core/util/reflection/AbstractGetAndSet.java b/wicket-core/src/main/java/org/apache/wicket/core/util/reflection/AbstractGetAndSet.java new file mode 100644 index 0000000..daafa81 --- /dev/null +++ b/wicket-core/src/main/java/org/apache/wicket/core/util/reflection/AbstractGetAndSet.java @@ -0,0 +1,43 @@ +package org.apache.wicket.core.util.reflection; + +import java.lang.reflect.Field; +import java.lang.reflect.Method; + +public abstract class AbstractGetAndSet implements IGetAndSet +{ + /** + * {@inheritDoc} + */ + @Override + public Field getField() + { + return null; + } + + /** + * {@inheritDoc} + */ + @Override + public Method getGetter() + { + return null; + } + + /** + * {@inheritDoc} + */ + @Override + public Method getSetter() + { + return null; + } + + /** + * {@inheritDoc} + */ + @Override + public Class<?> getTargetClass() + { + return null; + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/wicket/blob/3a6db1bf/wicket-core/src/main/java/org/apache/wicket/core/util/reflection/ArrayGetAndSet.java ---------------------------------------------------------------------- diff --git a/wicket-core/src/main/java/org/apache/wicket/core/util/reflection/ArrayGetAndSet.java b/wicket-core/src/main/java/org/apache/wicket/core/util/reflection/ArrayGetAndSet.java new file mode 100644 index 0000000..f4b6137 --- /dev/null +++ b/wicket-core/src/main/java/org/apache/wicket/core/util/reflection/ArrayGetAndSet.java @@ -0,0 +1,72 @@ +package org.apache.wicket.core.util.reflection; + +import java.lang.reflect.Array; + +import org.apache.wicket.core.util.lang.PropertyResolverConverter; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class ArrayGetAndSet extends AbstractGetAndSet +{ + private static final Logger log = LoggerFactory.getLogger(ArrayGetAndSet.class); + private final int index; + private final Class<?> clzComponentType; + + public ArrayGetAndSet(Class<?> clzComponentType, int index) + { + this.clzComponentType = clzComponentType; + this.index = index; + } + + /** + * {@inheritDoc} + */ + @Override + public Object getValue(Object object) + { + if (Array.getLength(object) > index) + { + return Array.get(object, index); + } + return null; + } + + /** + * {@inheritDoc} + */ + @Override + public void setValue(Object object, Object value, PropertyResolverConverter converter) + { + value = converter.convert(value, clzComponentType); + Array.set(object, index, value); + } + + /** + * {@inheritDoc} + */ + @Override + public Object newValue(Object object) + { + Object value = null; + try + { + value = clzComponentType.newInstance(); + Array.set(object, index, value); + } + catch (Exception e) + { + log.warn("Cannot set new value " + value + " at index " + index + + " for array holding elements of class " + clzComponentType, e); + } + return value; + } + + /** + * {@inheritDoc} + */ + @Override + public Class<?> getTargetClass() + { + return clzComponentType; + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/wicket/blob/3a6db1bf/wicket-core/src/main/java/org/apache/wicket/core/util/reflection/ArrayLengthGetAndSet.java ---------------------------------------------------------------------- diff --git a/wicket-core/src/main/java/org/apache/wicket/core/util/reflection/ArrayLengthGetAndSet.java b/wicket-core/src/main/java/org/apache/wicket/core/util/reflection/ArrayLengthGetAndSet.java new file mode 100644 index 0000000..e72cfb9 --- /dev/null +++ b/wicket-core/src/main/java/org/apache/wicket/core/util/reflection/ArrayLengthGetAndSet.java @@ -0,0 +1,51 @@ +package org.apache.wicket.core.util.reflection; + +import java.lang.reflect.Array; + +import org.apache.wicket.WicketRuntimeException; +import org.apache.wicket.core.util.lang.PropertyResolverConverter; + +public final class ArrayLengthGetAndSet extends AbstractGetAndSet +{ + public ArrayLengthGetAndSet() + { + } + + /** + * {@inheritDoc} + */ + @Override + public Object getValue(final Object object) + { + return Array.getLength(object); + } + + /** + * {@inheritDoc} + */ + @Override + public void setValue(final Object object, final Object value, + final PropertyResolverConverter converter) + { + throw new WicketRuntimeException("You can't set the length on an array:" + object); + } + + /** + * {@inheritDoc} + */ + @Override + public Object newValue(final Object object) + { + throw new WicketRuntimeException( + "Can't get a new value from a length of an array: " + object); + } + + /** + * {@inheritDoc} + */ + @Override + public Class<?> getTargetClass() + { + return int.class; + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/wicket/blob/3a6db1bf/wicket-core/src/main/java/org/apache/wicket/core/util/reflection/CachingPropertyLocator.java ---------------------------------------------------------------------- diff --git a/wicket-core/src/main/java/org/apache/wicket/core/util/reflection/CachingPropertyLocator.java b/wicket-core/src/main/java/org/apache/wicket/core/util/reflection/CachingPropertyLocator.java new file mode 100644 index 0000000..78fafc1 --- /dev/null +++ b/wicket-core/src/main/java/org/apache/wicket/core/util/reflection/CachingPropertyLocator.java @@ -0,0 +1,70 @@ +package org.apache.wicket.core.util.reflection; + +import java.util.concurrent.ConcurrentHashMap; + +import org.apache.wicket.core.util.lang.IPropertyResolver; +import org.apache.wicket.core.util.lang.PropertyResolverConverter; +import org.apache.wicket.util.lang.Generics; + +/** + * A wrapper for another {@link IPropertyLocator} that caches results of + * {@link #get(Class, String)}. + */ +public class CachingPropertyLocator implements IPropertyResolver +{ + private final ConcurrentHashMap<String, IGetAndSet> map = Generics.newConcurrentHashMap(16); + + /** + * Special token to put into the cache representing no located {@link IGetAndSet}. + */ + private IGetAndSet NONE = new AbstractGetAndSet() + { + + @Override + public Object getValue(Object object) + { + return null; + } + + @Override + public Object newValue(Object object) + { + return null; + } + + @Override + public void setValue(Object object, Object value, PropertyResolverConverter converter) + { + } + }; + + private IPropertyResolver resolver; + + public CachingPropertyLocator(IPropertyResolver locator) + { + this.resolver = locator; + } + + public IGetAndSet get(Class<?> clz, String exp) + { + String key = clz.getName() + "#" + exp; + + IGetAndSet located = map.get(key); + if (located == null) + { + located = resolver.get(clz, exp); + if (located == null) + { + located = NONE; + } + map.put(key, located); + } + + if (located == NONE) + { + located = null; + } + + return located; + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/wicket/blob/3a6db1bf/wicket-core/src/main/java/org/apache/wicket/core/util/reflection/ClassMetadataIndex.java ---------------------------------------------------------------------- diff --git a/wicket-core/src/main/java/org/apache/wicket/core/util/reflection/ClassMetadataIndex.java b/wicket-core/src/main/java/org/apache/wicket/core/util/reflection/ClassMetadataIndex.java new file mode 100644 index 0000000..ba2a6bd --- /dev/null +++ b/wicket-core/src/main/java/org/apache/wicket/core/util/reflection/ClassMetadataIndex.java @@ -0,0 +1,22 @@ +package org.apache.wicket.core.util.reflection; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +import org.apache.wicket.core.util.lang.IPropertyExpressionResolver; +import org.apache.wicket.util.lang.Generics; + +public class ClassMetadataIndex +{ + + private final static ConcurrentHashMap<Object, IPropertyExpressionResolver> applicationToClassesToGetAndSetters = Generics + .newConcurrentHashMap(2); + + + private final ConcurrentHashMap<Class<?>, Map<String, IGetAndSet>> map = Generics.newConcurrentHashMap(16); + +// void put(Class<?> clz, Map<String, IGetAndSet> values) +// IGetAndSet get(Class<?> clz, String exp); + + +} http://git-wip-us.apache.org/repos/asf/wicket/blob/3a6db1bf/wicket-core/src/main/java/org/apache/wicket/core/util/reflection/FieldGetAndSet.java ---------------------------------------------------------------------- diff --git a/wicket-core/src/main/java/org/apache/wicket/core/util/reflection/FieldGetAndSet.java b/wicket-core/src/main/java/org/apache/wicket/core/util/reflection/FieldGetAndSet.java new file mode 100644 index 0000000..12f5a3a --- /dev/null +++ b/wicket-core/src/main/java/org/apache/wicket/core/util/reflection/FieldGetAndSet.java @@ -0,0 +1,103 @@ +package org.apache.wicket.core.util.reflection; + +import java.lang.reflect.Field; + +import org.apache.wicket.WicketRuntimeException; +import org.apache.wicket.core.util.lang.PropertyResolverConverter; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * @author jcompagner + */ +public class FieldGetAndSet extends AbstractGetAndSet +{ + private static final Logger log = LoggerFactory.getLogger(FieldGetAndSet.class); + private final Field field; + + /** + * Construct. + * + * @param field + */ + public FieldGetAndSet(final Field field) + { + super(); + this.field = field; + this.field.setAccessible(true); + } + + /** + * {@inheritDoc} + */ + @Override + public Object getValue(final Object object) + { + try + { + return field.get(object); + } + catch (Exception ex) + { + throw new WicketRuntimeException( + "Error getting field value of field " + field + " from object " + object, ex); + } + } + + /** + * {@inheritDoc} + */ + @Override + public Object newValue(final Object object) + { + Class<?> clz = field.getType(); + Object value = null; + try + { + value = clz.newInstance(); + field.set(object, value); + } + catch (Exception e) + { + log.warn("Cannot set field " + field + " to " + value, e); + } + return value; + } + + /** + * {@inheritDoc} + */ + @Override + public void setValue(final Object object, Object value, + final PropertyResolverConverter converter) + { + value = converter.convert(value, field.getType()); + try + { + field.set(object, value); + } + catch (Exception ex) + { + throw new WicketRuntimeException("Error setting field value of field " + field + + " on object " + object + ", value " + value, ex); + } + } + + /** + * {@inheritDoc} + */ + @Override + public Class<?> getTargetClass() + { + return field.getType(); + } + + /** + * {@inheritDoc} + */ + @Override + public Field getField() + { + return field; + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/wicket/blob/3a6db1bf/wicket-core/src/main/java/org/apache/wicket/core/util/reflection/IGetAndSet.java ---------------------------------------------------------------------- diff --git a/wicket-core/src/main/java/org/apache/wicket/core/util/reflection/IGetAndSet.java b/wicket-core/src/main/java/org/apache/wicket/core/util/reflection/IGetAndSet.java new file mode 100644 index 0000000..5f32fa5 --- /dev/null +++ b/wicket-core/src/main/java/org/apache/wicket/core/util/reflection/IGetAndSet.java @@ -0,0 +1,58 @@ +package org.apache.wicket.core.util.reflection; + +import java.lang.reflect.Field; +import java.lang.reflect.Method; + +import org.apache.wicket.core.util.lang.PropertyResolverConverter; + +/** + * A property to get and set. + * + * @author jcompagner + */ +public interface IGetAndSet +{ + /** + * @param object + * The object where the value must be taken from. + * + * @return The value of this property + */ + public Object getValue(final Object object); + + /** + * @return The target class of the object that as to be set. + */ + public Class<?> getTargetClass(); + + /** + * @param object + * The object where the new value must be set on. + * + * @return The new value for the property that is set back on that object. + */ + public Object newValue(Object object); + + /** + * @param object + * @param value + * @param converter + */ + public void setValue(final Object object, final Object value, + PropertyResolverConverter converter); + + /** + * @return Field or null if there is no field + */ + public Field getField(); + + /** + * @return Getter method or null if there is no getter + */ + public Method getGetter(); + + /** + * @return Setter of null if there is no setter + */ + public Method getSetter(); +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/wicket/blob/3a6db1bf/wicket-core/src/main/java/org/apache/wicket/core/util/reflection/IndexedPropertyGetAndSet.java ---------------------------------------------------------------------- diff --git a/wicket-core/src/main/java/org/apache/wicket/core/util/reflection/IndexedPropertyGetAndSet.java b/wicket-core/src/main/java/org/apache/wicket/core/util/reflection/IndexedPropertyGetAndSet.java new file mode 100644 index 0000000..1eac5af --- /dev/null +++ b/wicket-core/src/main/java/org/apache/wicket/core/util/reflection/IndexedPropertyGetAndSet.java @@ -0,0 +1,149 @@ +package org.apache.wicket.core.util.reflection; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + +import org.apache.wicket.WicketRuntimeException; +import org.apache.wicket.core.util.lang.PropertyResolverConverter; +import org.apache.wicket.util.convert.ConversionException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public final class IndexedPropertyGetAndSet extends AbstractGetAndSet +{ + private static final Logger log = LoggerFactory.getLogger(IndexedPropertyGetAndSet.class); + final private Integer index; + final private Method getMethod; + private Method setMethod; + + public IndexedPropertyGetAndSet(final Method method, final int index) + { + this.index = index; + getMethod = method; + getMethod.setAccessible(true); + } + + private static Method findSetter(final Method getMethod, final Class<?> clz) + { + String name = getMethod.getName(); + name = MethodGetAndSet.SET + name.substring(3); + try + { + return clz.getMethod(name, new Class[] { int.class, getMethod.getReturnType() }); + } + catch (Exception e) + { + log.debug("Can't find setter method corresponding to " + getMethod); + } + return null; + } + + /** + * {@inheritDoc} + */ + @Override + public Object getValue(Object object) + { + Object ret; + try + { + ret = getMethod.invoke(object, index); + } + catch (InvocationTargetException ex) + { + throw new WicketRuntimeException( + "Error calling index property method: " + getMethod + " on object: " + object, + ex.getCause()); + } + catch (Exception ex) + { + throw new WicketRuntimeException( + "Error calling index property method: " + getMethod + " on object: " + object, ex); + } + return ret; + } + + /** + * {@inheritDoc} + */ + @Override + public void setValue(final Object object, final Object value, + final PropertyResolverConverter converter) + { + if (setMethod == null) + { + setMethod = findSetter(getMethod, object.getClass()); + } + if (setMethod != null) + { + setMethod.setAccessible(true); + Object converted = converter.convert(value, getMethod.getReturnType()); + if (converted == null && value != null) + { + throw new ConversionException("Can't convert value: " + value + " to class: " + + getMethod.getReturnType() + " for setting it on " + object); + } + try + { + setMethod.invoke(object, index, converted); + } + catch (InvocationTargetException ex) + { + throw new WicketRuntimeException( + "Error index property calling method: " + setMethod + " on object: " + object, + ex.getCause()); + } + catch (Exception ex) + { + throw new WicketRuntimeException( + "Error index property calling method: " + setMethod + " on object: " + object, + ex); + } + } + else + { + throw new WicketRuntimeException( + "No set method defined for value: " + value + " on object: " + object); + } + } + + /** + * {@inheritDoc} + */ + @Override + public Class<?> getTargetClass() + { + return getMethod.getReturnType(); + } + + /** + * {@inheritDoc} + */ + @Override + public Object newValue(Object object) + { + if (setMethod == null) + { + setMethod = findSetter(getMethod, object.getClass()); + } + + if (setMethod == null) + { + log.warn("Null setMethod"); + return null; + } + + Class<?> clz = getMethod.getReturnType(); + Object value = null; + try + { + value = clz.newInstance(); + setMethod.invoke(object, index, value); + } + catch (Exception e) + { + log.warn("Cannot set new value " + value + " at index " + index, e); + } + return value; + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/wicket/blob/3a6db1bf/wicket-core/src/main/java/org/apache/wicket/core/util/reflection/ListGetAndSet.java ---------------------------------------------------------------------- diff --git a/wicket-core/src/main/java/org/apache/wicket/core/util/reflection/ListGetAndSet.java b/wicket-core/src/main/java/org/apache/wicket/core/util/reflection/ListGetAndSet.java new file mode 100644 index 0000000..917e7f8 --- /dev/null +++ b/wicket-core/src/main/java/org/apache/wicket/core/util/reflection/ListGetAndSet.java @@ -0,0 +1,67 @@ +package org.apache.wicket.core.util.reflection; + +import java.util.List; + +import org.apache.wicket.core.util.lang.PropertyResolverConverter; + +public class ListGetAndSet extends AbstractGetAndSet +{ + final private int index; + + public ListGetAndSet(int index) + { + this.index = index; + } + + /** + * {@inheritDoc} + */ + @Override + public Object getValue(final Object object) + { + if (((List<?>)object).size() <= index) + { + return null; + } + return ((List<?>)object).get(index); + } + + /** + * {@inheritDoc} + */ + @Override + @SuppressWarnings("unchecked") + public void setValue(final Object object, final Object value, + final PropertyResolverConverter converter) + { + List<Object> lst = (List<Object>)object; + + if (lst.size() > index) + { + lst.set(index, value); + } + else if (lst.size() == index) + { + lst.add(value); + } + else + { + while (lst.size() < index) + { + lst.add(null); + } + lst.add(value); + } + } + + /** + * {@inheritDoc} + */ + @Override + public Object newValue(Object object) + { + // List can't make a newValue or should it look what is more in the + // list and try to make one of the class if finds? + return null; + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/wicket/blob/3a6db1bf/wicket-core/src/main/java/org/apache/wicket/core/util/reflection/MapGetAndSet.java ---------------------------------------------------------------------- diff --git a/wicket-core/src/main/java/org/apache/wicket/core/util/reflection/MapGetAndSet.java b/wicket-core/src/main/java/org/apache/wicket/core/util/reflection/MapGetAndSet.java new file mode 100644 index 0000000..62654aa --- /dev/null +++ b/wicket-core/src/main/java/org/apache/wicket/core/util/reflection/MapGetAndSet.java @@ -0,0 +1,46 @@ +package org.apache.wicket.core.util.reflection; + +import java.util.Map; + +import org.apache.wicket.core.util.lang.PropertyResolverConverter; + +public final class MapGetAndSet extends AbstractGetAndSet +{ + private final String key; + + public MapGetAndSet(String key) + { + this.key = key; + } + + /** + * {@inheritDoc} + */ + @Override + public Object getValue(final Object object) + { + return ((Map<?, ?>)object).get(key); + } + + /** + * {@inheritDoc} + */ + @Override + @SuppressWarnings("unchecked") + public void setValue(final Object object, final Object value, + final PropertyResolverConverter converter) + { + ((Map<String, Object>)object).put(key, value); + } + + /** + * {@inheritDoc} + */ + @Override + public Object newValue(final Object object) + { + // Map can't make a newValue or should it look what is more in the + // map and try to make one of the class if finds? + return null; + } +} \ No newline at end of file
