
org.jamon.codegen.Analyzer Maven / Gradle / Ivy
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.jamon.codegen;
import java.io.IOException;
import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Set;
import org.jamon.api.Location;
import org.jamon.compiler.ParserErrorImpl;
import org.jamon.compiler.ParserErrorsImpl;
import org.jamon.emit.EmitMode;
import org.jamon.node.*;
import org.jamon.util.StringUtils;
public class Analyzer {
static final String ABSTRACT_REPLACING_TEMPLATE_ERROR = "An abstract template cannot replace another template";
static final String ABSTRACT_REPLACEABLE_TEMPLATE_ERROR = "Abstract templates are not replaceable";
static final String REPLACING_NON_REPLACEABLE_TEMPLATE_ERROR = "Replaced template is not marked as <%replaceable>";
static final String XARGS_DECLARED_WITHOUT_PARENT_ERROR = "xargs may not be declared without extending another template";
public Analyzer(String templatePath, TemplateDescriber describer, Set children)
throws IOException {
templateUnit = new TemplateUnit(templatePath, errors);
templateDir = templatePath.substring(0, 1 + templatePath.lastIndexOf('/'));
currentStatementBlock = templateUnit;
this.describer = describer;
this.children = children;
emitMode = describer.getEmitMode(templatePath);
templateIdentifier = describer.getExternalIdentifier(templatePath);
templateUnit.setJamonContextType(describer.getJamonContextType(templatePath));
aliases.putAll(describer.getAliases(templatePath));
}
public Analyzer(String templatePath, TemplateDescriber describer) throws IOException {
this(templatePath, describer, new HashSet());
}
public TemplateUnit analyze() throws IOException {
TopNode top = describer.parseTemplate(templateUnit.getName());
templateUnit.setEncoding(top.getEncoding());
preAnalyze(top);
mainAnalyze(top);
checkForConcreteness(top);
checkTemplateReplacement(top);
if (errors.hasErrors()) {
throw errors;
}
return templateUnit;
}
private void addError(String message, Location location) {
errors.addError(new ParserErrorImpl(location, message));
}
private void preAnalyze(TopNode top) throws IOException {
topLevelAnalyze(top, new AliasAdapter());
topLevelAnalyze(top, new PreliminaryAdapter());
if (defaultEscaping == null) {
defaultEscaping = describer.getEscaping(templateUnit.getName());
}
if (defaultEscaping == null) {
defaultEscaping = EscapingDirective.get(EscapingDirective.DEFAULT_ESCAPE_CODE);
}
}
private void topLevelAnalyze(TopNode top, AnalysisAdapter adapter) {
for (AbstractNode node : top.getSubNodes()) {
node.apply(adapter);
}
}
private void mainAnalyze(TopNode top) {
top.apply(new Adapter());
}
private void checkForConcreteness(TopNode top) {
if (!getTemplateUnit().isParent() && !getTemplateUnit().getAbstractMethodNames().isEmpty()) {
topLevelAnalyze(top, new AnalysisAdapter() {
@Override
public void caseExtendsNode(ExtendsNode extendsNode) {
StringBuilder message = new StringBuilder("The abstract method(s) ");
StringUtils.commaJoin(message, getTemplateUnit().getAbstractMethodNames());
message.append(" have no concrete implementation");
addError(message.toString(), extendsNode.getLocation());
}
});
}
}
/**
* If this template is a replacement template (via the <%replaces> tag), verify that:
*
* - The replacing template is concrete
* - All required arguments have matching required arguments in the replaced template
* - All optional argument have matching required or optional arguments in the replaced template
*
* - All fragment arguments have matching fragment arguments in the replaced template
*
* @param top the top node of the syntax tree.
*/
private void checkTemplateReplacement(TopNode top) {
topLevelAnalyze(top, new AnalysisAdapter() {
@Override
public void caseReplaceableNode(ReplaceableNode replaceable) {
if (getTemplateUnit().isParent()) {
addError(ABSTRACT_REPLACEABLE_TEMPLATE_ERROR, replaceable.getLocation());
}
else {
getTemplateUnit().setReplaceable(true);
}
}
@Override
public void caseReplacesNode(ReplacesNode replaces) {
if (getTemplateUnit().isParent()) {
addError(ABSTRACT_REPLACING_TEMPLATE_ERROR, replaces.getLocation());
}
else {
String replacedTemplatePath = getAbsolutePath(computePath(replaces.getPath()));
TemplateDescription replacedTemplateDescription = getTemplateDescription(
replacedTemplatePath, replaces.getLocation());
if (replacedTemplateDescription != null) {
if (!replacedTemplateDescription.isReplaceable()) {
addError(REPLACING_NON_REPLACEABLE_TEMPLATE_ERROR, replaces.getLocation());
}
getTemplateUnit().setReplacedTemplatePathAndDescription(replacedTemplatePath,
replacedTemplateDescription);
verifyRequiredArgsComeFromReplacedTemplate(replaces.getLocation(),
replacedTemplateDescription);
verifyFragmentArgsComeFromReplacedTemplate(replaces.getLocation(),
replacedTemplateDescription);
verifyOptionalArgsComeFromReplacedTemplate(replaces.getLocation(),
replacedTemplateDescription);
}
}
}
});
}
private void verifyRequiredArgsComeFromReplacedTemplate(Location location,
TemplateDescription replacedTemplateDescription) {
verifyArgsComeFromReplacedTemplate(location, replacedTemplateDescription.getRequiredArgs()
.iterator(), getTemplateUnit().getSignatureRequiredArgs(), "required");
}
private void verifyFragmentArgsComeFromReplacedTemplate(Location location,
TemplateDescription replacedTemplateDescription) {
verifyArgsComeFromReplacedTemplate(location, replacedTemplateDescription
.getFragmentInterfaces().iterator(), getTemplateUnit().getFragmentArgs(), "fragment");
}
private void verifyOptionalArgsComeFromReplacedTemplate(Location location,
TemplateDescription replacedTemplateDescription) {
verifyArgsComeFromReplacedTemplate(location, new SequentialIterator(
replacedTemplateDescription.getRequiredArgs().iterator(), replacedTemplateDescription
.getOptionalArgs().iterator()), getTemplateUnit().getSignatureOptionalArgs(),
"required or optional");
}
private void verifyArgsComeFromReplacedTemplate(Location location,
Iterator replacedTemplateArgs, Collection extends T> templateArgs, String argumentKind) {
Set replacedTemplateArgNames = new HashSet();
while (replacedTemplateArgs.hasNext()) {
replacedTemplateArgNames.add(replacedTemplateArgs.next().getName());
}
for (T arg : templateArgs) {
if (!replacedTemplateArgNames.contains(arg.getName())) {
addError("Replaced template contains no " + argumentKind + " argument named "
+ arg.getName(), location);
}
}
}
private void pushDefUnit(String defName) {
currentStatementBlock = getTemplateUnit().getDefUnit(defName);
}
private void pushMethodUnit(String methodName) {
currentStatementBlock = getTemplateUnit().getMethodUnit(methodName);
}
private void pushOverriddenMethodUnit(OverrideNode node) {
currentStatementBlock = getTemplateUnit().makeOverridenMethodUnit(node.getName(),
node.getLocation());
}
private void pushFlowControlBlock(Location location, String header) {
FlowControlBlock flowControlBlock = new FlowControlBlock(currentStatementBlock, header,
location);
addStatement(flowControlBlock);
currentStatementBlock = flowControlBlock;
}
private FragmentUnit pushFragmentUnitImpl(String fragName, Location location) {
currentStatementBlock = new FragmentUnit(fragName, getCurrentStatementBlock(),
getTemplateUnit().getGenericParams(), errors, location);
return (FragmentUnit) currentStatementBlock;
}
private void pushFragmentArg(FragmentUnit frag) {
currentStatementBlock = frag;
}
private void popStatementBlock() {
currentStatementBlock = currentStatementBlock.getParent();
}
private void pushCallStatement(CallStatement callStatement) {
callStatements.add(callStatement);
}
private void popCallStatement() {
callStatements.removeLast();
}
private CallStatement getCurrentCallStatement() {
return callStatements.getLast();
}
private TemplateUnit getTemplateUnit() {
return templateUnit;
}
private StatementBlock getCurrentStatementBlock() {
return currentStatementBlock;
}
private final TemplateUnit templateUnit;
private StatementBlock currentStatementBlock;
private final TemplateDescriber describer;
private final Set children;
private final LinkedList callStatements = new LinkedList();
private final Map aliases = new HashMap();
private final String templateDir;
private final String templateIdentifier;
private final EmitMode emitMode;
private ParserErrorsImpl errors = new ParserErrorsImpl();
private String getAbsolutePath(String path) {
return path.charAt(0) == '/'
? path
: templateDir + path;
}
private String computePath(AbstractPathNode path) {
PathAdapter adapter = new PathAdapter(templateDir, aliases, errors);
path.apply(adapter);
return adapter.getPath();
}
private class AliasAdapter extends AnalysisAdapter {
@Override
public void caseAliasesNode(AliasesNode node) {
for (AliasDefNode defNode : node.getAliass()) {
handleAlias(defNode);
}
}
private void handleAlias(AliasDefNode node) {
if (aliases.containsKey(node.getName())) {
addError("Duplicate alias for " + node.getName(), node.getLocation());
}
else {
aliases.put(node.getName(), computePath(node.getPath()));
}
}
}
private class PreliminaryAdapter extends AnalysisAdapter {
@Override
public void caseEscapeDirectiveNode(EscapeDirectiveNode escape) {
if (defaultEscaping != null) {
addError("a template cannot specify multiple default escapings", escape.getLocation());
}
defaultEscaping = EscapingDirective.get(escape.getEscapeCode());
if (defaultEscaping == null) {
addError("Unknown escaping directive '" + escape.getEscapeCode() + "'", escape
.getLocation());
}
}
@Override
public void caseExtendsNode(ExtendsNode extendsNode) {
if (getTemplateUnit().hasParentPath()) {
addError("a template cannot extend multiple templates", extendsNode.getLocation());
}
String parentPath = getAbsolutePath(computePath(extendsNode.getPath()));
TemplateDescription parentDescription = getTemplateDescription(parentPath, extendsNode
.getLocation());
if (parentDescription != null) {
getTemplateUnit().setParentPath(parentPath);
getTemplateUnit().setParentDescription(parentDescription);
}
}
@Override
public void caseAnnotationNode(AnnotationNode node) {
templateUnit.addAnnotationNode(node);
}
@Override
public void caseParentMarkerNode(ParentMarkerNode node) {
getTemplateUnit().setIsParent();
}
@Override
public void caseDefNode(DefNode node) {
getTemplateUnit().makeDefUnit(node.getName(), node.getLocation());
}
@Override
public void caseMethodNode(MethodNode node) {
getTemplateUnit().makeMethodUnit(node.getName(), node.getLocation(), false);
}
@Override
public void caseAbsMethodNode(AbsMethodNode node) {
getTemplateUnit().makeMethodUnit(node.getName(), node.getLocation(), true);
}
}
private class Adapter extends DepthFirstAnalysisAdapter {
@Override
public void caseImportNode(ImportNode importNode) {
getTemplateUnit().addImport(importNode);
}
@Override
public void caseStaticImportNode(StaticImportNode staticImportNode) {
getTemplateUnit().addStaticImport(staticImportNode);
}
@Override
public void caseImplementNode(ImplementNode node) {
getTemplateUnit().addInterface(node.getName());
}
@Override
public void caseParentArgsNode(ParentArgsNode node) {
if (getCurrentStatementBlock() instanceof TemplateUnit
&& !((TemplateUnit) getCurrentStatementBlock()).hasParentPath()) {
addError(XARGS_DECLARED_WITHOUT_PARENT_ERROR, node.getLocation());
}
else {
super.caseParentArgsNode(node);
}
}
@Override
public void caseParentArgNode(ParentArgNode node) {
((InheritedUnit) getCurrentStatementBlock()).addParentArg(node);
}
@Override
public void caseParentArgWithDefaultNode(ParentArgWithDefaultNode node) {
((InheritedUnit) getCurrentStatementBlock()).addParentArg(node);
}
@Override
public void inFragmentArgsNode(FragmentArgsNode node) {
pushFragmentArg(getCurrentStatementBlock().addFragment(node,
getTemplateUnit().getGenericParams()));
}
@Override
public void outFragmentArgsNode(FragmentArgsNode node) {
popStatementBlock();
}
@Override
public void caseArgNode(ArgNode node) {
getCurrentStatementBlock().addRequiredArg(node);
}
@Override
public void caseOptionalArgNode(OptionalArgNode node) {
getCurrentStatementBlock().addOptionalArg(node);
}
@Override
public void inDefNode(DefNode node) {
pushDefUnit(node.getName());
}
@Override
public void outDefNode(DefNode def) {
popStatementBlock();
}
@Override
public void inMethodNode(MethodNode node) {
pushMethodUnit(node.getName());
}
@Override
public void outMethodNode(MethodNode node) {
popStatementBlock();
}
@Override
public void inAbsMethodNode(AbsMethodNode node) {
if (!getTemplateUnit().isParent()) {
addError("Non-abstract templates cannot have abstract methods", node.getLocation());
}
pushMethodUnit(node.getName());
}
@Override
public void outAbsMethodNode(AbsMethodNode node) {
popStatementBlock();
}
@Override
public void inOverrideNode(OverrideNode node) {
pushOverriddenMethodUnit(node);
}
@Override
public void outOverrideNode(OverrideNode node) {
popStatementBlock();
}
@Override
public void caseGenericsParamNode(GenericsParamNode node) {
if (templateUnit.isParent()) {
addError("<%generics> tag not allowed in abstract templates", node.getLocation());
}
templateUnit.addGenericsParamNode(node);
}
@Override
public void caseSimpleCallNode(SimpleCallNode node) {
addStatement(makeCallStatement(node));
}
@Override
public void caseChildCallNode(ChildCallNode node) {
TemplateUnit unit = getTemplateUnit();
if (!unit.isParent()) {
addError("<& *CHILD &> cannot be called from a template without an <%abstract> tag", node
.getLocation());
}
addStatement(new ChildCallStatement(unit.getInheritanceDepth() + 1));
}
@Override
public void caseClassNode(ClassNode node) {
getTemplateUnit().addClassContent(node);
}
@Override
public void caseTextNode(TextNode node) {
addStatement(new LiteralStatement(node.getText(), node.getLocation(),
templateIdentifier));
}
@Override
public void caseLiteralNode(LiteralNode node) {
addStatement(new LiteralStatement(node.getText(), node.getLocation(),
templateIdentifier));
}
@Override
public void inMultiFragmentCallNode(MultiFragmentCallNode node) {
CallStatement s = makeCallStatement(node);
addStatement(s);
pushCallStatement(s);
}
@Override
public void outMultiFragmentCallNode(MultiFragmentCallNode call) {
popCallStatement();
}
@Override
public void inNamedFragmentNode(NamedFragmentNode node) {
getCurrentCallStatement().addFragmentImpl(
pushFragmentUnitImpl(node.getName(), node.getLocation()), errors);
}
@Override
public void outNamedFragmentNode(NamedFragmentNode node) {
popStatementBlock();
}
@Override
public void inFragmentCallNode(FragmentCallNode node) {
CallStatement s = makeCallStatement(node);
addStatement(s);
s.addFragmentImpl(pushFragmentUnitImpl(FragmentUnit.DEFAULT_FRAGMENT_NAME, node.getLocation()), errors);
}
@Override
public void outFragmentCallNode(FragmentCallNode node) {
popStatementBlock();
}
@Override
public void inWhileNode(WhileNode node) {
pushFlowControlBlock(node.getLocation(), "while (" + node.getCondition() + ")");
}
@Override
public void outWhileNode(WhileNode node) {
popStatementBlock();
}
@Override
public void inForNode(ForNode node) {
pushFlowControlBlock(node.getLocation(), "for (" + node.getLoopSpecifier() + ")");
}
@Override
public void outForNode(ForNode node) {
popStatementBlock();
}
@Override
public void inIfNode(IfNode node) {
pushFlowControlBlock(node.getLocation(), "if (" + node.getCondition() + ")");
}
@Override
public void outIfNode(IfNode node) {
popStatementBlock();
}
@Override
public void inElseIfNode(ElseIfNode node) {
pushFlowControlBlock(node.getLocation(), "else if (" + node.getCondition() + ")");
}
@Override
public void outElseIfNode(ElseIfNode node) {
popStatementBlock();
}
@Override
public void inElseNode(ElseNode node) {
pushFlowControlBlock(node.getLocation(), "else");
}
@Override
public void outElseNode(ElseNode node) {
popStatementBlock();
}
@Override
public void outTopNode(TopNode node) {}
@Override
public void caseJavaNode(JavaNode node) {
addStatement(new RawStatement(node.getJava(), node.getLocation(), templateIdentifier));
}
private class EmitAdapter extends AnalysisAdapter {
EscapingDirective escapeCode = null;
@Override
public void caseEscapeNode(EscapeNode node) {
escapeCode = EscapingDirective.get(node.getEscapeCode());
if (escapeCode == null) {
addError("Unknown escaping directive '" + node.getEscapeCode() + "'", node
.getLocation());
}
}
@Override
public void caseDefaultEscapeNode(DefaultEscapeNode node) {
escapeCode = getDefaultEscaping();
}
public EscapingDirective getEscape(AbstractEscapeNode node) {
node.apply(this);
return escapeCode;
}
}
@Override
public void caseEmitNode(EmitNode node) {
addStatement(new WriteStatement(node.getEmitExpression(), new EmitAdapter()
.getEscape(node.getEscaping()), node.getLocation(), templateIdentifier, emitMode));
}
}
private EscapingDirective defaultEscaping;
private EscapingDirective getDefaultEscaping() {
return defaultEscaping;
}
private CallStatement makeCallStatement(AbstractComponentCallNode node) {
String path = computePath(node.getCallPath());
ParamValues paramValues = makeParamValues(node.getParams());
FragmentUnit fragmentUnit = getCurrentStatementBlock().getFragmentUnitIntf(path);
List genericParams = node.getGenericParams();
if (fragmentUnit != null) {
if (!genericParams.isEmpty()) {
addError("Fragment" + " calls may not have generic params", node.getLocation());
}
return new FargCallStatement(path, paramValues, fragmentUnit, node.getLocation(),
templateIdentifier);
}
else {
DefUnit defUnit = getTemplateUnit().getDefUnit(path);
if (defUnit != null) {
if (!genericParams.isEmpty()) {
addError("def " + defUnit.getName() + " is being called with generic parameters", node
.getLocation());
}
return new DefCallStatement(path, paramValues, defUnit, node.getLocation(),
templateIdentifier);
}
else {
MethodUnit methodUnit = getTemplateUnit().getMethodUnit(path);
if (methodUnit != null) {
if (!genericParams.isEmpty()) {
addError("method" + methodUnit.getName() + " is being called with generic parameters",
node.getLocation());
}
return new MethodCallStatement(path, paramValues, methodUnit, node.getLocation(),
templateIdentifier);
}
else {
getTemplateUnit().addCallPath(getAbsolutePath(path));
return new ComponentCallStatement(getAbsolutePath(path), paramValues, node
.getLocation(), templateIdentifier, genericParams, getTemplateUnit()
.getJamonContextType());
}
}
}
}
private static class ParamsAdapter extends DepthFirstAnalysisAdapter {
public ParamValues getParamValues(AbstractParamsNode node) {
node.apply(this);
if (paramsList != null) {
return new UnnamedParamValues(paramsList, node.getLocation());
}
else {
return new NamedParamValues(paramsMap, node.getLocation());
}
}
@Override
public void inNamedParamsNode(NamedParamsNode node) {
paramsMap = new HashMap();
}
@Override
public void inUnnamedParamsNode(UnnamedParamsNode node) {
paramsList = new LinkedList();
}
@Override
public void caseNamedParamNode(NamedParamNode node) {
paramsMap.put(node.getName().getName(), node.getValue().getValue());
}
@Override
public void caseParamValueNode(ParamValueNode node) {
paramsList.add(node.getValue());
}
private Map paramsMap = null;
private List paramsList = null;
}
private ParamValues makeParamValues(AbstractParamsNode params) {
return new ParamsAdapter().getParamValues(params);
}
private void addStatement(Statement statement) {
getCurrentStatementBlock().addStatement(statement);
}
private TemplateDescription getTemplateDescription(String path, Location location) {
if (children.contains(path)) {
addError("cyclic inheritance or replacement involving " + path, location);
return null;
}
else {
children.add(path);
try {
return describer.getTemplateDescription(path, location, children);
}
catch (ParserErrorImpl e) {
errors.addError(e);
}
catch (ParserErrorsImpl e) {
errors.addErrors(e);
}
catch (IOException e) {
addError(e.getMessage(), location);
}
return null;
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy