1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158
| package test;
import java.lang.reflect.Method;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
public class ObservableBeanAdapter<B, T> {
private B bean;
private String property;
/**
* Creates a new instance.
* @param bean The bean.
* @param property The property to access.
*/
public ObservableBeanAdapter(B bean, String property) {
this.bean = bean;
this.property = property;
populateValue();
valueProperty().addListener(valueChangeListener);
}
/**
* The observable property.
*/
private final ObjectProperty<T> value = new SimpleObjectProperty<>(this, "value", null);
public final void setValue(T propertyValue) {
value.set(propertyValue);
}
public final T getValue() {
return value.get();
}
public final ObjectProperty<T> valueProperty() {
return value;
}
/**
* Set initial value in the observable property by calling the appropriate getter in the underlying bean.
*/
private void populateValue() {
T propertyValue = null;
try {
String methodName = "get" + property.substring(0, 1).toUpperCase() + property.substring(1, property.length());
propertyValue = executeMethod(bean, methodName, null);
} catch (Exception e1) {
// e1.printStackTrace();
// Now look for boolean getter instead.
try {
String methodName = "is" + property.substring(0, 1).toUpperCase() + property.substring(1, property.length());
propertyValue = executeMethod(bean, methodName, null);
} catch (Exception e2) {
// e2.printStackTrace();
}
}
setValue(propertyValue);
}
/**
* Called whenever the value changes.
* <br/>Calls the setter in the underlying bean.
*/
private final ChangeListener<T> valueChangeListener = new ChangeListener<T>() {
@Override
public void changed(ObservableValue<? extends T> observableValue, T oldValue, T newValue) {
try {
String methodName = "set" + property.substring(0, 1).toUpperCase() + property.substring(1, property.length());
executeMethod(bean, methodName, newValue);
} catch (Exception e) {
e.printStackTrace();
}
}
};
/**
* Execute a method on the bean.
* @param bean The bean.
* @param methodName The name of the method
* @param arg Argument of the method:
* <ul>
* <li>{@code Null} when calling the getter.</li>
* <li>The new value when calling the setter.</li>
* </ul>
* @return
* @throws Exception
*/
private T executeMethod(B bean, String methodName, Object arg) throws Exception {
Class argType = (arg == null) ? null : arg.getClass();
T result = null;
try {
result = executeMethodForArgType(bean, methodName, argType, arg);
} catch (NoSuchMethodException nsme) {
// We only deal with the setter as the getter does not take any argument.
if (argType != null) {
// nsme.printStackTrace();
// For number classes try with literal classes instead.
if (argType == Boolean.class) {
result = executeMethodForArgType(bean, methodName, boolean.class, arg);
} else if (argType == Byte.class) {
result = executeMethodForArgType(bean, methodName, byte.class, arg);
} else if (argType == Character.class) {
result = executeMethodForArgType(bean, methodName, char.class, arg);
} else if (argType == Short.class) {
result = executeMethodForArgType(bean, methodName, short.class, arg);
} else if (argType == Integer.class) {
result = executeMethodForArgType(bean, methodName, int.class, arg);
} else if (argType == Long.class) {
result = executeMethodForArgType(bean, methodName, long.class, arg);
} else if (argType == Float.class) {
result = executeMethodForArgType(bean, methodName, float.class, arg);
} else if (argType == Double.class) {
result = executeMethodForArgType(bean, methodName, double.class, arg);
} // For other types, will try all known public parent classes and interfaces types.
else {
Class[] parents = argType.getClasses();
int errors = 0;
for (Class parentClass : parents) {
try {
result = executeMethodForArgType(bean, methodName, parentClass, arg);
break;
} catch (NoSuchMethodException nsme1) {
nsme.addSuppressed(nsme1);
}
}
// Still could not find the parent, rethrow the error.
if (errors == parents.length) {
throw nsme;
}
}
} else {
throw nsme;
}
}
return result;
}
private T executeMethodForArgType(B bean, String methodName, Class argType, Object arg) throws Exception {
Class<B> bClass = (Class<B>) bean.getClass();
Method method = null;
T result = null;
// No argument: the getter.
if (argType == null) {
method = bClass.getMethod(methodName);
Object methodResult = method.invoke(bean);
result = (T) methodResult;
} // Argument: the setter.
else {
method = bClass.getMethod(methodName, argType);
Object methodResult = method.invoke(bean, arg);
result = (T) methodResult;
}
return result;
}
} |
Partager