/*
* Copyright 2012 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.template.soy.passes;
import com.google.template.soy.basetree.AbstractNodeVisitor;
import com.google.template.soy.basetree.Node;
import com.google.template.soy.basetree.ParentNode;
import com.google.template.soy.exprtree.ExprRootNode;
import com.google.template.soy.exprtree.VarDefn;
import com.google.template.soy.exprtree.VarRefNode;
import com.google.template.soy.soytree.CallNode;
import com.google.template.soy.soytree.SoyNode.ExprHolderNode;
import com.google.template.soy.soytree.TemplateNode;
import com.google.template.soy.soytree.defn.TemplateParam;
/**
* Visitor for determining whether a template needs to ensure that its data is defined.
*
* <p>Important: Do not use outside of Soy code (treat as superpackage-private).
*
*/
public final class ShouldEnsureDataIsDefinedVisitor {
/** Runs this pass on the given template. */
public boolean exec(TemplateNode template) {
boolean hasOptional = false;
for (TemplateParam param : template.getParams()) {
if (param.isRequired()) {
// If there exists a required param, then data should already be defined (no need to
// ensure).
return false;
} else {
hasOptional = true;
}
}
if (hasOptional) {
// If all params are optional (and there is at least one), then we need to ensure data is
// defined. This is because the only legal way to have an optional param is if you reference
// it somewhere in the template, so there is no need to check.
return true;
}
// If we get here then the template has no declared params and we are observing a v1 compatible
// template. Search for things that could be data references:
// * possibleParams
// * data=All calls
// others?
return new AbstractNodeVisitor<Node, Boolean>() {
boolean shouldEnsureDataIsDefined;
@Override
public Boolean exec(Node node) {
visit(node);
return shouldEnsureDataIsDefined;
}
@Override
public void visit(Node node) {
if (node instanceof VarRefNode) {
VarRefNode varRefNode = (VarRefNode) node;
VarDefn var = varRefNode.getDefnDecl();
// Don't include injected params in this analysis
if (varRefNode.isPossibleParam()
&& (var.kind() != VarDefn.Kind.PARAM // a soydoc param -> not ij
|| !((TemplateParam) var).isInjected())) { // an {@param but not {@inject
shouldEnsureDataIsDefined = true;
return;
}
}
if (node instanceof CallNode) {
if (((CallNode) node).dataAttribute().isPassingAllData()) {
shouldEnsureDataIsDefined = true;
return;
}
}
if (node instanceof ParentNode) {
for (Node child : ((ParentNode<?>) node).getChildren()) {
visit(child);
if (shouldEnsureDataIsDefined) {
return;
}
}
}
if (node instanceof ExprHolderNode) {
for (ExprRootNode expr : ((ExprHolderNode) node).getExprList()) {
visit(expr);
if (shouldEnsureDataIsDefined) {
return;
}
}
}
}
}.exec(template);
}
}