/*
* Copyright 2008 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.common.base.Preconditions;
import com.google.template.soy.error.ErrorReporter;
import com.google.template.soy.error.SoyErrorKind;
import com.google.template.soy.soytree.AbstractSoyNodeVisitor;
import com.google.template.soy.soytree.CallBasicNode;
import com.google.template.soy.soytree.SoyFileNode;
import com.google.template.soy.soytree.SoyFileSetNode;
import com.google.template.soy.soytree.SoyNode;
import com.google.template.soy.soytree.SoyNode.ParentSoyNode;
import java.util.Map;
/**
* Sets the full callee name on each {@link CallBasicNode} whose callee name in the source code
* either is a partial template name or starts with an alias.
*
* <p>{@link #exec} should be called on a full parse tree or a Soy file. This pass mutates
* CallBasicNodes. There is no return value.
*
* <p>TODO(brndn): consider folding into TemplateParser.
*
*/
final class SetFullCalleeNamesVisitor extends AbstractSoyNodeVisitor<Void> {
private static final SoyErrorKind CALL_COLLIDES_WITH_NAMESPACE_ALIAS =
SoyErrorKind.of("Call collides with namespace alias ''{0}''");
/** The namespace of the current file that we're in (during the pass). */
private String currNamespace;
/** Alias-to-namespace map of the current file (during the pass). */
private Map<String, String> currAliasToNamespaceMap;
private final ErrorReporter errorReporter;
SetFullCalleeNamesVisitor(ErrorReporter errorReporter) {
this.errorReporter = errorReporter;
}
@Override
public Void exec(SoyNode soyNode) {
Preconditions.checkArgument(
soyNode instanceof SoyFileSetNode || soyNode instanceof SoyFileNode);
return super.exec(soyNode);
}
// -----------------------------------------------------------------------------------------------
// Implementations for specific nodes.
@Override
protected void visitSoyFileNode(SoyFileNode node) {
currNamespace = node.getNamespace();
currAliasToNamespaceMap = node.getAliasToNamespaceMap();
visitChildren(node);
}
@Override
protected void visitCallBasicNode(CallBasicNode node) {
String srcCalleeName = node.getSrcCalleeName();
// TODO: If feasible, change existing instances and remove the startsWith(".") part below.
if (srcCalleeName.startsWith(".")) {
// Case 1: Source callee name is partial.
node.setCalleeName(currNamespace + srcCalleeName);
} else if (srcCalleeName.contains(".")) {
// Case 2: Source callee name is a proper dotted ident.
String[] parts = srcCalleeName.split("[.]", 2);
if (currAliasToNamespaceMap.containsKey(parts[0])) {
// Case 2a: Source callee name's first part is an alias.
String aliasNamespace = currAliasToNamespaceMap.get(parts[0]);
node.setCalleeName(aliasNamespace + '.' + parts[1]);
} else {
// Case 2b: Source callee name's first part is not an alias.
node.setCalleeName(srcCalleeName);
}
} else {
// Case 3: Source callee name is a single ident (not dotted).
if (currAliasToNamespaceMap.containsKey(srcCalleeName)) {
errorReporter.report(
node.getSourceLocation(), CALL_COLLIDES_WITH_NAMESPACE_ALIAS, srcCalleeName);
}
node.setCalleeName(srcCalleeName);
}
visitChildren(node);
}
// -----------------------------------------------------------------------------------------------
// Fallback implementation.
@Override
protected void visitSoyNode(SoyNode node) {
if (node instanceof ParentSoyNode<?>) {
visitChildren((ParentSoyNode<?>) node);
}
}
}