123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776 |
- /*
- * Copyright 2000-2016 Vaadin Ltd.
- *
- * Licensed 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 com.vaadin.v7.data.util;
-
- import static com.vaadin.util.ReflectTools.convertPrimitiveType;
-
- import java.io.IOException;
- import java.lang.reflect.InvocationTargetException;
- import java.lang.reflect.Method;
- import java.util.Arrays;
- import java.util.logging.Level;
- import java.util.logging.Logger;
-
- import com.vaadin.shared.util.SharedUtil;
- import com.vaadin.v7.data.Property;
- import com.vaadin.v7.util.SerializerHelper;
-
- /**
- * <p>
- * Proxy class for creating Properties from pairs of getter and setter methods
- * of a Bean property. An instance of this class can be thought as having been
- * attached to a field of an object. Accessing the object through the Property
- * interface directly manipulates the underlying field.
- * </p>
- *
- * <p>
- * It's assumed that the return value returned by the getter method is
- * assignable to the type of the property, and the setter method parameter is
- * assignable to that value.
- * </p>
- *
- * <p>
- * A valid getter method must always be available, but instance of this class
- * can be constructed with a <code>null</code> setter method in which case the
- * resulting MethodProperty is read-only.
- * </p>
- *
- * <p>
- * MethodProperty implements Property.ValueChangeNotifier, but does not
- * automatically know whether or not the getter method will actually return a
- * new value - value change listeners are always notified when setValue is
- * called, without verifying what the getter returns.
- * </p>
- *
- * @author Vaadin Ltd.
- * @since 3.0
- */
- @Deprecated
- @SuppressWarnings("serial")
- public class MethodProperty<T> extends AbstractProperty<T> {
-
- /**
- * The object that includes the property the MethodProperty is bound to.
- */
- private transient Object instance;
-
- /**
- * Argument arrays for the getter and setter methods.
- */
- private transient Object[] setArgs, getArgs;
-
- /**
- * The getter and setter methods.
- */
- private transient Method setMethod, getMethod;
-
- /**
- * Index of the new value in the argument list for the setter method. If the
- * setter method requires several parameters, this index tells which one is
- * the actual value to change.
- */
- private int setArgumentIndex;
-
- /**
- * Type of the property.
- */
- private transient Class<? extends T> type;
-
- private static final Object[] DEFAULT_GET_ARGS = new Object[0];
-
- private static final Object[] DEFAULT_SET_ARGS = new Object[1];
-
- /* Special serialization to handle method references */
- private void writeObject(java.io.ObjectOutputStream out)
- throws IOException {
- out.defaultWriteObject();
- SerializerHelper.writeClass(out, type);
- out.writeObject(instance);
- out.writeObject(setArgs);
- out.writeObject(getArgs);
- if (setMethod != null) {
- out.writeObject(setMethod.getName());
- SerializerHelper.writeClassArray(out,
- setMethod.getParameterTypes());
- } else {
- out.writeObject(null);
- out.writeObject(null);
- }
- if (getMethod != null) {
- out.writeObject(getMethod.getName());
- SerializerHelper.writeClassArray(out,
- getMethod.getParameterTypes());
- } else {
- out.writeObject(null);
- out.writeObject(null);
- }
- }
-
- /* Special serialization to handle method references */
- private void readObject(java.io.ObjectInputStream in)
- throws IOException, ClassNotFoundException {
- in.defaultReadObject();
- try {
- @SuppressWarnings("unchecked")
- // business assumption; type parameters not checked at runtime
- Class<T> class1 = (Class<T>) SerializerHelper.readClass(in);
- type = class1;
- instance = in.readObject();
- Object[] setArgs = (Object[]) in.readObject();
- Object[] getArgs = (Object[]) in.readObject();
- setArguments(getArgs, setArgs, setArgumentIndex);
- String name = (String) in.readObject();
- Class<?>[] paramTypes = SerializerHelper.readClassArray(in);
- if (instance != null && name != null) {
- setMethod = instance.getClass().getMethod(name, paramTypes);
- } else {
- setMethod = null;
- }
-
- name = (String) in.readObject();
- paramTypes = SerializerHelper.readClassArray(in);
- if (instance != null && name != null) {
- getMethod = instance.getClass().getMethod(name, paramTypes);
- } else {
- getMethod = null;
- }
- } catch (SecurityException e) {
- getLogger().log(Level.SEVERE, "Internal deserialization error", e);
- } catch (NoSuchMethodException e) {
- getLogger().log(Level.SEVERE, "Internal deserialization error", e);
- }
- }
-
- /**
- * <p>
- * Creates a new instance of <code>MethodProperty</code> from a named bean
- * property. This constructor takes an object and the name of a bean
- * property and initializes itself with the accessor methods for the
- * property.
- * </p>
- * <p>
- * The getter method of a <code>MethodProperty</code> instantiated with this
- * constructor will be called with no arguments, and the setter method with
- * only the new value as the sole argument.
- * </p>
- *
- * <p>
- * If the setter method is unavailable, the resulting
- * <code>MethodProperty</code> will be read-only, otherwise it will be
- * read-write.
- * </p>
- *
- * <p>
- * Method names are constructed from the bean property by adding
- * get/is/are/set prefix and capitalising the first character in the name of
- * the given bean property.
- * </p>
- *
- * @param instance
- * the object that includes the property.
- * @param beanPropertyName
- * the name of the property to bind to.
- */
- @SuppressWarnings("unchecked")
- public MethodProperty(Object instance, String beanPropertyName) {
-
- final Class<?> beanClass = instance.getClass();
-
- // Assure that the first letter is upper cased (it is a common
- // mistake to write firstName, not FirstName).
- beanPropertyName = SharedUtil.capitalize(beanPropertyName);
-
- // Find the get method
- getMethod = null;
- try {
- getMethod = initGetterMethod(beanPropertyName, beanClass);
- } catch (final java.lang.NoSuchMethodException ignored) {
- throw new MethodException(this,
- "Bean property " + beanPropertyName + " can not be found");
- }
-
- // In case the get method is found, resolve the type
- Class<?> returnType = getMethod.getReturnType();
-
- // Finds the set method
- setMethod = null;
- try {
- setMethod = beanClass.getMethod("set" + beanPropertyName,
- new Class[] { returnType });
- } catch (final java.lang.NoSuchMethodException skipped) {
- }
-
- // Gets the return type from get method
- if (returnType.isPrimitive()) {
- type = (Class<T>) convertPrimitiveType(returnType);
- if (type.isPrimitive()) {
- throw new MethodException(this,
- "Bean property " + beanPropertyName
- + " getter return type must not be void");
- }
- } else {
- type = (Class<T>) returnType;
- }
-
- setArguments(DEFAULT_GET_ARGS, DEFAULT_SET_ARGS, 0);
- this.instance = instance;
- }
-
- /**
- * <p>
- * Creates a new instance of <code>MethodProperty</code> from named getter
- * and setter methods. The getter method of a <code>MethodProperty</code>
- * instantiated with this constructor will be called with no arguments, and
- * the setter method with only the new value as the sole argument.
- * </p>
- *
- * <p>
- * If the setter method is <code>null</code>, the resulting
- * <code>MethodProperty</code> will be read-only, otherwise it will be
- * read-write.
- * </p>
- *
- * @param type
- * the type of the property.
- * @param instance
- * the object that includes the property.
- * @param getMethodName
- * the name of the getter method.
- * @param setMethodName
- * the name of the setter method.
- *
- */
- public MethodProperty(Class<? extends T> type, Object instance,
- String getMethodName, String setMethodName) {
- this(type, instance, getMethodName, setMethodName, new Object[] {},
- new Object[] { null }, 0);
- }
-
- /**
- * <p>
- * Creates a new instance of <code>MethodProperty</code> with the getter and
- * setter methods. The getter method of a <code>MethodProperty</code>
- * instantiated with this constructor will be called with no arguments, and
- * the setter method with only the new value as the sole argument.
- * </p>
- *
- * <p>
- * If the setter method is <code>null</code>, the resulting
- * <code>MethodProperty</code> will be read-only, otherwise it will be
- * read-write.
- * </p>
- *
- * @param type
- * the type of the property.
- * @param instance
- * the object that includes the property.
- * @param getMethod
- * the getter method.
- * @param setMethod
- * the setter method.
- */
- public MethodProperty(Class<? extends T> type, Object instance,
- Method getMethod, Method setMethod) {
- this(type, instance, getMethod, setMethod, new Object[] {},
- new Object[] { null }, 0);
- }
-
- /**
- * <p>
- * Creates a new instance of <code>MethodProperty</code> from named getter
- * and setter methods and argument lists. The getter method of a
- * <code>MethodProperty</code> instantiated with this constructor will be
- * called with the getArgs as arguments. The setArgs will be used as the
- * arguments for the setter method, though the argument indexed by the
- * setArgumentIndex will be replaced with the argument passed to the
- * {@link #setValue(Object newValue)} method.
- * </p>
- *
- * <p>
- * For example, if the <code>setArgs</code> contains <code>A</code>,
- * <code>B</code> and <code>C</code>, and <code>setArgumentIndex =
- * 1</code>, the call <code>methodProperty.setValue(X)</code> would result
- * in the setter method to be called with the parameter set of
- * <code>{A, X, C}</code>
- * </p>
- *
- * @param type
- * the type of the property.
- * @param instance
- * the object that includes the property.
- * @param getMethodName
- * the name of the getter method.
- * @param setMethodName
- * the name of the setter method.
- * @param getArgs
- * the fixed argument list to be passed to the getter method.
- * @param setArgs
- * the fixed argument list to be passed to the setter method.
- * @param setArgumentIndex
- * the index of the argument in <code>setArgs</code> to be
- * replaced with <code>newValue</code> when
- * {@link #setValue(Object newValue)} is called.
- */
- @SuppressWarnings("unchecked")
- public MethodProperty(Class<? extends T> type, Object instance,
- String getMethodName, String setMethodName, Object[] getArgs,
- Object[] setArgs, int setArgumentIndex) {
-
- // Check the setargs and setargs index
- if (setMethodName != null && setArgs == null) {
- throw new IndexOutOfBoundsException("The setArgs can not be null");
- }
- if (setMethodName != null && (setArgumentIndex < 0
- || setArgumentIndex >= setArgs.length)) {
- throw new IndexOutOfBoundsException(
- "The setArgumentIndex must be >= 0 and < setArgs.length");
- }
-
- // Set type
- this.type = type;
-
- // Find set and get -methods
- final Method[] m = instance.getClass().getMethods();
-
- // Finds get method
- boolean found = false;
- for (int i = 0; i < m.length; i++) {
-
- // Tests the name of the get Method
- if (!m[i].getName().equals(getMethodName)) {
-
- // name does not match, try next method
- continue;
- }
-
- // Tests return type
- if (!type.equals(m[i].getReturnType())) {
- continue;
- }
-
- // Tests the parameter types
- final Class<?>[] c = m[i].getParameterTypes();
- if (c.length != getArgs.length) {
-
- // not the right amount of parameters, try next method
- continue;
- }
- int j = 0;
- while (j < c.length) {
- if (getArgs[j] != null
- && !c[j].isAssignableFrom(getArgs[j].getClass())) {
-
- // parameter type does not match, try next method
- break;
- }
- j++;
- }
- if (j == c.length) {
-
- // all paramteters matched
- if (found == true) {
- throw new MethodException(this,
- "Could not uniquely identify " + getMethodName
- + "-method");
- } else {
- found = true;
- getMethod = m[i];
- }
- }
- }
- if (found != true) {
- throw new MethodException(this,
- "Could not find " + getMethodName + "-method");
- }
-
- // Finds set method
- if (setMethodName != null) {
-
- // Finds setMethod
- found = false;
- for (int i = 0; i < m.length; i++) {
-
- // Checks name
- if (!m[i].getName().equals(setMethodName)) {
-
- // name does not match, try next method
- continue;
- }
-
- // Checks parameter compatibility
- final Class<?>[] c = m[i].getParameterTypes();
- if (c.length != setArgs.length) {
-
- // not the right amount of parameters, try next method
- continue;
- }
- int j = 0;
- while (j < c.length) {
- if (setArgs[j] != null
- && !c[j].isAssignableFrom(setArgs[j].getClass())) {
-
- // parameter type does not match, try next method
- break;
- } else if (j == setArgumentIndex && !c[j].equals(type)) {
-
- // Property type is not the same as setArg type
- break;
- }
- j++;
- }
- if (j == c.length) {
-
- // all parameters match
- if (found == true) {
- throw new MethodException(this,
- "Could not identify unique " + setMethodName
- + "-method");
- } else {
- found = true;
- setMethod = m[i];
- }
- }
- }
- if (found != true) {
- throw new MethodException(this,
- "Could not identify " + setMethodName + "-method");
- }
- }
-
- // Gets the return type from get method
- this.type = (Class<T>) convertPrimitiveType(type);
-
- setArguments(getArgs, setArgs, setArgumentIndex);
- this.instance = instance;
- }
-
- /**
- * <p>
- * Creates a new instance of <code>MethodProperty</code> from the getter and
- * setter methods, and argument lists.
- * </p>
- * <p>
- * This constructor behaves exactly like
- * {@link #MethodProperty(Class type, Object instance, String getMethodName, String setMethodName, Object [] getArgs, Object [] setArgs, int setArgumentIndex)}
- * except that instead of names of the getter and setter methods this
- * constructor is given the actual methods themselves.
- * </p>
- *
- * @param type
- * the type of the property.
- * @param instance
- * the object that includes the property.
- * @param getMethod
- * the getter method.
- * @param setMethod
- * the setter method.
- * @param getArgs
- * the fixed argument list to be passed to the getter method.
- * @param setArgs
- * the fixed argument list to be passed to the setter method.
- * @param setArgumentIndex
- * the index of the argument in <code>setArgs</code> to be
- * replaced with <code>newValue</code> when
- * {@link #setValue(Object newValue)} is called.
- */
- @SuppressWarnings("unchecked")
- // cannot use "Class<? extends T>" because of automatic primitive type
- // conversions
- public MethodProperty(Class<?> type, Object instance, Method getMethod,
- Method setMethod, Object[] getArgs, Object[] setArgs,
- int setArgumentIndex) {
-
- if (getMethod == null) {
- throw new MethodException(this,
- "Property GET-method cannot not be null: " + type);
- }
-
- if (setMethod != null) {
- if (setArgs == null) {
- throw new IndexOutOfBoundsException(
- "The setArgs can not be null");
- }
- if (setArgumentIndex < 0 || setArgumentIndex >= setArgs.length) {
- throw new IndexOutOfBoundsException(
- "The setArgumentIndex must be >= 0 and < setArgs.length");
- }
- }
-
- // Gets the return type from get method
- Class<? extends T> convertedType = (Class<? extends T>) convertPrimitiveType(
- type);
-
- this.getMethod = getMethod;
- this.setMethod = setMethod;
- setArguments(getArgs, setArgs, setArgumentIndex);
- this.instance = instance;
- this.type = convertedType;
- }
-
- /**
- * Find a getter method for a property (getXyz(), isXyz() or areXyz()).
- *
- * @param propertyName
- * name of the property
- * @param beanClass
- * class in which to look for the getter methods
- * @return Method
- * @throws NoSuchMethodException
- * if no getter found
- */
- static Method initGetterMethod(String propertyName,
- final Class<?> beanClass) throws NoSuchMethodException {
- propertyName = SharedUtil.capitalize(propertyName);
-
- Method getMethod = null;
- try {
- getMethod = beanClass.getMethod("get" + propertyName,
- new Class[] {});
- } catch (final java.lang.NoSuchMethodException ignored) {
- try {
- getMethod = beanClass.getMethod("is" + propertyName,
- new Class[] {});
- } catch (final java.lang.NoSuchMethodException ignoredAsWell) {
- getMethod = beanClass.getMethod("are" + propertyName,
- new Class[] {});
- }
- }
- return getMethod;
- }
-
- /**
- * Returns the type of the Property. The methods <code>getValue</code> and
- * <code>setValue</code> must be compatible with this type: one must be able
- * to safely cast the value returned from <code>getValue</code> to the given
- * type and pass any variable assignable to this type as an argument to
- * <code>setValue</code>.
- *
- * @return type of the Property
- */
- @Override
- public final Class<? extends T> getType() {
- return type;
- }
-
- /**
- * Tests if the object is in read-only mode. In read-only mode calls to
- * <code>setValue</code> will throw <code>ReadOnlyException</code> and will
- * not modify the value of the Property.
- *
- * @return <code>true</code> if the object is in read-only mode,
- * <code>false</code> if it's not
- */
- @Override
- public boolean isReadOnly() {
- return super.isReadOnly() || (setMethod == null);
- }
-
- /**
- * Gets the value stored in the Property. The value is resolved by calling
- * the specified getter method with the argument specified at instantiation.
- *
- * @return the value of the Property
- */
- @Override
- public T getValue() {
- try {
- if (instance == null) {
- return null;
- } else {
- return (T) getMethod.invoke(instance, getArgs);
- }
- } catch (final Throwable e) {
- throw new MethodException(this, e);
- }
- }
-
- /**
- * <p>
- * Sets the setter method and getter method argument lists.
- * </p>
- *
- * @param getArgs
- * the fixed argument list to be passed to the getter method.
- * @param setArgs
- * the fixed argument list to be passed to the setter method.
- * @param setArgumentIndex
- * the index of the argument in <code>setArgs</code> to be
- * replaced with <code>newValue</code> when
- * {@link #setValue(Object newValue)} is called.
- */
- public void setArguments(Object[] getArgs, Object[] setArgs,
- int setArgumentIndex) {
- if (getArgs.length == 0) {
- this.getArgs = DEFAULT_GET_ARGS;
- } else {
- this.getArgs = Arrays.copyOf(getArgs, getArgs.length);
- }
- if (Arrays.equals(setArgs, DEFAULT_SET_ARGS)) {
- this.setArgs = DEFAULT_SET_ARGS;
- } else {
- this.setArgs = Arrays.copyOf(setArgs, setArgs.length);
- }
- this.setArgumentIndex = setArgumentIndex;
- }
-
- /**
- * Sets the value of the property.
- *
- * Note that since Vaadin 7, no conversions are performed and the value must
- * be of the correct type.
- *
- * @param newValue
- * the New value of the property.
- * @throws <code>Property.ReadOnlyException</code>
- * if the object is in read-only mode.
- * @see #invokeSetMethod(Object)
- */
- @Override
- public void setValue(T newValue) throws Property.ReadOnlyException {
-
- // Checks the mode
- if (isReadOnly()) {
- throw new Property.ReadOnlyException();
- }
-
- invokeSetMethod(newValue);
- fireValueChange();
- }
-
- /**
- * Internal method to actually call the setter method of the wrapped
- * property.
- *
- * @param value
- */
- protected void invokeSetMethod(T value) {
-
- try {
- // Construct a temporary argument array only if needed
- if (setArgs.length == 1) {
- setMethod.invoke(instance, new Object[] { value });
- } else {
-
- // Sets the value to argument array
- final Object[] args = new Object[setArgs.length];
- for (int i = 0; i < setArgs.length; i++) {
- args[i] = (i == setArgumentIndex) ? value : setArgs[i];
- }
- setMethod.invoke(instance, args);
- }
- } catch (final InvocationTargetException e) {
- final Throwable targetException = e.getTargetException();
- throw new MethodException(this, targetException);
- } catch (final Exception e) {
- throw new MethodException(this, e);
- }
- }
-
- /**
- * <code>Exception</code> object that signals that there were problems
- * calling or finding the specified getter or setter methods of the
- * property.
- *
- * @author Vaadin Ltd.
- * @since 3.0
- */
- @SuppressWarnings("rawtypes")
- @Deprecated
- // Exceptions cannot be parameterized, ever.
- public static class MethodException extends RuntimeException {
-
- /**
- * The method property from which the exception originates from
- */
- private final Property property;
-
- /**
- * Cause of the method exception
- */
- private Throwable cause;
-
- /**
- * Constructs a new <code>MethodException</code> with the specified
- * detail message.
- *
- * @param property
- * the property.
- * @param msg
- * the detail message.
- */
- public MethodException(Property property, String msg) {
- super(msg);
- this.property = property;
- }
-
- /**
- * Constructs a new <code>MethodException</code> from another exception.
- *
- * @param property
- * the property.
- * @param cause
- * the cause of the exception.
- */
- public MethodException(Property property, Throwable cause) {
- this.property = property;
- this.cause = cause;
- }
-
- /**
- * @see java.lang.Throwable#getCause()
- */
- @Override
- public Throwable getCause() {
- return cause;
- }
-
- /**
- * Gets the method property this exception originates from.
- *
- * @return MethodProperty or null if not a valid MethodProperty
- */
- public MethodProperty getMethodProperty() {
- return (property instanceof MethodProperty)
- ? (MethodProperty) property : null;
- }
-
- /**
- * Gets the method property this exception originates from.
- *
- * @return Property from which the exception originates
- */
- public Property getProperty() {
- return property;
- }
- }
-
- /**
- * Sends a value change event to all registered listeners.
- *
- * Public for backwards compatibility, visibility may be reduced in future
- * versions.
- */
- @Override
- public void fireValueChange() {
- super.fireValueChange();
- }
-
- private static final Logger getLogger() {
- return Logger.getLogger(MethodProperty.class.getName());
- }
-
- }
|