Major refactoring of compiler pipeline.

This commit is contained in:
JesseBrault0709 2024-05-07 17:45:24 +02:00
parent 45781209c0
commit 2d4e085bb3
22 changed files with 680 additions and 306 deletions

View File

@ -1,6 +1,7 @@
package groowt.view.component; package groowt.view.component;
import groovy.lang.Closure; import groovy.lang.Closure;
import groowt.view.component.compiler.ComponentTemplateCompileErrorException;
import groowt.view.component.compiler.ComponentTemplateCompiler; import groowt.view.component.compiler.ComponentTemplateCompiler;
import groowt.view.component.context.ComponentContext; import groowt.view.component.context.ComponentContext;
import groowt.view.component.factory.ComponentTemplateSource; import groowt.view.component.factory.ComponentTemplateSource;
@ -29,13 +30,24 @@ public abstract class AbstractViewComponent implements ViewComponent {
} }
} }
protected AbstractViewComponent(ComponentTemplateSource source, Function<String, ComponentTemplateCompiler> getCompiler) { protected AbstractViewComponent(ComponentTemplateSource source, ComponentTemplateCompiler compiler) {
final Class<? extends AbstractViewComponent> selfClass = this.getSelfClass(); try {
this.template = getCompiler.apply(selfClass.getPackageName()).compile(selfClass, source); this.template = compiler.compileAndGet(this.getSelfClass(), source);
} catch (ComponentTemplateCompileErrorException e) {
throw new RuntimeException(e);
}
} }
protected AbstractViewComponent(ComponentTemplateSource source, ComponentTemplateCompiler compiler) { protected AbstractViewComponent(
this.template = compiler.compile(this.getSelfClass(), source); ComponentTemplateSource source,
Function<? super Class<? extends AbstractViewComponent>, ? extends ComponentTemplateCompiler> compilerFunction
) {
final var compiler = compilerFunction.apply(this.getSelfClass());
try {
this.template = compiler.compileAndGet(this.getSelfClass(), source);
} catch (ComponentTemplateCompileErrorException e) {
throw new RuntimeException(e);
}
} }
protected abstract Class<? extends AbstractViewComponent> getSelfClass(); protected abstract Class<? extends AbstractViewComponent> getSelfClass();

View File

@ -1,52 +1,41 @@
package groowt.view.component.compiler; package groowt.view.component.compiler;
import groovy.lang.GroovyClassLoader;
import groowt.view.component.ComponentTemplate; import groowt.view.component.ComponentTemplate;
import groowt.view.component.ViewComponent; import groowt.view.component.ViewComponent;
import groowt.view.component.factory.ComponentTemplateSource; import groowt.view.component.factory.ComponentTemplateSource;
import groowt.view.component.factory.ComponentTemplateSource.*;
import java.io.*; import java.io.IOException;
import java.net.URI; import java.io.Reader;
import java.net.URL;
public abstract class AbstractComponentTemplateCompiler implements ComponentTemplateCompiler { public abstract class AbstractComponentTemplateCompiler implements ComponentTemplateCompiler {
protected abstract ComponentTemplate compile( private final GroovyClassLoader groovyClassLoader;
public AbstractComponentTemplateCompiler(GroovyClassLoader groovyClassLoader) {
this.groovyClassLoader = groovyClassLoader;
}
protected abstract ComponentTemplateCompileResult compile(
ComponentTemplateSource componentTemplateSource, ComponentTemplateSource componentTemplateSource,
Class<? extends ViewComponent> forClass, Class<? extends ViewComponent> forClass,
Reader actualSource Reader actualSource
); ) throws ComponentTemplateCompileErrorException;
@Override @Override
public ComponentTemplate compile(Class<? extends ViewComponent> forClass, ComponentTemplateSource source) { public ComponentTemplateCompileResult compile(Class<? extends ViewComponent> forClass, ComponentTemplateSource source)
return switch (source) { throws ComponentTemplateCompileErrorException {
case FileSource(File file) -> { try (final Reader reader = ComponentTemplateSource.toReader(source)) {
try { return this.compile(source, forClass, reader);
yield this.compile(source, forClass, new FileReader(file));
} catch (FileNotFoundException e) {
throw new RuntimeException(e);
}
}
case StringSource(String rawSource) ->
this.compile(source, forClass, new StringReader(rawSource));
case InputStreamSource(InputStream inputStream) ->
this.compile(source, forClass, new InputStreamReader(inputStream));
case URISource(URI uri) -> {
try {
yield this.compile(source, forClass, new InputStreamReader(uri.toURL().openStream()));
} catch (IOException e) { } catch (IOException e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }
} }
case URLSource(URL url) -> {
try { @Override
yield this.compile(source, forClass, new InputStreamReader(url.openStream())); public ComponentTemplate compileAndGet(Class<? extends ViewComponent> forClass, ComponentTemplateSource source)
} catch (IOException e) { throws ComponentTemplateCompileErrorException {
throw new RuntimeException(e); return this.compileAndGet(this.groovyClassLoader, forClass, source);
}
}
case ReaderSource(Reader reader) -> this.compile(source, forClass, reader);
};
} }
} }

View File

@ -1,8 +1,11 @@
package groowt.view.component.compiler; package groowt.view.component.compiler;
import groovy.lang.GroovyClassLoader;
import groowt.view.component.ComponentTemplate; import groowt.view.component.ComponentTemplate;
import groowt.view.component.ViewComponent; import groowt.view.component.ViewComponent;
import groowt.view.component.factory.ComponentTemplateSource; import groowt.view.component.factory.ComponentTemplateSource;
import org.codehaus.groovy.tools.GroovyClass;
import org.jetbrains.annotations.Nullable;
import java.io.Reader; import java.io.Reader;
import java.util.HashMap; import java.util.HashMap;
@ -10,21 +13,92 @@ import java.util.Map;
public abstract class CachingComponentTemplateCompiler extends AbstractComponentTemplateCompiler { public abstract class CachingComponentTemplateCompiler extends AbstractComponentTemplateCompiler {
private final Map<Class<? extends ViewComponent>, ComponentTemplate> cache = new HashMap<>(); private record CachedTemplate(
ComponentTemplateCompileResult compileResult,
@Nullable ComponentTemplate template
) {}
@Override private final Map<Class<? extends ViewComponent>, CachedTemplate> cache = new HashMap<>();
protected final ComponentTemplate compile(
ComponentTemplateSource source, public CachingComponentTemplateCompiler(GroovyClassLoader groovyClassLoader) {
Class<? extends ViewComponent> forClass, super(groovyClassLoader);
Reader sourceReader
) {
return this.cache.computeIfAbsent(forClass, ignored -> this.doCompile(source, forClass, sourceReader));
} }
protected abstract ComponentTemplate doCompile( private ComponentTemplate instantiate(
GroovyClassLoader groovyClassLoader,
ComponentTemplateCompileResult compileResult
) {
for (final var groovyClass : compileResult.otherClasses()) {
// Try to find it. If we can't, we need to load it via the groovy loader
try {
Class.forName(groovyClass.getName(), true, groovyClassLoader);
} catch (ClassNotFoundException ignored) {
groovyClassLoader.defineClass(groovyClass.getName(), groovyClass.getBytes());
} catch (LinkageError ignored) {
// no-op, because we already have it
}
}
final GroovyClass templateGroovyClass = compileResult.templateClass();
Class<?> templateClass;
// Try to find it. If we can't, we need to load it via the groovy loader
try {
templateClass = Class.forName(templateGroovyClass.getName(), true, groovyClassLoader);
} catch (ClassNotFoundException ignored) {
templateClass = groovyClassLoader.defineClass(
templateGroovyClass.getName(),
templateGroovyClass.getBytes()
);
}
try {
return (ComponentTemplate) templateClass.getConstructor().newInstance();
} catch (Exception e) {
throw new RuntimeException("Unable to instantiate ComponentTemplate " + templateClass.getName(), e);
}
}
@Override
public final ComponentTemplate compileAndGet(
GroovyClassLoader groovyClassLoader,
Class<? extends ViewComponent> forClass,
ComponentTemplateSource source
) throws ComponentTemplateCompileErrorException {
if (this.cache.containsKey(forClass)) {
final var cached = this.cache.get(forClass);
if (cached.template() == null) {
final ComponentTemplate template = this.instantiate(groovyClassLoader, cached.compileResult());
this.cache.put(forClass, new CachedTemplate(cached.compileResult(), template));
return template;
} else {
return cached.template();
}
} else {
final ComponentTemplateCompileResult compileResult = this.compile(forClass, source);
final ComponentTemplate template = this.instantiate(groovyClassLoader, compileResult);
this.cache.put(forClass, new CachedTemplate(compileResult, template));
return template;
}
}
@Override
protected final ComponentTemplateCompileResult compile(
ComponentTemplateSource componentTemplateSource,
Class<? extends ViewComponent> forClass,
Reader actualSource
) throws ComponentTemplateCompileErrorException {
if (this.cache.containsKey(forClass)) {
return this.cache.get(forClass).compileResult();
} else {
final ComponentTemplateCompileResult compileResult =
this.doCompile(componentTemplateSource, forClass, actualSource);
this.cache.put(forClass, new CachedTemplate(compileResult, null));
return compileResult;
}
}
protected abstract ComponentTemplateCompileResult doCompile(
ComponentTemplateSource source, ComponentTemplateSource source,
Class<? extends ViewComponent> forClass, Class<? extends ViewComponent> forClass,
Reader sourceReader Reader reader
); ) throws ComponentTemplateCompileErrorException;
} }

View File

@ -5,12 +5,12 @@ import groowt.view.component.ViewComponent;
/** /**
* Represents an exception thrown while attempting to instantiate a ComponentTemplate during compilation. * Represents an exception thrown while attempting to instantiate a ComponentTemplate during compilation.
*/ */
public class ComponentTemplateCompileException extends RuntimeException { public class ComponentTemplateCompileErrorException extends Exception {
private final Class<? extends ViewComponent> forClass; private final Class<? extends ViewComponent> forClass;
private final Object templateSource; private final Object templateSource;
public ComponentTemplateCompileException( public ComponentTemplateCompileErrorException(
String message, String message,
Class<? extends ViewComponent> forClass, Class<? extends ViewComponent> forClass,
Object templateSource Object templateSource
@ -20,7 +20,7 @@ public class ComponentTemplateCompileException extends RuntimeException {
this.templateSource = templateSource; this.templateSource = templateSource;
} }
public ComponentTemplateCompileException( public ComponentTemplateCompileErrorException(
String message, String message,
Throwable cause, Throwable cause,
Class<? extends ViewComponent> forClass, Class<? extends ViewComponent> forClass,
@ -31,7 +31,7 @@ public class ComponentTemplateCompileException extends RuntimeException {
this.templateSource = templateSource; this.templateSource = templateSource;
} }
public ComponentTemplateCompileException( public ComponentTemplateCompileErrorException(
Throwable cause, Throwable cause,
Class<? extends ViewComponent> forClass, Class<? extends ViewComponent> forClass,
Object templateSource Object templateSource
@ -41,6 +41,15 @@ public class ComponentTemplateCompileException extends RuntimeException {
this.templateSource = templateSource; this.templateSource = templateSource;
} }
public ComponentTemplateCompileErrorException(
Class<? extends ViewComponent> forClass,
Object templateSource
) {
super("Compile error in " + templateSource + " for " + forClass.getName());
this.forClass = forClass;
this.templateSource = templateSource;
}
public Class<? extends ViewComponent> getForClass() { public Class<? extends ViewComponent> getForClass() {
return this.forClass; return this.forClass;
} }

View File

@ -1,9 +1,34 @@
package groowt.view.component.compiler; package groowt.view.component.compiler;
import groovy.lang.GroovyClassLoader;
import groowt.view.component.ComponentTemplate; import groowt.view.component.ComponentTemplate;
import groowt.view.component.ViewComponent; import groowt.view.component.ViewComponent;
import groowt.view.component.factory.ComponentTemplateSource; import groowt.view.component.factory.ComponentTemplateSource;
import org.codehaus.groovy.tools.GroovyClass;
import java.io.IOException;
import java.util.List;
public interface ComponentTemplateCompiler { public interface ComponentTemplateCompiler {
ComponentTemplate compile(Class<? extends ViewComponent> forClass, ComponentTemplateSource source);
record ComponentTemplateCompileResult(GroovyClass templateClass, List<GroovyClass> otherClasses) {}
ComponentTemplateCompileResult compile(Class<? extends ViewComponent> forClass, ComponentTemplateSource source)
throws ComponentTemplateCompileErrorException;
ComponentTemplate compileAndGet(
GroovyClassLoader groovyClassLoader,
Class<? extends ViewComponent> forClass,
ComponentTemplateSource source
) throws ComponentTemplateCompileErrorException;
default ComponentTemplate compileAndGet(Class<? extends ViewComponent> forClass, ComponentTemplateSource source)
throws ComponentTemplateCompileErrorException {
try (final GroovyClassLoader groovyClassLoader = new GroovyClassLoader(this.getClass().getClassLoader())) {
return this.compileAndGet(groovyClassLoader, forClass, source);
} catch (IOException ioException) {
throw new RuntimeException(ioException);
}
}
} }

View File

@ -54,7 +54,7 @@ public class DefaultComponentContext implements ComponentContext {
final var missingStack = new LinkedList<>(this.scopeStack); final var missingStack = new LinkedList<>(this.scopeStack);
NoFactoryMissingException first = null; NoFactoryMissingException first = null;
while (!missingStack.isEmpty()) { while (!missingStack.isEmpty()) {
final ComponentScope scope = getStack.pop(); final ComponentScope scope = missingStack.pop();
try { try {
return new DefaultResolved(component, scope.factoryMissing(component)); return new DefaultResolved(component, scope.factoryMissing(component));
} catch (NoFactoryMissingException e) { } catch (NoFactoryMissingException e) {

View File

@ -1,8 +1,6 @@
package groowt.view.component.factory; package groowt.view.component.factory;
import java.io.File; import java.io.*;
import java.io.InputStream;
import java.io.Reader;
import java.net.URI; import java.net.URI;
import java.net.URL; import java.net.URL;
@ -40,6 +38,35 @@ public sealed interface ComponentTemplateSource {
return of(ComponentTemplateSource.class.getClassLoader().getResource(resourceName)); return of(ComponentTemplateSource.class.getClassLoader().getResource(resourceName));
} }
static Reader toReader(ComponentTemplateSource source) {
return switch (source) {
case FileSource(File file) -> {
try {
yield new FileReader(file);
} catch (FileNotFoundException e) {
throw new RuntimeException(e);
}
}
case StringSource(String rawSource) -> new StringReader(rawSource);
case InputStreamSource(InputStream inputStream) -> new InputStreamReader(inputStream);
case URISource(URI uri) -> {
try {
yield new InputStreamReader(uri.toURL().openStream());
} catch (IOException e) {
throw new RuntimeException(e);
}
}
case URLSource(URL url) -> {
try {
yield new InputStreamReader(url.openStream());
} catch (IOException e) {
throw new RuntimeException(e);
}
}
case ReaderSource(Reader reader) -> reader;
};
}
record StringSource(String template) implements ComponentTemplateSource {} record StringSource(String template) implements ComponentTemplateSource {}
record FileSource(File templateFile) implements ComponentTemplateSource {} record FileSource(File templateFile) implements ComponentTemplateSource {}

View File

@ -6,5 +6,5 @@ void consume(out) {
@groovy.transform.Field @groovy.transform.Field
String greeting = 'Hello' String greeting = 'Hello'
--- ---
$greeting, ${consume(out)}! $greeting, ${consume(it)}!
What a nice day. What a nice day.

View File

@ -2,28 +2,60 @@ package groowt.view.web
import groowt.view.component.AbstractViewComponent import groowt.view.component.AbstractViewComponent
import groowt.view.component.ComponentTemplate import groowt.view.component.ComponentTemplate
import groowt.view.component.compiler.ComponentTemplateCompiler
import groowt.view.component.factory.ComponentTemplateSource import groowt.view.component.factory.ComponentTemplateSource
import groowt.view.web.compiler.DefaultWebViewComponentTemplateCompiler
import groowt.view.web.compiler.WebViewComponentTemplateCompiler
import org.codehaus.groovy.control.CompilerConfiguration
class DefaultWebViewComponent extends AbstractWebViewComponent { import java.util.function.Function
DefaultWebViewComponent() {} abstract class DefaultWebViewComponent extends AbstractWebViewComponent {
DefaultWebViewComponent(ComponentTemplate template) { private static final GroovyClassLoader groovyClassLoader =
new GroovyClassLoader(DefaultWebViewComponent.classLoader)
private static final Function<Class, ComponentTemplateCompiler> compilerFunction = { Class givenSelfClass ->
new DefaultWebViewComponentTemplateCompiler(
groovyClassLoader,
CompilerConfiguration.DEFAULT,
givenSelfClass.packageName
)
}
protected DefaultWebViewComponent() {}
protected DefaultWebViewComponent(ComponentTemplate template) {
super(template) super(template)
} }
DefaultWebViewComponent(Class<? extends ComponentTemplate> templateType) { protected DefaultWebViewComponent(Class<? extends ComponentTemplate> templateType) {
super(templateType) super(templateType)
} }
DefaultWebViewComponent(ComponentTemplateSource source) { protected DefaultWebViewComponent(ComponentTemplateSource source) {
super(source) super(source, compilerFunction)
} }
DefaultWebViewComponent(ComponentTemplateSource source, WebViewComponentTemplateCompiler compiler) { protected DefaultWebViewComponent(ComponentTemplateSource source, WebViewComponentTemplateCompiler compiler) {
super(source, compiler) super(source, compiler)
} }
/**
* A convenience constructor which creates a {@link ComponentTemplateSource}
* from the given {@code source} parameter and passes it to super. See
* {@link ComponentTemplateSource} for possible types.
*
* @param source the object passed to {@link ComponentTemplateSource#of}
* @param compiler the compiler to use
*
* @see ComponentTemplateSource
*/
@SuppressWarnings('GroovyAssignabilityCheck')
protected DefaultWebViewComponent(Object source, WebViewComponentTemplateCompiler compiler) {
super(ComponentTemplateSource.of(source), compiler)
}
/** /**
* A convenience constructor which creates a {@link ComponentTemplateSource} * A convenience constructor which creates a {@link ComponentTemplateSource}
* from the given {@code source} parameter and passes it to super. See * from the given {@code source} parameter and passes it to super. See
@ -34,8 +66,8 @@ class DefaultWebViewComponent extends AbstractWebViewComponent {
* @see ComponentTemplateSource * @see ComponentTemplateSource
*/ */
@SuppressWarnings('GroovyAssignabilityCheck') @SuppressWarnings('GroovyAssignabilityCheck')
DefaultWebViewComponent(Object source) { protected DefaultWebViewComponent(Object source) {
super(ComponentTemplateSource.of(source)) super(ComponentTemplateSource.of(source), compilerFunction)
} }
@Override @Override

View File

@ -3,15 +3,17 @@ package groowt.view.web;
import groovy.lang.Closure; import groovy.lang.Closure;
import groowt.view.component.AbstractViewComponent; import groowt.view.component.AbstractViewComponent;
import groowt.view.component.ComponentTemplate; import groowt.view.component.ComponentTemplate;
import groowt.view.component.compiler.ComponentTemplateCompiler;
import groowt.view.component.factory.ComponentTemplateSource; import groowt.view.component.factory.ComponentTemplateSource;
import groowt.view.web.compiler.WebViewComponentTemplateCompiler;
import groowt.view.web.runtime.DefaultWebViewComponentWriter; import groowt.view.web.runtime.DefaultWebViewComponentWriter;
import groowt.view.web.runtime.WebViewComponentWriter; import groowt.view.web.runtime.WebViewComponentWriter;
import org.codehaus.groovy.control.CompilerConfiguration;
import java.io.IOException; import java.io.IOException;
import java.io.Writer; import java.io.Writer;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.function.Function;
public abstract class AbstractWebViewComponent extends AbstractViewComponent implements WebViewComponent { public abstract class AbstractWebViewComponent extends AbstractViewComponent implements WebViewComponent {
@ -27,17 +29,18 @@ public abstract class AbstractWebViewComponent extends AbstractViewComponent imp
super(templateClass); super(templateClass);
} }
protected AbstractWebViewComponent(ComponentTemplateSource source) {
super(source, packageName -> new DefaultWebViewComponentTemplateCompiler(
CompilerConfiguration.DEFAULT,
packageName
));
}
protected AbstractWebViewComponent(ComponentTemplateSource source, WebViewComponentTemplateCompiler compiler) { protected AbstractWebViewComponent(ComponentTemplateSource source, WebViewComponentTemplateCompiler compiler) {
super(source, compiler); super(source, compiler);
} }
@SuppressWarnings("unchecked")
protected AbstractWebViewComponent(
ComponentTemplateSource source,
Function<? super Class<? extends AbstractWebViewComponent>, ? extends ComponentTemplateCompiler> compilerFunction
) {
super(source, selfClass -> compilerFunction.apply((Class<AbstractWebViewComponent>) selfClass));
}
@Override @Override
public List<WebViewChildRenderer> getChildRenderers() { public List<WebViewChildRenderer> getChildRenderers() {
if (this.childRenderers == null) { if (this.childRenderers == null) {

View File

@ -1,180 +0,0 @@
package groowt.view.web;
import groovy.lang.GroovyClassLoader;
import groowt.view.component.ComponentTemplate;
import groowt.view.component.ViewComponent;
import groowt.view.component.compiler.CachingComponentTemplateCompiler;
import groowt.view.component.compiler.ComponentTemplateCompileException;
import groowt.view.component.factory.ComponentTemplateSource;
import groowt.view.web.analysis.MismatchedComponentTypeError;
import groowt.view.web.analysis.MismatchedComponentTypeAnalysis;
import groowt.view.web.antlr.CompilationUnitParseResult;
import groowt.view.web.antlr.ParserUtil;
import groowt.view.web.antlr.TokenList;
import groowt.view.web.ast.DefaultAstBuilder;
import groowt.view.web.ast.DefaultNodeFactory;
import groowt.view.web.ast.node.CompilationUnitNode;
import groowt.view.web.transpile.DefaultGroovyTranspiler;
import groowt.view.web.transpile.DefaultTranspilerConfiguration;
import org.codehaus.groovy.control.CompilationUnit;
import org.codehaus.groovy.control.CompilerConfiguration;
import org.codehaus.groovy.control.Phases;
import org.codehaus.groovy.control.io.AbstractReaderSource;
import org.codehaus.groovy.tools.GroovyClass;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Nullable;
import java.io.IOException;
import java.io.Reader;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.List;
import java.util.Objects;
public class DefaultWebViewComponentTemplateCompiler extends CachingComponentTemplateCompiler
implements WebViewComponentTemplateCompiler {
private final CompilerConfiguration configuration;
private final String defaultPackageName;
private final int phase;
private GroovyClassLoader groovyClassLoader;
public DefaultWebViewComponentTemplateCompiler(CompilerConfiguration configuration, String defaultPackageName) {
this(configuration, defaultPackageName, Phases.CLASS_GENERATION);
}
@ApiStatus.Internal
public DefaultWebViewComponentTemplateCompiler(
CompilerConfiguration configuration,
String defaultPackageName,
int phase
) {
this.configuration = configuration;
this.defaultPackageName = defaultPackageName;
this.phase = phase;
}
protected GroovyClassLoader getGroovyClassLoader() {
if (this.groovyClassLoader == null) {
this.groovyClassLoader = new GroovyClassLoader(this.getClass().getClassLoader());
}
return this.groovyClassLoader;
}
public void setGroovyClassLoader(GroovyClassLoader groovyClassLoader) {
this.groovyClassLoader = Objects.requireNonNull(groovyClassLoader);
}
public void useOwnClassLoader() {
this.groovyClassLoader = null;
}
@Override
protected ComponentTemplate doCompile(
@Nullable ComponentTemplateSource source,
@Nullable Class<? extends ViewComponent> forClass,
Reader sourceReader
) {
if (source instanceof ComponentTemplateSource.URISource uriSource) {
return this.doCompile(forClass, sourceReader, uriSource.templateURI());
} else if (source instanceof ComponentTemplateSource.URLSource urlSource) {
try {
return this.doCompile(forClass, sourceReader, urlSource.templateURL().toURI());
} catch (URISyntaxException e) {
throw new RuntimeException(e);
}
} else {
return this.doCompile(forClass, sourceReader, null);
}
}
protected ComponentTemplate doCompile(
@Nullable Class<? extends ViewComponent> forClass,
Reader reader,
@Nullable URI uri
) {
final CompilationUnitParseResult parseResult = ParserUtil.parseCompilationUnit(reader);
final List<MismatchedComponentTypeError> mismatchedComponentTypeErrors =
MismatchedComponentTypeAnalysis.check(parseResult.getCompilationUnitContext());
final var tokenList = new TokenList(parseResult.getTokenStream());
final var astBuilder = new DefaultAstBuilder(new DefaultNodeFactory(tokenList));
final var cuNode = (CompilationUnitNode) astBuilder.build(parseResult.getCompilationUnitContext());
final var groovyCompilationUnit = new CompilationUnit(this.configuration);
final var transpiler = new DefaultGroovyTranspiler(
groovyCompilationUnit,
this.defaultPackageName,
DefaultTranspilerConfiguration::new
);
final var ownerComponentName = forClass != null ? forClass.getSimpleName() : "AnonymousComponent";
final var templateClassName = ownerComponentName + "Template";
final var fqn = this.defaultPackageName + "." + templateClassName;
final var readerSource = new AbstractReaderSource(this.configuration) {
@Override
public Reader getReader() throws IOException {
reader.reset();
return reader;
}
@Override
public @Nullable URI getURI() {
return uri;
}
@Override
public void cleanup() {
super.cleanup();
try {
reader.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
};
transpiler.transpile(cuNode, tokenList, ownerComponentName, readerSource);
groovyCompilationUnit.compile(this.phase);
final var classes = groovyCompilationUnit.getClasses();
Class<?> templateClass = null;
for (final GroovyClass groovyClass : classes) {
if (groovyClass.getName().equals(fqn)) {
if (templateClass == null) {
templateClass = this.getGroovyClassLoader().defineClass(
groovyClass.getName(), groovyClass.getBytes()
);
} else {
throw new IllegalStateException("Somehow found two classes with same name.");
}
} else {
this.getGroovyClassLoader().defineClass(
groovyClass.getName(), groovyClass.getBytes()
);
}
}
if (templateClass == null) {
throw new IllegalStateException("Did not find templateClass");
}
try {
return (ComponentTemplate) templateClass.getConstructor().newInstance();
} catch (Exception e) {
throw new ComponentTemplateCompileException(e, forClass, reader);
}
}
@Override
public ComponentTemplate compileAnonymous(Reader reader) {
return this.doCompile(null, null, reader);
}
}

View File

@ -1,10 +0,0 @@
package groowt.view.web;
import groowt.view.component.ComponentTemplate;
import groowt.view.component.compiler.ComponentTemplateCompiler;
import java.io.Reader;
public interface WebViewComponentTemplateCompiler extends ComponentTemplateCompiler {
ComponentTemplate compileAnonymous(Reader reader);
}

View File

@ -17,8 +17,8 @@ private fun getErrorMessage(
openType: ComponentTypeContext, openType: ComponentTypeContext,
closingType: ComponentTypeContext closingType: ComponentTypeContext
) = "The component's opening and closing tags' types must match exactly. " + ) = "The component's opening and closing tags' types must match exactly. " +
"Found ${openType.text} at [${SourcePosition.formatStartOfToken(openType.start)}] " + "Found '${openType.text}' at ${SourcePosition.formatStartOfTokenLong(openType.start)} " +
"and ${closingType.text} at [${SourcePosition.formatStartOfToken(closingType.start)}]." "and '${closingType.text}' at ${SourcePosition.formatStartOfTokenLong(closingType.start)}."
private fun test( private fun test(
openIdentifiers: List<Token>, openIdentifiers: List<Token>,
@ -52,7 +52,7 @@ private fun doCheck(tree: ParseTree, destination: MutableList<MismatchedComponen
} }
} }
data class MismatchedComponentTypeError(val tree: ParseTree, val message: String) data class MismatchedComponentTypeError(val component: ComponentWithChildrenContext, val message: String)
fun check(tree: ParseTree): List<MismatchedComponentTypeError> { fun check(tree: ParseTree): List<MismatchedComponentTypeError> {
val result: MutableList<MismatchedComponentTypeError> = ArrayList() val result: MutableList<MismatchedComponentTypeError> = ArrayList()

View File

@ -5,7 +5,6 @@ import org.antlr.v4.runtime.tree.ErrorNode;
import org.antlr.v4.runtime.tree.Tree; import org.antlr.v4.runtime.tree.Tree;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection;
import java.util.List; import java.util.List;
public final class AntlrUtil { public final class AntlrUtil {
@ -36,8 +35,8 @@ public final class AntlrUtil {
return this.nodesWithRecognitionException.size() + this.errorNodes.size(); return this.nodesWithRecognitionException.size() + this.errorNodes.size();
} }
public Collection<Tree> getAll() { public List<Tree> getAll() {
final Collection<Tree> all = new ArrayList<>(this.nodesWithRecognitionException); final List<Tree> all = new ArrayList<>(this.nodesWithRecognitionException);
all.addAll(this.errorNodes); all.addAll(this.errorNodes);
return all; return all;
} }

View File

@ -0,0 +1,330 @@
package groowt.view.web.compiler;
import groovy.lang.GroovyClassLoader;
import groowt.view.component.AbstractViewComponent;
import groowt.view.component.ComponentTemplate;
import groowt.view.component.ViewComponent;
import groowt.view.component.compiler.CachingComponentTemplateCompiler;
import groowt.view.component.compiler.ComponentTemplateCompileErrorException;
import groowt.view.component.context.ComponentContext;
import groowt.view.component.factory.ComponentTemplateSource;
import groowt.view.web.analysis.MismatchedComponentTypeAnalysis;
import groowt.view.web.analysis.MismatchedComponentTypeError;
import groowt.view.web.antlr.AntlrUtil;
import groowt.view.web.antlr.CompilationUnitParseResult;
import groowt.view.web.antlr.ParserUtil;
import groowt.view.web.antlr.TokenList;
import groowt.view.web.ast.DefaultAstBuilder;
import groowt.view.web.ast.DefaultNodeFactory;
import groowt.view.web.ast.node.CompilationUnitNode;
import groowt.view.web.transpile.DefaultGroovyTranspiler;
import groowt.view.web.transpile.DefaultTranspilerConfiguration;
import groowt.view.web.util.SourcePosition;
import org.antlr.v4.runtime.ParserRuleContext;
import org.antlr.v4.runtime.Token;
import org.antlr.v4.runtime.tree.TerminalNode;
import org.antlr.v4.runtime.tree.Tree;
import org.codehaus.groovy.control.CompilationFailedException;
import org.codehaus.groovy.control.CompilationUnit;
import org.codehaus.groovy.control.CompilerConfiguration;
import org.codehaus.groovy.control.Phases;
import org.codehaus.groovy.control.io.AbstractReaderSource;
import org.codehaus.groovy.tools.GroovyClass;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Nullable;
import java.io.IOException;
import java.io.Reader;
import java.io.Writer;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.List;
public class DefaultWebViewComponentTemplateCompiler extends CachingComponentTemplateCompiler
implements WebViewComponentTemplateCompiler {
protected static final class AnonymousWebViewComponent extends AbstractViewComponent {
// DO NOT INSTANTIATE, this is merely a marker class
private AnonymousWebViewComponent() {
throw new UnsupportedOperationException();
}
@Override
public void setContext(ComponentContext context) {
throw new UnsupportedOperationException();
}
@Override
public ComponentContext getContext() {
throw new UnsupportedOperationException();
}
@Override
protected ComponentTemplate getTemplate() {
throw new UnsupportedOperationException();
}
@Override
protected void setTemplate(ComponentTemplate template) {
throw new UnsupportedOperationException();
}
@Override
protected void beforeRender() {
throw new UnsupportedOperationException();
}
@Override
protected void afterRender() {
throw new UnsupportedOperationException();
}
@Override
public void renderTo(Writer out) {
throw new UnsupportedOperationException();
}
@Override
protected Class<? extends AbstractViewComponent> getSelfClass() {
throw new UnsupportedOperationException();
}
}
private final CompilerConfiguration configuration;
private final String defaultPackageName;
private final int phase;
public DefaultWebViewComponentTemplateCompiler(
GroovyClassLoader groovyClassLoader,
CompilerConfiguration configuration,
String defaultPackageName
) {
this(groovyClassLoader, configuration, defaultPackageName, Phases.CLASS_GENERATION);
}
@ApiStatus.Internal
public DefaultWebViewComponentTemplateCompiler(
GroovyClassLoader groovyClassLoader,
CompilerConfiguration configuration,
String defaultPackageName,
int phase
) {
super(groovyClassLoader);
this.configuration = configuration;
this.defaultPackageName = defaultPackageName;
this.phase = phase;
}
protected WebViewComponentTemplateCompileException getException(
TerminalNode terminalNode,
Class<? extends ViewComponent> forClass,
Reader reader
) {
final Token offending = terminalNode.getSymbol();
return new WebViewComponentTemplateCompileException(
"Compile error on token at " + SourcePosition.fromStartOfToken(offending).toStringLong() + ".",
forClass,
reader,
offending
);
}
protected WebViewComponentTemplateCompileException getException(
ParserRuleContext parserRuleContext,
Class<? extends ViewComponent> forClass,
Reader reader
) {
return new WebViewComponentTemplateCompileException(
"Compile error at " + SourcePosition.fromStartOfToken(parserRuleContext.getStart()).toStringLong()
+ ".",
forClass,
reader,
parserRuleContext
);
}
protected WebViewComponentTemplateCompileException mapToErrorException(
Tree tree,
Class<? extends ViewComponent> forClass,
Reader reader
) {
if (tree instanceof TerminalNode terminalNode) {
return getException(terminalNode, forClass, reader);
} else if (tree instanceof ParserRuleContext parserRuleContext) {
return getException(parserRuleContext, forClass, reader);
} else {
return new WebViewComponentTemplateCompileException(
"Compile error with " + tree + ".",
forClass,
reader,
tree
);
}
}
protected WebViewComponentTemplateCompileException mapToErrorException(
MismatchedComponentTypeError error,
Class<? extends ViewComponent> forClass,
Reader reader
) {
return new WebViewComponentTemplateCompileException(
error.getMessage(),
forClass,
reader,
error.getComponent()
);
}
protected ComponentTemplateCompileResult doCompile(
Class<? extends ViewComponent> forClass,
Reader reader,
@Nullable URI uri
) throws ComponentTemplateCompileErrorException {
final CompilationUnitParseResult parseResult = ParserUtil.parseCompilationUnit(reader);
// check for parser/lexer errors
final var parseErrors = AntlrUtil.findErrorNodes(parseResult.getCompilationUnitContext());
if (!parseErrors.isEmpty()) {
if (parseErrors.getErrorCount() == 1) {
final var errorNode = parseErrors.getAll().getFirst();
throw mapToErrorException(errorNode, forClass, reader);
} else {
final var errorExceptions = parseErrors.getAll().stream()
.map(errorNode -> mapToErrorException(errorNode, forClass, reader))
.toList();
throw new MultipleWebViewComponentCompileErrorsException(errorExceptions, forClass, reader);
}
}
// check for mismatched type errors
final List<MismatchedComponentTypeError> mismatchedComponentTypeErrors =
MismatchedComponentTypeAnalysis.check(parseResult.getCompilationUnitContext());
if (!mismatchedComponentTypeErrors.isEmpty()) {
if (mismatchedComponentTypeErrors.size() == 1) {
throw mapToErrorException(mismatchedComponentTypeErrors.getFirst(), forClass, reader);
} else {
final var errorExceptions = mismatchedComponentTypeErrors.stream()
.map(error -> mapToErrorException(error, forClass, reader))
.toList();
throw new MultipleWebViewComponentCompileErrorsException(
errorExceptions,
forClass,
reader
);
}
}
// build ast
final var tokenList = new TokenList(parseResult.getTokenStream());
final var astBuilder = new DefaultAstBuilder(new DefaultNodeFactory(tokenList));
final var cuNode = (CompilationUnitNode) astBuilder.build(parseResult.getCompilationUnitContext());
// transpile to Groovy
final var groovyCompilationUnit = new CompilationUnit(this.configuration);
final var transpiler = new DefaultGroovyTranspiler(
groovyCompilationUnit,
this.defaultPackageName,
DefaultTranspilerConfiguration::new
);
final var ownerComponentName = forClass != null ? forClass.getSimpleName() : "AnonymousComponent" + System.nanoTime();
final var templateClassName = ownerComponentName + "Template";
final var fqn = this.defaultPackageName + "." + templateClassName;
final var readerSource = new AbstractReaderSource(this.configuration) {
@Override
public Reader getReader() throws IOException {
reader.reset();
return reader;
}
@Override
public @Nullable URI getURI() {
return uri;
}
@Override
public void cleanup() {
super.cleanup();
try {
reader.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
};
transpiler.transpile(cuNode, tokenList, ownerComponentName, readerSource);
try {
groovyCompilationUnit.compile(this.phase);
} catch (CompilationFailedException compilationFailedException) {
throw new WebViewComponentTemplateCompileException(
"Error while compiling Groovy in " + templateClassName + " for component class " +
forClass.getName() + ".",
compilationFailedException,
forClass,
forClass,
reader
);
}
// get the classes
final var allClasses = groovyCompilationUnit.getClasses();
GroovyClass templateGroovyClass = null;
final List<GroovyClass> otherClasses = new ArrayList<>();
for (final GroovyClass groovyClass : allClasses) {
if (groovyClass.getName().equals(fqn)) {
if (templateGroovyClass != null) {
throw new IllegalStateException("Already found a templateGroovyClass.");
}
templateGroovyClass = groovyClass;
} else {
otherClasses.add(groovyClass);
}
}
if (templateGroovyClass == null) {
throw new IllegalStateException("Did not find templateClass");
}
return new ComponentTemplateCompileResult(templateGroovyClass, otherClasses);
}
@Override
protected ComponentTemplateCompileResult doCompile(
@Nullable ComponentTemplateSource source,
Class<? extends ViewComponent> forClass,
Reader sourceReader
) throws ComponentTemplateCompileErrorException {
if (source instanceof ComponentTemplateSource.URISource uriSource) {
return this.doCompile(forClass, sourceReader, uriSource.templateURI());
} else if (source instanceof ComponentTemplateSource.URLSource urlSource) {
try {
return this.doCompile(forClass, sourceReader, urlSource.templateURL().toURI());
} catch (URISyntaxException e) {
throw new RuntimeException(e);
}
} else {
return this.doCompile(forClass, sourceReader, null);
}
}
@Override
public ComponentTemplateCompileResult compileAnonymous(ComponentTemplateSource source)
throws ComponentTemplateCompileErrorException {
return this.compile(AnonymousWebViewComponent.class, source);
}
@Override
public ComponentTemplate compileAndGetAnonymous(ComponentTemplateSource source) throws ComponentTemplateCompileErrorException {
return this.compileAndGet(AnonymousWebViewComponent.class, source);
}
}

View File

@ -0,0 +1,36 @@
package groowt.view.web.compiler;
import groowt.view.component.ViewComponent;
import groowt.view.component.compiler.ComponentTemplateCompileErrorException;
import java.util.ArrayList;
import java.util.List;
public class MultipleWebViewComponentCompileErrorsException extends ComponentTemplateCompileErrorException {
private final List<Throwable> errors = new ArrayList<>();
public MultipleWebViewComponentCompileErrorsException(
String message,
List<? extends Throwable> errors,
Class<? extends ViewComponent> forClass,
Object templateSource
) {
super(message, forClass, templateSource);
this.errors.addAll(errors);
}
public MultipleWebViewComponentCompileErrorsException(
List<? extends Throwable> errors,
Class<? extends ViewComponent> forClass,
Object templateSource
) {
super(forClass, templateSource);
this.errors.addAll(errors);
}
public List<Throwable> getErrors() {
return this.errors;
}
}

View File

@ -1,19 +1,19 @@
package groowt.view.web; package groowt.view.web.compiler;
import groowt.view.component.ViewComponent; import groowt.view.component.ViewComponent;
import groowt.view.component.compiler.ComponentTemplateCompileException; import groowt.view.component.compiler.ComponentTemplateCompileErrorException;
import groowt.view.web.ast.node.Node; import groowt.view.web.ast.node.Node;
import groowt.view.web.util.SourcePosition; import groowt.view.web.util.SourcePosition;
public class WebViewComponentTemplateCompileException extends ComponentTemplateCompileException { public class WebViewComponentTemplateCompileException extends ComponentTemplateCompileErrorException {
private final Node node; private final Object node;
public WebViewComponentTemplateCompileException( public WebViewComponentTemplateCompileException(
String message, String message,
Class<? extends ViewComponent> forClass, Class<? extends ViewComponent> forClass,
Object templateSource, Object templateSource,
Node node Object node
) { ) {
super(message, forClass, templateSource); super(message, forClass, templateSource);
this.node = node; this.node = node;
@ -24,7 +24,7 @@ public class WebViewComponentTemplateCompileException extends ComponentTemplateC
Throwable cause, Throwable cause,
Class<? extends ViewComponent> forClass, Class<? extends ViewComponent> forClass,
Object templateSource, Object templateSource,
Node node Object node
) { ) {
super(message, cause, forClass, templateSource); super(message, cause, forClass, templateSource);
this.node = node; this.node = node;
@ -34,20 +34,24 @@ public class WebViewComponentTemplateCompileException extends ComponentTemplateC
Throwable cause, Throwable cause,
Class<? extends ViewComponent> forClass, Class<? extends ViewComponent> forClass,
Object templateSource, Object templateSource,
Node node Object node
) { ) {
super(cause, forClass, templateSource); super(cause, forClass, templateSource);
this.node = node; this.node = node;
} }
public Node getNode() { public Object getNode() {
return this.node; return this.node;
} }
@Override @Override
public String getMessage() { public String getMessage() {
final SourcePosition start = this.node.getTokenRange().getStartPosition(); if (this.node instanceof Node asNode) {
return "Line " + start.line() + ", column " + start.column() + ": " + super.getMessage(); final SourcePosition start = asNode.getTokenRange().getStartPosition();
return "At " + start.toStringLong() + ": " + super.getMessage();
} else {
return super.getMessage();
}
} }
} }

View File

@ -0,0 +1,11 @@
package groowt.view.web.compiler;
import groowt.view.component.ComponentTemplate;
import groowt.view.component.compiler.ComponentTemplateCompileErrorException;
import groowt.view.component.compiler.ComponentTemplateCompiler;
import groowt.view.component.factory.ComponentTemplateSource;
public interface WebViewComponentTemplateCompiler extends ComponentTemplateCompiler {
ComponentTemplateCompileResult compileAnonymous(ComponentTemplateSource source) throws ComponentTemplateCompileErrorException;
ComponentTemplate compileAndGetAnonymous(ComponentTemplateSource source) throws ComponentTemplateCompileErrorException;
}

View File

@ -6,10 +6,14 @@ public record SourcePosition(int line, int column) {
public static final SourcePosition UNKNOWN = new SourcePosition(-1, -1); public static final SourcePosition UNKNOWN = new SourcePosition(-1, -1);
public static String formatStartOfToken(Token token) { public static String formatStartOfTokenShort(Token token) {
return fromStartOfToken(token).toStringShort(); return fromStartOfToken(token).toStringShort();
} }
public static String formatStartOfTokenLong(Token token) {
return fromStartOfToken(token).toStringLong();
}
public static SourcePosition fromStartOfToken(Token token) { public static SourcePosition fromStartOfToken(Token token) {
return new SourcePosition(token.getLine(), token.getCharPositionInLine() + 1); return new SourcePosition(token.getLine(), token.getCharPositionInLine() + 1);
} }
@ -46,4 +50,8 @@ public record SourcePosition(int line, int column) {
return this.line + "," + this.column; return this.line + "," + this.column;
} }
public String toStringLong() {
return "line " + this.line + ", column " + this.column;
}
} }

View File

@ -1,12 +1,13 @@
package groowt.view.web package groowt.view.web
import groowt.view.component.factory.ComponentFactoryBase import groowt.view.component.factory.ComponentFactoryBase
import groowt.view.web.lib.AbstractWebViewComponentTests
import groowt.view.web.lib.WithContext import groowt.view.web.lib.WithContext
import org.junit.jupiter.api.Test import org.junit.jupiter.api.Test
import static org.junit.jupiter.api.Assertions.assertEquals import static org.junit.jupiter.api.Assertions.assertEquals
class DefaultWebViewComponentTests implements WithContext { class DefaultWebViewComponentTests extends AbstractWebViewComponentTests {
private static final class Greeter extends DefaultWebViewComponent { private static final class Greeter extends DefaultWebViewComponent {
@ -41,8 +42,7 @@ class DefaultWebViewComponentTests implements WithContext {
@Test @Test
void withPreambleImport() { void withPreambleImport() {
def c = new DefaultWebViewComponent( this.doTest('''
'''
--- ---
import groovy.transform.Field import groovy.transform.Field
@ -50,10 +50,7 @@ class DefaultWebViewComponentTests implements WithContext {
String greeting = 'Hello, World!' String greeting = 'Hello, World!'
--- ---
$greeting $greeting
'''.stripIndent().trim() '''.stripIndent().trim(), "Hello, World!")
)
c.context = this.context()
assertEquals("Hello, World!", c.render())
} }
@Test @Test
@ -62,9 +59,7 @@ class DefaultWebViewComponentTests implements WithContext {
this.configureContext(it) this.configureContext(it)
currentScope.add('Greeter', new GreeterFactory()) currentScope.add('Greeter', new GreeterFactory())
} }
def c = new DefaultWebViewComponent('<Greeter target="World" />') this.doTest('<Greeter target="World" />', 'Hello, World!', context)
c.context = context
assertEquals('Hello, World!', c.render())
} }
@Test @Test
@ -74,9 +69,7 @@ class DefaultWebViewComponentTests implements WithContext {
currentScope.add('UsingGreeter') { new UsingGreeter() } currentScope.add('UsingGreeter') { new UsingGreeter() }
currentScope.add('Greeter', new GreeterFactory()) currentScope.add('Greeter', new GreeterFactory())
} }
def c = new DefaultWebViewComponent('<UsingGreeter />') this.doTest('<UsingGreeter />', 'Hello, World!', context)
c.context = context
assertEquals('Hello, World!', c.render())
} }
} }

View File

@ -2,8 +2,9 @@ package groowt.view.web.lib
import groowt.view.component.context.ComponentContext import groowt.view.component.context.ComponentContext
import groowt.view.web.DefaultWebViewComponentTemplateCompiler import groowt.view.component.factory.ComponentTemplateSource
import groowt.view.web.WebViewComponentTemplateCompiler import groowt.view.web.compiler.DefaultWebViewComponentTemplateCompiler
import groowt.view.web.compiler.WebViewComponentTemplateCompiler
import groowt.view.web.runtime.DefaultWebViewComponentWriter import groowt.view.web.runtime.DefaultWebViewComponentWriter
import org.codehaus.groovy.control.CompilerConfiguration import org.codehaus.groovy.control.CompilerConfiguration
@ -13,13 +14,14 @@ abstract class AbstractWebViewComponentTests implements WithContext {
protected WebViewComponentTemplateCompiler compiler() { protected WebViewComponentTemplateCompiler compiler() {
new DefaultWebViewComponentTemplateCompiler( new DefaultWebViewComponentTemplateCompiler(
new GroovyClassLoader(this.class.classLoader),
CompilerConfiguration.DEFAULT, CompilerConfiguration.DEFAULT,
this.class.packageName this.class.packageName
) )
} }
protected void doTest(Reader source, String expected, ComponentContext context) { protected void doTest(Reader sourceReader, String expected, ComponentContext context) {
def template = this.compiler().compileAnonymous(source) def template = this.compiler().compileAndGetAnonymous(ComponentTemplateSource.of(sourceReader))
def renderer = template.getRenderer() def renderer = template.getRenderer()
def sw = new StringWriter() def sw = new StringWriter()
def out = new DefaultWebViewComponentWriter(sw) def out = new DefaultWebViewComponentWriter(sw)

View File

@ -1,7 +1,10 @@
package groowt.view.web.tools package groowt.view.web.tools
import groowt.view.component.context.DefaultComponentContext import groowt.view.component.context.DefaultComponentContext
import groowt.view.web.DefaultWebViewComponent import groowt.view.component.factory.ComponentTemplateSource
import groowt.view.web.compiler.DefaultWebViewComponentTemplateCompiler
import groowt.view.web.runtime.DefaultWebViewComponentWriter
import org.codehaus.groovy.control.CompilerConfiguration
import picocli.CommandLine import picocli.CommandLine
import picocli.CommandLine.Command import picocli.CommandLine.Command
import picocli.CommandLine.Option import picocli.CommandLine.Option
@ -26,13 +29,20 @@ class RunTemplate implements Callable<Integer> {
@Override @Override
Integer call() throws Exception { Integer call() throws Exception {
def component = new DefaultWebViewComponent(this.template) def gcl = new GroovyClassLoader(this.class.classLoader)
def compiler = new DefaultWebViewComponentTemplateCompiler(
gcl,
CompilerConfiguration.DEFAULT,
'groowt.view.web.tools'
)
def template = compiler.compileAndGetAnonymous(ComponentTemplateSource.of(this.template))
def context = new DefaultComponentContext() def context = new DefaultComponentContext()
context.pushDefaultScope() context.pushDefaultScope()
component.context = context
println component.render() def componentWriter = new DefaultWebViewComponentWriter(new OutputStreamWriter(System.out))
template.renderer.call(context, componentWriter)
return 0 return 0
} }