// ------ Build the call data. ------
SoyMapData dataToPass;
if (node.isPassingAllData()) {
dataToPass = data;
} else if (node.isPassingData()) {
SoyData dataRefValue = eval(node.getDataExpr());
if (!(dataRefValue instanceof SoyMapData)) {
throw new RenderException(
"In 'call' command " + node.toSourceString() +
", the data reference does not resolve to a SoyMapData.");
}
dataToPass = (SoyMapData) dataRefValue;
} else {
dataToPass = null;
}
SoyMapData callData;
if (!node.isPassingData()) {
// Case 1: Not passing data. Start with a fresh SoyMapData object.
callData = new SoyMapData();
} else if (node.numChildren() == 0) {
// Case 2: No params. Just pass in the current data.
callData = dataToPass;
} else {
// Case 3: Passing data and adding params. Need to augment the current data.
callData = new AugmentedSoyMapData(dataToPass);
}
for (CallParamNode child : node.getChildren()) {
if (child instanceof CallParamValueNode) {
callData.putSingle(
child.getKey(), eval(((CallParamValueNode) child).getValueExprUnion().getExpr()));
} else if (child instanceof CallParamContentNode) {
CallParamContentNode childCpcn = (CallParamContentNode) child;
SoyData renderedBlock = renderBlock(childCpcn);
// If the param node has a content kind attribute, it will have been autoescaped in the
// corresponding context by the strict contextual autoescaper. Hence, the result of
// evaluating the param block is wrapped in SanitizedContent of the specified kind.
if (childCpcn.getContentKind() != null) {
renderedBlock = UnsafeSanitizedContentOrdainer.ordainAsSafe(
renderedBlock.stringValue(), childCpcn.getContentKind());
}
callData.putSingle(child.getKey(), renderedBlock);
} else {
throw new AssertionError();
}
}
// ------ Render the callee template with the callData built above. ------
if (node.getEscapingDirectiveNames().isEmpty()) {
// No escaping at the call site -- render directly into the output buffer.
RenderVisitor rv = createHelperInstance(currOutputBuf, callData);
rv.exec(callee);
} else {
// Escaping the call site's result, such as at a strict template boundary.
// TODO: Some optimization is needed here before Strict Soy can be widely used:
// - Only create this temporary buffer when contexts mismatch. We could run a pre-pass that
// eliminates escaping directives when all callers are known.
// - Instead of creating a temporary buffer and copying, wrap with an escaping StringBuilder.
StringBuilder calleeBuilder = new StringBuilder();
RenderVisitor rv = createHelperInstance(calleeBuilder, callData);
rv.exec(callee);
SoyData resultData = (callee.getContentKind() != null) ?
UnsafeSanitizedContentOrdainer.ordainAsSafe(
calleeBuilder.toString(), callee.getContentKind()) :
StringData.forValue(calleeBuilder.toString());
for (String directiveName : node.getEscapingDirectiveNames()) {
resultData = applyDirective(directiveName, resultData, ImmutableList.<SoyData>of(), node);
}
append(currOutputBuf, resultData.toString());
}
}