/** * */ 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; } }