diff --git a/view-components/src/main/java/groowt/view/component/compiler/AbstractComponentTemplateCompileUnit.java b/view-components/src/main/java/groowt/view/component/compiler/AbstractComponentTemplateCompileUnit.java index 6db1fc4..899fad9 100644 --- a/view-components/src/main/java/groowt/view/component/compiler/AbstractComponentTemplateCompileUnit.java +++ b/view-components/src/main/java/groowt/view/component/compiler/AbstractComponentTemplateCompileUnit.java @@ -6,17 +6,25 @@ import groowt.view.component.compiler.source.ComponentTemplateSource; public abstract class AbstractComponentTemplateCompileUnit implements ComponentTemplateCompileUnit { + private final String descriptiveName; private final Class forClass; private final ComponentTemplateSource source; public AbstractComponentTemplateCompileUnit( + String descriptiveName, Class forClass, ComponentTemplateSource source ) { + this.descriptiveName = descriptiveName; this.forClass = forClass; this.source = source; } + @Override + public String getDescriptiveName() { + return this.descriptiveName; + } + @Override public Class getForClass() { return this.forClass; diff --git a/view-components/src/main/java/groowt/view/component/compiler/ComponentTemplateCompileException.java b/view-components/src/main/java/groowt/view/component/compiler/ComponentTemplateCompileException.java index eae8051..291fb7c 100644 --- a/view-components/src/main/java/groowt/view/component/compiler/ComponentTemplateCompileException.java +++ b/view-components/src/main/java/groowt/view/component/compiler/ComponentTemplateCompileException.java @@ -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); diff --git a/view-components/src/main/java/groowt/view/component/compiler/ComponentTemplateCompileUnit.java b/view-components/src/main/java/groowt/view/component/compiler/ComponentTemplateCompileUnit.java index 4dc8bec..0d2595a 100644 --- a/view-components/src/main/java/groowt/view/component/compiler/ComponentTemplateCompileUnit.java +++ b/view-components/src/main/java/groowt/view/component/compiler/ComponentTemplateCompileUnit.java @@ -5,6 +5,7 @@ import groowt.view.component.compiler.source.ComponentTemplateSource; public interface ComponentTemplateCompileUnit { + String getDescriptiveName(); Class getForClass(); String getDefaultPackageName(); ComponentTemplateSource getSource(); diff --git a/view-components/src/main/java/groowt/view/component/compiler/source/ComponentTemplateSource.java b/view-components/src/main/java/groowt/view/component/compiler/source/ComponentTemplateSource.java index bdee155..bd926e1 100644 --- a/view-components/src/main/java/groowt/view/component/compiler/source/ComponentTemplateSource.java +++ b/view-components/src/main/java/groowt/view/component/compiler/source/ComponentTemplateSource.java @@ -46,7 +46,7 @@ public interface ComponentTemplateSource { } Reader toReader() throws Exception; - String getDescription(); + String getDescriptiveName(); boolean canReopen(); List getLines(); diff --git a/view-components/src/main/java/groowt/view/component/compiler/source/FileSource.java b/view-components/src/main/java/groowt/view/component/compiler/source/FileSource.java index 8e177bb..a975196 100644 --- a/view-components/src/main/java/groowt/view/component/compiler/source/FileSource.java +++ b/view-components/src/main/java/groowt/view/component/compiler/source/FileSource.java @@ -21,7 +21,7 @@ public class FileSource implements ComponentTemplateSource { } @Override - public String getDescription() { + public String getDescriptiveName() { return this.templateFile.toString(); } diff --git a/view-components/src/main/java/groowt/view/component/compiler/source/InputStreamSource.java b/view-components/src/main/java/groowt/view/component/compiler/source/InputStreamSource.java index 6c8dcef..74c938b 100644 --- a/view-components/src/main/java/groowt/view/component/compiler/source/InputStreamSource.java +++ b/view-components/src/main/java/groowt/view/component/compiler/source/InputStreamSource.java @@ -23,7 +23,7 @@ public class InputStreamSource implements ComponentTemplateSource { } @Override - public String getDescription() { + public String getDescriptiveName() { return this.description != null ? this.description : ""; } diff --git a/view-components/src/main/java/groowt/view/component/compiler/source/ReaderSource.java b/view-components/src/main/java/groowt/view/component/compiler/source/ReaderSource.java index 18b6adc..9b5a4e2 100644 --- a/view-components/src/main/java/groowt/view/component/compiler/source/ReaderSource.java +++ b/view-components/src/main/java/groowt/view/component/compiler/source/ReaderSource.java @@ -21,7 +21,7 @@ public class ReaderSource implements ComponentTemplateSource { } @Override - public String getDescription() { + public String getDescriptiveName() { return this.description != null ? this.description : ""; } diff --git a/view-components/src/main/java/groowt/view/component/compiler/source/StringSource.java b/view-components/src/main/java/groowt/view/component/compiler/source/StringSource.java index 0063aa1..5171a96 100644 --- a/view-components/src/main/java/groowt/view/component/compiler/source/StringSource.java +++ b/view-components/src/main/java/groowt/view/component/compiler/source/StringSource.java @@ -22,7 +22,7 @@ public class StringSource implements ComponentTemplateSource { } @Override - public String getDescription() { + public String getDescriptiveName() { return this.name != null ? this.name : ""; } diff --git a/view-components/src/main/java/groowt/view/component/compiler/source/URISource.java b/view-components/src/main/java/groowt/view/component/compiler/source/URISource.java index ce9bb46..beabd29 100644 --- a/view-components/src/main/java/groowt/view/component/compiler/source/URISource.java +++ b/view-components/src/main/java/groowt/view/component/compiler/source/URISource.java @@ -21,7 +21,7 @@ public class URISource implements ComponentTemplateSource { } @Override - public String getDescription() { + public String getDescriptiveName() { return this.templateURI.toString(); } diff --git a/view-components/src/main/java/groowt/view/component/compiler/source/URLSource.java b/view-components/src/main/java/groowt/view/component/compiler/source/URLSource.java index 6c31fe8..a828928 100644 --- a/view-components/src/main/java/groowt/view/component/compiler/source/URLSource.java +++ b/view-components/src/main/java/groowt/view/component/compiler/source/URLSource.java @@ -25,7 +25,7 @@ public class URLSource implements ComponentTemplateSource { } @Override - public String getDescription() { + public String getDescriptiveName() { return this.url.toString(); } diff --git a/web-view-components-compiler/src/main/java/groowt/view/component/web/groovyc/DelegatingWebViewComponentTemplateParserPlugin.java b/web-view-components-compiler/src/main/java/groowt/view/component/web/groovyc/DelegatingWebViewComponentTemplateParserPlugin.java index f400f6d..1fdb469 100644 --- a/web-view-components-compiler/src/main/java/groowt/view/component/web/groovyc/DelegatingWebViewComponentTemplateParserPlugin.java +++ b/web-view-components-compiler/src/main/java/groowt/view/component/web/groovyc/DelegatingWebViewComponentTemplateParserPlugin.java @@ -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) { diff --git a/web-view-components-compiler/src/main/java/groowt/view/component/web/transpile/DefaultGStringTranspiler.java b/web-view-components-compiler/src/main/java/groowt/view/component/web/transpile/DefaultGStringTranspiler.java index b537589..44adfac 100644 --- a/web-view-components-compiler/src/main/java/groowt/view/component/web/transpile/DefaultGStringTranspiler.java +++ b/web-view-components-compiler/src/main/java/groowt/view/component/web/transpile/DefaultGStringTranspiler.java @@ -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; diff --git a/web-view-components-compiler/src/main/java/groowt/view/component/web/transpile/DefaultGroovyTranspiler.java b/web-view-components-compiler/src/main/java/groowt/view/component/web/transpile/DefaultGroovyTranspiler.java index c8e71d5..127158b 100644 --- a/web-view-components-compiler/src/main/java/groowt/view/component/web/transpile/DefaultGroovyTranspiler.java +++ b/web-view-components-compiler/src/main/java/groowt/view/component/web/transpile/DefaultGroovyTranspiler.java @@ -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}. - *

- * 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 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 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 errors = state.getErrors(); if (errors.size() == 1) { @@ -298,6 +365,7 @@ public class DefaultGroovyTranspiler implements GroovyTranspiler { } } + // return the sourceUnit for later processing return sourceUnit; } diff --git a/web-view-components-compiler/src/main/java/groowt/view/component/web/transpile/DefaultTranspilerConfiguration.java b/web-view-components-compiler/src/main/java/groowt/view/component/web/transpile/DefaultTranspilerConfiguration.java index a1df3d7..2494c11 100644 --- a/web-view-components-compiler/src/main/java/groowt/view/component/web/transpile/DefaultTranspilerConfiguration.java +++ b/web-view-components-compiler/src/main/java/groowt/view/component/web/transpile/DefaultTranspilerConfiguration.java @@ -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 getImports() { + return Map.of( + COMPONENT_TEMPLATE.getNameWithoutPackage(), COMPONENT_TEMPLATE, + COMPONENT_CONTEXT_TYPE.getNameWithoutPackage(), COMPONENT_CONTEXT_TYPE + ); + } + + @Override + public Set getStarImports() { + return Set.of( + GROOWT_VIEW_COMPONENT_WEB + ".lib", + "groowt.view.component.runtime", + GROOWT_VIEW_COMPONENT_WEB + ".runtime" + ); + } + + @Override + public Set> getStaticImports() { + return Set.of(); + } + + @Override + public Map getStaticStarImports() { + return Map.of(); + } + + @Override + public ClassNode getRenderContextImplementation() { + return DEFAULT_RENDER_CONTEXT_IMPLEMENTATION; + } + } diff --git a/web-view-components-compiler/src/main/java/groowt/view/component/web/transpile/GStringTranspiler.java b/web-view-components-compiler/src/main/java/groowt/view/component/web/transpile/GStringTranspiler.java index f27ed51..fbdcb1e 100644 --- a/web-view-components-compiler/src/main/java/groowt/view/component/web/transpile/GStringTranspiler.java +++ b/web-view-components-compiler/src/main/java/groowt/view/component/web/transpile/GStringTranspiler.java @@ -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); } diff --git a/web-view-components-compiler/src/main/java/groowt/view/component/web/transpile/GroovyTranspiler.java b/web-view-components-compiler/src/main/java/groowt/view/component/web/transpile/GroovyTranspiler.java index d3747c5..6dce471 100644 --- a/web-view-components-compiler/src/main/java/groowt/view/component/web/transpile/GroovyTranspiler.java +++ b/web-view-components-compiler/src/main/java/groowt/view/component/web/transpile/GroovyTranspiler.java @@ -11,7 +11,7 @@ public interface GroovyTranspiler { ComponentTemplateCompilerConfiguration compilerConfiguration, WebViewComponentTemplateCompileUnit compileUnit, CompilationUnitNode compilationUnitNode, - String templateClassName + String templateClassSimpleName ) throws ComponentTemplateCompileException; } diff --git a/web-view-components-compiler/src/main/java/groowt/view/component/web/transpile/PositionSetter.java b/web-view-components-compiler/src/main/java/groowt/view/component/web/transpile/PositionSetter.java index f00d52b..1d719dc 100644 --- a/web-view-components-compiler/src/main/java/groowt/view/component/web/transpile/PositionSetter.java +++ b/web-view-components-compiler/src/main/java/groowt/view/component/web/transpile/PositionSetter.java @@ -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: + *

+     * 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
+     * 
+ * 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); diff --git a/web-view-components-compiler/src/main/java/groowt/view/component/web/transpile/PositionVisitor.java b/web-view-components-compiler/src/main/java/groowt/view/component/web/transpile/PositionVisitor.java new file mode 100644 index 0000000..05bcc13 --- /dev/null +++ b/web-view-components-compiler/src/main/java/groowt/view/component/web/transpile/PositionVisitor.java @@ -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); + } + +} diff --git a/web-view-components-compiler/src/main/java/groowt/view/component/web/transpile/SimplePositionSetter.java b/web-view-components-compiler/src/main/java/groowt/view/component/web/transpile/SimplePositionSetter.java index 62e848a..6ff5d82 100644 --- a/web-view-components-compiler/src/main/java/groowt/view/component/web/transpile/SimplePositionSetter.java +++ b/web-view-components-compiler/src/main/java/groowt/view/component/web/transpile/SimplePositionSetter.java @@ -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()); diff --git a/web-view-components-compiler/src/main/java/groowt/view/component/web/transpile/TranspilerConfiguration.java b/web-view-components-compiler/src/main/java/groowt/view/component/web/transpile/TranspilerConfiguration.java index 04d8498..dc09cb6 100644 --- a/web-view-components-compiler/src/main/java/groowt/view/component/web/transpile/TranspilerConfiguration.java +++ b/web-view-components-compiler/src/main/java/groowt/view/component/web/transpile/TranspilerConfiguration.java @@ -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 getImports(); + Set getStarImports(); + Set> getStaticImports(); + Map getStaticStarImports(); + ClassNode getRenderContextImplementation(); } diff --git a/web-view-components-compiler/src/main/java/groowt/view/component/web/transpile/TranspilerUtil.java b/web-view-components-compiler/src/main/java/groowt/view/component/web/transpile/TranspilerUtil.java index addcee7..b19da46 100644 --- a/web-view-components-compiler/src/main/java/groowt/view/component/web/transpile/TranspilerUtil.java +++ b/web-view-components-compiler/src/main/java/groowt/view/component/web/transpile/TranspilerUtil.java @@ -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"; diff --git a/web-view-components-compiler/src/main/java/groowt/view/component/web/transpile/WebViewComponentModuleNode.java b/web-view-components-compiler/src/main/java/groowt/view/component/web/transpile/WebViewComponentModuleNode.java index ef3f62b..1130f6e 100644 --- a/web-view-components-compiler/src/main/java/groowt/view/component/web/transpile/WebViewComponentModuleNode.java +++ b/web-view-components-compiler/src/main/java/groowt/view/component/web/transpile/WebViewComponentModuleNode.java @@ -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 imports = new ArrayList<>(); protected final List starImports = new ArrayList<>(); protected final Map staticImports = new LinkedHashMap<>(); diff --git a/web-view-components-compiler/src/main/java/groowt/view/component/web/transpile/resolve/ClassLoaderComponentClassNodeResolver.java b/web-view-components-compiler/src/main/java/groowt/view/component/web/transpile/resolve/ClassLoaderComponentClassNodeResolver.java index e2424a7..716e33e 100644 --- a/web-view-components-compiler/src/main/java/groowt/view/component/web/transpile/resolve/ClassLoaderComponentClassNodeResolver.java +++ b/web-view-components-compiler/src/main/java/groowt/view/component/web/transpile/resolve/ClassLoaderComponentClassNodeResolver.java @@ -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; } diff --git a/web-view-components-compiler/src/main/java/groowt/view/component/web/transpile/resolve/ModuleNodeComponentClassNodeResolver.java b/web-view-components-compiler/src/main/java/groowt/view/component/web/transpile/resolve/ModuleNodeComponentClassNodeResolver.java index 1241432..d8a33de 100644 --- a/web-view-components-compiler/src/main/java/groowt/view/component/web/transpile/resolve/ModuleNodeComponentClassNodeResolver.java +++ b/web-view-components-compiler/src/main/java/groowt/view/component/web/transpile/resolve/ModuleNodeComponentClassNodeResolver.java @@ -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 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; diff --git a/web-view-components-compiler/src/testFixtures/java/groowt/view/component/web/transpiler/GroovyTranspilerTests.java b/web-view-components-compiler/src/testFixtures/java/groowt/view/component/web/transpiler/GroovyTranspilerTests.java index a210980..ed138d6 100644 --- a/web-view-components-compiler/src/testFixtures/java/groowt/view/component/web/transpiler/GroovyTranspilerTests.java +++ b/web-view-components-compiler/src/testFixtures/java/groowt/view/component/web/transpiler/GroovyTranspilerTests.java @@ -57,6 +57,7 @@ public abstract class GroovyTranspilerTests { this.transpiler.transpile( new DefaultComponentTemplateCompilerConfiguration(), new DefaultWebViewComponentTemplateCompileUnit( + "", AnonymousWebViewComponent.class, new StringSource(source, null), "groowt.view.component.web.transpiler" diff --git a/web-view-components/src/main/java/groowt/view/component/web/AbstractWebViewComponent.java b/web-view-components/src/main/java/groowt/view/component/web/AbstractWebViewComponent.java index c37998b..cb34305 100644 --- a/web-view-components/src/main/java/groowt/view/component/web/AbstractWebViewComponent.java +++ b/web-view-components/src/main/java/groowt/view/component/web/AbstractWebViewComponent.java @@ -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()) ); } diff --git a/web-view-components/src/main/java/groowt/view/component/web/compiler/DefaultWebViewComponentTemplateCompileUnit.java b/web-view-components/src/main/java/groowt/view/component/web/compiler/DefaultWebViewComponentTemplateCompileUnit.java index 1dd2472..dd6b5bc 100644 --- a/web-view-components/src/main/java/groowt/view/component/web/compiler/DefaultWebViewComponentTemplateCompileUnit.java +++ b/web-view-components/src/main/java/groowt/view/component/web/compiler/DefaultWebViewComponentTemplateCompileUnit.java @@ -24,11 +24,12 @@ public class DefaultWebViewComponentTemplateCompileUnit extends AbstractComponen private final CompilationUnit groovyCompilationUnit = new CompilationUnit(); public DefaultWebViewComponentTemplateCompileUnit( + String descriptiveName, Class forClass, ComponentTemplateSource source, String defaultPackageName ) { - super(forClass, source); + super(descriptiveName, forClass, source); if (!defaultPackageName.isEmpty() && !defaultPackageName.endsWith(".")) { this.defaultPackageName = defaultPackageName + "."; } else { diff --git a/web-view-components/src/main/java/groowt/view/component/web/compiler/WebViewComponentTemplateCompiler.java b/web-view-components/src/main/java/groowt/view/component/web/compiler/WebViewComponentTemplateCompiler.java index ac72c68..d241b78 100644 --- a/web-view-components/src/main/java/groowt/view/component/web/compiler/WebViewComponentTemplateCompiler.java +++ b/web-view-components/src/main/java/groowt/view/component/web/compiler/WebViewComponentTemplateCompiler.java @@ -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