0001: /*
0002: * Licensed to the Apache Software Foundation (ASF) under one or more
0003: * contributor license agreements. See the NOTICE file distributed with
0004: * this work for additional information regarding copyright ownership.
0005: * The ASF licenses this file to You under the Apache License, Version 2.0
0006: * (the "License"); you may not use this file except in compliance with
0007: * the License. You may obtain a copy of the License at
0008: *
0009: * http://www.apache.org/licenses/LICENSE-2.0
0010: *
0011: * Unless required by applicable law or agreed to in writing, software
0012: * distributed under the License is distributed on an "AS IS" BASIS,
0013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0014: * See the License for the specific language governing permissions and
0015: * limitations under the License.
0016: */
0017: package org.apache.wicket.util.lang;
0018:
0019: import java.lang.reflect.Array;
0020: import java.lang.reflect.Field;
0021: import java.lang.reflect.InvocationTargetException;
0022: import java.lang.reflect.Method;
0023: import java.util.List;
0024: import java.util.Map;
0025:
0026: import org.apache.wicket.Application;
0027: import org.apache.wicket.Session;
0028: import org.apache.wicket.WicketRuntimeException;
0029: import org.apache.wicket.util.concurrent.ConcurrentHashMap;
0030: import org.apache.wicket.util.convert.ConversionException;
0031: import org.apache.wicket.util.string.Strings;
0032: import org.slf4j.Logger;
0033: import org.slf4j.LoggerFactory;
0034:
0035: /**
0036: * This class parses expressions to lookup or set a value on the object that is
0037: * given. <br/> The supported expressions are:
0038: * <p>
0039: * "property": This can can then be a bean property with get and set method. Or
0040: * if a map is given as an object it will be lookup with the property as a key
0041: * when there is not get method for that property. <p/>
0042: * <p>
0043: * "property1.property2": Both properties are lookup as written above. If
0044: * property1 evaluates to null then if there is a setMethod (or if it is a map)
0045: * and the Class of the property has a default constructor then the object will
0046: * be constructed and set on the object. <p/>
0047: * <p>
0048: * "property.index": If the property is a List or Array then the second property
0049: * can be a index on that list like: 'mylist.0' this expression will also map on
0050: * a getProperty(index) or setProperty(index,value) methods. If the object is a
0051: * List then the list will grow automaticaly if the index is greater then the
0052: * size <p/>
0053: * <p>
0054: * Index or map properties can also be written as: "property[index]" or
0055: * "property[key]" <p/>
0056: *
0057: * @author jcompagner
0058: */
0059: public final class PropertyResolver {
0060: private final static int RETURN_NULL = 0;
0061: private final static int CREATE_NEW_VALUE = 1;
0062: private final static int RESOLVE_CLASS = 2;
0063:
0064: private final static Map applicationToClassesToGetAndSetters = new ConcurrentHashMap(
0065: 2);
0066:
0067: /** Log. */
0068: private static final Logger log = LoggerFactory
0069: .getLogger(PropertyResolver.class);
0070:
0071: /**
0072: * Looksup the value from the object with the given expression. If the
0073: * expresion, the object itself or one property evalutes to null then a null
0074: * will be returned.
0075: *
0076: * @param expression
0077: * The expression string with the property to be lookup.
0078: * @param object
0079: * The object which is evaluated.
0080: * @return The value that is evaluted. Null something in the expression
0081: * evaluted to null.
0082: */
0083: public final static Object getValue(final String expression,
0084: final Object object) {
0085: if (expression == null || expression.equals("")
0086: || object == null) {
0087: return object;
0088: }
0089:
0090: ObjectAndGetSetter getter = getObjectAndGetSetter(expression,
0091: object, RETURN_NULL);
0092: if (getter == null) {
0093: return null;
0094: }
0095: return getter.getValue();
0096:
0097: }
0098:
0099: /**
0100: * Set the value on the object with the given expression. If the expression
0101: * can't be evaluated then a WicketRuntimeException will be thrown. If a
0102: * null object is encounted then it will try to generate it by calling the
0103: * default constructor and set it on the object.
0104: *
0105: * The value will be tried to convert to the right type with the given
0106: * converter.
0107: *
0108: * @param expression
0109: * The expression string with the property to be set.
0110: * @param object
0111: * The object which is evaluated to set the value on.
0112: * @param value
0113: * The value to set.
0114: * @param converter
0115: * The convertor to convert the value if needed to the right
0116: * type.
0117: */
0118: public final static void setValue(final String expression,
0119: final Object object, Object value,
0120: PropertyResolverConverter converter) {
0121: if (expression == null || expression.equals("")) {
0122: throw new WicketRuntimeException(
0123: "Empty expression setting value: " + value
0124: + " on object: " + object);
0125: }
0126: if (object == null) {
0127: throw new WicketRuntimeException(
0128: "Null object setting value: " + value
0129: + " with expression: " + expression);
0130: }
0131:
0132: ObjectAndGetSetter setter = getObjectAndGetSetter(expression,
0133: object, CREATE_NEW_VALUE);
0134: if (setter == null) {
0135: throw new WicketRuntimeException(
0136: "Null object returned for expression: "
0137: + expression + " for setting value: "
0138: + value + " on: " + object);
0139: }
0140: setter.setValue(value,
0141: converter == null ? new PropertyResolverConverter(
0142: Application.get().getConverterLocator(),
0143: Session.get().getLocale()) : converter);
0144: }
0145:
0146: /**
0147: * @param expression
0148: * @param object
0149: * @return class of the target property object
0150: */
0151: public final static Class getPropertyClass(String expression,
0152: Object object) {
0153: ObjectAndGetSetter setter = getObjectAndGetSetter(expression,
0154: object, RESOLVE_CLASS);
0155: if (setter == null) {
0156: throw new WicketRuntimeException(
0157: "Null object returned for expression: "
0158: + expression
0159: + " for getting the target classs of: "
0160: + object);
0161: }
0162: return setter.getTargetClass();
0163: }
0164:
0165: /**
0166: * @param expression
0167: * @param object
0168: * @return Field for the property expression or null if such field doesn't
0169: * exist (only getters and setters)
0170: */
0171: public final static Field getPropertyField(String expression,
0172: Object object) {
0173: ObjectAndGetSetter setter = getObjectAndGetSetter(expression,
0174: object, RESOLVE_CLASS);
0175: if (setter == null) {
0176: throw new WicketRuntimeException(
0177: "Null object returned for expression: "
0178: + expression
0179: + " for getting the target classs of: "
0180: + object);
0181: }
0182: return setter.getField();
0183: }
0184:
0185: /**
0186: * @param expression
0187: * @param object
0188: * @return Getter method for the property expression or null if such getter
0189: * doesn't exist (only field)
0190: */
0191: public final static Method getPropertyGetter(String expression,
0192: Object object) {
0193: ObjectAndGetSetter setter = getObjectAndGetSetter(expression,
0194: object, RESOLVE_CLASS);
0195: if (setter == null) {
0196: throw new WicketRuntimeException(
0197: "Null object returned for expression: "
0198: + expression
0199: + " for getting the target classs of: "
0200: + object);
0201: }
0202: return setter.getGetter();
0203: }
0204:
0205: /**
0206: * @param expression
0207: * @param object
0208: * @return Setter method for the property expression or null if such setter
0209: * doesn't exist (only field)
0210: */
0211: public final static Method getPropertySetter(String expression,
0212: Object object) {
0213: ObjectAndGetSetter setter = getObjectAndGetSetter(expression,
0214: object, RESOLVE_CLASS);
0215: if (setter == null) {
0216: throw new WicketRuntimeException(
0217: "Null object returned for expression: "
0218: + expression
0219: + " for getting the target classs of: "
0220: + object);
0221: }
0222: return setter.getSetter();
0223: }
0224:
0225: private static ObjectAndGetSetter getObjectAndGetSetter(
0226: final String expression, final Object object,
0227: int tryToCreateNull) {
0228: final String expressionBracketsSeperated = Strings.replaceAll(
0229: expression, "[", ".[").toString();
0230: int index = expressionBracketsSeperated.indexOf('.');
0231: int lastIndex = 0;
0232: Object value = object;
0233: Class clz = value.getClass();
0234: String exp = expressionBracketsSeperated;
0235: while (index != -1) {
0236: exp = expressionBracketsSeperated.substring(lastIndex,
0237: index);
0238: IGetAndSet getAndSetter = null;
0239: try {
0240: getAndSetter = getGetAndSetter(exp, clz);
0241: } catch (WicketRuntimeException ex) {
0242:
0243: // expression by it self can't be found. try to find a
0244: // setPropertyByIndex(int,value) method
0245: index = expressionBracketsSeperated.indexOf('.',
0246: index + 1);
0247: if (index != -1) {
0248: String indexExpression = expressionBracketsSeperated
0249: .substring(lastIndex, index);
0250: getAndSetter = getGetAndSetter(indexExpression, clz);
0251: } else {
0252: exp = expressionBracketsSeperated
0253: .substring(lastIndex);
0254: break;
0255: }
0256: }
0257: Object newValue = null;
0258: if (value != null) {
0259: newValue = getAndSetter.getValue(value);
0260: }
0261: if (newValue == null) {
0262: if (tryToCreateNull == CREATE_NEW_VALUE) {
0263: newValue = getAndSetter.newValue(value);
0264: if (newValue == null) {
0265: return null;
0266: }
0267: } else if (tryToCreateNull == RESOLVE_CLASS) {
0268: clz = getAndSetter.getTargetClass();
0269: } else {
0270: return null;
0271: }
0272: }
0273: value = newValue;
0274: if (value != null) {
0275: // value can be null if we are in the RESOLVE_CLASS
0276: clz = value.getClass();
0277: }
0278:
0279: lastIndex = index + 1;
0280: index = expressionBracketsSeperated.indexOf('.', lastIndex);
0281: if (index == -1) {
0282: exp = expressionBracketsSeperated.substring(lastIndex);
0283: break;
0284: }
0285: }
0286: IGetAndSet getAndSetter = getGetAndSetter(exp, clz);
0287: return new ObjectAndGetSetter(getAndSetter, value);
0288: }
0289:
0290: private final static IGetAndSet getGetAndSetter(String exp,
0291: Class clz) {
0292: Map classesToGetAndSetters = getClassesToGetAndSetters();
0293: Map getAndSetters = (Map) classesToGetAndSetters.get(clz);
0294: if (getAndSetters == null) {
0295: getAndSetters = new ConcurrentHashMap(8);
0296: classesToGetAndSetters.put(clz, getAndSetters);
0297: }
0298:
0299: IGetAndSet getAndSetter = (IGetAndSet) getAndSetters.get(exp);
0300: if (getAndSetter == null) {
0301: Method method = null;
0302: Field field = null;
0303: if (exp.startsWith("[")) {
0304: // if expression begins with [ skip method finding and use it as
0305: // a key/index lookup on a map.
0306: exp = exp.substring(1, exp.length() - 1);
0307: } else if (exp.endsWith("()")) {
0308: // if expression ends with (), don't test for setters just skip
0309: // directly to method finding.
0310: method = findMethod(clz, exp);
0311: } else {
0312: method = findGetter(clz, exp);
0313: }
0314: if (method == null) {
0315: if (List.class.isAssignableFrom(clz)) {
0316: try {
0317: int index = Integer.parseInt(exp);
0318: getAndSetter = new ListGetSet(index);
0319: } catch (NumberFormatException ex) {
0320: // can't parse the exp als a index maybe the exp was a
0321: // method.
0322: method = findMethod(clz, exp);
0323: if (method != null) {
0324: getAndSetter = new MethodGetAndSet(method,
0325: MethodGetAndSet.findSetter(method,
0326: clz), null);
0327: } else {
0328: field = findField(clz, exp);
0329: if (field != null) {
0330: getAndSetter = new FieldGetAndSetter(
0331: field);
0332: } else {
0333: throw new WicketRuntimeException(
0334: "The expression '"
0335: + exp
0336: + "' is neither an index nor is it a method or field for the list "
0337: + clz);
0338: }
0339: }
0340: }
0341: } else if (Map.class.isAssignableFrom(clz)) {
0342: getAndSetter = new MapGetSet(exp);
0343: } else if (clz.isArray()) {
0344: try {
0345: int index = Integer.parseInt(exp);
0346: getAndSetter = new ArrayGetSet(clz
0347: .getComponentType(), index);
0348: } catch (NumberFormatException ex) {
0349: if (exp.equals("length") || exp.equals("size")) {
0350: getAndSetter = new ArrayLengthGetSet();
0351: } else {
0352: throw new WicketRuntimeException(
0353: "can't parse the exp "
0354: + exp
0355: + " as an index for an array lookup");
0356: }
0357: }
0358: } else {
0359: field = findField(clz, exp);
0360: if (field == null) {
0361: method = findMethod(clz, exp);
0362: if (method == null) {
0363: int index = exp.indexOf('.');
0364: if (index != -1) {
0365: String propertyName = exp.substring(0,
0366: index);
0367: String propertyIndex = exp
0368: .substring(index + 1);
0369: try {
0370:
0371: int parsedIndex = Integer
0372: .parseInt(propertyIndex);
0373: // if so then it could be a
0374: // getPropertyIndex(int)
0375: // and setPropertyIndex(int, object)
0376: String name = Character
0377: .toUpperCase(propertyName
0378: .charAt(0))
0379: + propertyName.substring(1);
0380: method = clz.getMethod(
0381: "get" + name,
0382: new Class[] { int.class });
0383: getAndSetter = new ArrayPropertyGetSet(
0384: method, parsedIndex);
0385:
0386: } catch (Exception e) {
0387: throw new WicketRuntimeException(
0388: "no get method defined for class: "
0389: + clz
0390: + " expression: "
0391: + propertyName);
0392: }
0393: } else {
0394: // We do not look for a public FIELD because
0395: // that is
0396: // not good
0397: // programming with beans patterns
0398: throw new WicketRuntimeException(
0399: "No get method defined for class: "
0400: + clz + " expression: "
0401: + exp);
0402: }
0403: } else {
0404: getAndSetter = new MethodGetAndSet(method,
0405: MethodGetAndSet.findSetter(method,
0406: clz), field);
0407: }
0408: } else {
0409: getAndSetter = new FieldGetAndSetter(field);
0410: }
0411: }
0412: } else {
0413: field = findField(clz, exp);
0414: getAndSetter = new MethodGetAndSet(method,
0415: MethodGetAndSet.findSetter(method, clz), field);
0416: }
0417: getAndSetters.put(exp, getAndSetter);
0418: }
0419: return getAndSetter;
0420: }
0421:
0422: /**
0423: * @param clz
0424: * @param expression
0425: * @return introspected field
0426: */
0427: private static Field findField(Class clz, String expression) {
0428: Field field = null;
0429: try {
0430: field = clz.getField(expression);
0431: } catch (Exception e) {
0432: Class tmp = clz;
0433: while (tmp != null && tmp != Object.class) {
0434: Field[] fields = tmp.getDeclaredFields();
0435: for (int i = 0; i < fields.length; i++) {
0436: if (fields[i].getName().equals(expression)) {
0437: fields[i].setAccessible(true);
0438: return fields[i];
0439: }
0440: }
0441: tmp = tmp.getSuperclass();
0442: }
0443: log.debug("Cannot find field " + clz + "." + expression, e);
0444: }
0445: return field;
0446: }
0447:
0448: /**
0449: * @param clz
0450: * @param expression
0451: * @return The method for the expression null if not found
0452: */
0453: private final static Method findGetter(Class clz, String expression) {
0454: String name = Character.toUpperCase(expression.charAt(0))
0455: + expression.substring(1);
0456: Method method = null;
0457: try {
0458: method = clz.getMethod("get" + name, null);
0459: } catch (Exception e) {
0460: }
0461: if (method == null) {
0462: try {
0463: method = clz.getMethod("is" + name, null);
0464: } catch (Exception e) {
0465: log.debug("Cannot find getter " + clz + "."
0466: + expression, e);
0467: }
0468: }
0469: return method;
0470: }
0471:
0472: private final static Method findMethod(Class clz, String expression) {
0473: if (expression.endsWith("()")) {
0474: expression = expression.substring(0,
0475: expression.length() - 2);
0476: }
0477: Method method = null;
0478: try {
0479: method = clz.getMethod(expression, null);
0480: } catch (Exception e) {
0481: log
0482: .debug("Cannot find method " + clz + "."
0483: + expression, e);
0484: }
0485: return method;
0486: }
0487:
0488: /**
0489: * Utility class: instantiation not allowed.
0490: */
0491: private PropertyResolver() {
0492: }
0493:
0494: /**
0495: * @author jcompagner
0496: *
0497: */
0498: private final static class ObjectAndGetSetter {
0499:
0500: private final IGetAndSet getAndSetter;
0501: private final Object value;
0502:
0503: /**
0504: * @param getAndSetter
0505: * @param value
0506: */
0507: public ObjectAndGetSetter(IGetAndSet getAndSetter, Object value) {
0508: this .getAndSetter = getAndSetter;
0509: this .value = value;
0510: }
0511:
0512: /**
0513: * @param value
0514: * @param converter
0515: */
0516: public void setValue(Object value,
0517: PropertyResolverConverter converter) {
0518: getAndSetter.setValue(this .value, value, converter);
0519: }
0520:
0521: /**
0522: * @return The value
0523: */
0524: public Object getValue() {
0525: return getAndSetter.getValue(value);
0526: }
0527:
0528: /**
0529: * @return class of property value
0530: */
0531: public Class getTargetClass() {
0532: return getAndSetter.getTargetClass();
0533: }
0534:
0535: /**
0536: * @return Field or null if no field exists for expression
0537: */
0538: public Field getField() {
0539: return getAndSetter.getField();
0540: }
0541:
0542: /**
0543: * @return Getter method or null if no getter exists for expression
0544: */
0545: public Method getGetter() {
0546: return getAndSetter.getGetter();
0547: }
0548:
0549: /**
0550: * @return Setter method or null if no setter exists for expression
0551: */
0552: public Method getSetter() {
0553: return getAndSetter.getSetter();
0554: }
0555:
0556: }
0557:
0558: private static interface IGetAndSet {
0559: /**
0560: * @param object
0561: * The object where the value must be taken from.
0562: *
0563: * @return The value of this property
0564: */
0565: public Object getValue(final Object object);
0566:
0567: /**
0568: * @return
0569: */
0570: public Class getTargetClass();
0571:
0572: /**
0573: * @param object
0574: * The object where the new value must be set on.
0575: *
0576: * @return The new value for the property that is set back on that
0577: * object.
0578: */
0579: public Object newValue(Object object);
0580:
0581: /**
0582: * @param object
0583: * @param value
0584: * @param converter
0585: */
0586: public void setValue(final Object object, final Object value,
0587: PropertyResolverConverter converter);
0588:
0589: /**
0590: * @return Field or null if there is no field
0591: */
0592: public Field getField();
0593:
0594: /**
0595: * @return Getter method or null if there is no getter
0596: */
0597: public Method getGetter();
0598:
0599: /**
0600: * @return Setter of null if there is no setter
0601: */
0602: public Method getSetter();
0603: }
0604:
0605: private static abstract class AbstractGetAndSet implements
0606: IGetAndSet {
0607: /**
0608: * @see org.apache.wicket.util.lang.PropertyResolver.IGetAndSet#getField()
0609: */
0610: public Field getField() {
0611: return null;
0612: }
0613:
0614: /**
0615: * @see org.apache.wicket.util.lang.PropertyResolver.IGetAndSet#getGetter()
0616: */
0617: public Method getGetter() {
0618: return null;
0619: }
0620:
0621: /**
0622: * @see org.apache.wicket.util.lang.PropertyResolver.IGetAndSet#getSetter()
0623: */
0624: public Method getSetter() {
0625: return null;
0626: }
0627:
0628: /**
0629: * @see org.apache.wicket.util.lang.PropertyResolver.IGetAndSet#getTargetClass()
0630: */
0631: public Class getTargetClass() {
0632: return null;
0633: }
0634: }
0635:
0636: private static final class MapGetSet extends AbstractGetAndSet {
0637: final private String key;
0638:
0639: MapGetSet(String key) {
0640: this .key = key;
0641: }
0642:
0643: /**
0644: * @see org.apache.wicket.util.lang.PropertyResolver.IGetAndSet#getValue(java.lang.Object)
0645: */
0646: public Object getValue(Object object) {
0647: return ((Map) object).get(key);
0648: }
0649:
0650: /**
0651: * @see org.apache.wicket.util.lang.PropertyResolver.IGetAndSet#setValue(java.lang.Object,
0652: * java.lang.Object, org.apache.wicket.util.convert.IConverter)
0653: */
0654: public void setValue(Object object, Object value,
0655: PropertyResolverConverter converter) {
0656: ((Map) object).put(key, value);
0657: }
0658:
0659: /**
0660: * @see org.apache.wicket.util.lang.PropertyResolver.IGetAndSet#newValue(Object)
0661: */
0662: public Object newValue(Object object) {
0663: // Map can't make a newValue or should it look what is more in the
0664: // map and try to make one of the class if finds?
0665: return null;
0666: }
0667: }
0668:
0669: private static final class ListGetSet extends AbstractGetAndSet {
0670: final private int index;
0671:
0672: ListGetSet(int index) {
0673: this .index = index;
0674: }
0675:
0676: /**
0677: * @see org.apache.wicket.util.lang.PropertyResolver.IGetAndSet#getValue(java.lang.Object)
0678: */
0679: public Object getValue(Object object) {
0680: return ((List) object).get(index);
0681: }
0682:
0683: /**
0684: * @see org.apache.wicket.util.lang.PropertyResolver.IGetAndSet#setValue(java.lang.Object,
0685: * java.lang.Object, org.apache.wicket.util.convert.IConverter)
0686: */
0687: public void setValue(Object object, Object value,
0688: PropertyResolverConverter converter) {
0689: List lst = (List) object;
0690:
0691: if (lst.size() > index) {
0692: lst.set(index, value);
0693: } else if (lst.size() == index) {
0694: lst.add(value);
0695: } else {
0696: while (lst.size() < index) {
0697: lst.add(null);
0698: }
0699: lst.add(value);
0700: }
0701: }
0702:
0703: /**
0704: * @see org.apache.wicket.util.lang.PropertyResolver.IGetAndSet#newValue(Object)
0705: */
0706: public Object newValue(Object object) {
0707: // List can't make a newValue or should it look what is more in the
0708: // list and try to make one of the class if finds?
0709: return null;
0710: }
0711: }
0712:
0713: private static final class ArrayGetSet extends AbstractGetAndSet {
0714: private final int index;
0715: private final Class clzComponentType;
0716:
0717: ArrayGetSet(Class clzComponentType, int index) {
0718: this .clzComponentType = clzComponentType;
0719: this .index = index;
0720: }
0721:
0722: /**
0723: * @see org.apache.wicket.util.lang.PropertyResolver.IGetAndSet#getValue(java.lang.Object)
0724: */
0725: public Object getValue(Object object) {
0726: return Array.get(object, index);
0727: }
0728:
0729: /**
0730: * @see org.apache.wicket.util.lang.PropertyResolver.IGetAndSet#setValue(java.lang.Object,
0731: * java.lang.Object, org.apache.wicket.util.convert.IConverter)
0732: */
0733: public void setValue(Object object, Object value,
0734: PropertyResolverConverter converter) {
0735: value = converter.convert(value, clzComponentType);
0736: Array.set(object, index, value);
0737: }
0738:
0739: /**
0740: * @see org.apache.wicket.util.lang.PropertyResolver.IGetAndSet#newValue(java.lang.Object)
0741: */
0742: public Object newValue(Object object) {
0743: Object value = null;
0744: try {
0745: value = clzComponentType.newInstance();
0746: Array.set(object, index, value);
0747: } catch (Exception e) {
0748: log.warn("Cannot set new value " + value + " at index "
0749: + index
0750: + " for array holding elements of class "
0751: + clzComponentType, e);
0752: }
0753: return value;
0754: }
0755:
0756: /**
0757: * @see org.apache.wicket.util.lang.PropertyResolver.IGetAndSet#getTargetClass()
0758: */
0759: public Class getTargetClass() {
0760: return clzComponentType;
0761: }
0762: }
0763:
0764: private static final class ArrayLengthGetSet extends
0765: AbstractGetAndSet {
0766: ArrayLengthGetSet() {
0767: }
0768:
0769: /**
0770: * @see org.apache.wicket.util.lang.PropertyResolver.IGetAndSet#getValue(java.lang.Object)
0771: */
0772: public Object getValue(Object object) {
0773: return new Integer(Array.getLength(object));
0774: }
0775:
0776: /**
0777: * @see org.apache.wicket.util.lang.PropertyResolver.IGetAndSet#setValue(java.lang.Object,
0778: * java.lang.Object, org.apache.wicket.util.convert.IConverter)
0779: */
0780: public void setValue(Object object, Object value,
0781: PropertyResolverConverter converter) {
0782: throw new WicketRuntimeException(
0783: "Cant set the length on an array");
0784: }
0785:
0786: /**
0787: * @see org.apache.wicket.util.lang.PropertyResolver.IGetAndSet#newValue(java.lang.Object)
0788: */
0789: public Object newValue(Object object) {
0790: throw new WicketRuntimeException(
0791: "Cant get a new value from a length of an array");
0792: }
0793:
0794: /**
0795: * @see org.apache.wicket.util.lang.PropertyResolver.IGetAndSet#getTargetClass()
0796: */
0797: public Class getTargetClass() {
0798: return int.class;
0799: }
0800: }
0801:
0802: private static final class ArrayPropertyGetSet extends
0803: AbstractGetAndSet {
0804: final private Integer index;
0805: final private Method getMethod;
0806: private Method setMethod;
0807:
0808: ArrayPropertyGetSet(Method method, int index) {
0809: this .index = new Integer(index);
0810: getMethod = method;
0811: }
0812:
0813: private final static Method findSetter(Method getMethod,
0814: Class clz) {
0815: String name = getMethod.getName();
0816: name = "set" + name.substring(3);
0817: try {
0818: return clz.getMethod(name, new Class[] { int.class,
0819: getMethod.getReturnType() });
0820: } catch (Exception e) {
0821: log.debug("Cannot find setter method corresponding to "
0822: + getMethod, e);
0823: }
0824: return null;
0825: }
0826:
0827: /**
0828: * @see org.apache.wicket.util.lang.PropertyResolver.IGetAndSet#getValue(java.lang.Object)
0829: */
0830: public Object getValue(Object object) {
0831: Object ret = null;
0832: try {
0833: ret = getMethod.invoke(object, new Object[] { index });
0834: } catch (InvocationTargetException ex) {
0835: throw new WicketRuntimeException(
0836: "Error calling index property method: "
0837: + getMethod + " on object: " + object,
0838: ex.getCause());
0839: } catch (Exception ex) {
0840: throw new WicketRuntimeException(
0841: "Error calling index property method: "
0842: + getMethod + " on object: " + object,
0843: ex);
0844: }
0845: return ret;
0846: }
0847:
0848: /**
0849: * @see org.apache.wicket.util.lang.PropertyResolver.IGetAndSet#setValue(java.lang.Object,
0850: * java.lang.Object, org.apache.wicket.util.convert.IConverter)
0851: */
0852: public void setValue(Object object, Object value,
0853: PropertyResolverConverter converter) {
0854: if (setMethod == null) {
0855: setMethod = findSetter(getMethod, object.getClass());
0856: }
0857: if (setMethod != null) {
0858: Object converted = converter.convert(value, getMethod
0859: .getReturnType());
0860: if (converted == null && value != null) {
0861: throw new ConversionException(
0862: "Can't convert value: " + value
0863: + " to class: "
0864: + getMethod.getReturnType()
0865: + " for setting it on " + object);
0866: }
0867: try {
0868: setMethod.invoke(object, new Object[] { index,
0869: converted });
0870: } catch (InvocationTargetException ex) {
0871: throw new WicketRuntimeException(
0872: "Error index property calling method: "
0873: + setMethod + " on object: "
0874: + object, ex.getCause());
0875: } catch (Exception ex) {
0876: throw new WicketRuntimeException(
0877: "Error index property calling method: "
0878: + setMethod + " on object: "
0879: + object, ex);
0880: }
0881: } else {
0882: throw new WicketRuntimeException(
0883: "no set method defined for value: " + value
0884: + " on object: " + object);
0885: }
0886: }
0887:
0888: /**
0889: * @see org.apache.wicket.util.lang.PropertyResolver.IGetAndSet#getTargetClass()
0890: */
0891: public Class getTargetClass() {
0892: return getMethod.getReturnType();
0893: }
0894:
0895: /**
0896: * @see org.apache.wicket.util.lang.PropertyResolver.IGetAndSet#newValue(java.lang.Object)
0897: */
0898: public Object newValue(Object object) {
0899: if (setMethod == null) {
0900: setMethod = findSetter(getMethod, object.getClass());
0901: }
0902:
0903: if (setMethod == null) {
0904: log.warn("Null setMethod");
0905: return null;
0906: }
0907:
0908: Class clz = getMethod.getReturnType();
0909: Object value = null;
0910: try {
0911: value = clz.newInstance();
0912: setMethod.invoke(object, new Object[] { index, value });
0913: } catch (Exception e) {
0914: log.warn("Cannot set new value " + value + " at index "
0915: + index, e);
0916: }
0917: return value;
0918: }
0919: }
0920:
0921: private static final class MethodGetAndSet extends
0922: AbstractGetAndSet {
0923: private final Method getMethod;
0924: private final Method setMethod;
0925: private final Field field;
0926:
0927: MethodGetAndSet(Method getMethod, Method setMethod, Field field) {
0928: this .getMethod = getMethod;
0929: this .getMethod.setAccessible(true);
0930: this .field = field;
0931: this .setMethod = setMethod;
0932: }
0933:
0934: /**
0935: * @see org.apache.wicket.util.lang.PropertyResolver.IGetAndSet#getValue(java.lang.Object)
0936: */
0937: public final Object getValue(final Object object) {
0938: Object ret = null;
0939: try {
0940: ret = getMethod.invoke(object, null);
0941: } catch (InvocationTargetException ex) {
0942: throw new WicketRuntimeException(
0943: "Error calling method: " + getMethod
0944: + " on object: " + object, ex
0945: .getCause());
0946: } catch (Exception ex) {
0947: throw new WicketRuntimeException(
0948: "Error calling method: " + getMethod
0949: + " on object: " + object, ex);
0950: }
0951: return ret;
0952: }
0953:
0954: /**
0955: * @param object
0956: * @param value
0957: * @param converter
0958: */
0959: public final void setValue(final Object object,
0960: final Object value, PropertyResolverConverter converter) {
0961: if (setMethod != null) {
0962: Object converted = converter.convert(value, getMethod
0963: .getReturnType());
0964: if (converted == null) {
0965: if (value != null) {
0966: throw new ConversionException(
0967: "Can't convert value: " + value
0968: + " to class: "
0969: + getMethod.getReturnType()
0970: + " for setting it on "
0971: + object);
0972: } else if (getMethod.getReturnType().isPrimitive()) {
0973: throw new ConversionException(
0974: "Can't convert null value to a primitive class: "
0975: + getMethod.getReturnType()
0976: + " for setting it on "
0977: + object);
0978: }
0979: }
0980: try {
0981: setMethod
0982: .invoke(object, new Object[] { converted });
0983: } catch (InvocationTargetException ex) {
0984: throw new WicketRuntimeException(
0985: "Error calling method: " + setMethod
0986: + " on object: " + object, ex
0987: .getCause());
0988: } catch (Exception ex) {
0989: throw new WicketRuntimeException(
0990: "Error calling method: " + setMethod
0991: + " on object: " + object, ex);
0992: }
0993: } else {
0994: throw new WicketRuntimeException(
0995: "no set method defined for value: " + value
0996: + " on object: " + object);
0997: }
0998: }
0999:
1000: private final static Method findSetter(Method getMethod,
1001: Class clz) {
1002: String name = getMethod.getName();
1003: if (name.startsWith("get")) {
1004: name = "set" + name.substring(3);
1005: } else {
1006: name = "set" + name.substring(2);
1007: }
1008: try {
1009: Method method = clz.getMethod(name,
1010: new Class[] { getMethod.getReturnType() });
1011: if (method != null) {
1012: method.setAccessible(true);
1013: }
1014: return method;
1015: } catch (Exception e) {
1016: log.debug("Cannot find setter corresponding to "
1017: + getMethod, e);
1018: }
1019: return null;
1020: }
1021:
1022: /**
1023: * @see org.apache.wicket.util.lang.PropertyResolver.IGetAndSet#newValue(java.lang.Object)
1024: */
1025: public Object newValue(Object object) {
1026: if (setMethod == null) {
1027: log.warn("Null setMethod");
1028: return null;
1029: }
1030:
1031: Class clz = getMethod.getReturnType();
1032: Object value = null;
1033: try {
1034: value = clz.newInstance();
1035: setMethod.invoke(object, new Object[] { value });
1036: } catch (Exception e) {
1037: log.warn("Cannot set new value " + value, e);
1038: }
1039: return value;
1040: }
1041:
1042: /**
1043: * @see org.apache.wicket.util.lang.PropertyResolver.IGetAndSet#getTargetClass()
1044: */
1045: public Class getTargetClass() {
1046: return getMethod.getReturnType();
1047: }
1048:
1049: /**
1050: * @see org.apache.wicket.util.lang.PropertyResolver.AbstractGetAndSet#getGetter()
1051: */
1052: public Method getGetter() {
1053: return getMethod;
1054: }
1055:
1056: /**
1057: * @see org.apache.wicket.util.lang.PropertyResolver.AbstractGetAndSet#getSetter()
1058: */
1059: public Method getSetter() {
1060: return setMethod;
1061: }
1062:
1063: /**
1064: * @see org.apache.wicket.util.lang.PropertyResolver.AbstractGetAndSet#getField()
1065: */
1066: public Field getField() {
1067: return field;
1068: }
1069: }
1070:
1071: /**
1072: * @author jcompagner
1073: */
1074: private static class FieldGetAndSetter extends AbstractGetAndSet {
1075:
1076: private final Field field;
1077:
1078: /**
1079: * Construct.
1080: *
1081: * @param field
1082: */
1083: public FieldGetAndSetter(Field field) {
1084: super ();
1085: this .field = field;
1086: this .field.setAccessible(true);
1087: }
1088:
1089: /**
1090: * @see org.apache.wicket.util.lang.PropertyResolver.IGetAndSet#getValue(java.lang.Object)
1091: */
1092: public Object getValue(Object object) {
1093: try {
1094: return field.get(object);
1095: } catch (Exception ex) {
1096: throw new WicketRuntimeException(
1097: "Error getting field value of field " + field
1098: + " from object " + object, ex);
1099: }
1100: }
1101:
1102: /**
1103: * @see org.apache.wicket.util.lang.PropertyResolver.IGetAndSet#newValue(java.lang.Object)
1104: */
1105: public Object newValue(Object object) {
1106: Class clz = field.getType();
1107: Object value = null;
1108: try {
1109: value = clz.newInstance();
1110: field.set(object, value);
1111: } catch (Exception e) {
1112: log.warn("Cannot set field " + field + " to " + value,
1113: e);
1114: }
1115: return value;
1116: }
1117:
1118: /**
1119: * @see org.apache.wicket.util.lang.PropertyResolver.IGetAndSet#setValue(java.lang.Object,
1120: * java.lang.Object, org.apache.wicket.util.convert.IConverter)
1121: */
1122: public void setValue(Object object, Object value,
1123: PropertyResolverConverter converter) {
1124: value = converter.convert(value, field.getType());
1125: try {
1126: field.set(object, value);
1127: } catch (Exception ex) {
1128: throw new WicketRuntimeException(
1129: "Error setting field value of field " + field
1130: + " on object " + object + ", value "
1131: + value, ex);
1132: }
1133: }
1134:
1135: /**
1136: * @see org.apache.wicket.util.lang.PropertyResolver.IGetAndSet#getTargetClass()
1137: */
1138: public Class getTargetClass() {
1139: return field.getType();
1140: }
1141:
1142: public Field getField() {
1143: return field;
1144: }
1145: }
1146:
1147: private static Map getClassesToGetAndSetters() {
1148: Application app = Application.get();
1149: Map result = (Map) applicationToClassesToGetAndSetters
1150: .get(Application.get());
1151: if (result == null) {
1152: // Don't synchronize this - Doesn't matter if we create two of them,
1153: // as it's only a cache and the first will go out of scope and get
1154: // GC'ed.
1155: applicationToClassesToGetAndSetters.put(app,
1156: result = new ConcurrentHashMap(64));
1157: }
1158: return result;
1159: }
1160:
1161: /**
1162: * Clean up cache for this app.
1163: *
1164: * @param application
1165: */
1166: public static void destroy(Application application) {
1167: applicationToClassesToGetAndSetters.remove(application);
1168: }
1169: }
|