All Downloads are FREE. Search and download functionalities are using the official Maven repository.

groovy.text.markup.MarkupBuilderCodeTransformer Maven / Gradle / Ivy

There is a newer version: 3.0.23
Show newest version
/*
 *  Licensed to the Apache Software Foundation (ASF) under one
 *  or more contributor license agreements.  See the NOTICE file
 *  distributed with this work for additional information
 *  regarding copyright ownership.  The ASF licenses this file
 *  to you 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 groovy.text.markup;

import org.codehaus.groovy.ast.ClassCodeExpressionTransformer;
import org.codehaus.groovy.ast.ClassHelper;
import org.codehaus.groovy.ast.ClassNode;
import org.codehaus.groovy.ast.DynamicVariable;
import org.codehaus.groovy.ast.Parameter;
import org.codehaus.groovy.ast.VariableScope;
import org.codehaus.groovy.ast.expr.ArgumentListExpression;
import org.codehaus.groovy.ast.expr.ArrayExpression;
import org.codehaus.groovy.ast.expr.BinaryExpression;
import org.codehaus.groovy.ast.expr.ClosureExpression;
import org.codehaus.groovy.ast.expr.ConstantExpression;
import org.codehaus.groovy.ast.expr.DeclarationExpression;
import org.codehaus.groovy.ast.expr.EmptyExpression;
import org.codehaus.groovy.ast.expr.Expression;
import org.codehaus.groovy.ast.expr.MapEntryExpression;
import org.codehaus.groovy.ast.expr.MapExpression;
import org.codehaus.groovy.ast.expr.MethodCallExpression;
import org.codehaus.groovy.ast.expr.TupleExpression;
import org.codehaus.groovy.ast.expr.VariableExpression;
import org.codehaus.groovy.ast.stmt.BlockStatement;
import org.codehaus.groovy.ast.stmt.ExpressionStatement;
import org.codehaus.groovy.ast.stmt.Statement;
import org.codehaus.groovy.control.SourceUnit;
import org.codehaus.groovy.syntax.Types;

import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 

This AST transformer is responsible for modifying a source template into something which can be compiled as a * {@link groovy.text.markup.BaseTemplate} subclass.

*

*

It performs the following operations:

*

*

  • replace dynamic variables with getModel().get(dynamicVariable) calls
  • optionally wrap * getModel().get(...) calls into tryEscape calls for automatic escaping
  • replace include * XXX:'...' calls with the appropriate includeXXXX method calls
  • replace ':tagName'() calls * into methodMissing('tagName', ...) calls
*/ class MarkupBuilderCodeTransformer extends ClassCodeExpressionTransformer { static final String TARGET_VARIABLE = "target.variable"; private final SourceUnit unit; private final boolean autoEscape; private final ClassNode classNode; public MarkupBuilderCodeTransformer(final SourceUnit unit, final ClassNode classNode, final boolean autoEscape) { this.unit = unit; this.autoEscape = autoEscape; this.classNode = classNode; } @Override protected SourceUnit getSourceUnit() { return unit; } @Override public Expression transform(final Expression exp) { if (exp instanceof BinaryExpression) { return transformBinaryExpression((BinaryExpression) exp); } if (exp instanceof MethodCallExpression) { return transformMethodCall((MethodCallExpression) exp); } if (exp instanceof ClosureExpression) { ClosureExpression cl = (ClosureExpression) exp; cl.getCode().visit(this); return cl; } if (exp instanceof VariableExpression) { VariableExpression var = (VariableExpression) exp; if (var.getAccessedVariable() instanceof DynamicVariable) { MethodCallExpression callGetModel = new MethodCallExpression( new VariableExpression("this"), "getModel", ArgumentListExpression.EMPTY_ARGUMENTS ); callGetModel.setImplicitThis(true); callGetModel.setSourcePosition(exp); String varName = var.getName(); if ("model".equals(varName) || "unescaped".equals(varName)) { return callGetModel; } MethodCallExpression mce = new MethodCallExpression( callGetModel, "get", new ArgumentListExpression(new ConstantExpression(varName)) ); mce.setSourcePosition(exp); mce.setImplicitThis(false); MethodCallExpression yield = new MethodCallExpression( new VariableExpression("this"), "tryEscape", new ArgumentListExpression(mce) ); yield.setImplicitThis(true); yield.setSourcePosition(exp); yield.putNodeMetaData(TARGET_VARIABLE, varName); return autoEscape?yield:mce; } } return super.transform(exp); } private Expression transformBinaryExpression(final BinaryExpression bin) { Expression left = bin.getLeftExpression(); Expression right = bin.getRightExpression(); boolean assignment = bin.getOperation().getType() == Types.ASSIGN; if (assignment && left instanceof VariableExpression) { VariableExpression var = (VariableExpression) left; if (var.getAccessedVariable() instanceof DynamicVariable) { String varName = var.getName(); if (!"modelTypes".equals(varName)) { MethodCallExpression callGetModel = new MethodCallExpression( new VariableExpression("this"), "getModel", ArgumentListExpression.EMPTY_ARGUMENTS ); callGetModel.setImplicitThis(true); callGetModel.setSourcePosition(left); MethodCallExpression mce = new MethodCallExpression( callGetModel, "put", new ArgumentListExpression(new ConstantExpression(varName), right) ); mce.setSourcePosition(left); mce.setImplicitThis(false); return transform(mce); } } } if (assignment && left instanceof VariableExpression && right instanceof ClosureExpression) { VariableExpression var = (VariableExpression) left; if ("modelTypes".equals(var.getName())) { // template declaring its expected types from model directly // modelTypes = { // List items // ... // } Map modelTypes = extractModelTypesFromClosureExpression((ClosureExpression)right); Expression result = EmptyExpression.INSTANCE; classNode.putNodeMetaData(MarkupTemplateEngine.MODELTYPES_ASTKEY, modelTypes); return result; } } return super.transform(bin); } private Map extractModelTypesFromClosureExpression(final ClosureExpression expression) { Map model = new HashMap(); extractModelTypesFromStatement(expression.getCode(), model); return model; } private void extractModelTypesFromStatement(final Statement code, final Map model) { if (code instanceof BlockStatement) { BlockStatement block = (BlockStatement) code; for (Statement statement : block.getStatements()) { extractModelTypesFromStatement(statement, model); } } else if (code instanceof ExpressionStatement) { Expression expression = ((ExpressionStatement) code).getExpression(); if (expression instanceof DeclarationExpression) { VariableExpression var = ((DeclarationExpression) expression).getVariableExpression(); model.put(var.getName(), var.getOriginType()); } } } private Expression transformMethodCall(final MethodCallExpression exp) { String name = exp.getMethodAsString(); if (exp.isImplicitThis() && "include".equals(name)) { return tryTransformInclude(exp); } else if (exp.isImplicitThis() && name.startsWith(":")) { List args; if (exp.getArguments() instanceof ArgumentListExpression) { args = ((ArgumentListExpression) exp.getArguments()).getExpressions(); } else { args = Collections.singletonList(exp.getArguments()); } Expression newArguments = transform(new ArgumentListExpression(new ConstantExpression(name.substring(1)), new ArrayExpression(ClassHelper.OBJECT_TYPE, args))); MethodCallExpression call = new MethodCallExpression( new VariableExpression("this"), "methodMissing", newArguments ); call.setImplicitThis(true); call.setSafe(exp.isSafe()); call.setSpreadSafe(exp.isSpreadSafe()); call.setSourcePosition(exp); return call; } else if (name!=null && name.startsWith("$")) { MethodCallExpression reformatted = new MethodCallExpression( exp.getObjectExpression(), name.substring(1), exp.getArguments() ); reformatted.setImplicitThis(exp.isImplicitThis()); reformatted.setSafe(exp.isSafe()); reformatted.setSpreadSafe(exp.isSpreadSafe()); reformatted.setSourcePosition(exp); // wrap in a stringOf { ... } closure call ClosureExpression clos = new ClosureExpression(Parameter.EMPTY_ARRAY, new ExpressionStatement(reformatted)); clos.setVariableScope(new VariableScope()); MethodCallExpression stringOf = new MethodCallExpression(new VariableExpression("this"), "stringOf", clos); stringOf.setImplicitThis(true); stringOf.setSourcePosition(reformatted); return stringOf; } return super.transform(exp); } private Expression tryTransformInclude(final MethodCallExpression exp) { Expression arguments = exp.getArguments(); if (arguments instanceof TupleExpression) { List expressions = ((TupleExpression) arguments).getExpressions(); if (expressions.size() == 1 && expressions.get(0) instanceof MapExpression) { MapExpression map = (MapExpression) expressions.get(0); List entries = map.getMapEntryExpressions(); if (entries.size() == 1) { MapEntryExpression mapEntry = entries.get(0); Expression keyExpression = mapEntry.getKeyExpression(); try { IncludeType includeType = IncludeType.valueOf(keyExpression.getText().toLowerCase()); MethodCallExpression call = new MethodCallExpression( exp.getObjectExpression(), includeType.getMethodName(), new ArgumentListExpression( mapEntry.getValueExpression() ) ); call.setImplicitThis(true); call.setSafe(exp.isSafe()); call.setSpreadSafe(exp.isSpreadSafe()); call.setSourcePosition(exp); return call; } catch (IllegalArgumentException e) { // not a valid import type, do not modify the code } } } } return super.transform(exp); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy