*/
protected ExpressionInfo findAccessors(ExpressionInfo base, final String path, final boolean matchAsync, boolean staticAccess) throws UnableToCompleteException {
LocalTreeLogger.pushLogger(logger.branch(TreeLogger.INFO, "findAccessors('"+base+"', path='"+path+"', matchAsync="+matchAsync+", staticAccess="+staticAccess+")"));
try {
GeneratorTypeInfo inType = base.getType();
if(inType.isPrimitive()) {
logger.log(TreeLogger.ERROR, "Can't find any member inside of non-class type "+base.getType()+" with path "+path);
throw new UnableToCompleteException();
}
String expr = base.getGetter();
//System.out.println("findAccessors("+inType+", '"+expr+"', '"+path+"', "+matchAsync+")");
// Split "path" into two parts - the part before the first dot and the "rest" of the expression
String[] splitPath = smartSplit(path, '.', 2);
String name = splitPath[0];
if (name.length() == 0) {
return null;
}
String getter;
boolean asyncGetter = false;
String setter = null;
boolean asyncSetter = false;
GeneratorTypeInfo type;
/*if(name.endsWith("]")) {
int openBraceIdx = smartIndexOf(name, '[');
if(openBraceIdx == -1) {
logger.log(TreeLogger.ERROR, "Can't find opening [ for ] in "+name, null);
throw new UnableToCompleteException();
}
// TODO array indexing
throw new UnableToCompleteException();
} else */
boolean endsWithParen = name.endsWith(")");
boolean methodInvokation = endsWithParen // Has (), assume method call
|| inType.hasMethodMatching(name, true, null) // no parens, but expression has the same name as a zero-arg method
|| (matchAsync && inType.hasMethodMatching(name, true, null, commonTypes.asyncCallback)); // no parens, but expression has the same name as an async method and async is OK
boolean lastOrOnlyPartOfTheExpression = splitPath.length == 1;
if(methodInvokation) {
String getterMethodName;
ExpressionInfo[] args;
GeneratorTypeInfo[] argTypes;
if(endsWithParen) {
int openIdx = smartIndexOf(name, '(');
if(openIdx == -1) {
logger.log(TreeLogger.ERROR, "Can't find opening ( for ) in "+name, null);
throw new UnableToCompleteException();
}
String[] argExprs = smartSplit(name.substring(openIdx + 1, name.length() - 1), ',', 100);
//System.out.println("Splitting '"+name.substring(openIdx + 1, name.length() - 1)+" around ',' gives "+args.length+" args: "+joinWithCommas(0, args));
// Check for an empty parameter list
if(argExprs.length == 1 && argExprs[0].length() == 0)
argExprs = new String[0];
getterMethodName = identifier(name.substring(0, openIdx));
args = new ExpressionInfo[argExprs.length];
argTypes = new GeneratorTypeInfo[argExprs.length];
for(int i=0; i < argExprs.length; i++) {
String arg = argExprs[i].trim();
ExpressionInfo argAccessors = findAccessors(arg, true, false);
if (argAccessors == null) {
logger.log(TreeLogger.ERROR, "Couldn't evaluate '" + arg + "' as argument to '" + name + "'", null);
throw new UnableToCompleteException();
}
args[i] = argAccessors;
argTypes[i] = argAccessors.getType();
}
} else {
getterMethodName = identifier(name);
args = new ExpressionInfo[0];
argTypes = new GeneratorTypeInfo[0];
}
GeneratorTypeInfo objectType = inType;
boolean searchingThis = objectType.equals(myClass);
boolean asyncMethod = false;
GeneratorMethodInfo getterMethod = null;
for(;;) {
GeneratorTypeInfo[] syncArgTypesWildcard = new GeneratorTypeInfo[argTypes.length];
GeneratorTypeInfo[] asyncArgTypesWildcard = new GeneratorTypeInfo[argTypes.length+1];
asyncArgTypesWildcard[argTypes.length] = commonTypes.asyncCallback;
GeneratorTypeInfo[] asyncArgTypes = Arrays.copyOf(argTypes, argTypes.length+1);
asyncArgTypes[argTypes.length] = commonTypes.asyncCallback;
HashSet<String> candidates = new HashSet<String>();
candidates.add(getterMethodName);
String capGetterMethodName = capitalize(getterMethodName);
candidates.add("get"+capGetterMethodName);
candidates.add("is"+capGetterMethodName);
for(String candidate : candidates) {
getterMethod = objectType.findMethodMatching(candidate, true, null, argTypes);
if(getterMethod == null)
getterMethod = objectType.findMethodMatching(candidate, true, null, syncArgTypesWildcard);
if(getterMethod != null) {
asyncMethod = false;
type = getterMethod.getReturnType();
break;
}
if(matchAsync) {
getterMethod = objectType.findMethodMatching(candidate, true, null, asyncArgTypes);
if(getterMethod == null)
getterMethod = objectType.findMethodMatching(candidate, true, null, asyncArgTypesWildcard);
if(getterMethod != null) {
asyncMethod = true;
type = getterMethod.getAsyncReturnType();
if(type == null) type = commonTypes.object;
break;
}
}
}
if(searchingThis && getterMethod == null && objectType instanceof GeneratedInnerClassInfo) {
objectType = objectType.getFieldType(PARENT_VIEW_FIELD_NAME, true);
if(objectType == null)
break;
expr = expr+"."+PARENT_VIEW_FIELD_NAME;
//System.out.println("Ascending to "+expr+" "+objectType);
} else break;
}
if (getterMethod == null) {
logger.log(TreeLogger.ERROR, "findAccessors(): Unable to find a "+(staticAccess?"static":"instance")+" method with the right number of arguments ("
+ args.length + (matchAsync?" [ + optional AsyncCallback]":"")+") with name '" + getterMethodName + "' in " + inType
+ " for expression '" + path + "'", null);
throw new UnableToCompleteException();
}
StringBuffer getterBuf = new StringBuffer();
getterBuf.append(expr).append('.').append(getterMethod.getName()).append('(');
for (int i = 0; i < args.length; i++) {
if(i > 0) getterBuf.append(", ");
getterBuf.append(args[i].conversionExpr(getterMethod.getParameterTypes()[i]));
}
if(asyncMethod) {
if(args.length > 0) getterBuf.append(","); // trailing comma for async methods so we can append the callback parameter when we call the method
} else getterBuf.append(')');
getter = getterBuf.toString();
asyncGetter = asyncMethod;
type = asyncMethod?getterMethod.getAsyncReturnType():getterMethod.getReturnType();
// Find the matching setter method (if any).
String setterMethodName = getterMethod.getName().replaceFirst("^(is|get)", "set");
GeneratorTypeInfo[] setterArgTypes = Arrays.copyOf(argTypes, argTypes.length+1);
setterArgTypes[argTypes.length] = type;
GeneratorMethodInfo setterMethod = objectType.findMethodMatching(setterMethodName, true, null, setterArgTypes);
// If searching for something matching the types we got doesn't work, try it a wildcard for the type
if(setterMethod == null) {
setterArgTypes = new GeneratorTypeInfo[argTypes.length+1];
setterArgTypes[argTypes.length] = type;
setterMethod = objectType.findMethodMatching(setterMethodName, true, null, setterArgTypes);
}
if(setterMethod == null) {
setterArgTypes = Arrays.copyOf(argTypes, argTypes.length+2);
setterArgTypes[argTypes.length] = type;
setterArgTypes[argTypes.length+1] = commonTypes.asyncCallback;
setterMethod = objectType.findMethodMatching(setterMethodName, true, PrimitiveTypeInfo.VOID, setterArgTypes);
if(setterMethod == null) {
setterArgTypes = new GeneratorTypeInfo[argTypes.length+2];
setterArgTypes[argTypes.length] = type;
setterArgTypes[argTypes.length+1] = commonTypes.asyncCallback;
setterMethod = objectType.findMethodMatching(setterMethodName, true, PrimitiveTypeInfo.VOID, setterArgTypes);
}
if(setterMethod != null)
asyncSetter = true;
} else {
asyncSetter = false;