/**
*
*/
package org.openflexo.antar.binding;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.StringTokenizer;
import java.util.Vector;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.openflexo.antar.binding.BindingValue.DecodingPreProcessor;
import org.openflexo.antar.expr.DefaultExpressionParser;
import org.openflexo.antar.expr.Expression;
import org.openflexo.antar.expr.Function;
import org.openflexo.antar.expr.oldparser.ExpressionParser;
import org.openflexo.antar.expr.oldparser.ParseException;
import org.openflexo.xmlcode.StringEncoder;
public class BindingValueFactory extends StringEncoder.Converter<BindingValue> {
private static final Logger logger = Logger.getLogger(BindingValueFactory.class.getPackage().getName());
boolean warnOnFailure = true;
private DecodingPreProcessor _preProcessor = null;
private BindingFactory _bindingFactory;
public BindingValueFactory() {
super(BindingValue.class);
}
public BindingValueFactory(BindingFactory bindingStringFactory) {
this();
setBindingFactory(bindingStringFactory);
}
public BindingFactory getBindingFactory() {
return _bindingFactory;
}
public void setBindingFactory(BindingFactory bindingStringFactory) {
_bindingFactory = bindingStringFactory;
}
public void setWarnOnFailure(boolean aFlag) {
warnOnFailure = aFlag;
}
private MethodCall tryToDecodeAsMethodCall(BindingValue owner, Type currentType, String aValue, Bindable bindable) {
/*boolean debug = (aValue.indexOf("evaluateSelectableCondition") > -1);
if (debug) {
System.out.println("OK, dans tryToDecodeAsMethodCall "+aValue);
System.out.println("currentType="+currentType);
System.out.println("owner="+owner);
}*/
if (BindingValue.logger.isLoggable(Level.FINE)) {
BindingValue.logger.fine("tryToDecodeAsMethodCall " + aValue);
}
String callName;
Vector<String> paramsAsString;
try {
ExpressionParser parser = new DefaultExpressionParser();
Expression parsedExpression = parser.parse(aValue, bindable);
// if (debug) System.out.println("parsedExpression="+parsedExpression);
if (parsedExpression instanceof Function) {
callName = ((Function) parsedExpression).getName();
paramsAsString = new Vector<String>();
for (Expression e : ((Function) parsedExpression).getArgs()) {
paramsAsString.add(e.toString());
}
// if (debug) System.out.println("methodName="+callName);
} else {
if (BindingValue.logger.isLoggable(Level.WARNING) && warnOnFailure) {
BindingValue.logger.warning("Could not decode BindingValue : trying to find method call matching '" + aValue
+ " this is not a function call");
}
return null;
}
} catch (ParseException e) {
if (BindingValue.logger.isLoggable(Level.WARNING) && warnOnFailure) {
BindingValue.logger.warning("Could not decode BindingValue : parse error while trying to find method call matching '"
+ aValue);
}
return null;
}
Class<?> typeClass = TypeUtils.getBaseClass(currentType);
Method[] allMethods = typeClass.getMethods();
if (BindingValue.logger.isLoggable(Level.FINE)) {
BindingValue.logger.fine("paramsAsString=" + paramsAsString);
}
String propertyNameWithFirstCharToUpperCase = callName.substring(0, 1).toUpperCase() + callName.substring(1, callName.length());
String[] tries = new String[4];
tries[0] = "get" + propertyNameWithFirstCharToUpperCase;
tries[1] = callName;
tries[2] = "_" + callName;
tries[3] = "_get" + propertyNameWithFirstCharToUpperCase;
List<Method> possiblyMatchingMethods = new ArrayList<Method>();
for (Method method : allMethods) {
if (method.getGenericParameterTypes().length == paramsAsString.size()) {
for (int i = 0; i < 4; i++) {
if (method.getName().equals(tries[i])) {
possiblyMatchingMethods.add(method);
}
}
}
}
BindingValue.logger.fine("possiblyMatchingMethods=" + possiblyMatchingMethods);
// if (debug) System.out.println("possiblyMatchingMethods=" + possiblyMatchingMethods);
List<MethodCall> results = new ArrayList<MethodCall>();
for (Method method : possiblyMatchingMethods) {
boolean successfull = true;
MethodCall methodCall = new MethodCall(owner, method, currentType);
int i = 0;
for (Type genericType : method.getGenericParameterTypes()) {
Type type = TypeUtils.makeInstantiatedType(genericType, currentType);
// if (debug) System.out.println("Instead of considering "+genericType+" consider "+type);
String bindingAsString = paramsAsString.elementAt(i);
String paramName = "arg" + i;
i++;
if (BindingValue.logger.isLoggable(Level.FINE)) {
BindingValue.logger.fine("Attempt to parse: " + bindingAsString);
}
// if (debug) System.out.println("Attempt to parse: "+bindingAsString);
AbstractBinding paramBindingValue = _bindingFactory.convertFromString(bindingAsString, bindable);
if (paramBindingValue != null) {
paramBindingValue.setOwner(bindable);
if (BindingValue.logger.isLoggable(Level.FINE)) {
BindingValue.logger.fine("paramBindingValue=" + paramBindingValue + " of " + paramBindingValue.getAccessedType());
}
// if (debug) System.out.println("paramBindingValue="+paramBindingValue+" of "+paramBindingValue.getAccessedType());
if (paramBindingValue.isStaticValue()) {
paramBindingValue.setBindingDefinition(methodCall.new MethodCallParamBindingDefinition(paramName, type));
}
if (type != null && paramBindingValue.getAccessedType() != null
&& TypeUtils.isTypeAssignableFrom(type, paramBindingValue.getAccessedType(), true)) {
if (BindingValue.logger.isLoggable(Level.FINE)) {
BindingValue.logger.fine("Lookup on " + type + " succeded: " + paramBindingValue.getStringRepresentation());
}
// if (debug) System.out.println("Lookup on " + type + " succeded: " + paramBindingValue.getStringRepresentation());
methodCall.setBindingValueForParam(paramBindingValue, paramName);
} else {
if (BindingValue.logger.isLoggable(Level.FINE)) {
BindingValue.logger.fine("Lookup on type " + type + " failed (wrong type): "
+ paramBindingValue.getStringRepresentation() + "types: " + "looking " + type + " found "
+ paramBindingValue.getAccessedType());
}
/*if (debug) System.out.println("Lookup on type " + type + " failed (wrong type): "
+ paramBindingValue.getStringRepresentation() + "types: "
+"looking "+type+" found "+paramBindingValue.getAccessedType());*/
successfull = false;
}
} else {
if (BindingValue.logger.isLoggable(Level.FINE)) {
BindingValue.logger.fine("Lookup on " + type + " failed (cannot analysing): " + bindingAsString);
}
// if (debug) System.out.println("Lookup on " + type + " failed (cannot analysing): " + bindingAsString);
successfull = false;
}
}
if (successfull) {
results.add(methodCall);
}
}
if (results.size() == 1) {
return results.get(0);
} else if (results.size() > 1) {
if (BindingValue.logger.isLoggable(Level.WARNING) && warnOnFailure) {
BindingValue.logger.warning("While decoding BindingValue '" + aValue + "' : found ambigous methods " + callName);
}
return results.get(0);
}
if (BindingValue.logger.isLoggable(Level.WARNING) && warnOnFailure) {
BindingValue.logger.warning("Could not decode BindingValue : cannot find method call matching '" + aValue);
}
return null;
}
protected class PathTokenizer {
private final Vector<String> _tokens;
private final Enumeration<String> enumeration;
protected PathTokenizer(String value) {
super();
_tokens = new Vector<String>();
StringTokenizer st = new StringTokenizer(value, ".()", true);
String current = "";
int level = 0;
while (st.hasMoreElements()) {
String next = st.nextToken();
if (next.equals(".") && current.trim().length() > 0 && level == 0) {
_tokens.add(current);
current = "";
} else if (next.equals("(")) {
current += next;
level++;
} else if (next.equals(")")) {
current += next;
level--;
} else {
current += next;
}
}
if (current.trim().length() > 0 && level == 0) {
_tokens.add(current);
current = "";
}
enumeration = _tokens.elements();
}
public boolean hasMoreTokens() {
return enumeration.hasMoreElements();
}
public String nextToken() {
String returned = enumeration.nextElement();
if (BindingValue.logger.isLoggable(Level.FINE)) {
BindingValue.logger.fine("> " + returned);
}
return returned;
}
}
public boolean debug = false;
@Override
public BindingValue convertFromString(String value) {
throw new UnsupportedOperationException("No bindable provided");
}
public BindingValue convertFromString(String aValue, Bindable bindable) {
// boolean debug = (aValue.startsWith("individual.positionName"));
if (debug) {
System.out.println("OK, decoding BindingValue " + aValue);
System.out.println("_bindable=" + bindable);
System.out.println("binding model =" + bindable.getBindingModel());
}
if (BindingValue.logger.isLoggable(Level.FINE)) {
BindingValue.logger.fine("Decode " + aValue);
}
if (bindable == null) {
if (BindingValue.logger.isLoggable(Level.WARNING) && warnOnFailure) {
BindingValue.logger.warning("Could not decode BindingValue '" + aValue + "' : bindable not set !");
}
return null;
} else {
String value;
if (_preProcessor != null) {
value = _preProcessor.preProcessString(aValue);
} else {
value = aValue;
}
if ("null".equals(value)) {
return null;
}
BindingValue returned = new BindingValue();
PathTokenizer st = new PathTokenizer(value);
if (st.hasMoreTokens()) {
String bindingVariableName = st.nextToken();
if (debug) {
System.out.println("bindingVariableName=" + bindingVariableName);
}
if (bindable == null) {
if (BindingValue.logger.isLoggable(Level.WARNING) && warnOnFailure) {
BindingValue.logger.warning("Could not decode BindingValue '" + value + "' : no declared bindable !");
}
return null;
}
if (bindable.getBindingModel() == null) {
if (BindingValue.logger.isLoggable(Level.WARNING) && warnOnFailure) {
BindingValue.logger.warning("Could not decode BindingValue '" + value
+ "' : declared bindable has a null binding model !");
}
return null;
}
BindingVariable bv = bindable.getBindingModel().bindingVariableNamed(bindingVariableName);
if (bv == null) {
if (BindingValue.logger.isLoggable(Level.WARNING) && warnOnFailure) {
BindingValue.logger.warning("Could not decode BindingValue '" + value + "' : variable " + bindingVariableName
+ " not found in binding model !");
}
if (BindingValue.logger.isLoggable(Level.FINE)) {
BindingValue.logger.fine("NOT Found binding variable " + bv);
}
return null;
} else {
if (BindingValue.logger.isLoggable(Level.FINE)) {
BindingValue.logger.fine("Found binding variable " + bv);
}
if (debug) {
System.out.println("Found binding variable " + bv);
}
BindingPathElement currentElement = bv;
Type currentType = bv.getType();
if (currentType == null) {
if (BindingValue.logger.isLoggable(Level.WARNING) && warnOnFailure) {
BindingValue.logger.warning("Could not decode BindingValue '" + value + "' : variable " + bindingVariableName
+ " doesn't implement any type !");
}
return null;
}
returned.setBindingVariable(bv);
while (st.hasMoreTokens()) {
String nextTokenName = st.nextToken();
if (BindingValue.logger.isLoggable(Level.FINE)) {
BindingValue.logger.fine("nextTokenName=" + nextTokenName + " currentType=" + currentType);
}
if (debug) {
System.out.println("nextTokenName=" + nextTokenName + " currentType=" + currentType);
}
BindingPathElement nextElement;
if (TypeUtils.getBaseClass(currentType) == null) {
if (BindingValue.logger.isLoggable(Level.WARNING) && warnOnFailure) {
BindingValue.logger.warning("Could not decode BindingValue '" + value
+ "' : cannot find base entity for type '" + currentType);
}
return null;
}
// nextElement = KeyValueLibrary.getKeyValueProperty(currentType, nextTokenName);
nextElement = _bindingFactory.getBindingPathElement(currentElement, nextTokenName);
// nextElement = currentElement.getBindingPathElement(nextTokenName);
/*if (currentType instanceof Class
&& ((Class)currentType).isPrimitive()) {
currentType = TypeUtils.fromPrimitive((Class)currentType);
//nextElement = KeyValueLibrary.getKeyValueProperty(currentType, nextTokenName);
nextElement = _bindingFactory.getBindingPathElement(currentElement, nextTokenName);
}*/
if (nextElement == null) {
// OK, may be this is a MethodCall
// if (debug) System.out.println("is it a method call ? "+nextTokenName);
nextElement = tryToDecodeAsMethodCall(returned, currentType, nextTokenName, bindable);
}
if (nextElement == null) {
if (debug) {
System.out.println("cannot find next element");
}
if (debug) {
logger.info("cannot find next element for " + aValue + " factory=" + _bindingFactory);
}
if (BindingValue.logger.isLoggable(Level.WARNING) && warnOnFailure) {
BindingValue.logger.warning("Could not decode BindingValue '" + value
+ "' : cannot find property nor method matching '" + nextTokenName + "' for type " + currentType
+ " !");
}
return null;
} else {
if (debug) {
System.out.println("added to binding path " + nextElement);
}
if (debug) {
logger.info("found next element for " + aValue + " factory=" + _bindingFactory);
}
currentType = returned.addBindingPathElement(nextElement);
if (debug) {
logger.info("type=" + returned.getAccessedType());
}
// returned.addBindingPathElement(nextElement);
// currentType = nextElement.getType();
if (currentType == null) {
if (BindingValue.logger.isLoggable(Level.WARNING) && warnOnFailure) {
BindingValue.logger.warning("Could not decode BindingValue '" + value + "' : token " + nextTokenName
+ " doesn't implement any type !");
}
return null;
}
}
currentElement = nextElement;
}
if (debug) {
logger.info("Tout va bien pour decoder " + aValue);
}
// Before to receive its owner, we set to knonwn bindable, in order to check validity
returned.setOwner(bindable);
if (debug) {
logger.info("Je mets le bindable " + bindable);
}
if (debug) {
logger.info("Et c'est bon ? " + returned.isBindingValidWithoutBindingDefinition(true));
}
if (returned.isBindingValidWithoutBindingDefinition(false)) {
returned._isConnected = true;
return returned;
} else {
if (BindingValue.logger.isLoggable(Level.WARNING) && warnOnFailure) {
BindingValue.logger.warning("Could not decode BindingValue '" + value + "' : invalid binding !");
}
return null;
}
}
} else {
if (BindingValue.logger.isLoggable(Level.WARNING) && warnOnFailure) {
BindingValue.logger.warning("Could not decode BindingValue '" + value + "' : variable not set !");
}
return null;
}
}
}
@Override
public String convertToString(BindingValue value) {
return value.getStringRepresentation();
}
public DecodingPreProcessor getPreProcessor() {
return _preProcessor;
}
public void setPreProcessor(DecodingPreProcessor preProcessor) {
_preProcessor = preProcessor;
}
}