|
Setting a property of a object (or even a object graph) can be very useful - especially when used in parsing code. This small class allows very easy access to any getter and setter that can be reached by public getXXX methods.
Ok, lets start with a small usage example. Assume you have the following java code:
obj.getBla().getBlub().setValue(10);
That's easy to write - but what if need to put this in a callbacks - and ofcourse each callback has different code. This will create a lot of class files relatively fast.
So now try to image that you could write the above code as a string - and pass this string as a paramter to your callback class ? Then you have reduce everything down to 2 classes - your callback and a utility class.
Lets take a look at how the above code would look like when using our utility class:
new PropertyAccessor("bla", "blub", "value").setProperty(obj, int.class, 10);
Here I assumed that the setValue() method from above takes an int as a parameter. As you can see it's a bit longer - but you can even change the class of the object without any code changed. Lets create an example callback which uses ourt class:
class MyCallback implements SomeInterface {
/** the accessor which does all the work */
final PropertyAccessor accessor;
public MyCallback (String ... propertyPath) {
accessor = new PropertyAccessor(propertyPath);
}
public void myCallbackMethod(Object dataObject, int parsedValue) {
accessor.setProperty(dataObject, int.class, parsedValue);
}
}
Now you can use this callback to set any int property on any object - as long as the object has the getter and setter methods required by the propertyPath.
Now enough with examples - here comes the code:
public class PropertyAccessor {
private static final Class[] EMPTY_PARAMS = new Class[0];
private static final Object[] EMPTY_ARGS = new Object[0];
final String[] pathMethods;
final String getMethod;
final String setMethod;
/** Creates a new instance of PropertyAccessor
* If more then one property is specified then the first act as a path.
* @param path Contains the name of properties as defined by the java beans spec
*/
public PropertyAccessor(String ... path) {
int n = path.length - 1;
pathMethods = new String[n];
for(int i=0 ; i<n ; i++) {
pathMethods[i] = "get".concat(upcaseFirst(path[i]));
}
String lastPart = upcaseFirst(path[n]);
getMethod = "get".concat(lastPart);
setMethod = "set".concat(lastPart);
}
/**
* Sets the property specified by the path of this PropertyAccessor
* to the given value.
* @param obj The object where the path starts
* @param clazz The type of the property (used to select the right setXXX method)
* @param value The new value of the property
*/
public<T> void setProperty(Object obj, Class<T> clazz, T value) {
try {
obj = getObj(obj);
Method m = obj.getClass().getMethod(setMethod, clazz);
m.invoke(obj, value);
} catch (Exception ex) {
ex.printStackTrace();
}
}
/**
* Gets the value of the property specified by the path of this PropertyAccessor.
* @param obj The object where the path starts
* @param clazz The type of the property - used to get the right return type
* @return The value of the property - or null in case of an error
*/
public<T> T getProperty(Object obj, Class<T> clazz) {
try {
obj = getObj(obj);
Method m = obj.getClass().getMethod(getMethod, EMPTY_PARAMS);
return clazz.cast(m.invoke(obj, EMPTY_ARGS));
} catch (Exception ex) {
ex.printStackTrace();
return null;
}
}
private Object getObj(Object obj) throws Exception {
for(String pathMethod : pathMethods) {
Method m = obj.getClass().getMethod(pathMethod, EMPTY_PARAMS);
obj = m.invoke(obj, EMPTY_ARGS);
}
return obj;
}
private static String upcaseFirst(String property) {
return Character.toUpperCase(property.charAt(0))+property.substring(1);
}
}
I hope you find this useful. It was fun to write :D |