Refactoring DefaultGroovyTranspiler.
This commit is contained in:
parent
449c83975f
commit
3f45609236
@ -6,17 +6,25 @@ import groowt.view.component.compiler.source.ComponentTemplateSource;
|
||||
public abstract class AbstractComponentTemplateCompileUnit implements
|
||||
ComponentTemplateCompileUnit {
|
||||
|
||||
private final String descriptiveName;
|
||||
private final Class<? extends ViewComponent> forClass;
|
||||
private final ComponentTemplateSource source;
|
||||
|
||||
public AbstractComponentTemplateCompileUnit(
|
||||
String descriptiveName,
|
||||
Class<? extends ViewComponent> forClass,
|
||||
ComponentTemplateSource source
|
||||
) {
|
||||
this.descriptiveName = descriptiveName;
|
||||
this.forClass = forClass;
|
||||
this.source = source;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescriptiveName() {
|
||||
return this.descriptiveName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<? extends ViewComponent> getForClass() {
|
||||
return this.forClass;
|
||||
|
@ -22,7 +22,7 @@ public class ComponentTemplateCompileException extends Exception {
|
||||
|
||||
@Override
|
||||
public String getMessage() {
|
||||
final var sb = new StringBuilder("Error in ").append(compileUnit.getSource().getDescription());
|
||||
final var sb = new StringBuilder("Error in ").append(compileUnit.getSource().getDescriptiveName());
|
||||
final @Nullable String position = this.formatPosition();
|
||||
if (position != null) {
|
||||
sb.append(" at ").append(position);
|
||||
|
@ -5,6 +5,7 @@ import groowt.view.component.compiler.source.ComponentTemplateSource;
|
||||
|
||||
public interface ComponentTemplateCompileUnit {
|
||||
|
||||
String getDescriptiveName();
|
||||
Class<? extends ViewComponent> getForClass();
|
||||
String getDefaultPackageName();
|
||||
ComponentTemplateSource getSource();
|
||||
|
@ -46,7 +46,7 @@ public interface ComponentTemplateSource {
|
||||
}
|
||||
|
||||
Reader toReader() throws Exception;
|
||||
String getDescription();
|
||||
String getDescriptiveName();
|
||||
boolean canReopen();
|
||||
List<String> getLines();
|
||||
|
||||
|
@ -21,7 +21,7 @@ public class FileSource implements ComponentTemplateSource {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
public String getDescriptiveName() {
|
||||
return this.templateFile.toString();
|
||||
}
|
||||
|
||||
|
@ -23,7 +23,7 @@ public class InputStreamSource implements ComponentTemplateSource {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
public String getDescriptiveName() {
|
||||
return this.description != null ? this.description : "<anonymous InputStream source>";
|
||||
}
|
||||
|
||||
|
@ -21,7 +21,7 @@ public class ReaderSource implements ComponentTemplateSource {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
public String getDescriptiveName() {
|
||||
return this.description != null ? this.description : "<anonymous Reader source>";
|
||||
}
|
||||
|
||||
|
@ -22,7 +22,7 @@ public class StringSource implements ComponentTemplateSource {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
public String getDescriptiveName() {
|
||||
return this.name != null ? this.name : "<anonymous string source>";
|
||||
}
|
||||
|
||||
|
@ -21,7 +21,7 @@ public class URISource implements ComponentTemplateSource {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
public String getDescriptiveName() {
|
||||
return this.templateURI.toString();
|
||||
}
|
||||
|
||||
|
@ -25,7 +25,7 @@ public class URLSource implements ComponentTemplateSource {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
public String getDescriptiveName() {
|
||||
return this.url.toString();
|
||||
}
|
||||
|
||||
|
@ -65,6 +65,7 @@ public class DelegatingWebViewComponentTemplateParserPlugin implements ParserPlu
|
||||
final String sourceUnitFileName = sourceUnitFullName.substring(lastSlashIndex + 1);
|
||||
if (sourceUnitFileName.endsWith(".wvc")) {
|
||||
final var compileUnit = new DefaultWebViewComponentTemplateCompileUnit(
|
||||
sourceUnitFileName,
|
||||
AnonymousWebViewComponent.class,
|
||||
ComponentTemplateSource.of(sourceUnit.getSource().getURI()),
|
||||
"" // default package
|
||||
@ -79,13 +80,13 @@ public class DelegatingWebViewComponentTemplateParserPlugin implements ParserPlu
|
||||
}
|
||||
|
||||
final var groovyTranspiler = new DefaultGroovyTranspiler();
|
||||
final String nameWithoutExtension = sourceUnitFileName.substring(0, sourceUnitFileName.length() - 4);
|
||||
final String teplateClassSimpleName = sourceUnitFileName.substring(0, sourceUnitFileName.length() - 4);
|
||||
try {
|
||||
final SourceUnit transpiledSourceUnit = groovyTranspiler.transpile(
|
||||
new DefaultComponentTemplateCompilerConfiguration(),
|
||||
compileUnit,
|
||||
cuNode,
|
||||
nameWithoutExtension
|
||||
teplateClassSimpleName
|
||||
);
|
||||
return transpiledSourceUnit.getAST();
|
||||
} catch (ComponentTemplateCompileException e) {
|
||||
|
@ -24,6 +24,7 @@ import java.util.List;
|
||||
import java.util.ListIterator;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Deprecated
|
||||
public class DefaultGStringTranspiler implements GStringTranspiler {
|
||||
|
||||
private final PositionSetter positionSetter;
|
||||
|
@ -2,6 +2,7 @@ package groowt.view.component.web.transpile;
|
||||
|
||||
import groovy.transform.Field;
|
||||
import groowt.view.component.compiler.ComponentTemplateCompileException;
|
||||
import groowt.view.component.compiler.ComponentTemplateCompileUnit;
|
||||
import groowt.view.component.compiler.ComponentTemplateCompilerConfiguration;
|
||||
import groowt.view.component.web.WebViewComponentBugError;
|
||||
import groowt.view.component.web.ast.node.BodyNode;
|
||||
@ -10,7 +11,7 @@ import groowt.view.component.web.ast.node.PreambleNode;
|
||||
import groowt.view.component.web.compiler.MultipleWebViewComponentCompileErrorsException;
|
||||
import groowt.view.component.web.compiler.WebViewComponentTemplateCompileException;
|
||||
import groowt.view.component.web.compiler.WebViewComponentTemplateCompileUnit;
|
||||
import groowt.view.component.web.runtime.DefaultWebViewRenderContext;
|
||||
import groowt.view.component.web.transpile.BodyTranspiler.AddOrAppendCallback;
|
||||
import groowt.view.component.web.transpile.groovy.GroovyUtil;
|
||||
import groowt.view.component.web.transpile.resolve.ClassLoaderComponentClassNodeResolver;
|
||||
import org.codehaus.groovy.ast.*;
|
||||
@ -19,9 +20,8 @@ import org.codehaus.groovy.ast.stmt.BlockStatement;
|
||||
import org.codehaus.groovy.ast.stmt.ExpressionStatement;
|
||||
import org.codehaus.groovy.ast.stmt.ReturnStatement;
|
||||
import org.codehaus.groovy.ast.stmt.Statement;
|
||||
import org.codehaus.groovy.control.CompilationUnit;
|
||||
import org.codehaus.groovy.control.CompilerConfiguration;
|
||||
import org.codehaus.groovy.control.ErrorCollector;
|
||||
import org.codehaus.groovy.control.SourceUnit;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@ -30,29 +30,58 @@ import java.util.List;
|
||||
import static groowt.view.component.web.transpile.TranspilerUtil.*;
|
||||
import static org.objectweb.asm.Opcodes.ACC_PUBLIC;
|
||||
|
||||
/**
|
||||
* Takes a Groovy {@link CompilationUnit} in the constructor and adds a {@link SourceUnit} representing the target
|
||||
* Groovy code for the template represented by the AST passed to {@link #transpile}.
|
||||
* <p>
|
||||
* Note that while the terminology is similar, a Groovy {@link CompilationUnit} is distinct from our own
|
||||
* {@link CompilationUnitNode}.
|
||||
*/
|
||||
public class DefaultGroovyTranspiler implements GroovyTranspiler {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(DefaultGroovyTranspiler.class);
|
||||
|
||||
private static final ClassNode FIELD_ANNOTATION = ClassHelper.make(Field.class);
|
||||
private static final ClassNode RENDER_CONTEXT_IMPLEMENTATION =
|
||||
ClassHelper.make(DefaultWebViewRenderContext.class);
|
||||
|
||||
protected TranspilerConfiguration getConfiguration(
|
||||
WebViewComponentTemplateCompileUnit compileUnit,
|
||||
ModuleNode moduleNode,
|
||||
ClassLoader classLoader
|
||||
) {
|
||||
return new DefaultTranspilerConfiguration(new ClassLoaderComponentClassNodeResolver(
|
||||
compileUnit, moduleNode, classLoader
|
||||
return new DefaultTranspilerConfiguration(new ClassLoaderComponentClassNodeResolver(compileUnit, classLoader));
|
||||
}
|
||||
|
||||
protected void addAutomaticImports(WebViewComponentModuleNode moduleNode, TranspilerConfiguration configuration) {
|
||||
configuration.getImports().forEach(moduleNode::addImport);
|
||||
configuration.getStaticImports().forEach(staticImport -> moduleNode.addStaticImport(
|
||||
staticImport.getV1(), staticImport.getV2(), staticImport.getV3()
|
||||
));
|
||||
configuration.getStarImports().forEach(moduleNode::addStarImport);
|
||||
configuration.getStaticStarImports().forEach(moduleNode::addStaticStarImport);
|
||||
}
|
||||
|
||||
protected WebViewComponentModuleNode initModuleNode(
|
||||
ComponentTemplateCompileUnit compileUnit,
|
||||
WebViewComponentSourceUnit sourceUnit,
|
||||
TranspilerConfiguration configuration
|
||||
) {
|
||||
final var moduleNode = new WebViewComponentModuleNode(sourceUnit);
|
||||
sourceUnit.setModuleNode(moduleNode);
|
||||
|
||||
final String defaultPackageName = compileUnit.getDefaultPackageName();
|
||||
if (!defaultPackageName.trim().isEmpty()) {
|
||||
moduleNode.setPackageName(defaultPackageName);
|
||||
}
|
||||
|
||||
this.addAutomaticImports(moduleNode, configuration);
|
||||
return moduleNode;
|
||||
}
|
||||
|
||||
protected ClassNode initMainClassNode(
|
||||
ComponentTemplateCompileUnit compileUnit,
|
||||
String templateClassName,
|
||||
WebViewComponentModuleNode moduleNode
|
||||
) {
|
||||
final ClassNode mainClassNode = new ClassNode(
|
||||
compileUnit.getDefaultPackageName() + templateClassName,
|
||||
ACC_PUBLIC,
|
||||
ClassHelper.OBJECT_TYPE
|
||||
);
|
||||
mainClassNode.addInterface(TranspilerUtil.COMPONENT_TEMPLATE);
|
||||
moduleNode.addClass(mainClassNode);
|
||||
return mainClassNode;
|
||||
}
|
||||
|
||||
protected void checkPreambleClasses(String templateName, List<ClassNode> classNodes) {
|
||||
@ -72,17 +101,27 @@ public class DefaultGroovyTranspiler implements GroovyTranspiler {
|
||||
String templateClassName,
|
||||
PreambleNode preambleNode,
|
||||
ClassNode mainClassNode,
|
||||
WebViewComponentModuleNode moduleNode
|
||||
WebViewComponentModuleNode moduleNode,
|
||||
PositionSetter positionSetter
|
||||
) {
|
||||
final GroovyUtil.ConvertResult convertResult = GroovyUtil.convert(
|
||||
preambleNode.getGroovyCode().getAsValidGroovyCode()
|
||||
);
|
||||
final ModuleNode convertModuleNode = convertResult.moduleNode();
|
||||
|
||||
WebViewComponentModuleNode.copyTo(convertResult.moduleNode(), moduleNode);
|
||||
final PositionVisitor positionVisitor = new PositionVisitor(positionSetter, preambleNode);
|
||||
|
||||
convertModuleNode.getImports().forEach(moduleNode::addImport);
|
||||
convertModuleNode.getStarImports().forEach(moduleNode::addStarImport);
|
||||
convertModuleNode.getStaticImports().forEach(moduleNode::addStaticImport);
|
||||
convertModuleNode.getStaticStarImports().forEach(moduleNode::addStaticStarImport);
|
||||
positionVisitor.visitImports(moduleNode);
|
||||
|
||||
// if user supplied a package, use it
|
||||
if (convertResult.moduleNode().hasPackage()) {
|
||||
moduleNode.setPackage(convertResult.moduleNode().getPackage());
|
||||
mainClassNode.setName(moduleNode.getPackageName() + templateClassName);
|
||||
positionVisitor.visitPackage(moduleNode.getPackage());
|
||||
}
|
||||
|
||||
final BlockStatement preambleBlock = convertResult.blockStatement();
|
||||
@ -100,6 +139,7 @@ public class DefaultGroovyTranspiler implements GroovyTranspiler {
|
||||
)
|
||||
.toList();
|
||||
if (declarationsWithField.size() != preambleStatements.size()) {
|
||||
// TODO: figure out why we have extraneous statements sometimes when it seems otherwise not
|
||||
logger.warn(
|
||||
"{} contains script statements which are not supported. " +
|
||||
"Currently, only classes, methods, and field declarations " +
|
||||
@ -110,13 +150,19 @@ public class DefaultGroovyTranspiler implements GroovyTranspiler {
|
||||
}
|
||||
declarationsWithField.forEach(declaration -> {
|
||||
declaration.setDeclaringClass(mainClassNode);
|
||||
positionVisitor.visitDeclarationExpression(declaration);
|
||||
});
|
||||
}
|
||||
|
||||
// move methods from script class
|
||||
final ClassNode scriptClass = convertResult.scriptClass();
|
||||
if (scriptClass != null) {
|
||||
scriptClass.getMethods().forEach(mainClassNode::addMethod);
|
||||
scriptClass.getMethods().stream()
|
||||
.filter(method -> !(method.getName().equals("main") || method.getName().equals("run")))
|
||||
.forEach(method -> {
|
||||
mainClassNode.addMethod(method);
|
||||
positionVisitor.visitMethod(method);
|
||||
});
|
||||
}
|
||||
|
||||
// handle classes
|
||||
@ -124,62 +170,127 @@ public class DefaultGroovyTranspiler implements GroovyTranspiler {
|
||||
this.checkPreambleClasses(templateClassName, classNodes);
|
||||
classNodes.stream()
|
||||
.filter(classNode -> classNode != convertResult.scriptClass())
|
||||
.forEach(moduleNode::addClass);
|
||||
.forEach(classNode -> {
|
||||
moduleNode.addClass(classNode);
|
||||
positionVisitor.visitClass(classNode);
|
||||
});
|
||||
}
|
||||
|
||||
protected ExpressionStatement constructRenderContext(
|
||||
ClassNode renderContextImplementation,
|
||||
Variable componentContextParam,
|
||||
Variable writerParam,
|
||||
VariableExpression renderContextVariableExpr
|
||||
) {
|
||||
final ConstructorCallExpression renderContextConstructor = new ConstructorCallExpression(
|
||||
renderContextImplementation,
|
||||
new ArgumentListExpression(
|
||||
new VariableExpression(componentContextParam),
|
||||
new VariableExpression(writerParam)
|
||||
)
|
||||
);
|
||||
final BinaryExpression renderContextAssignExpr = new DeclarationExpression(
|
||||
renderContextVariableExpr,
|
||||
getAssignToken(),
|
||||
renderContextConstructor
|
||||
);
|
||||
return new ExpressionStatement(renderContextAssignExpr);
|
||||
}
|
||||
|
||||
protected ExpressionStatement assignComponentContextRenderContext(
|
||||
Parameter componentContextParam,
|
||||
VariableExpression renderContextVariableExpr
|
||||
) {
|
||||
final BinaryExpression componentContextRenderContextAssign = new BinaryExpression(
|
||||
new PropertyExpression(new VariableExpression(componentContextParam), "renderContext"),
|
||||
getAssignToken(),
|
||||
renderContextVariableExpr
|
||||
);
|
||||
return new ExpressionStatement(componentContextRenderContextAssign);
|
||||
}
|
||||
|
||||
protected ExpressionStatement assignWriterRenderContext(
|
||||
Parameter writerParam,
|
||||
VariableExpression renderContextVariableExpr
|
||||
) {
|
||||
final BinaryExpression writerRenderContextAssign = new BinaryExpression(
|
||||
new PropertyExpression(new VariableExpression(writerParam), "renderContext"),
|
||||
getAssignToken(),
|
||||
renderContextVariableExpr
|
||||
);
|
||||
return new ExpressionStatement(writerRenderContextAssign);
|
||||
}
|
||||
|
||||
protected ExpressionStatement assignWriterComponentContext(Parameter writerParam, Parameter componentContextParam) {
|
||||
final BinaryExpression writerComponentContextAssign = new BinaryExpression(
|
||||
new PropertyExpression(new VariableExpression(writerParam), "componentContext"),
|
||||
getAssignToken(),
|
||||
new VariableExpression(componentContextParam)
|
||||
);
|
||||
return new ExpressionStatement(writerComponentContextAssign);
|
||||
}
|
||||
|
||||
protected Statement handleBody(
|
||||
BodyNode bodyNode,
|
||||
TranspilerConfiguration transpilerConfiguration,
|
||||
TranspilerState state
|
||||
) {
|
||||
final var appendOrAddStatementFactory = transpilerConfiguration.getAppendOrAddStatementFactory();
|
||||
final AddOrAppendCallback callback = (source, expr) -> appendOrAddStatementFactory.addOrAppend(
|
||||
source,
|
||||
state,
|
||||
action -> {
|
||||
if (action == AppendOrAddStatementFactory.Action.ADD) {
|
||||
throw new WebViewComponentBugError(new IllegalStateException(
|
||||
"Should not be adding from document root, only appending!"
|
||||
));
|
||||
}
|
||||
return expr;
|
||||
}
|
||||
);
|
||||
return transpilerConfiguration.getBodyTranspiler().transpileBody(bodyNode, callback, state);
|
||||
}
|
||||
|
||||
// Cases:
|
||||
// - no preamble -> create our own class
|
||||
// - some preamble, but no script -> create our own class but use imports/packageName from preamble
|
||||
// - preamble with script -> use the script class from the converted preamble,
|
||||
// and don't forget to call run in our render method
|
||||
@Override
|
||||
public WebViewComponentSourceUnit transpile(
|
||||
ComponentTemplateCompilerConfiguration compilerConfiguration,
|
||||
WebViewComponentTemplateCompileUnit compileUnit,
|
||||
CompilationUnitNode compilationUnitNode,
|
||||
String templateClassName
|
||||
String templateClassSimpleName
|
||||
) throws ComponentTemplateCompileException {
|
||||
final var groovyCompilerConfiguration = compilerConfiguration.getGroovyCompilerConfiguration();
|
||||
final var sourceUnit = new WebViewComponentSourceUnit(
|
||||
templateClassName,
|
||||
// transpilerConfiguration and positionSetter
|
||||
final var transpilerConfiguration = this.getConfiguration(
|
||||
compileUnit, compileUnit.getGroovyCompilationUnit().getClassLoader()
|
||||
);
|
||||
final PositionSetter positionSetter = transpilerConfiguration.getPositionSetter();
|
||||
|
||||
// prepare sourceUnit
|
||||
final CompilerConfiguration groovyCompilerConfiguration =
|
||||
compilerConfiguration.getGroovyCompilerConfiguration();
|
||||
final WebViewComponentSourceUnit sourceUnit = new WebViewComponentSourceUnit(
|
||||
compileUnit.getDescriptiveName(),
|
||||
compileUnit.getGroovyReaderSource(),
|
||||
groovyCompilerConfiguration,
|
||||
compilerConfiguration.getGroovyClassLoader(),
|
||||
new ErrorCollector(groovyCompilerConfiguration)
|
||||
);
|
||||
|
||||
final var moduleNode = new WebViewComponentModuleNode(sourceUnit);
|
||||
sourceUnit.setModuleNode(moduleNode);
|
||||
|
||||
final String defaultPackageName = compileUnit.getDefaultPackageName();
|
||||
if (!defaultPackageName.trim().isEmpty()) {
|
||||
moduleNode.setPackageName(defaultPackageName);
|
||||
}
|
||||
|
||||
moduleNode.addStarImport(GROOWT_VIEW_COMPONENT_WEB + ".lib");
|
||||
moduleNode.addImport(COMPONENT_TEMPLATE.getNameWithoutPackage(), COMPONENT_TEMPLATE);
|
||||
moduleNode.addImport(COMPONENT_CONTEXT_TYPE.getNameWithoutPackage(), COMPONENT_CONTEXT_TYPE);
|
||||
moduleNode.addStarImport("groowt.view.component.runtime");
|
||||
moduleNode.addStarImport(GROOWT_VIEW_COMPONENT_WEB + ".runtime");
|
||||
|
||||
final ClassNode mainClassNode = new ClassNode(
|
||||
compileUnit.getDefaultPackageName() + templateClassName,
|
||||
ACC_PUBLIC,
|
||||
ClassHelper.OBJECT_TYPE
|
||||
// prepare moduleNode
|
||||
final WebViewComponentModuleNode moduleNode = this.initModuleNode(
|
||||
compileUnit, sourceUnit, transpilerConfiguration
|
||||
);
|
||||
mainClassNode.setScript(true);
|
||||
mainClassNode.addInterface(TranspilerUtil.COMPONENT_TEMPLATE);
|
||||
|
||||
moduleNode.addClass(mainClassNode);
|
||||
// prepare mainClassNode
|
||||
final ClassNode mainClassNode = this.initMainClassNode(compileUnit, templateClassSimpleName, moduleNode);
|
||||
|
||||
// preamble
|
||||
// handle preamble
|
||||
final PreambleNode preambleNode = compilationUnitNode.getPreambleNode();
|
||||
if (preambleNode != null) {
|
||||
this.handlePreamble(templateClassName, preambleNode, mainClassNode, moduleNode);
|
||||
this.handlePreamble(templateClassSimpleName, preambleNode, mainClassNode, moduleNode, positionSetter);
|
||||
}
|
||||
|
||||
// getRenderer
|
||||
// params
|
||||
// getRenderer method and render closure
|
||||
// first, getRenderer params
|
||||
final Parameter componentContextParam = new Parameter(COMPONENT_CONTEXT_TYPE, COMPONENT_CONTEXT_NAME);
|
||||
final Parameter writerParam = new Parameter(COMPONENT_WRITER_TYPE, COMPONENT_WRITER_NAME);
|
||||
final VariableExpression renderContextVariable = new VariableExpression(
|
||||
@ -187,98 +298,53 @@ public class DefaultGroovyTranspiler implements GroovyTranspiler {
|
||||
WEB_VIEW_COMPONENT_RENDER_CONTEXT_TYPE
|
||||
);
|
||||
|
||||
// closure body
|
||||
// returned closure body
|
||||
final BlockStatement renderBlock = new BlockStatement();
|
||||
|
||||
// init renderContext, componentContext, and writer properties
|
||||
renderBlock.addStatement(this.constructRenderContext(
|
||||
transpilerConfiguration.getRenderContextImplementation(),
|
||||
componentContextParam,
|
||||
writerParam,
|
||||
renderContextVariable
|
||||
));
|
||||
renderBlock.addStatement(this.assignComponentContextRenderContext(
|
||||
componentContextParam, renderContextVariable
|
||||
));
|
||||
renderBlock.addStatement(this.assignWriterRenderContext(writerParam, renderContextVariable));
|
||||
renderBlock.addStatement(this.assignWriterComponentContext(writerParam, componentContextParam));
|
||||
|
||||
// init transpiler state
|
||||
final TranspilerState state = TranspilerState.withRootScope(
|
||||
componentContextParam,
|
||||
writerParam,
|
||||
renderContextVariable
|
||||
);
|
||||
renderBlock.setVariableScope(state.getCurrentScope());
|
||||
renderBlock.setVariableScope(state.getCurrentScope()); // root scope
|
||||
|
||||
// init: construct RenderContext
|
||||
final ConstructorCallExpression renderContextConstructor = new ConstructorCallExpression(
|
||||
RENDER_CONTEXT_IMPLEMENTATION,
|
||||
new ArgumentListExpression(
|
||||
new VariableExpression(componentContextParam), // component context
|
||||
new VariableExpression(writerParam)
|
||||
)
|
||||
);
|
||||
final BinaryExpression renderContextAssignExpr = new DeclarationExpression(
|
||||
renderContextVariable,
|
||||
getAssignToken(),
|
||||
renderContextConstructor
|
||||
);
|
||||
renderBlock.addStatement(new ExpressionStatement(renderContextAssignExpr));
|
||||
|
||||
// init: componentContext.renderContext = renderContext
|
||||
final BinaryExpression componentContextRenderContextAssign = new BinaryExpression(
|
||||
new PropertyExpression(new VariableExpression(componentContextParam), "renderContext"),
|
||||
getAssignToken(),
|
||||
renderContextVariable
|
||||
);
|
||||
renderBlock.addStatement(new ExpressionStatement(componentContextRenderContextAssign));
|
||||
|
||||
// init: writer.renderContext = renderContext
|
||||
final BinaryExpression writerRenderContextAssign = new BinaryExpression(
|
||||
new PropertyExpression(new VariableExpression(writerParam), "renderContext"),
|
||||
getAssignToken(),
|
||||
renderContextVariable
|
||||
);
|
||||
renderBlock.addStatement(new ExpressionStatement(writerRenderContextAssign));
|
||||
|
||||
// init: writer.componentContext = componentContext
|
||||
final BinaryExpression writerComponentContextAssign = new BinaryExpression(
|
||||
new PropertyExpression(new VariableExpression(writerParam), "componentContext"),
|
||||
getAssignToken(),
|
||||
new VariableExpression(componentContextParam)
|
||||
);
|
||||
renderBlock.addStatement(new ExpressionStatement(writerComponentContextAssign));
|
||||
|
||||
// actual rendering of body
|
||||
final var configuration = this.getConfiguration(
|
||||
compileUnit,
|
||||
moduleNode,
|
||||
compilerConfiguration.getGroovyClassLoader()
|
||||
);
|
||||
// body
|
||||
final BodyNode bodyNode = compilationUnitNode.getBodyNode();
|
||||
if (bodyNode != null) {
|
||||
final var appendOrAddStatementFactory = configuration.getAppendOrAddStatementFactory();
|
||||
renderBlock.addStatement(
|
||||
configuration.getBodyTranspiler()
|
||||
.transpileBody(
|
||||
compilationUnitNode.getBodyNode(),
|
||||
(source, expr) -> appendOrAddStatementFactory.addOrAppend(
|
||||
source,
|
||||
state,
|
||||
action -> {
|
||||
if (action == AppendOrAddStatementFactory.Action.ADD) {
|
||||
throw new WebViewComponentBugError(new IllegalStateException(
|
||||
"Should not be adding from document root!"
|
||||
));
|
||||
}
|
||||
return expr;
|
||||
}
|
||||
),
|
||||
state
|
||||
)
|
||||
);
|
||||
renderBlock.addStatement(this.handleBody(bodyNode, transpilerConfiguration, state));
|
||||
}
|
||||
|
||||
// return null from render closure
|
||||
renderBlock.addStatement(new ReturnStatement(ConstantExpression.NULL));
|
||||
|
||||
// make the closure
|
||||
final ClosureExpression renderer = new ClosureExpression(
|
||||
new Parameter[] { componentContextParam, writerParam },
|
||||
renderBlock
|
||||
);
|
||||
|
||||
// getRenderer()
|
||||
// getRenderer() return statement
|
||||
final Statement returnRendererStmt = new ReturnStatement(renderer);
|
||||
|
||||
// getRenderer() return type is Closure<Void>
|
||||
final var voidClosure = ClassHelper.CLOSURE_TYPE.getPlainNodeReference();
|
||||
voidClosure.setGenericsTypes(new GenericsType[] { new GenericsType(ClassHelper.void_WRAPPER_TYPE) });
|
||||
|
||||
// getRenderer method
|
||||
final var getRenderer = new MethodNode(
|
||||
GET_RENDERER,
|
||||
ACC_PUBLIC,
|
||||
@ -289,6 +355,7 @@ public class DefaultGroovyTranspiler implements GroovyTranspiler {
|
||||
);
|
||||
mainClassNode.addMethod(getRenderer);
|
||||
|
||||
// check for errors
|
||||
if (state.hasErrors()) {
|
||||
final List<ComponentTemplateCompileException> errors = state.getErrors();
|
||||
if (errors.size() == 1) {
|
||||
@ -298,6 +365,7 @@ public class DefaultGroovyTranspiler implements GroovyTranspiler {
|
||||
}
|
||||
}
|
||||
|
||||
// return the sourceUnit for later processing
|
||||
return sourceUnit;
|
||||
}
|
||||
|
||||
|
@ -1,18 +1,26 @@
|
||||
package groowt.view.component.web.transpile;
|
||||
|
||||
import groovy.lang.Tuple3;
|
||||
import groowt.util.fp.provider.DefaultProvider;
|
||||
import groowt.view.component.web.transpile.resolve.ComponentClassNodeResolver;
|
||||
import org.codehaus.groovy.ast.ClassNode;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import static groowt.view.component.web.transpile.TranspilerUtil.*;
|
||||
|
||||
public class DefaultTranspilerConfiguration implements TranspilerConfiguration {
|
||||
|
||||
private final PositionSetter positionSetter;
|
||||
private final AppendOrAddStatementFactory appendOrAddStatementFactory = new DefaultAppendOrAddStatementFactory();
|
||||
private final BodyTranspiler bodyTranspiler;
|
||||
private final ValueNodeTranspiler valueNodeTranspiler;
|
||||
|
||||
public DefaultTranspilerConfiguration(ComponentClassNodeResolver classNodeResolver) {
|
||||
final var positionSetter = new SimplePositionSetter();
|
||||
final var jStringTranspiler = new DefaultJStringTranspiler(positionSetter);
|
||||
final var gStringTranspiler = new DefaultGStringTranspiler(positionSetter, jStringTranspiler);
|
||||
this.positionSetter = new SimplePositionSetter();
|
||||
final var jStringTranspiler = new DefaultJStringTranspiler(this.positionSetter);
|
||||
final var gStringTranspiler = new DefaultGStringTranspiler(this.positionSetter, jStringTranspiler);
|
||||
final var componentTranspiler = new DefaultComponentTranspiler(
|
||||
DefaultProvider.of(this.appendOrAddStatementFactory),
|
||||
DefaultProvider.of(classNodeResolver),
|
||||
@ -23,6 +31,11 @@ public class DefaultTranspilerConfiguration implements TranspilerConfiguration {
|
||||
this.bodyTranspiler = new DefaultBodyTranspiler(gStringTranspiler, jStringTranspiler, componentTranspiler);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PositionSetter getPositionSetter() {
|
||||
return this.positionSetter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BodyTranspiler getBodyTranspiler() {
|
||||
return this.bodyTranspiler;
|
||||
@ -37,4 +50,36 @@ public class DefaultTranspilerConfiguration implements TranspilerConfiguration {
|
||||
return this.valueNodeTranspiler;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, ClassNode> getImports() {
|
||||
return Map.of(
|
||||
COMPONENT_TEMPLATE.getNameWithoutPackage(), COMPONENT_TEMPLATE,
|
||||
COMPONENT_CONTEXT_TYPE.getNameWithoutPackage(), COMPONENT_CONTEXT_TYPE
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> getStarImports() {
|
||||
return Set.of(
|
||||
GROOWT_VIEW_COMPONENT_WEB + ".lib",
|
||||
"groowt.view.component.runtime",
|
||||
GROOWT_VIEW_COMPONENT_WEB + ".runtime"
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Tuple3<ClassNode, String, String>> getStaticImports() {
|
||||
return Set.of();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, ClassNode> getStaticStarImports() {
|
||||
return Map.of();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClassNode getRenderContextImplementation() {
|
||||
return DEFAULT_RENDER_CONTEXT_IMPLEMENTATION;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -3,6 +3,7 @@ package groowt.view.component.web.transpile;
|
||||
import groowt.view.component.web.ast.node.GStringBodyTextNode;
|
||||
import org.codehaus.groovy.ast.expr.GStringExpression;
|
||||
|
||||
@Deprecated
|
||||
public interface GStringTranspiler {
|
||||
GStringExpression createGStringExpression(GStringBodyTextNode parent);
|
||||
}
|
||||
|
@ -11,7 +11,7 @@ public interface GroovyTranspiler {
|
||||
ComponentTemplateCompilerConfiguration compilerConfiguration,
|
||||
WebViewComponentTemplateCompileUnit compileUnit,
|
||||
CompilationUnitNode compilationUnitNode,
|
||||
String templateClassName
|
||||
String templateClassSimpleName
|
||||
) throws ComponentTemplateCompileException;
|
||||
|
||||
}
|
||||
|
@ -5,6 +5,25 @@ import groowt.view.component.web.util.TokenRange;
|
||||
import org.codehaus.groovy.ast.ASTNode;
|
||||
|
||||
public interface PositionSetter {
|
||||
|
||||
/**
|
||||
* Sets the position of the Groovy node off-set from the start
|
||||
* of the start of the Wvc node. The formula is
|
||||
* thus:
|
||||
* <pre>
|
||||
* target.line = container.line + target.line - 1
|
||||
* target.column = target.line == 1 ? container.column + target.column - 1 : target.column
|
||||
* target.lastLine = container.line + target.lastLine - 1
|
||||
* target.lastColumn = target.lastLine == 1 ? container.column + target.lastColumn - 1 : target.lastColumn
|
||||
* </pre>
|
||||
* For example, if the container has 2,1..4,1 and the target has
|
||||
* 3,1..3,1 (in its source), the target will be adjusted to 4,1..4,1.
|
||||
*
|
||||
* @param target The (Groovy) node whose position is to be set.
|
||||
* @param container The containing (Wvc) node.
|
||||
*/
|
||||
void setPositionOffsetInContainer(ASTNode target, Node container);
|
||||
|
||||
void setPosition(ASTNode target, TokenRange tokenRange);
|
||||
void setPosition(ASTNode target, Node source);
|
||||
void setPosition(ASTNode target, Node start, Node end);
|
||||
|
@ -0,0 +1,426 @@
|
||||
package groowt.view.component.web.transpile;
|
||||
|
||||
import groowt.view.component.web.ast.node.Node;
|
||||
import org.codehaus.groovy.ast.*;
|
||||
import org.codehaus.groovy.ast.expr.*;
|
||||
import org.codehaus.groovy.ast.stmt.*;
|
||||
import org.codehaus.groovy.classgen.BytecodeExpression;
|
||||
import org.codehaus.groovy.control.SourceUnit;
|
||||
|
||||
// TODO: create a utility walker-visitor class to make this much simpler
|
||||
public class PositionVisitor extends ClassCodeVisitorSupport {
|
||||
|
||||
private final PositionSetter positionSetter;
|
||||
private final Node container;
|
||||
|
||||
public PositionVisitor(PositionSetter positionSetter, Node container) {
|
||||
this.positionSetter = positionSetter;
|
||||
this.container = container;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected SourceUnit getSourceUnit() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitClass(ClassNode node) {
|
||||
super.visitClass(node);
|
||||
this.positionSetter.setPositionOffsetInContainer(node, container);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitAnnotations(AnnotatedNode node) {
|
||||
super.visitAnnotations(node);
|
||||
this.positionSetter.setPositionOffsetInContainer(node, container);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void visitAnnotation(AnnotationNode node) {
|
||||
super.visitAnnotation(node);
|
||||
this.positionSetter.setPositionOffsetInContainer(node, container);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitPackage(PackageNode node) {
|
||||
super.visitPackage(node);
|
||||
this.positionSetter.setPositionOffsetInContainer(node, container);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitImports(ModuleNode node) {
|
||||
super.visitImports(node);
|
||||
this.positionSetter.setPositionOffsetInContainer(node, container);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitConstructor(ConstructorNode node) {
|
||||
super.visitConstructor(node);
|
||||
this.positionSetter.setPositionOffsetInContainer(node, container);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitMethod(MethodNode node) {
|
||||
super.visitMethod(node);
|
||||
this.positionSetter.setPositionOffsetInContainer(node, container);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void visitConstructorOrMethod(MethodNode node, boolean isConstructor) {
|
||||
super.visitConstructorOrMethod(node, isConstructor);
|
||||
this.positionSetter.setPositionOffsetInContainer(node, container);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitField(FieldNode node) {
|
||||
super.visitField(node);
|
||||
this.positionSetter.setPositionOffsetInContainer(node, container);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitProperty(PropertyNode node) {
|
||||
super.visitProperty(node);
|
||||
this.positionSetter.setPositionOffsetInContainer(node, container);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void visitObjectInitializerStatements(ClassNode node) {
|
||||
super.visitObjectInitializerStatements(node);
|
||||
this.positionSetter.setPositionOffsetInContainer(node, container);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitDeclarationExpression(DeclarationExpression expression) {
|
||||
super.visitDeclarationExpression(expression);
|
||||
this.positionSetter.setPositionOffsetInContainer(expression, container);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitAssertStatement(AssertStatement statement) {
|
||||
super.visitAssertStatement(statement);
|
||||
this.positionSetter.setPositionOffsetInContainer(statement, container);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitBlockStatement(BlockStatement statement) {
|
||||
super.visitBlockStatement(statement);
|
||||
this.positionSetter.setPositionOffsetInContainer(statement, container);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitBreakStatement(BreakStatement statement) {
|
||||
super.visitBreakStatement(statement);
|
||||
this.positionSetter.setPositionOffsetInContainer(statement, container);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitCaseStatement(CaseStatement statement) {
|
||||
super.visitCaseStatement(statement);
|
||||
this.positionSetter.setPositionOffsetInContainer(statement, container);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitCatchStatement(CatchStatement statement) {
|
||||
super.visitCatchStatement(statement);
|
||||
this.positionSetter.setPositionOffsetInContainer(statement, container);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitContinueStatement(ContinueStatement statement) {
|
||||
super.visitContinueStatement(statement);
|
||||
this.positionSetter.setPositionOffsetInContainer(statement, container);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitDoWhileLoop(DoWhileStatement statement) {
|
||||
super.visitDoWhileLoop(statement);
|
||||
this.positionSetter.setPositionOffsetInContainer(statement, container);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitExpressionStatement(ExpressionStatement statement) {
|
||||
super.visitExpressionStatement(statement);
|
||||
this.positionSetter.setPositionOffsetInContainer(statement, container);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitForLoop(ForStatement statement) {
|
||||
super.visitForLoop(statement);
|
||||
this.positionSetter.setPositionOffsetInContainer(statement, container);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitIfElse(IfStatement statement) {
|
||||
super.visitIfElse(statement);
|
||||
this.positionSetter.setPositionOffsetInContainer(statement, container);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitReturnStatement(ReturnStatement statement) {
|
||||
super.visitReturnStatement(statement);
|
||||
this.positionSetter.setPositionOffsetInContainer(statement, container);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitSwitch(SwitchStatement statement) {
|
||||
super.visitSwitch(statement);
|
||||
this.positionSetter.setPositionOffsetInContainer(statement, container);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitSynchronizedStatement(SynchronizedStatement statement) {
|
||||
super.visitSynchronizedStatement(statement);
|
||||
this.positionSetter.setPositionOffsetInContainer(statement, container);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitThrowStatement(ThrowStatement statement) {
|
||||
super.visitThrowStatement(statement);
|
||||
this.positionSetter.setPositionOffsetInContainer(statement, container);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitTryCatchFinally(TryCatchStatement statement) {
|
||||
super.visitTryCatchFinally(statement);
|
||||
this.positionSetter.setPositionOffsetInContainer(statement, container);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitWhileLoop(WhileStatement statement) {
|
||||
super.visitWhileLoop(statement);
|
||||
this.positionSetter.setPositionOffsetInContainer(statement, container);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitEmptyStatement(EmptyStatement statement) {
|
||||
super.visitEmptyStatement(statement);
|
||||
this.positionSetter.setPositionOffsetInContainer(statement, container);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitMethodCallExpression(MethodCallExpression call) {
|
||||
super.visitMethodCallExpression(call);
|
||||
this.positionSetter.setPositionOffsetInContainer(call, container);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitStaticMethodCallExpression(StaticMethodCallExpression call) {
|
||||
super.visitStaticMethodCallExpression(call);
|
||||
this.positionSetter.setPositionOffsetInContainer(call, container);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitConstructorCallExpression(ConstructorCallExpression call) {
|
||||
super.visitConstructorCallExpression(call);
|
||||
this.positionSetter.setPositionOffsetInContainer(call, container);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitBinaryExpression(BinaryExpression expression) {
|
||||
super.visitBinaryExpression(expression);
|
||||
this.positionSetter.setPositionOffsetInContainer(expression, container);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitTernaryExpression(TernaryExpression expression) {
|
||||
super.visitTernaryExpression(expression);
|
||||
this.positionSetter.setPositionOffsetInContainer(expression, container);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitShortTernaryExpression(ElvisOperatorExpression expression) {
|
||||
super.visitShortTernaryExpression(expression);
|
||||
this.positionSetter.setPositionOffsetInContainer(expression, container);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitPostfixExpression(PostfixExpression expression) {
|
||||
super.visitPostfixExpression(expression);
|
||||
this.positionSetter.setPositionOffsetInContainer(expression, container);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitPrefixExpression(PrefixExpression expression) {
|
||||
super.visitPrefixExpression(expression);
|
||||
this.positionSetter.setPositionOffsetInContainer(expression, container);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitBooleanExpression(BooleanExpression expression) {
|
||||
super.visitBooleanExpression(expression);
|
||||
this.positionSetter.setPositionOffsetInContainer(expression, container);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitNotExpression(NotExpression expression) {
|
||||
super.visitNotExpression(expression);
|
||||
this.positionSetter.setPositionOffsetInContainer(expression, container);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitClosureExpression(ClosureExpression expression) {
|
||||
super.visitClosureExpression(expression);
|
||||
this.positionSetter.setPositionOffsetInContainer(expression, container);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitLambdaExpression(LambdaExpression expression) {
|
||||
super.visitLambdaExpression(expression);
|
||||
this.positionSetter.setPositionOffsetInContainer(expression, container);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitTupleExpression(TupleExpression expression) {
|
||||
super.visitTupleExpression(expression);
|
||||
this.positionSetter.setPositionOffsetInContainer(expression, container);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitListExpression(ListExpression expression) {
|
||||
super.visitListExpression(expression);
|
||||
this.positionSetter.setPositionOffsetInContainer(expression, container);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitArrayExpression(ArrayExpression expression) {
|
||||
super.visitArrayExpression(expression);
|
||||
this.positionSetter.setPositionOffsetInContainer(expression, container);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitMapExpression(MapExpression expression) {
|
||||
super.visitMapExpression(expression);
|
||||
this.positionSetter.setPositionOffsetInContainer(expression, container);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitMapEntryExpression(MapEntryExpression expression) {
|
||||
super.visitMapEntryExpression(expression);
|
||||
this.positionSetter.setPositionOffsetInContainer(expression, container);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitRangeExpression(RangeExpression expression) {
|
||||
super.visitRangeExpression(expression);
|
||||
this.positionSetter.setPositionOffsetInContainer(expression, container);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitSpreadExpression(SpreadExpression expression) {
|
||||
super.visitSpreadExpression(expression);
|
||||
this.positionSetter.setPositionOffsetInContainer(expression, container);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitSpreadMapExpression(SpreadMapExpression expression) {
|
||||
super.visitSpreadMapExpression(expression);
|
||||
this.positionSetter.setPositionOffsetInContainer(expression, container);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitMethodPointerExpression(MethodPointerExpression expression) {
|
||||
super.visitMethodPointerExpression(expression);
|
||||
this.positionSetter.setPositionOffsetInContainer(expression, container);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitMethodReferenceExpression(MethodReferenceExpression expression) {
|
||||
super.visitMethodReferenceExpression(expression);
|
||||
this.positionSetter.setPositionOffsetInContainer(expression, container);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitUnaryMinusExpression(UnaryMinusExpression expression) {
|
||||
super.visitUnaryMinusExpression(expression);
|
||||
this.positionSetter.setPositionOffsetInContainer(expression, container);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitUnaryPlusExpression(UnaryPlusExpression expression) {
|
||||
super.visitUnaryPlusExpression(expression);
|
||||
this.positionSetter.setPositionOffsetInContainer(expression, container);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitBitwiseNegationExpression(BitwiseNegationExpression expression) {
|
||||
super.visitBitwiseNegationExpression(expression);
|
||||
this.positionSetter.setPositionOffsetInContainer(expression, container);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitCastExpression(CastExpression expression) {
|
||||
super.visitCastExpression(expression);
|
||||
this.positionSetter.setPositionOffsetInContainer(expression, container);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitConstantExpression(ConstantExpression expression) {
|
||||
super.visitConstantExpression(expression);
|
||||
this.positionSetter.setPositionOffsetInContainer(expression, container);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitClassExpression(ClassExpression expression) {
|
||||
super.visitClassExpression(expression);
|
||||
this.positionSetter.setPositionOffsetInContainer(expression, container);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitVariableExpression(VariableExpression expression) {
|
||||
super.visitVariableExpression(expression);
|
||||
this.positionSetter.setPositionOffsetInContainer(expression, container);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitPropertyExpression(PropertyExpression expression) {
|
||||
super.visitPropertyExpression(expression);
|
||||
this.positionSetter.setPositionOffsetInContainer(expression, container);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitAttributeExpression(AttributeExpression expression) {
|
||||
super.visitAttributeExpression(expression);
|
||||
this.positionSetter.setPositionOffsetInContainer(expression, container);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitFieldExpression(FieldExpression expression) {
|
||||
super.visitFieldExpression(expression);
|
||||
this.positionSetter.setPositionOffsetInContainer(expression, container);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitGStringExpression(GStringExpression expression) {
|
||||
super.visitGStringExpression(expression);
|
||||
this.positionSetter.setPositionOffsetInContainer(expression, container);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitArgumentlistExpression(ArgumentListExpression expression) {
|
||||
super.visitArgumentlistExpression(expression);
|
||||
this.positionSetter.setPositionOffsetInContainer(expression, container);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitClosureListExpression(ClosureListExpression expression) {
|
||||
super.visitClosureListExpression(expression);
|
||||
this.positionSetter.setPositionOffsetInContainer(expression, container);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitBytecodeExpression(BytecodeExpression expression) {
|
||||
super.visitBytecodeExpression(expression);
|
||||
this.positionSetter.setPositionOffsetInContainer(expression, container);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(Statement statement) {
|
||||
super.visit(statement);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitEmptyExpression(EmptyExpression expression) {
|
||||
this.positionSetter.setPositionOffsetInContainer(expression, container);
|
||||
}
|
||||
|
||||
}
|
@ -18,6 +18,24 @@ public class SimplePositionSetter implements PositionSetter {
|
||||
this.set(target, start.line(), start.column(), end.line(), end.column());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPositionOffsetInContainer(ASTNode target, Node container) {
|
||||
final SourcePosition containerStart = container.getTokenRange().getStartPosition();
|
||||
final SourcePosition startPosition = new SourcePosition(
|
||||
containerStart.line() + target.getLineNumber() - 1,
|
||||
target.getLineNumber() == 1
|
||||
? containerStart.column() + target.getColumnNumber() - 1
|
||||
: target.getColumnNumber()
|
||||
);
|
||||
final SourcePosition endPosition = new SourcePosition(
|
||||
containerStart.line() + target.getLastLineNumber() - 1,
|
||||
target.getLastLineNumber() == 1
|
||||
? containerStart.column() + target.getLastColumnNumber() - 1
|
||||
: target.getLastColumnNumber()
|
||||
);
|
||||
this.set(target, startPosition, endPosition);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPosition(ASTNode target, TokenRange tokenRange) {
|
||||
this.set(target, tokenRange.getStartPosition(), tokenRange.getEndPosition());
|
||||
|
@ -1,6 +1,18 @@
|
||||
package groowt.view.component.web.transpile;
|
||||
|
||||
import groovy.lang.Tuple3;
|
||||
import org.codehaus.groovy.ast.ClassNode;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
public interface TranspilerConfiguration {
|
||||
PositionSetter getPositionSetter();
|
||||
BodyTranspiler getBodyTranspiler();
|
||||
AppendOrAddStatementFactory getAppendOrAddStatementFactory();
|
||||
Map<String, ClassNode> getImports();
|
||||
Set<String> getStarImports();
|
||||
Set<Tuple3<ClassNode, String, String>> getStaticImports();
|
||||
Map<String, ClassNode> getStaticStarImports();
|
||||
ClassNode getRenderContextImplementation();
|
||||
}
|
||||
|
@ -5,7 +5,7 @@ import groowt.view.component.ComponentTemplate;
|
||||
import groowt.view.component.compiler.ComponentTemplateCompileException;
|
||||
import groowt.view.component.context.ComponentContext;
|
||||
import groowt.view.component.runtime.ComponentWriter;
|
||||
import groowt.view.component.web.WebViewComponent;
|
||||
import groowt.view.component.web.runtime.DefaultWebViewRenderContext;
|
||||
import groowt.view.component.web.runtime.WebViewComponentRenderContext;
|
||||
import groowt.view.component.web.util.SourcePosition;
|
||||
import org.codehaus.groovy.ast.*;
|
||||
@ -23,8 +23,10 @@ public final class TranspilerUtil {
|
||||
public static final ClassNode COMPONENT_TEMPLATE = ClassHelper.make(ComponentTemplate.class);
|
||||
public static final ClassNode COMPONENT_CONTEXT_TYPE = ClassHelper.make(ComponentContext.class);
|
||||
public static final ClassNode COMPONENT_WRITER_TYPE = ClassHelper.make(ComponentWriter.class);
|
||||
public static final ClassNode WEB_VIEW_COMPONENT_RENDER_CONTEXT_TYPE = ClassHelper.make(WebViewComponentRenderContext.class);
|
||||
public static final ClassNode WEB_VIEW_COMPONENT_TYPE = ClassHelper.make(WebViewComponent.class);
|
||||
public static final ClassNode WEB_VIEW_COMPONENT_RENDER_CONTEXT_TYPE =
|
||||
ClassHelper.make(WebViewComponentRenderContext.class);
|
||||
public static final ClassNode DEFAULT_RENDER_CONTEXT_IMPLEMENTATION =
|
||||
ClassHelper.make(DefaultWebViewRenderContext.class);
|
||||
|
||||
public static final String GROOWT_VIEW_COMPONENT_WEB = "groowt.view.component.web";
|
||||
public static final String COMPONENT_CONTEXT_NAME = "componentContext";
|
||||
|
@ -11,13 +11,6 @@ import java.util.*;
|
||||
|
||||
public class WebViewComponentModuleNode extends ModuleNode {
|
||||
|
||||
public static void copyTo(ModuleNode from, WebViewComponentModuleNode to) {
|
||||
from.getImports().forEach(to::addImport);
|
||||
from.getStarImports().forEach(to::addStarImport);
|
||||
from.getStaticImports().forEach(to::addStaticImport);
|
||||
from.getStaticStarImports().forEach(to::addStaticStarImport);
|
||||
}
|
||||
|
||||
protected final List<ImportNode> imports = new ArrayList<>();
|
||||
protected final List<ImportNode> starImports = new ArrayList<>();
|
||||
protected final Map<String, ImportNode> staticImports = new LinkedHashMap<>();
|
||||
|
@ -3,7 +3,6 @@ package groowt.view.component.web.transpile.resolve;
|
||||
import groowt.util.fp.either.Either;
|
||||
import groowt.view.component.web.compiler.WebViewComponentTemplateCompileUnit;
|
||||
import org.codehaus.groovy.ast.ClassNode;
|
||||
import org.codehaus.groovy.ast.ModuleNode;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@ -15,10 +14,9 @@ public class ClassLoaderComponentClassNodeResolver extends ModuleNodeComponentCl
|
||||
|
||||
public ClassLoaderComponentClassNodeResolver(
|
||||
WebViewComponentTemplateCompileUnit compileUnit,
|
||||
ModuleNode moduleNode,
|
||||
ClassLoader classLoader
|
||||
) {
|
||||
super(compileUnit, moduleNode);
|
||||
super(compileUnit);
|
||||
this.classLoader = classLoader;
|
||||
}
|
||||
|
||||
|
@ -5,30 +5,36 @@ import groowt.view.component.web.compiler.WebViewComponentTemplateCompileUnit;
|
||||
import org.codehaus.groovy.ast.ClassNode;
|
||||
import org.codehaus.groovy.ast.ModuleNode;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
public class ModuleNodeComponentClassNodeResolver extends CachingComponentClassNodeResolver {
|
||||
|
||||
private final ModuleNode moduleNode;
|
||||
private ModuleNode moduleNode;
|
||||
|
||||
public ModuleNodeComponentClassNodeResolver(
|
||||
WebViewComponentTemplateCompileUnit compileUnit,
|
||||
ModuleNode moduleNode
|
||||
) {
|
||||
public ModuleNodeComponentClassNodeResolver(WebViewComponentTemplateCompileUnit compileUnit) {
|
||||
super(compileUnit);
|
||||
this.moduleNode = moduleNode;
|
||||
}
|
||||
|
||||
public ModuleNode getModuleNode() {
|
||||
return Objects.requireNonNull(this.moduleNode);
|
||||
}
|
||||
|
||||
public void setModuleNode(ModuleNode moduleNode) {
|
||||
this.moduleNode = Objects.requireNonNull(moduleNode);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Either<ClassNodeResolveException, ClassNode> getClassForNameWithoutPackage(String nameWithoutPackage) {
|
||||
return super.getClassForNameWithoutPackage(nameWithoutPackage).flatMapLeft(ignored -> {
|
||||
// try regular imports first
|
||||
final var importedClassNode = this.moduleNode.getImportType(nameWithoutPackage);
|
||||
final var importedClassNode = this.getModuleNode().getImportType(nameWithoutPackage);
|
||||
if (importedClassNode != null) {
|
||||
this.addClassNode(importedClassNode);
|
||||
return Either.right(importedClassNode);
|
||||
}
|
||||
|
||||
// try star imports
|
||||
final var starImports = this.moduleNode.getStarImports();
|
||||
final var starImports = this.getModuleNode().getStarImports();
|
||||
for (final var starImport : starImports) {
|
||||
final var packageName = starImport.getPackageName();
|
||||
final String fqn;
|
||||
@ -44,7 +50,7 @@ public class ModuleNodeComponentClassNodeResolver extends CachingComponentClassN
|
||||
}
|
||||
|
||||
// try pre-pending package and asking for fqn
|
||||
final String moduleNodePackageName = this.moduleNode.getPackageName();
|
||||
final String moduleNodePackageName = this.getModuleNode().getPackageName();
|
||||
final String packageName;
|
||||
if (moduleNodePackageName != null) {
|
||||
packageName = moduleNodePackageName;
|
||||
|
@ -57,6 +57,7 @@ public abstract class GroovyTranspilerTests {
|
||||
this.transpiler.transpile(
|
||||
new DefaultComponentTemplateCompilerConfiguration(),
|
||||
new DefaultWebViewComponentTemplateCompileUnit(
|
||||
"<anonymous string source>",
|
||||
AnonymousWebViewComponent.class,
|
||||
new StringSource(source, null),
|
||||
"groowt.view.component.web.transpiler"
|
||||
|
@ -37,7 +37,7 @@ public abstract class AbstractWebViewComponent extends AbstractViewComponent imp
|
||||
|
||||
public AbstractWebViewComponent(ComponentTemplateSource source) {
|
||||
this(selfClass -> new DefaultWebViewComponentTemplateCompileUnit(
|
||||
selfClass, source, selfClass.getPackageName())
|
||||
source.getDescriptiveName(), selfClass, source, selfClass.getPackageName())
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -24,11 +24,12 @@ public class DefaultWebViewComponentTemplateCompileUnit extends AbstractComponen
|
||||
private final CompilationUnit groovyCompilationUnit = new CompilationUnit();
|
||||
|
||||
public DefaultWebViewComponentTemplateCompileUnit(
|
||||
String descriptiveName,
|
||||
Class<? extends ViewComponent> forClass,
|
||||
ComponentTemplateSource source,
|
||||
String defaultPackageName
|
||||
) {
|
||||
super(forClass, source);
|
||||
super(descriptiveName, forClass, source);
|
||||
if (!defaultPackageName.isEmpty() && !defaultPackageName.endsWith(".")) {
|
||||
this.defaultPackageName = defaultPackageName + ".";
|
||||
} else {
|
||||
|
@ -26,6 +26,7 @@ public interface WebViewComponentTemplateCompiler
|
||||
default ComponentTemplateCompileResult compileAnonymous(ComponentTemplateSource source, String packageName)
|
||||
throws ComponentTemplateCompileException {
|
||||
return this.compile(new DefaultWebViewComponentTemplateCompileUnit(
|
||||
source.getDescriptiveName(),
|
||||
AnonymousWebViewComponent.class,
|
||||
source,
|
||||
packageName
|
||||
|
Loading…
Reference in New Issue
Block a user