use of com.google.template.soy.data.SoyValue in project closure-templates by google.
the class RenderVisitor method visitForNode.
@Override
protected void visitForNode(ForNode node) {
Optional<RangeArgs> exprAsRangeArgs = RangeArgs.createFromNode(node);
if (exprAsRangeArgs.isPresent()) {
RangeArgs args = exprAsRangeArgs.get();
int step = args.increment().isPresent() ? evalRangeArg(node, args.increment().get()) : 1;
int start = args.start().isPresent() ? evalRangeArg(node, args.start().get()) : 0;
int end = evalRangeArg(node, args.limit());
int length = end - start;
if ((length ^ step) < 0) {
// handle ifempty, if present
if (node.numChildren() == 2) {
visit(node.getChild(1));
}
} else {
ForNonemptyNode child = (ForNonemptyNode) node.getChild(0);
int size = length / step + (length % step == 0 ? 0 : 1);
for (int i = 0; i < size; ++i) {
executeForeachBody(child, i, IntegerData.forValue(start + step * i), size);
}
}
} else {
SoyValue dataRefValue = eval(node.getExpr(), node);
if (!(dataRefValue instanceof SoyList)) {
throw RenderException.createWithSource("In 'foreach' command " + node.toSourceString() + ", the data reference does not " + "resolve to a SoyList " + "(encountered type " + dataRefValue.getClass().getName() + ").", node);
}
SoyList foreachList = (SoyList) dataRefValue;
int listLength = foreachList.length();
if (listLength > 0) {
// Case 1: Nonempty list.
ForNonemptyNode child = (ForNonemptyNode) node.getChild(0);
for (int i = 0; i < listLength; ++i) {
executeForeachBody(child, i, foreachList.getProvider(i), listLength);
}
} else {
// Case 2: Empty list. If the 'ifempty' node exists, visit it.
if (node.numChildren() == 2) {
visit(node.getChild(1));
}
}
}
}
use of com.google.template.soy.data.SoyValue in project closure-templates by google.
the class RenderVisitor method visitMsgFallbackGroupNode.
@Override
protected void visitMsgFallbackGroupNode(MsgFallbackGroupNode node) {
if (assistantForMsgs == null) {
assistantForMsgs = new RenderVisitorAssistantForMsgs(this, msgBundle);
}
if (!node.getEscapingDirectives().isEmpty()) {
// The entire message needs to be escaped, so we need to render to a temporary buffer.
// Fortunately, for most messages (in HTML context) this is unnecessary.
pushOutputBuf(new StringBuilder());
}
assistantForMsgs.visitForUseByMaster(node);
if (!node.getEscapingDirectives().isEmpty()) {
// Escape the entire message with the required directives.
SoyValue wholeMsg = StringData.forValue(popOutputBuf().toString());
for (SoyPrintDirective directive : node.getEscapingDirectives()) {
wholeMsg = applyDirective(directive, wholeMsg, ImmutableList.<SoyValue>of(), node);
}
append(currOutputBuf, wholeMsg.stringValue());
}
}
use of com.google.template.soy.data.SoyValue in project closure-templates by google.
the class RenderVisitor method visitCallDelegateNode.
@Override
protected void visitCallDelegateNode(CallDelegateNode node) {
ExprRootNode variantExpr = node.getDelCalleeVariantExpr();
String variant;
if (variantExpr == null) {
variant = "";
} else {
try {
SoyValue variantData = eval(variantExpr, node);
if (variantData instanceof IntegerData) {
// An integer constant is being used as variant. Use the value string representation as
// variant.
variant = String.valueOf(variantData.longValue());
} else {
// Variant is either a StringData or a SanitizedContent. Use the value as a string. If
// the value is not a string, and exception will be thrown.
variant = variantData.stringValue();
}
} catch (SoyDataException e) {
throw RenderException.createWithSource(String.format("Variant expression \"%s\" doesn't evaluate to a valid type " + "(Only string and integer are supported).", variantExpr.toSourceString()), e, node);
}
}
DelTemplateKey delegateKey = DelTemplateKey.create(node.getDelCalleeName(), variant);
TemplateDelegateNode callee;
try {
callee = templateRegistry.selectDelTemplate(delegateKey, activeDelPackageSelector);
} catch (IllegalArgumentException e) {
throw RenderException.createWithSource(e.getMessage(), e, node);
}
if (callee != null) {
visitCallNodeHelper(node, callee);
} else if (node.allowEmptyDefault()) {
// no active delegate implementation, so the call output is empty string
return;
} else {
throw RenderException.createWithSource("Found no active impl for delegate call to \"" + node.getDelCalleeName() + (variant.isEmpty() ? "" : ":" + variant) + "\" (and delcall does not set allowemptydefault=\"true\").", node);
}
}
use of com.google.template.soy.data.SoyValue in project closure-templates by google.
the class RenderVisitor method checkStrictParamType.
/**
* Check that the given {@code paramValue} matches the static type of {@code param}.
*/
private void checkStrictParamType(final TemplateNode node, final TemplateParam param, @Nullable SoyValueProvider paramValue) {
Kind kind = param.type().getKind();
if (kind == Kind.ANY || kind == Kind.UNKNOWN) {
// Nothing to check. ANY and UKNOWN match all types.
return;
}
if (paramValue == null) {
paramValue = NullData.INSTANCE;
} else if (paramValue instanceof SoyAbstractCachingValueProvider) {
SoyAbstractCachingValueProvider typedValue = (SoyAbstractCachingValueProvider) paramValue;
if (!typedValue.isComputed()) {
// in order to preserve laziness we tell the value provider to assert the type when
// computation is triggered
typedValue.addValueAssertion(new ValueAssertion() {
@Override
public void check(SoyValue value) {
checkValueType(param, value, node);
}
});
return;
}
}
checkValueType(param, paramValue.resolve(), node);
}
use of com.google.template.soy.data.SoyValue in project closure-templates by google.
the class RenderVisitor method visitCallNodeHelper.
// for IntelliJ
@SuppressWarnings("ConstantConditions")
private void visitCallNodeHelper(CallNode node, TemplateNode callee) {
// ------ Build the call data. ------
SoyRecord dataToPass;
if (node.isPassingAllData()) {
dataToPass = data;
} else if (node.isPassingData()) {
SoyValue dataRefValue = eval(node.getDataExpr(), node);
if (!(dataRefValue instanceof SoyRecord)) {
throw RenderException.create("In 'call' command " + node.toSourceString() + ", the data reference does not resolve to a SoyRecord.").addStackTraceElement(node);
}
dataToPass = (SoyRecord) dataRefValue;
} else {
dataToPass = null;
}
SoyRecord callData;
int numChildren = node.numChildren();
if (numChildren == 0) {
// --- Cases 1 and 2: Not passing params. ---
if (dataToPass == null) {
// Case 1: Not passing data and not passing params.
callData = ParamStore.EMPTY_INSTANCE;
} else {
// Case 2: Passing data and not passing params.
callData = dataToPass;
}
} else {
// --- Cases 3 and 4: Passing params. ---
ParamStore mutableCallData;
if (dataToPass == null) {
// Case 3: Not passing data and passing params.
mutableCallData = new BasicParamStore(numChildren);
} else {
// Case 4: Passing data and passing params.
mutableCallData = new AugmentedParamStore(dataToPass, numChildren);
}
for (CallParamNode child : node.getChildren()) {
if (child instanceof CallParamValueNode) {
mutableCallData.setField(child.getKey().identifier(), lazyEval(((CallParamValueNode) child).getExpr(), child));
} else if (child instanceof CallParamContentNode) {
mutableCallData.setField(child.getKey().identifier(), renderRenderUnitNode((CallParamContentNode) child));
} else {
throw new AssertionError();
}
}
callData = mutableCallData;
}
if (node.getEscapingDirectives().isEmpty()) {
// No escaping at the call site -- render directly into the output buffer.
RenderVisitor rv = this.createHelperInstance(currOutputBuf, callData);
try {
rv.renderTemplate(callee, node.getParamsToRuntimeCheck(callee));
} catch (RenderException re) {
// this template call.
throw re.addStackTraceElement(node);
}
} 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 = this.createHelperInstance(calleeBuilder, callData);
try {
rv.renderTemplate(callee, node.getParamsToRuntimeCheck(callee));
} catch (RenderException re) {
// this template call.
throw re.addStackTraceElement(node);
}
ContentKind calleeKind = fromSanitizedContentKind(callee.getContentKind());
SoyValue resultData = calleeKind != null ? UnsafeSanitizedContentOrdainer.ordainAsSafe(calleeBuilder.toString(), calleeKind) : StringData.forValue(calleeBuilder.toString());
for (SoyPrintDirective directive : node.getEscapingDirectives()) {
resultData = applyDirective(directive, resultData, ImmutableList.<SoyValue>of(), node);
}
append(currOutputBuf, resultData, node);
}
}
Aggregations