📄 propertyresolver.java
字号:
/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */package org.apache.wicket.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 org.apache.wicket.Application;import org.apache.wicket.Session;import org.apache.wicket.WicketRuntimeException;import org.apache.wicket.util.concurrent.ConcurrentHashMap;import org.apache.wicket.util.convert.ConversionException;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: * <p> * "property": This can can then be a bean property with get and set method. Or if a map is given as * an object it will be lookup with the property as a key when there is not get method for that * property. <p/> * <p> * "property1.property2": Both properties are lookup as written above. If property1 evaluates to * null then if there is a setMethod (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. <p/> * <p> * "property.index": If the property is a List or Array then the second property can be a index on * that list like: 'mylist.0' this expression will also map on a getProperty(index) or * setProperty(index,value) methods. If the object is a List then the list will grow automatically * if the index is greater then the size <p/> * <p> * Index or map properties can also be written as: "property[index]" or "property[key]" <p/> * * @author jcompagner */public final class PropertyResolver{ 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 Map applicationToClassesToGetAndSetters = new ConcurrentHashMap(2); /** Log. */ private static final Logger log = LoggerFactory.getLogger(PropertyResolver.class); /** * 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 final static Object getValue(final String expression, final Object object) { if (expression == null || expression.equals("") || object == null) { return object; } ObjectAndGetSetter getter = getObjectAndGetSetter(expression, object, RETURN_NULL); if (getter == null) { return null; } return getter.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. */ public final static void setValue(final String expression, final Object object, Object value, PropertyResolverConverter converter) { if (expression == null || expression.equals("")) { 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); } ObjectAndGetSetter setter = getObjectAndGetSetter(expression, object, CREATE_NEW_VALUE); if (setter == null) { throw new WicketRuntimeException("Null object returned for expression: " + expression + " for setting value: " + value + " on: " + object); } setter.setValue(value, converter == null ? new PropertyResolverConverter(Application.get() .getConverterLocator(), Session.get().getLocale()) : converter); } /** * @param expression * @param object * @return class of the target property object */ public final static Class getPropertyClass(String expression, Object object) { ObjectAndGetSetter setter = getObjectAndGetSetter(expression, object, RESOLVE_CLASS); if (setter == null) { throw new WicketRuntimeException("Null object returned for expression: " + expression + " for getting the target classs of: " + object); } return setter.getTargetClass(); } /** * @param expression * @param object * @return Field for the property expression or null if such field doesn't exist (only getters * and setters) */ public final static Field getPropertyField(String expression, Object object) { ObjectAndGetSetter setter = getObjectAndGetSetter(expression, object, RESOLVE_CLASS); if (setter == null) { throw new WicketRuntimeException("Null object returned for expression: " + expression + " for getting the target classs of: " + object); } return setter.getField(); } /** * @param expression * @param object * @return Getter method for the property expression or null if such getter doesn't exist (only * field) */ public final static Method getPropertyGetter(String expression, Object object) { ObjectAndGetSetter setter = getObjectAndGetSetter(expression, object, RESOLVE_CLASS); if (setter == null) { throw new WicketRuntimeException("Null object returned for expression: " + expression + " for getting the target classs of: " + object); } return setter.getGetter(); } /** * @param expression * @param object * @return Setter method for the property expression or null if such setter doesn't exist (only * field) */ public final static Method getPropertySetter(String expression, Object object) { ObjectAndGetSetter setter = getObjectAndGetSetter(expression, object, RESOLVE_CLASS); if (setter == null) { throw new WicketRuntimeException("Null object returned for expression: " + expression + " for getting the target classs of: " + object); } return setter.getSetter(); } private static ObjectAndGetSetter getObjectAndGetSetter(final String expression, final Object object, int tryToCreateNull) { final String expressionBracketsSeperated = Strings.replaceAll(expression, "[", ".[") .toString(); int index = getNextDotIndex(expressionBracketsSeperated, 0); int lastIndex = 0; Object value = object; Class clz = value.getClass(); String exp = expressionBracketsSeperated; while (index != -1) { exp = expressionBracketsSeperated.substring(lastIndex, index); IGetAndSet getAndSetter = null; try { getAndSetter = getGetAndSetter(exp, clz); } catch (WicketRuntimeException ex) { // expression by it self can't be found. try to find a // setPropertyByIndex(int,value) method index = getNextDotIndex(expressionBracketsSeperated, index + 1); if (index != -1) { String indexExpression = expressionBracketsSeperated.substring(lastIndex, index); getAndSetter = getGetAndSetter(indexExpression, clz); } else { exp = expressionBracketsSeperated.substring(lastIndex); break; } } Object newValue = null; if (value != null) { newValue = getAndSetter.getValue(value); } if (newValue == null) { if (tryToCreateNull == CREATE_NEW_VALUE) { newValue = getAndSetter.newValue(value); if (newValue == null) { return null; } } else if (tryToCreateNull == RESOLVE_CLASS) { clz = getAndSetter.getTargetClass(); } else { return null; } } value = newValue; 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 getAndSetter = getGetAndSetter(exp, clz); return new ObjectAndGetSetter(getAndSetter, value); } private static int getNextDotIndex(String expression, 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 final static IGetAndSet getGetAndSetter(String exp, Class clz) { Map classesToGetAndSetters = getClassesToGetAndSetters(); Map getAndSetters = (Map)classesToGetAndSetters.get(clz); if (getAndSetters == null) { getAndSetters = new ConcurrentHashMap(8); classesToGetAndSetters.put(clz, getAndSetters); } IGetAndSet getAndSetter = (IGetAndSet)getAndSetters.get(exp); if (getAndSetter == null) { Method method = null; Field field = null; 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); getAndSetter = new ListGetSet(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) { getAndSetter = new MethodGetAndSet(method, MethodGetAndSet.findSetter( method, clz), null); } else { field = findField(clz, exp); if (field != null) { getAndSetter = new FieldGetAndSetter(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)) { getAndSetter = new MapGetSet(exp); } else if (clz.isArray()) { try { int index = Integer.parseInt(exp); getAndSetter = new ArrayGetSet(clz.getComponentType(), index); } catch (NumberFormatException ex) { if (exp.equals("length") || exp.equals("size")) { getAndSetter = new ArrayLengthGetSet(); } else { throw new WicketRuntimeException("can't parse the exp " + 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 }); getAndSetter = new ArrayPropertyGetSet(method, parsedIndex); } catch (Exception e) { throw new WicketRuntimeException( "no get method defined for class: " + clz + " expression: " + propertyName); } } else { // We do not look for a public FIELD because // that is // not good // programming with beans patterns throw new WicketRuntimeException( "No get method defined for class: " + clz + " expression: " + exp); } } else { getAndSetter = new MethodGetAndSet(method, MethodGetAndSet.findSetter( method, clz), field); } }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -