package org.jboss.processFlow.console.binding;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Pattern;
import org.mvel2.DataConversion;
import org.mvel2.MVEL;
/**
* The dataBinder adapts to MVEL.<br/>
* This is used by the default dataBinder since it work quite well except the following limition(s):
* <ul>
* <li>do NOT support datetime conversion, since MVEL use global registry for the type conversion which could result in
* unexpected value in complex runtime env</li>
* </ul>
*
* @author tanxu
* @date Feb 18, 2012
* @since
*/
public class MvelDataBinder extends AbstractDataBinder {
public MvelDataBinder() {
super();
}
@Override
protected Object doConvert(Class targetType, Object source) {
return DataConversion.convert(source, targetType);
}
@Override
protected Map<String, Object> doBind(Object target, Map<String, Object> sourceContext) throws Exception {
for (Map.Entry<String, Object> entry : sourceContext.entrySet()) {
String key = entry.getKey();
Object value = entry.getValue();
MVEL.setProperty(target, key, value);
}
Map newValues = null;
if (target instanceof Map) {
newValues = ((Map) target);
}
else {
newValues = getProperties(target);
}
return newValues;
}
@Override
protected Map<String, Object> renderContextIfNeeded(Object target, Map<String, Object> sourceContext) {
HashMap<String, Object> newContext = new HashMap<String, Object>(sourceContext.size());
if (target instanceof Map) {
for (Map.Entry<String, Object> sourceEntry : sourceContext.entrySet()) {
String sourceKey = sourceEntry.getKey();
if (!isNavigatiableProperty(sourceKey))
continue;
// parse the property name from the sourceKey, for example, a[0].b -> a
String prop = getProperty(sourceKey);
// filter the interested context according to the property
// otherwise, we might get the error if non-used context values exists:
// "Property referenced in indexed property path is neither an array nor a List nor a Map"
for (Map.Entry<String, Object> targetEntry : ((Map<String, Object>) target).entrySet()) {
if (prop.equals(targetEntry.getKey())) {
// deal with map binding from special expression:
// for example, (a[0].key=testkey, a[0].value=testvalue) -> a['testky']=testvalue
if (targetEntry.getValue() instanceof Map) {
// convert a[0].key, a[0].value to map entry
if (Pattern.matches(prop + "\\[\\d\\].(key|value)", sourceKey)) {
int dotIdx = sourceKey.indexOf('.');
String k = (String) sourceContext.get(sourceKey.substring(0, dotIdx) + ".key");
Object v = sourceContext.get(sourceKey.substring(0, dotIdx) + ".value");
if (k != null && !k.isEmpty() && v != null) {
String newKey = prop + "['" + k + "']";
newContext.put(newKey, v);
continue;
}
}
}
newContext.put(sourceKey, sourceEntry.getValue());
}
}
}
}
else {
newContext.putAll(sourceContext);
}
return newContext;
}
protected String getProperty(String keyWithToken) {
int bracketIdx = keyWithToken.indexOf('[');
int dotIdx = keyWithToken.indexOf('.');
if (bracketIdx > 0 && dotIdx <= 0) {
return keyWithToken.substring(0, bracketIdx);
}
else if (bracketIdx <= 0 && dotIdx > 0) {
return keyWithToken.substring(0, dotIdx);
}
else if (bracketIdx > 0 && dotIdx > 0) {
return keyWithToken.substring(0, Math.min(bracketIdx, dotIdx));
}
else {
return keyWithToken;
}
}
@Override
protected Object renderTargetIfNeeded(Object target, Map<String, Object> sourceContext) {
return target;
}
}