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;
}
if(setterMethod != null) {
StringBuffer setterBuf = new StringBuffer();
setterBuf.append(expr).append('.').append(setterMethod.getName()).append('(');
for (int i = 0; i < args.length; i++) {
if(i > 0) setterBuf.append(", ");
String convertedArg = args[i].conversionExpr(setterMethod.getParameterTypes()[i]);
if(convertedArg.isEmpty()) {
throw new IllegalStateException("Got empty result back from "+args[i]+" converted to "+setterMethod.getParameterTypes()[i]);
}
setterBuf.append(convertedArg);
}
if(args.length > 0)
setterBuf.append(","); // trailing comma for setters so we can append the value parameter and possibly the async callback
setter = setterBuf.toString();
}
} else {
// No array or function specifier, so look for a normal property or field
String baseExpr = (expr.equals("this") ? "" : expr + ".");
name = identifier(name);
String getterName = "get" + capitalize(name);
String setterName = "set" + capitalize(name);
GeneratorMethodInfo getterMethod = inType.findMethodMatching(getterName, true, null);
if(getterMethod == null && matchAsync) { // Check for async version, if allowed in this context
getterMethod = inType.findMethodMatching(getterName, true, PrimitiveTypeInfo.VOID, commonTypes.asyncCallback);
asyncGetter = getterMethod != null;
}
if (getterMethod == null) {
getterName = "is" + capitalize(name);
getterMethod = inType.findMethodMatching(getterName, true, null);
if(getterMethod == null && matchAsync) { // Check for async version, if allowed in this context
getterMethod = inType.findMethodMatching(getterName, true, PrimitiveTypeInfo.VOID, commonTypes.asyncCallback);
asyncGetter = getterMethod != null;
}
}
if (getterMethod != null) {
getter = baseExpr + getterName + (asyncGetter?"":"()"); // No trailing brackets for an async call
if(asyncGetter) {
type = getterMethod.getAsyncReturnType();
if(type == null) type = commonTypes.object;
} else {
type = getterMethod.getReturnType();
}
} else {
asyncGetter = false;
// Try direct field access
type = inType.getFieldType(name, baseExpr.startsWith("this."));
if(type != null) {
getter = baseExpr + name;
setter = baseExpr + name + "=";
asyncSetter = false;
asyncGetter = false;
} else {
getter = null;
}
}
if(setter == null) {
GeneratorMethodInfo setterMethod;
// Only look for the setter if this is the last (or only) part of the chain. i.e. for an expression
// a.b.c we would only look for a setter for c, not a or b.
if(lastOrOnlyPartOfTheExpression) {
setterMethod = inType.findMethodMatching(setterName, true, null, (GeneratorTypeInfo)null);
if(setterMethod == null) {
setterMethod = inType.findMethodMatching(setterName, true, PrimitiveTypeInfo.VOID, (GeneratorTypeInfo)null, commonTypes.asyncCallback);
asyncSetter = setterMethod != null;
}
if(setterMethod != null) {
//System.out.println("Found setter "+setterMethod);
setter = baseExpr + setterName + "(";
type = setterMethod.getParameterTypes()[0];
}
}
}
}
//System.out.println("Looking for "+name+" in "+inType+", found "+getter+" and "+setter);
if(getter != null && splitPath.length == 2) {
if(type.isArray()) {
if("length".equals(splitPath[1])) {
return new ExpressionInfo(path, getter+"."+splitPath[1], PrimitiveTypeInfo.INT, false);
} else {
logger.log(TreeLogger.ERROR, "Attempting to access a property of array that I don't recognize: "+getter+"."+splitPath[1], null);
throw new UnableToCompleteException();
}
} else if(type.isPrimitive()) {
logger.log(TreeLogger.ERROR, "Attempting to access a property of a primitive type: "+getter+" of type "+type+" async = "+asyncGetter, null);
throw new UnableToCompleteException();
}
if(asyncGetter == false) {
// Easy... just get them to create a new getter based on this one
return findAccessors(new ExpressionInfo(path, getter, type, isConstants(type)), splitPath[1], matchAsync, staticAccess);
} else {
// Oops, we're getting a property of an async property, time for some magic
// The trick: generate a new method that does the first async operation and
// then returns the result of the getter of the proceeding attributes.
ExpressionInfo subexpr = findAccessors(new ExpressionInfo(path, "base", type, false), splitPath[1], matchAsync, staticAccess);
if(subexpr == null) {
logger.log(TreeLogger.ERROR, "Failed to find property '"+splitPath[1]+"' of type '"+type+"' of expression '"+getter+"'", null);
throw new UnableToCompleteException();
}
String getterName = "getAsync"+asyncProxies.size();
if(subexpr.hasGetter()) {
if(subexpr.hasSynchronousGetter()) {
// Synchronous sub-expression, how merciful!
asyncProxies.add(" public void "+getterName+"(AsyncCallback<Object> callback) {\n"+
" "+ExpressionInfo.callAsyncGetter(getter, "new AsyncCallbackDirectProxy<Object>(callback, \""+path+"\") {\n"+
" public void onSuccess(Object result) {\n"+
" "+type.getParameterizedQualifiedSourceName()+" base = ("+type.getParameterizedQualifiedSourceName()+") result;\n"+
" returnSuccess("+subexpr.getterExpr()+");\n"+
" }\n"+
" }")+";\n"+
" }\n");
} else if(subexpr.hasAsyncGetter()) {
asyncProxies.add(" public void "+getterName+"(AsyncCallback<Object> callback) {\n"+
" "+ExpressionInfo.callAsyncGetter(getter, "new AsyncCallbackDirectProxy<Object>(callback, \""+path+"\") {\n"+
" public void onSuccess(Object result) {"+
" "+type.getParameterizedQualifiedSourceName()+" base = ("+type.getParameterizedQualifiedSourceName()+") result;\n"+
" "+subexpr.callAsyncGetter("callback")+";\n"+
" }\n"+
" }")+";\n"+
" }\n");
}
} else getterName = null;
String setterName = "setAync"+asyncProxies.size();
if(subexpr.hasSetter()) {
if(subexpr.hasSynchronousSetter()) {
// Synchronous sub-expression, how merciful!
asyncProxies.add(" public void "+setterName+"(final "+subexpr.getType().getParameterizedQualifiedSourceName()+" value, AsyncCallback<Void> callback) {\n"+
" "+ExpressionInfo.callAsyncGetter(getter, "new AsyncCallbackDirectProxy<Void>(callback, \""+path+"\") {\n"+
" public void onSuccess(Void result) {\n"+
" "+type.getParameterizedQualifiedSourceName()+" base = ("+type.getParameterizedQualifiedSourceName()+") result;\n"+
" "+subexpr.callSetter("value")+"\n"+
" returnSuccess(null);\n"+
" }\n"+
" }")+";\n"+
" }");
} else if(subexpr.hasAsynchronousSetter()) {
asyncProxies.add(" public void "+setterName+"(final "+subexpr.getType().getParameterizedQualifiedSourceName()+" value, AsyncCallback<Void> callback) {\n"+
" "+ExpressionInfo.callAsyncGetter(getter, "new AsyncCallbackDirectProxy<Void>(callback, \""+path+"\") {\n"+
" public void onSuccess(Void result) {\n"+
" "+type.getParameterizedQualifiedSourceName()+" base = ("+type.getParameterizedQualifiedSourceName()+") result;\n"+
" "+subexpr.callAsyncSetter("value", "callback")+"\n"+
" }\n"+
" }")+";\n"+
" }");
}
} else setterName = null;
return new ExpressionInfo(path, getterName, setterName, subexpr.getType(), true, true, false);
}
} else if(setter != null && lastOrOnlyPartOfTheExpression) {
return new ExpressionInfo(path, getter, setter, type, asyncGetter, asyncSetter, false);
}
/*
JClassType superclass = inType.getSuperclass();
if (superclass != null && !ReflectedClassInfo.OBJECT.equals(superclass)) {
ExpressionInfo inherited = findAccessors(superclass, expr, path, matchAsync);
if(inherited != null) {
if(getter == null) {
if(inherited.getter != null) { getter = inherited.getter; asyncGetter = false; }
else if(inherited.hasAsynchronousGetter()) { getter = inherited.asyncGetter; asyncGetter = true; }
}
if(setter == null) {
if(inherited.hasSynchronousSetter()) { setter = inherited.setter; asyncSetter = false; }
else if(inherited.hasAsynchronousSetter()) { setter = inherited.asyncSetter; asyncSetter = true; }
}
if(type == null) type = inherited.getType();
}
}
*/
if(type != null) {
if((getter != null) && (setter == null) && !asyncGetter
&& (isConstants(inType))) {
//logger.log(TreeLogger.INFO, "Considering value to be constant since it is part of a Constants or DictionaryConstants: "+getter);
return new ExpressionInfo(path, getter, type, base.isConstant());
} else {
return new ExpressionInfo(path, getter, setter, type, asyncGetter, asyncSetter, false);
}
}
//System.out.println("Failed to find property "+name+" on "+inType);
return null;
} finally {