/*******************************************************************************
* Copyright (c) 2008 Ralf Ebert
* 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:
* Ralf Ebert - initial API and implementation
*******************************************************************************/
package de.ralfebert.rcputils.properties;
import java.beans.PropertyDescriptor;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.core.internal.databinding.beans.BeanPropertyHelper;
/**
* PropertyValue: Resolves nested property Strings like "company.country.name".
*
* Use like this:
*
* <pre>
* PropertyValue prop = new PropertyValue("country.name");
* Object countryName = prop.getValue(someObject);
* </pre>
*
* This class allows to use JFace Data Binding property support to get and set
* nested property values without observing them.
*
* Please note that this class is not prepared for a case where the classes of
* the objects change on the path to the property. For example, if you use
* "country.name" on a "Company" object once, it expects a "Company" object you
* use it the next time.
*
* WARNING: This uses internal data binding classes. This enhancement described
* in https://bugs.eclipse.org/bugs/show_bug.cgi?id=285307 would allow to use
* official API instead of this class.
*
* @author Ralf Ebert <info@ralfebert.de>
*/
@SuppressWarnings("restriction")
public class PropertyValue implements IValue {
private final Object[] properties;
/**
* Creates a PropertyValue to access the bean property referred by
* propertyName.
*/
public PropertyValue(String propertyName) {
properties = split(propertyName);
}
private static Object[] split(String propertyName) {
if (propertyName.indexOf('.') == -1) {
return new Object[] { propertyName };
}
List<String> propertyNames = new ArrayList<String>();
int index;
while ((index = propertyName.indexOf('.')) != -1) {
propertyNames.add(propertyName.substring(0, index));
propertyName = propertyName.substring(index + 1);
}
propertyNames.add(propertyName);
return propertyNames.toArray(new Object[propertyNames.size()]);
}
/**
* Returns the property value referred by object."propertyName"
*/
@Override
public Object getValue(Object object) {
Object currentObject = object;
for (int i = 0; i < properties.length; i++) {
if (currentObject == null) {
return null;
}
if (properties[i] instanceof String) {
properties[i] = BeanPropertyHelper.getPropertyDescriptor(currentObject.getClass(), (String) properties[i]);
}
if (properties[i] instanceof PropertyDescriptor) {
currentObject = BeanPropertyHelper.readProperty(currentObject, (PropertyDescriptor) properties[i]);
} else {
throw new RuntimeException("Invalid property: " + properties[i]);
}
}
return currentObject;
}
/**
* Sets the property value referred by object."propertyName" to value
*/
@Override
public void setValue(Object object, Object value) {
Object currentObject = object;
for (int i = 0; i < properties.length; i++) {
if (currentObject == null) {
throw new RuntimeException("Value cannot be set because of null value in nested property!");
}
if (properties[i] instanceof String) {
properties[i] = BeanPropertyHelper.getPropertyDescriptor(currentObject.getClass(), (String) properties[i]);
}
if (properties[i] instanceof PropertyDescriptor) {
boolean lastProperty = i == properties.length - 1;
if (lastProperty) {
BeanPropertyHelper.writeProperty(currentObject, (PropertyDescriptor) properties[i], value);
} else {
currentObject = BeanPropertyHelper.readProperty(currentObject, (PropertyDescriptor) properties[i]);
}
} else {
throw new RuntimeException("Invalid property: " + properties[i]);
}
}
}
}