/******************************************************************************* * Copyright (c) 2008, 2014 Matthew Hall and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Matthew Hall - initial API and implementation (bug 194734) * Martin Frey <martin.frey@logica.com> - bug 256150 * Matthew Hall - bug 264307 * Simon Scholz <simon.scholz@vogella.com> - Bug 445446 ******************************************************************************/ package org.eclipse.core.internal.databinding.beans; import java.beans.BeanInfo; import java.beans.IntrospectionException; import java.beans.Introspector; import java.beans.PropertyDescriptor; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.List; import org.eclipse.core.databinding.observable.value.IObservableValue; import org.eclipse.core.databinding.util.Policy; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; /** * @since 1.2 * */ public class BeanPropertyHelper { /** * Sets the contents of the given property on the given source object to the * given value. * * @param source * the source object which has the property being updated * @param propertyDescriptor * the property being changed * @param value * the new value of the property */ public static void writeProperty(Object source, PropertyDescriptor propertyDescriptor, Object value) { try { Method writeMethod = propertyDescriptor.getWriteMethod(); if (null == writeMethod) { throw new IllegalArgumentException( "Missing public setter method for " //$NON-NLS-1$ + propertyDescriptor.getName() + " property"); //$NON-NLS-1$ } if (!writeMethod.isAccessible()) { writeMethod.setAccessible(true); } writeMethod.invoke(source, new Object[] { value }); } catch (InvocationTargetException e) { /* * InvocationTargetException wraps any exception thrown by the * invoked method. */ throw new RuntimeException(e.getCause()); } catch (Exception e) { Policy.getLog() .log(new Status( IStatus.WARNING, Policy.JFACE_DATABINDING, IStatus.OK, "Could not change value of " + source + "." + propertyDescriptor.getName(), e)); //$NON-NLS-1$ //$NON-NLS-2$ } } /** * Returns the contents of the given property for the given bean. * * @param source * the source bean * @param propertyDescriptor * the property to retrieve * @return the contents of the given property for the given bean. */ public static Object readProperty(Object source, PropertyDescriptor propertyDescriptor) { try { Method readMethod = propertyDescriptor.getReadMethod(); if (readMethod == null) { throw new IllegalArgumentException(propertyDescriptor.getName() + " property does not have a read method."); //$NON-NLS-1$ } if (!readMethod.isAccessible()) { readMethod.setAccessible(true); } return readMethod.invoke(source); } catch (InvocationTargetException e) { /* * InvocationTargetException wraps any exception thrown by the * invoked method. */ throw new RuntimeException(e.getCause()); } catch (Exception e) { Policy.getLog() .log(new Status( IStatus.WARNING, Policy.JFACE_DATABINDING, IStatus.OK, "Could not read value of " + source + "." + propertyDescriptor.getName(), e)); //$NON-NLS-1$ //$NON-NLS-2$ return null; } } /** * Returns the element type of the given collection-typed property for the * given bean. * * @param descriptor * the property being inspected * @return the element type of the given collection-typed property if it is * an array property, or Object.class otherwise. */ public static Class<?> getCollectionPropertyElementType( PropertyDescriptor descriptor) { Class<?> propertyType = descriptor.getPropertyType(); return propertyType.isArray() ? propertyType.getComponentType() : Object.class; } /** * @param beanClass * @param propertyName * @return the PropertyDescriptor for the named property on the given bean * class */ public static PropertyDescriptor getPropertyDescriptor(Class<?> beanClass, String propertyName) { if (!beanClass.isInterface()) { BeanInfo beanInfo; try { beanInfo = Introspector.getBeanInfo(beanClass); } catch (IntrospectionException e) { // cannot introspect, give up return null; } PropertyDescriptor[] propertyDescriptors = beanInfo .getPropertyDescriptors(); for (PropertyDescriptor descriptor : propertyDescriptors) { if (descriptor.getName().equals(propertyName)) { return descriptor; } } } else { try { List<PropertyDescriptor> pds = new ArrayList<PropertyDescriptor>(); getInterfacePropertyDescriptors(pds, beanClass); if (pds.size() > 0) { for (PropertyDescriptor descriptor : pds.toArray(new PropertyDescriptor[pds.size()])) { if (descriptor.getName().equals(propertyName)) return descriptor; } } } catch (IntrospectionException e) { // cannot introspect, give up return null; } } throw new IllegalArgumentException( "Could not find property with name " + propertyName + " in class " + beanClass); //$NON-NLS-1$ //$NON-NLS-2$ } /** * Goes recursively into the interface and gets all defined * propertyDescriptors * * @param propertyDescriptors * The result list of all PropertyDescriptors the given interface * defines (hierarchical) * @param iface * The interface to fetch the PropertyDescriptors * @throws IntrospectionException */ private static void getInterfacePropertyDescriptors( List<PropertyDescriptor> propertyDescriptors, Class<?> iface) throws IntrospectionException { BeanInfo beanInfo = Introspector.getBeanInfo(iface); PropertyDescriptor[] pds = beanInfo.getPropertyDescriptors(); for (PropertyDescriptor pd : pds) { propertyDescriptors.add(pd); } Class<?>[] subIntfs = iface.getInterfaces(); for (Class<?> subIntf : subIntfs) { getInterfacePropertyDescriptors(propertyDescriptors, subIntf); } } /** * @param observable * @param propertyName * @return property descriptor or <code>null</code> */ /* package */public static PropertyDescriptor getValueTypePropertyDescriptor( IObservableValue observable, String propertyName) { if (observable.getValueType() != null) return getPropertyDescriptor((Class<?>) observable.getValueType(), propertyName); return null; } /** * @param propertyDescriptor * @return String description of property descriptor */ public static String propertyName(PropertyDescriptor propertyDescriptor) { Class<?> beanClass = propertyDescriptor.getReadMethod() .getDeclaringClass(); return shortClassName(beanClass) + "." + propertyDescriptor.getName() + ""; //$NON-NLS-1$ //$NON-NLS-2$ } /** * @param beanClass * @return class name excluding package */ public static String shortClassName(Class<?> beanClass) { if (beanClass == null) return "?"; //$NON-NLS-1$ String className = beanClass.getName(); int lastDot = className.lastIndexOf('.'); if (lastDot != -1) className = className.substring(lastDot + 1); return className; } }