params.add(expr.append(")").toString());
code.log(Type.DEBUG, "Final compiled expression: " + expr);
} else if (contentChunk.type == ContentType.REFERENCE) {
sb.append("{").append(argCount++).append("}");
JType argType = scopeContext.getType(contentChunk.content);
if (argType == null) {
logger.log(Type.ERROR, "Reference could not be found: '" + contentChunk.content + "'. Please fix the expression in your template.");
throw new UnableToCompleteException();
}
paramTypes.add(argType.getParameterizedQualifiedSourceName());
params.add(scopeContext.deref(contentChunk.content));
} else {
assert false : "Content type not supported + " + contentChunk.type;
}
} else if (chunk instanceof ControlChunk) {
ControlChunk controlChunk = (ControlChunk) chunk;
// build logic, get scoped name
boolean hasIf = controlChunk.controls.containsKey("if");
boolean hasFor = controlChunk.controls.containsKey("for");
if (!hasIf && !hasFor) {
logger.log(Type.ERROR, "<tpl> tag did not define a 'for' or 'if' attribute!");
throw new UnableToCompleteException();
}
// declare a sub-template, and stash content in there, interleaving it
// into the current template
String subTemplate = scopeContext.declareLocalVariable("subTemplate");
String templateInBlock = scopeContext.declareLocalVariable("innerTemplate");
sb.append("{").append(argCount++).append("}");
paramTypes.add("com.google.gwt.safehtml.shared.SafeHtml");
params.add(subTemplate);
sw.println("SafeHtml %1$s;", subTemplate);
sw.println("SafeHtmlBuilder %1$s_builder = new SafeHtmlBuilder();", subTemplate);
// find the context that should be passed to the child template
final Context childScope;
// if we have both for and if, if needs to wrap the for
if (hasIf) {
ConditionParser p = new ConditionParser(logger);
List<Token> tokens = p.parse(controlChunk.controls.get("if"));
StringBuilder condition = new StringBuilder();
for (Token t : tokens) {
switch (t.type) {
case ExpressionLiteral:
condition.append(t.contents);
break;
case MethodInvocation:
Matcher invoke = Pattern.compile("([a-zA-Z0-9\\._]+)\\:([a-zA-Z0-9_]+)\\(([^\\)]*)\\)").matcher(
t.contents);
invoke.matches();
String deref = scopeContext.deref(invoke.group(1));
String methodName = invoke.group(2);
String args = "";
for (String a : invoke.group(3).split(",")) {
String possible = scopeContext.deref(a);
args += possible == null ? a : possible;
}
condition.append(invokables.getMethodInvocation(methodName, deref, args));
break;
case Reference:
condition.append("(").append(scopeContext.deref(t.contents)).append(")");
break;
default:
logger.log(Type.ERROR, "Unexpected token type: " + t.type);
throw new UnableToCompleteException();
}
}
sw.println("if (%1$s) {", condition.toString());
sw.indent();
}
// if there is a for, print it out, and change scope
if (hasFor) {
String loopRef = controlChunk.controls.get("for");
JType collectionType = scopeContext.getType(loopRef);
if (collectionType == null) {
logger.log(Type.ERROR, "Reference in 'for' attribute could not be found: '" + loopRef + "'. Please fix the expression in your template.");
throw new UnableToCompleteException();
}
final JType localType;// type accessed within the loop
final String localAccessor;// expr to access looped instance, where
// %1$s is the loop obj, and %2$s is the
// int index
if (collectionType.isArray() != null) {
localType = collectionType.isArray().getComponentType();