Compare commits
	
		
			8 Commits
		
	
	
		
			04866b4d3a
			...
			2b935da385
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|   | 2b935da385 | ||
|   | 7a28b0530d | ||
|   | 45d188d064 | ||
|   | bd4dee98fa | ||
|   | 1a528465f9 | ||
|   | 5ce934b3fe | ||
|   | 74c5698d1b | ||
|   | 525932668f | 
							
								
								
									
										9
									
								
								TODO.md
									
									
									
									
									
								
							
							
						
						
									
										9
									
								
								TODO.md
									
									
									
									
									
								
							| @ -24,9 +24,12 @@ For example: | ||||
| - [ ] Get rid of wvc compiler dependency on di | ||||
| 
 | ||||
| ## 0.1.3 | ||||
| - [ ] refactor tools/gradle start scripts to use dist instead of custom bin script | ||||
|   - [ ] have custom bin/* scripts which point to dist(s) for convenience | ||||
| - [ ] di bug: @Singleton toSelf() causes stack overflow | ||||
| - [ ] ~~refactor tools/gradle start scripts to use dist instead of custom bin script~~ | ||||
|   - [ ] ~~have custom bin/* scripts which point to dist(s) for convenience~~ | ||||
| - [x] di bug: @Singleton toSelf() causes stack overflow | ||||
| - [x] wvcc bug: Nested static view classes are not seen by compiler | ||||
|   - This required tweaking how the configurations are passed around. Ultimately, we should strive for less complexity | ||||
|     in this regard. | ||||
| - [ ] `OutletContainer` trait or interface for components which can contain an `<Outlet />` child. | ||||
| - [ ] `Context` should have methods for simply finding an ancestor of a certain type without the need for a predicate. | ||||
| 
 | ||||
|  | ||||
| @ -3,31 +3,58 @@ package groowt.util.di; | ||||
| import org.jetbrains.annotations.Nullable; | ||||
| 
 | ||||
| import java.lang.annotation.Annotation; | ||||
| import java.util.ArrayList; | ||||
| import java.util.Collection; | ||||
| import java.util.List; | ||||
| import java.util.*; | ||||
| import java.util.function.Consumer; | ||||
| import java.util.function.Function; | ||||
| import java.util.function.Predicate; | ||||
| 
 | ||||
| public class DefaultRegistry implements Registry { | ||||
| 
 | ||||
|     protected record ClassKeyBinding<T>(Class<T> key, Binding<T> binding) {} | ||||
|     protected static class BindingContainer { | ||||
| 
 | ||||
|     protected final Collection<ClassKeyBinding<?>> classBindings = new ArrayList<>(); | ||||
|         private final Map<Class<?>, Binding<?>> bindings = new HashMap<>(); | ||||
| 
 | ||||
|         @SuppressWarnings("unchecked") | ||||
|         public <T> @Nullable Binding<T> get(Class<T> key) { | ||||
|             for (final var entry : bindings.entrySet()) { | ||||
|                 if (entry.getKey().isAssignableFrom(key)) { | ||||
|                     return (Binding<T>) entry.getValue(); | ||||
|                 } | ||||
|             } | ||||
|             return null; | ||||
|         } | ||||
| 
 | ||||
|         public <T> void put(Class<T> key, Binding<T> binding) { | ||||
|             this.bindings.put(key, binding); | ||||
|         } | ||||
| 
 | ||||
|         public void remove(Class<?> key) { | ||||
|             this.bindings.remove(key); | ||||
|         } | ||||
| 
 | ||||
|         public <T> void removeIf(Class<T> key, Predicate<? super Binding<T>> filter) { | ||||
|             if (filter.test(this.get(key))) { | ||||
|                 this.bindings.remove(key); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         public void clear() { | ||||
|             this.bindings.clear(); | ||||
|         } | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
|     protected final BindingContainer bindingContainer = new BindingContainer(); | ||||
|     protected final Collection<RegistryExtension> extensions = new ArrayList<>(); | ||||
| 
 | ||||
|     @Override | ||||
|     public void removeBinding(Class<?> key) { | ||||
|         this.classBindings.removeIf(classKeyBinding -> classKeyBinding.key().equals(key)); | ||||
|         this.bindingContainer.remove(key); | ||||
|     } | ||||
| 
 | ||||
|     @SuppressWarnings("unchecked") | ||||
|     @Override | ||||
|     public <T> void removeBindingIf(Class<T> key, Predicate<Binding<T>> filter) { | ||||
|         this.classBindings.removeIf(classKeyBinding -> | ||||
|                 classKeyBinding.key().equals(key) && filter.test((Binding<T>) classKeyBinding.binding()) | ||||
|         ); | ||||
|         this.bindingContainer.removeIf(key, filter); | ||||
|     } | ||||
| 
 | ||||
|     private <E extends RegistryExtension> List<E> getAllRegistryExtensions(Class<E> extensionType) { | ||||
| @ -117,18 +144,12 @@ public class DefaultRegistry implements Registry { | ||||
|     public <T> void bind(Class<T> key, Consumer<? super BindingConfigurator<T>> configure) { | ||||
|         final var configurator = new SimpleBindingConfigurator<>(key); | ||||
|         configure.accept(configurator); | ||||
|         this.classBindings.add(new ClassKeyBinding<>(key, configurator.getBinding())); | ||||
|         this.bindingContainer.put(key, configurator.getBinding()); | ||||
|     } | ||||
| 
 | ||||
|     @SuppressWarnings("unchecked") | ||||
|     @Override | ||||
|     public @Nullable <T> Binding<T> getBinding(Class<T> key) { | ||||
|         for (final var classKeyBinding : this.classBindings) { | ||||
|             if (key.isAssignableFrom(classKeyBinding.key())) { | ||||
|                 return (Binding<T>) classKeyBinding.binding(); | ||||
|             } | ||||
|         } | ||||
|         return null; | ||||
|     public <T> @Nullable Binding<T> getBinding(Class<T> key) { | ||||
|         return this.bindingContainer.get(key); | ||||
|     } | ||||
| 
 | ||||
|     private KeyBinder<?> findKeyBinder(Class<?> keyClass) { | ||||
| @ -189,7 +210,7 @@ public class DefaultRegistry implements Registry { | ||||
| 
 | ||||
|     @Override | ||||
|     public void clearAllBindings() { | ||||
|         this.classBindings.clear(); | ||||
|         this.bindingContainer.clear(); | ||||
|         for (final var extension : this.extensions) { | ||||
|             if (extension instanceof KeyBinder<?> keyBinder) { | ||||
|                 keyBinder.clearAllBindings(); | ||||
|  | ||||
| @ -1,8 +1,9 @@ | ||||
| package groowt.util.di; | ||||
| 
 | ||||
| import jakarta.inject.Provider; | ||||
| import jakarta.inject.Singleton; | ||||
| 
 | ||||
| import static groowt.util.di.BindingUtil.toSingleton; | ||||
| import static groowt.util.di.BindingUtil.toLazySingleton; | ||||
| 
 | ||||
| public final class SingletonScopeHandler implements ScopeHandler<Singleton> { | ||||
| 
 | ||||
| @ -19,12 +20,22 @@ public final class SingletonScopeHandler implements ScopeHandler<Singleton> { | ||||
|             RegistryObjectFactory objectFactory | ||||
|     ) { | ||||
|         final Binding<T> potentialBinding = this.owner.getBinding(dependencyClass); | ||||
|         if (potentialBinding != null) { | ||||
|             return potentialBinding; | ||||
|         } else { | ||||
|             this.owner.bind(dependencyClass, toSingleton(objectFactory.createInstance(dependencyClass))); | ||||
|             return this.owner.getBinding(dependencyClass); | ||||
|         } | ||||
|         return switch (potentialBinding) { | ||||
|             case ClassBinding<T>(Class<T> from, Class<? extends T> to) -> { | ||||
|                 this.owner.bind(from, toLazySingleton(() -> objectFactory.createInstance(to))); | ||||
|                 yield this.owner.getBinding(from); | ||||
|             } | ||||
|             case ProviderBinding<T>(Class<T> from, Provider<? extends T> provider) -> { | ||||
|                 this.owner.bind(from, toLazySingleton(provider::get)); | ||||
|                 yield this.owner.getBinding(from); | ||||
|             } | ||||
|             case SingletonBinding<T> singletonBinding -> singletonBinding; | ||||
|             case LazySingletonBinding<T> lazySingletonBinding -> lazySingletonBinding; | ||||
|             case null -> { | ||||
|                 this.owner.bind(dependencyClass, toLazySingleton(() -> objectFactory.createInstance(dependencyClass))); | ||||
|                 yield this.owner.getBinding(dependencyClass); | ||||
|             } | ||||
|         }; | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|  | ||||
| @ -2,6 +2,7 @@ package groowt.util.di; | ||||
| 
 | ||||
| import jakarta.inject.Inject; | ||||
| import jakarta.inject.Named; | ||||
| import jakarta.inject.Singleton; | ||||
| import org.junit.jupiter.api.Test; | ||||
| 
 | ||||
| import static groowt.util.di.BindingUtil.*; | ||||
| @ -250,4 +251,25 @@ public class DefaultRegistryObjectFactoryTests { | ||||
|         assertEquals("Given Greeting", g.greet()); | ||||
|     } | ||||
| 
 | ||||
|     @Singleton | ||||
|     public static final class SingletonGreeter implements Greeter { | ||||
| 
 | ||||
|         @Override | ||||
|         public String greet() { | ||||
|             return "Hello, World!"; | ||||
|         } | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
|     @Test | ||||
|     public void singletonDoesNotOverflow() { | ||||
|         final var b = DefaultRegistryObjectFactory.Builder.withDefaults(); | ||||
|         b.configureRegistry(r -> { | ||||
|             r.bind(SingletonGreeter.class, toSelf()); | ||||
|         }); | ||||
|         final var f = b.build(); | ||||
|         final var g = f.get(SingletonGreeter.class); | ||||
|         assertEquals("Hello, World!", g.greet()); | ||||
|     } | ||||
| 
 | ||||
| } | ||||
|  | ||||
| @ -10,41 +10,8 @@ public abstract class CachingComponentTemplateCompiler<U extends ComponentTempla | ||||
| 
 | ||||
|     private final Map<Class<? extends ViewComponent>, ComponentTemplateCompileResult> cache = new HashMap<>(); | ||||
| 
 | ||||
| //    private ComponentTemplate instantiate( | ||||
| //            GroovyClassLoader groovyClassLoader, | ||||
| //            CompileResult 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 ComponentTemplateCompileResult compile(U compileUnit) | ||||
|             throws ComponentTemplateCompileException { | ||||
|     public final ComponentTemplateCompileResult compile(U compileUnit) throws ComponentTemplateCompileException { | ||||
|         if (this.cache.containsKey(compileUnit.getForClass())) { | ||||
|             return this.cache.get(compileUnit.getForClass()); | ||||
|         } else { | ||||
|  | ||||
| @ -4,16 +4,9 @@ import groowt.view.component.ViewComponent; | ||||
| import groowt.view.component.compiler.source.ComponentTemplateSource; | ||||
| 
 | ||||
| public interface ComponentTemplateCompileUnit { | ||||
| 
 | ||||
|     String getDescriptiveName(); | ||||
|     Class<? extends ViewComponent> getForClass(); | ||||
|     String getDefaultPackageName(); | ||||
|     ComponentTemplateSource getSource(); | ||||
|     ComponentTemplateCompileResult compile(ComponentTemplateCompilerConfiguration configuration) | ||||
|             throws ComponentTemplateCompileException; | ||||
| 
 | ||||
|     default ComponentTemplateCompileResult compile() throws ComponentTemplateCompileException { | ||||
|         return this.compile(new DefaultComponentTemplateCompilerConfiguration()); | ||||
|     } | ||||
| 
 | ||||
|     ComponentTemplateCompileResult compile() throws ComponentTemplateCompileException; | ||||
| } | ||||
|  | ||||
| @ -1,11 +0,0 @@ | ||||
| package groowt.view.component.compiler; | ||||
| 
 | ||||
| import groovy.lang.GroovyClassLoader; | ||||
| import org.codehaus.groovy.control.CompilePhase; | ||||
| import org.codehaus.groovy.control.CompilerConfiguration; | ||||
| 
 | ||||
| public interface ComponentTemplateCompilerConfiguration { | ||||
|     GroovyClassLoader getGroovyClassLoader(); | ||||
|     CompilerConfiguration getGroovyCompilerConfiguration(); | ||||
|     CompilePhase getToCompilePhase(); | ||||
| } | ||||
| @ -1,48 +0,0 @@ | ||||
| package groowt.view.component.compiler; | ||||
| 
 | ||||
| import groovy.lang.GroovyClassLoader; | ||||
| import org.codehaus.groovy.control.CompilePhase; | ||||
| import org.codehaus.groovy.control.CompilerConfiguration; | ||||
| 
 | ||||
| import static java.util.Objects.requireNonNull; | ||||
| 
 | ||||
| public class DefaultComponentTemplateCompilerConfiguration implements ComponentTemplateCompilerConfiguration { | ||||
| 
 | ||||
|     private GroovyClassLoader groovyClassLoader; | ||||
|     private CompilerConfiguration groovyCompilerConfiguration; | ||||
|     private CompilePhase toCompilePhase; | ||||
| 
 | ||||
|     public DefaultComponentTemplateCompilerConfiguration() { | ||||
|         this.groovyClassLoader = new GroovyClassLoader(Thread.currentThread().getContextClassLoader()); | ||||
|         this.groovyCompilerConfiguration = new CompilerConfiguration(); | ||||
|         this.toCompilePhase = CompilePhase.CLASS_GENERATION; | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public GroovyClassLoader getGroovyClassLoader() { | ||||
|         return this.groovyClassLoader; | ||||
|     } | ||||
| 
 | ||||
|     public void setGroovyClassLoader(GroovyClassLoader groovyClassLoader) { | ||||
|         this.groovyClassLoader = requireNonNull(groovyClassLoader); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public CompilerConfiguration getGroovyCompilerConfiguration() { | ||||
|         return this.groovyCompilerConfiguration; | ||||
|     } | ||||
| 
 | ||||
|     public void setGroovyCompilerConfiguration(CompilerConfiguration groovyCompilerConfiguration) { | ||||
|         this.groovyCompilerConfiguration = requireNonNull(groovyCompilerConfiguration); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public CompilePhase getToCompilePhase() { | ||||
|         return this.toCompilePhase; | ||||
|     } | ||||
| 
 | ||||
|     public void setToCompilePhase(CompilePhase toCompilePhase) { | ||||
|         this.toCompilePhase = requireNonNull(toCompilePhase); | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -199,7 +199,9 @@ tasks.register('uberJar', Jar) { | ||||
|     group 'groovyc' | ||||
|     archiveBaseName = 'web-view-components-uber' | ||||
|     from sourceSets.main.output | ||||
|     from sourceSets.main.runtimeClasspath.filter(File.&exists).collect { it.isDirectory() ? it : zipTree(it) } | ||||
|     from sourceSets.main.runtimeClasspath | ||||
|             .filter(File.&exists) | ||||
|             .collect { it.isDirectory() ? it : zipTree(it) } | ||||
|     duplicatesStrategy = DuplicatesStrategy.EXCLUDE | ||||
| } | ||||
| 
 | ||||
|  | ||||
| @ -4,14 +4,15 @@ if [ "$1" == "--debug" ]; then | ||||
|   shift | ||||
|   gradle -q uberJar && \ | ||||
|     java -agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=*:8192 \ | ||||
|       -cp build/libs/web-view-components-uber-0.1.0.jar \ | ||||
|       -cp build/libs/web-view-components-uber-0.1.2.jar \ | ||||
|       org.codehaus.groovy.tools.FileSystemCompiler \ | ||||
|       --configscript src/main/resources/groowt/view/component/web/groovyc/groovycConfigurationScript.groovy \ | ||||
|       -d groovyc-out \ | ||||
|       "$@" | ||||
| else | ||||
|   gradle -q uberJar && \ | ||||
|     groovyc -cp build/libs/web-view-components-uber-0.1.0.jar \ | ||||
|     java -cp build/libs/web-view-components-uber-0.1.2.jar \ | ||||
|       org.codehaus.groovy.tools.FileSystemCompiler \ | ||||
|       --configscript src/main/resources/groowt/view/component/web/groovyc/groovycConfigurationScript.groovy \ | ||||
|       -d groovyc-out \ | ||||
|       "$@" | ||||
|  | ||||
| @ -0,0 +1 @@ | ||||
| <Echo>Hello, World!</Echo> | ||||
| @ -4,7 +4,6 @@ import groowt.view.component.compiler.*; | ||||
| import groowt.view.component.web.WebViewComponentBugError; | ||||
| import groowt.view.component.web.ast.node.CompilationUnitNode; | ||||
| import groowt.view.component.web.transpile.DefaultGroovyTranspiler; | ||||
| import org.antlr.v4.runtime.ParserRuleContext; | ||||
| import org.codehaus.groovy.control.CompilationFailedException; | ||||
| import org.codehaus.groovy.control.SourceUnit; | ||||
| import org.codehaus.groovy.tools.GroovyClass; | ||||
| @ -16,25 +15,12 @@ public class DefaultWebViewComponentTemplateCompiler | ||||
|         extends CachingComponentTemplateCompiler<WebViewComponentTemplateCompileUnit> | ||||
|         implements WebViewComponentTemplateCompiler { | ||||
| 
 | ||||
|     private final ComponentTemplateCompilerConfiguration configuration; | ||||
|     private final WebViewComponentTemplateCompilerConfiguration configuration; | ||||
| 
 | ||||
|     public DefaultWebViewComponentTemplateCompiler(ComponentTemplateCompilerConfiguration configuration) { | ||||
|     public DefaultWebViewComponentTemplateCompiler(WebViewComponentTemplateCompilerConfiguration configuration) { | ||||
|         this.configuration = configuration; | ||||
|     } | ||||
| 
 | ||||
|     protected WebViewComponentTemplateCompileException getException( | ||||
|             WebViewComponentTemplateCompileUnit compileUnit, | ||||
|             ParserRuleContext parserRuleContext | ||||
|     ) { | ||||
|         final var exception = new WebViewComponentTemplateCompileException( | ||||
|                 compileUnit, | ||||
|                 "Parser error: " + parserRuleContext.exception.getMessage(), | ||||
|                 parserRuleContext.exception | ||||
|         ); | ||||
|         exception.setParserRuleContext(parserRuleContext); | ||||
|         return exception; | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     protected ComponentTemplateCompileResult doCompile(WebViewComponentTemplateCompileUnit compileUnit) | ||||
|             throws ComponentTemplateCompileException { | ||||
| @ -49,22 +35,12 @@ public class DefaultWebViewComponentTemplateCompiler | ||||
|                 : "AnonymousWebViewComponent" + System.nanoTime(); | ||||
|         final var templateClassSimpleName = ownerComponentName + "Template"; | ||||
| 
 | ||||
|         final SourceUnit sourceUnit = transpiler.transpile( | ||||
|                 this.configuration, | ||||
|                 compileUnit, | ||||
|                 cuNode, | ||||
|                 templateClassSimpleName | ||||
|         ); | ||||
|         final SourceUnit sourceUnit = transpiler.transpile(compileUnit, cuNode, templateClassSimpleName); | ||||
|         compileUnit.getGroovyCompilationUnit().addSource(sourceUnit); | ||||
| 
 | ||||
|         // set the groovy compile unit's class loader to the configuration's classloader. | ||||
|         compileUnit.getGroovyCompilationUnit().setClassLoader( | ||||
|                 this.configuration.getGroovyClassLoader() | ||||
|         ); | ||||
| 
 | ||||
|         // compile groovy | ||||
|         try { | ||||
|             compileUnit.getGroovyCompilationUnit().compile(this.configuration.getToCompilePhase().getPhaseNumber()); | ||||
|             compileUnit.getGroovyCompilationUnit().compile(this.configuration.getCompilePhase().getPhaseNumber()); | ||||
|         } catch (CompilationFailedException compilationFailedException) { | ||||
|             throw new WebViewComponentTemplateCompileException( | ||||
|                     compileUnit, | ||||
|  | ||||
| @ -1,11 +1,9 @@ | ||||
| package groowt.view.component.web.compiler; | ||||
| 
 | ||||
| import groowt.view.component.compiler.ComponentTemplateCompilerConfiguration; | ||||
| 
 | ||||
| public class DefaultWebViewComponentTemplateCompilerFactory implements WebViewComponentTemplateCompilerFactory { | ||||
| 
 | ||||
|     @Override | ||||
|     public WebViewComponentTemplateCompiler create(ComponentTemplateCompilerConfiguration configuration) { | ||||
|     public WebViewComponentTemplateCompiler create(WebViewComponentTemplateCompilerConfiguration configuration) { | ||||
|         return new DefaultWebViewComponentTemplateCompiler(configuration); | ||||
|     } | ||||
| 
 | ||||
|  | ||||
| @ -1,7 +1,6 @@ | ||||
| package groowt.view.component.web.groovyc; | ||||
| 
 | ||||
| import groowt.view.component.compiler.ComponentTemplateCompileException; | ||||
| import groowt.view.component.compiler.DefaultComponentTemplateCompilerConfiguration; | ||||
| import groowt.view.component.compiler.source.ComponentTemplateSource; | ||||
| import groowt.view.component.web.WebViewComponentBugError; | ||||
| import groowt.view.component.web.ast.node.CompilationUnitNode; | ||||
| @ -80,13 +79,12 @@ public class DelegatingWebViewComponentTemplateParserPlugin implements ParserPlu | ||||
|             } | ||||
| 
 | ||||
|             final var groovyTranspiler = new DefaultGroovyTranspiler(); | ||||
|             final String teplateClassSimpleName = sourceUnitFileName.substring(0, sourceUnitFileName.length() - 4); | ||||
|             final String templateClassName = sourceUnitFileName.substring(0, sourceUnitFileName.length() - 4); | ||||
|             try { | ||||
|                 final SourceUnit transpiledSourceUnit = groovyTranspiler.transpile( | ||||
|                         new DefaultComponentTemplateCompilerConfiguration(), | ||||
|                         compileUnit, | ||||
|                         cuNode, | ||||
|                         teplateClassSimpleName | ||||
|                         templateClassName | ||||
|                 ); | ||||
|                 return transpiledSourceUnit.getAST(); | ||||
|             } catch (ComponentTemplateCompileException e) { | ||||
|  | ||||
| @ -6,7 +6,6 @@ import groowt.view.component.web.WebViewComponentBugError; | ||||
| import groowt.view.component.web.ast.node.*; | ||||
| import groowt.view.component.web.transpile.groovy.GroovyUtil; | ||||
| import groowt.view.component.web.transpile.groovy.GroovyUtil.ConvertResult; | ||||
| import groowt.view.component.web.transpile.resolve.ComponentClassNodeResolver; | ||||
| import groowt.view.component.web.util.SourcePosition; | ||||
| import org.codehaus.groovy.ast.*; | ||||
| import org.codehaus.groovy.ast.expr.*; | ||||
| @ -15,7 +14,6 @@ import org.jetbrains.annotations.Nullable; | ||||
| 
 | ||||
| import java.util.ArrayList; | ||||
| import java.util.List; | ||||
| import java.util.regex.Pattern; | ||||
| 
 | ||||
| import static groowt.view.component.web.transpile.TranspilerUtil.*; | ||||
| 
 | ||||
| @ -25,13 +23,9 @@ public class DefaultComponentTranspiler implements ComponentTranspiler { | ||||
|     private static final ClassNode COMPONENT_RESOLVE_EXCEPTION_TYPE = ClassHelper.make(ComponentResolveException.class); | ||||
|     private static final ClassNode COMPONENT_CREATE_EXCEPTION_TYPE = ClassHelper.make(ComponentCreateException.class); | ||||
| 
 | ||||
|     private static final Pattern isFqn = Pattern.compile("^(\\p{Ll}.+\\.)+\\p{Lu}.+$"); | ||||
|     private static final Pattern isWithPackage = Pattern.compile("^\\p{Ll}.+\\."); | ||||
| 
 | ||||
|     private LeftShiftFactory leftShiftFactory; | ||||
|     private ValueNodeTranspiler valueNodeTranspiler; | ||||
|     private BodyTranspiler bodyTranspiler; | ||||
|     private ComponentClassNodeResolver componentClassNodeResolver; | ||||
| 
 | ||||
|     public void setLeftShiftFactory(LeftShiftFactory leftShiftFactory) { | ||||
|         this.leftShiftFactory = leftShiftFactory; | ||||
| @ -45,10 +39,6 @@ public class DefaultComponentTranspiler implements ComponentTranspiler { | ||||
|         this.bodyTranspiler = bodyTranspiler; | ||||
|     } | ||||
| 
 | ||||
|     public void setComponentClassNodeResolver(ComponentClassNodeResolver componentClassNodeResolver) { | ||||
|         this.componentClassNodeResolver = componentClassNodeResolver; | ||||
|     } | ||||
| 
 | ||||
|     /* UTIL */ | ||||
| 
 | ||||
|     protected String getComponentName(int componentNumber) { | ||||
| @ -74,49 +64,14 @@ public class DefaultComponentTranspiler implements ComponentTranspiler { | ||||
| 
 | ||||
|     /* RESOLVE */ | ||||
| 
 | ||||
|     protected List<Expression> getArgsAsList( | ||||
|             TypedComponentNode componentNode, | ||||
|             TranspilerState state | ||||
|     ) { | ||||
|     protected List<Expression> getArgsAsList(TypedComponentNode componentNode) { | ||||
|         return switch (componentNode.getArgs().getType()) { | ||||
|             case ClassComponentTypeNode classComponentTypeNode -> { | ||||
|                 final String identifier = classComponentTypeNode.getIdentifier(); | ||||
|                 final ConstantExpression alias = getStringLiteral(identifier); | ||||
|                 final var matcher = isFqn.matcher(identifier); | ||||
|                 if (matcher.matches()) { | ||||
|                     final ClassNode classNode = ClassHelper.make(identifier); | ||||
|                     final ClassExpression classExpression = new ClassExpression(classNode); | ||||
|                     yield List.of(alias, classExpression); | ||||
|                 } else { | ||||
|                     // we need to resolve it | ||||
|                     final var isWithPackageMatcher = isWithPackage.matcher(identifier); | ||||
|                     if (isWithPackageMatcher.matches()) { | ||||
|                         final var resolveResult = this.componentClassNodeResolver.getClassForFqn(identifier); | ||||
|                         if (resolveResult.isLeft()) { | ||||
|                             final var error = resolveResult.getLeft(); | ||||
|                             error.setNode(componentNode.getArgs().getType()); | ||||
|                             state.addError(error); | ||||
|                             yield List.of(); | ||||
|                         } else { | ||||
|                             final ClassNode classNode = resolveResult.getRight(); | ||||
|                             final ClassExpression classExpression = new ClassExpression(classNode); // TODO: pos | ||||
|                             yield List.of(alias, classExpression); | ||||
|                         } | ||||
|                     } else { | ||||
|                         final var resolveResult = | ||||
|                                 this.componentClassNodeResolver.getClassForNameWithoutPackage(identifier); | ||||
|                         if (resolveResult.isLeft()) { | ||||
|                             final var error = resolveResult.getLeft(); | ||||
|                             error.setNode(componentNode.getArgs().getType()); | ||||
|                             state.addError(error); | ||||
|                             yield List.of(); | ||||
|                         } else { | ||||
|                             final ClassNode classNode = resolveResult.getRight(); | ||||
|                             final ClassExpression classExpression = new ClassExpression(classNode); // TODO: pos | ||||
|                             yield List.of(alias, classExpression); | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|                 final ClassNode classNode = ClassHelper.make(identifier); | ||||
|                 final var classExpression = new ClassExpression(classNode); | ||||
|                 yield List.of(alias, classExpression); | ||||
|             } | ||||
|             case StringComponentTypeNode stringComponentTypeNode -> { | ||||
|                 final String identifier = stringComponentTypeNode.getIdentifier(); | ||||
| @ -127,8 +82,8 @@ public class DefaultComponentTranspiler implements ComponentTranspiler { | ||||
|     } | ||||
| 
 | ||||
|     // 'h1' | 'MyComponent', MyComponent(.class) | ||||
|     protected ArgumentListExpression getResolveArgs(TypedComponentNode componentNode, TranspilerState state) { | ||||
|         final List<Expression> args = this.getArgsAsList(componentNode, state); | ||||
|     protected ArgumentListExpression getResolveArgs(TypedComponentNode componentNode) { | ||||
|         final List<Expression> args = this.getArgsAsList(componentNode); | ||||
|         final ArgumentListExpression argsListExpr = new ArgumentListExpression(); | ||||
|         args.forEach(argsListExpr::addExpression); | ||||
|         return argsListExpr; | ||||
| @ -142,7 +97,7 @@ public class DefaultComponentTranspiler implements ComponentTranspiler { | ||||
|         return new MethodCallExpression( | ||||
|                 new VariableExpression(state.getRenderContext()), | ||||
|                 "resolve", | ||||
|                 this.getResolveArgs(componentNode, state) | ||||
|                 this.getResolveArgs(componentNode) | ||||
|         ); | ||||
|     } | ||||
| 
 | ||||
|  | ||||
| @ -2,7 +2,6 @@ package groowt.view.component.web.transpile; | ||||
| 
 | ||||
| import groowt.view.component.compiler.ComponentTemplateCompileException; | ||||
| import groowt.view.component.compiler.ComponentTemplateCompileUnit; | ||||
| import groowt.view.component.compiler.ComponentTemplateCompilerConfiguration; | ||||
| import groowt.view.component.web.ast.node.BodyNode; | ||||
| import groowt.view.component.web.ast.node.CompilationUnitNode; | ||||
| import groowt.view.component.web.ast.node.PreambleNode; | ||||
| @ -10,7 +9,6 @@ import groowt.view.component.web.compiler.MultipleWebViewComponentCompileErrorsE | ||||
| import groowt.view.component.web.compiler.WebViewComponentTemplateCompileException; | ||||
| import groowt.view.component.web.compiler.WebViewComponentTemplateCompileUnit; | ||||
| import groowt.view.component.web.transpile.groovy.GroovyUtil; | ||||
| import groowt.view.component.web.transpile.resolve.ClassLoaderComponentClassNodeResolver; | ||||
| import org.codehaus.groovy.ast.*; | ||||
| import org.codehaus.groovy.ast.expr.*; | ||||
| import org.codehaus.groovy.ast.stmt.BlockStatement; | ||||
| @ -31,25 +29,13 @@ public class DefaultGroovyTranspiler implements GroovyTranspiler { | ||||
| 
 | ||||
|     private static final Logger logger = LoggerFactory.getLogger(DefaultGroovyTranspiler.class); | ||||
| 
 | ||||
|     protected TranspilerConfiguration getConfiguration( | ||||
|             ClassLoaderComponentClassNodeResolver classLoaderComponentClassNodeResolver | ||||
|     ) { | ||||
|         return SimpleTranspilerConfiguration.withDefaults(classLoaderComponentClassNodeResolver); | ||||
|     } | ||||
| 
 | ||||
|     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 TranspilerConfiguration getConfiguration() { | ||||
|         return SimpleTranspilerConfiguration.withDefaults(); | ||||
|     } | ||||
| 
 | ||||
|     protected WebViewComponentModuleNode initModuleNode( | ||||
|             ComponentTemplateCompileUnit compileUnit, | ||||
|             WebViewComponentSourceUnit sourceUnit, | ||||
|             TranspilerConfiguration configuration | ||||
|             WebViewComponentSourceUnit sourceUnit | ||||
|     ) { | ||||
|         final var moduleNode = new WebViewComponentModuleNode(sourceUnit); | ||||
|         sourceUnit.setModuleNode(moduleNode); | ||||
| @ -59,22 +45,16 @@ public class DefaultGroovyTranspiler implements GroovyTranspiler { | ||||
|             moduleNode.setPackageName(defaultPackageName); | ||||
|         } | ||||
| 
 | ||||
|         this.addAutomaticImports(moduleNode, configuration); | ||||
|         return moduleNode; | ||||
|     } | ||||
| 
 | ||||
|     protected ClassNode initMainClassNode( | ||||
|             ComponentTemplateCompileUnit compileUnit, | ||||
|             String templateClassName, | ||||
|             WebViewComponentModuleNode moduleNode | ||||
|     ) { | ||||
|     protected ClassNode initMainClassNode(ComponentTemplateCompileUnit compileUnit, String templateClassName) { | ||||
|         final ClassNode mainClassNode = new ClassNode( | ||||
|                 compileUnit.getDefaultPackageName() + templateClassName, | ||||
|                 ACC_PUBLIC, | ||||
|                 ClassHelper.OBJECT_TYPE | ||||
|         ); | ||||
|         mainClassNode.addInterface(TranspilerUtil.COMPONENT_TEMPLATE); | ||||
|         moduleNode.addClass(mainClassNode); | ||||
|         return mainClassNode; | ||||
|     } | ||||
| 
 | ||||
| @ -216,40 +196,32 @@ public class DefaultGroovyTranspiler implements GroovyTranspiler { | ||||
| 
 | ||||
|     @Override | ||||
|     public WebViewComponentSourceUnit transpile( | ||||
|             ComponentTemplateCompilerConfiguration compilerConfiguration, | ||||
|             WebViewComponentTemplateCompileUnit compileUnit, | ||||
|             CompilationUnitNode compilationUnitNode, | ||||
|             String templateClassSimpleName | ||||
|     ) throws ComponentTemplateCompileException { | ||||
|         // resolver, transpilerConfiguration, and positionSetter | ||||
|         final ClassLoaderComponentClassNodeResolver resolver = new ClassLoaderComponentClassNodeResolver( | ||||
|                 compileUnit, | ||||
|                 compilerConfiguration.getGroovyClassLoader() | ||||
|         ); | ||||
|         final var transpilerConfiguration = this.getConfiguration(resolver); | ||||
|         // transpilerConfiguration, and positionSetter | ||||
|         final var transpilerConfiguration = this.getConfiguration(); | ||||
|         final PositionSetter positionSetter = transpilerConfiguration.getPositionSetter(); | ||||
| 
 | ||||
|         // prepare sourceUnit | ||||
|         final CompilerConfiguration groovyCompilerConfiguration = | ||||
|                 compilerConfiguration.getGroovyCompilerConfiguration(); | ||||
|         final CompilerConfiguration groovyCompilerConfiguration = compileUnit.getGroovyCompilationUnit() | ||||
|                 .getConfiguration(); | ||||
|         final WebViewComponentSourceUnit sourceUnit = new WebViewComponentSourceUnit( | ||||
|                 compileUnit.getDescriptiveName(), | ||||
|                 compileUnit.getGroovyReaderSource(), | ||||
|                 groovyCompilerConfiguration, | ||||
|                 compilerConfiguration.getGroovyClassLoader(), | ||||
|                 compileUnit.getGroovyCompilationUnit().getClassLoader(), | ||||
|                 new ErrorCollector(groovyCompilerConfiguration) | ||||
|         ); | ||||
| 
 | ||||
|         // prepare moduleNode | ||||
|         final WebViewComponentModuleNode moduleNode = this.initModuleNode( | ||||
|                 compileUnit, sourceUnit, transpilerConfiguration | ||||
|                 compileUnit, sourceUnit | ||||
|         ); | ||||
| 
 | ||||
|         // set resolver's moduleNode | ||||
|         resolver.setModuleNode(moduleNode); | ||||
| 
 | ||||
|         // prepare mainClassNode | ||||
|         final ClassNode mainClassNode = this.initMainClassNode(compileUnit, templateClassSimpleName, moduleNode); | ||||
|         final ClassNode mainClassNode = this.initMainClassNode(compileUnit, templateClassSimpleName); | ||||
| 
 | ||||
|         // handle preamble | ||||
|         final PreambleNode preambleNode = compilationUnitNode.getPreambleNode(); | ||||
| @ -257,6 +229,9 @@ public class DefaultGroovyTranspiler implements GroovyTranspiler { | ||||
|             this.handlePreamble(templateClassSimpleName, preambleNode, mainClassNode, moduleNode, positionSetter); | ||||
|         } | ||||
| 
 | ||||
|         // Moved here so that moduleNode#getMainClassName reflects fqn of mainClassName | ||||
|         moduleNode.addClass(mainClassNode); | ||||
| 
 | ||||
|         // getRenderer method and render closure | ||||
|         // first, getRenderer params | ||||
|         final Parameter componentContextParam = new Parameter(COMPONENT_CONTEXT_TYPE, COMPONENT_CONTEXT_NAME); | ||||
|  | ||||
| @ -1,14 +1,12 @@ | ||||
| package groowt.view.component.web.transpile; | ||||
| 
 | ||||
| import groowt.view.component.compiler.ComponentTemplateCompileException; | ||||
| import groowt.view.component.compiler.ComponentTemplateCompilerConfiguration; | ||||
| import groowt.view.component.web.ast.node.CompilationUnitNode; | ||||
| import groowt.view.component.web.compiler.WebViewComponentTemplateCompileUnit; | ||||
| 
 | ||||
| public interface GroovyTranspiler { | ||||
| 
 | ||||
|     WebViewComponentSourceUnit transpile( | ||||
|             ComponentTemplateCompilerConfiguration compilerConfiguration, | ||||
|             WebViewComponentTemplateCompileUnit compileUnit, | ||||
|             CompilationUnitNode compilationUnitNode, | ||||
|             String templateClassSimpleName | ||||
|  | ||||
| @ -1,7 +1,6 @@ | ||||
| package groowt.view.component.web.transpile; | ||||
| 
 | ||||
| import groovy.lang.Tuple3; | ||||
| import groowt.view.component.web.transpile.resolve.ComponentClassNodeResolver; | ||||
| import org.codehaus.groovy.ast.ClassNode; | ||||
| 
 | ||||
| import java.util.Map; | ||||
| @ -12,9 +11,8 @@ import static groowt.view.component.web.transpile.TranspilerUtil.*; | ||||
| 
 | ||||
| public class SimpleTranspilerConfiguration implements TranspilerConfiguration { | ||||
| 
 | ||||
|     public static TranspilerConfiguration withDefaults(ComponentClassNodeResolver componentClassNodeResolver) { | ||||
|     public static TranspilerConfiguration withDefaults() { | ||||
|         final var c = new SimpleTranspilerConfiguration(); | ||||
|         c.setComponentClassNodeResolver(componentClassNodeResolver); | ||||
| 
 | ||||
|         final var ct = new DefaultComponentTranspiler(); | ||||
|         final PositionSetter ps = new SimplePositionSetter(); | ||||
| @ -27,7 +25,6 @@ public class SimpleTranspilerConfiguration implements TranspilerConfiguration { | ||||
|         ct.setLeftShiftFactory(lsf); | ||||
|         ct.setBodyTranspiler(bt); | ||||
|         ct.setValueNodeTranspiler(vnt); | ||||
|         ct.setComponentClassNodeResolver(componentClassNodeResolver); | ||||
| 
 | ||||
|         c.setComponentTranspiler(ct); | ||||
|         c.setPositionSetter(ps); | ||||
| @ -40,7 +37,6 @@ public class SimpleTranspilerConfiguration implements TranspilerConfiguration { | ||||
|         return c; | ||||
|     } | ||||
| 
 | ||||
|     private ComponentClassNodeResolver componentClassNodeResolver; | ||||
|     private ComponentTranspiler componentTranspiler; | ||||
|     private PositionSetter positionSetter; | ||||
|     private LeftShiftFactory leftShiftFactory; | ||||
| @ -49,14 +45,6 @@ public class SimpleTranspilerConfiguration implements TranspilerConfiguration { | ||||
|     private BodyTranspiler bodyTranspiler; | ||||
|     private ValueNodeTranspiler valueNodeTranspiler; | ||||
| 
 | ||||
|     public ComponentClassNodeResolver getComponentClassNodeResolver() { | ||||
|         return Objects.requireNonNull(this.componentClassNodeResolver); | ||||
|     } | ||||
| 
 | ||||
|     public void setComponentClassNodeResolver(ComponentClassNodeResolver componentClassNodeResolver) { | ||||
|         this.componentClassNodeResolver = componentClassNodeResolver; | ||||
|     } | ||||
| 
 | ||||
|     public ComponentTranspiler getComponentTranspiler() { | ||||
|         return Objects.requireNonNull(this.componentTranspiler); | ||||
|     } | ||||
|  | ||||
| @ -1,60 +0,0 @@ | ||||
| package groowt.view.component.web.transpile.resolve; | ||||
| 
 | ||||
| import groowt.util.fp.either.Either; | ||||
| import groowt.view.component.web.WebViewComponent; | ||||
| import groowt.view.component.web.compiler.WebViewComponentTemplateCompileUnit; | ||||
| import org.codehaus.groovy.ast.ClassHelper; | ||||
| import org.codehaus.groovy.ast.ClassNode; | ||||
| 
 | ||||
| import java.util.ArrayList; | ||||
| import java.util.List; | ||||
| 
 | ||||
| public class CachingComponentClassNodeResolver implements ComponentClassNodeResolver { | ||||
| 
 | ||||
|     private final List<ClassNode> classNodes = new ArrayList<>(); | ||||
| 
 | ||||
|     protected final WebViewComponentTemplateCompileUnit compileUnit; | ||||
| 
 | ||||
|     public CachingComponentClassNodeResolver(WebViewComponentTemplateCompileUnit compileUnit) { | ||||
|         this.compileUnit = compileUnit; | ||||
|     } | ||||
| 
 | ||||
|     public void addClass(Class<? extends WebViewComponent> clazz) { | ||||
|         this.classNodes.add(ClassHelper.make(clazz)); | ||||
|     } | ||||
| 
 | ||||
|     public void addClassNode(ClassNode classNode) { | ||||
|         this.classNodes.add(classNode); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public Either<ClassNodeResolveException, ClassNode> getClassForFqn(String fqn) { | ||||
|         for (final var classNode : this.classNodes) { | ||||
|             if (classNode.getName().equals(fqn)) { | ||||
|                 return Either.right(classNode); | ||||
|             } | ||||
|         } | ||||
|         return Either.left(new ClassNodeResolveException( | ||||
|                 this.compileUnit, | ||||
|                 fqn, | ||||
|                 "Could not resolve ClassNode for fqn: " + fqn, | ||||
|                 null | ||||
|         )); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public Either<ClassNodeResolveException, ClassNode> getClassForNameWithoutPackage(String nameWithoutPackage) { | ||||
|         for (final var classNode : this.classNodes) { | ||||
|             if (classNode.getNameWithoutPackage().equals(nameWithoutPackage)) { | ||||
|                 return Either.right(classNode); | ||||
|             } | ||||
|         } | ||||
|         return Either.left(new ClassNodeResolveException( | ||||
|                 this.compileUnit, | ||||
|                 nameWithoutPackage, | ||||
|                 "Could not resolve ClassNode for nameWithoutPackage: " + nameWithoutPackage, | ||||
|                 null | ||||
|         )); | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -1,53 +0,0 @@ | ||||
| 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.slf4j.Logger; | ||||
| import org.slf4j.LoggerFactory; | ||||
| 
 | ||||
| public class ClassLoaderComponentClassNodeResolver extends ModuleNodeComponentClassNodeResolver { | ||||
| 
 | ||||
|     private static final Logger logger = LoggerFactory.getLogger(ModuleNodeComponentClassNodeResolver.class); | ||||
| 
 | ||||
|     protected final ClassLoader classLoader; | ||||
| 
 | ||||
|     public ClassLoaderComponentClassNodeResolver( | ||||
|             WebViewComponentTemplateCompileUnit compileUnit, | ||||
|             ClassLoader classLoader | ||||
|     ) { | ||||
|         super(compileUnit); | ||||
|         this.classLoader = classLoader; | ||||
|     } | ||||
| 
 | ||||
|     protected final Either<ClassNodeResolveException, ClassNode> resolveWithClassLoader(String fqn) { | ||||
|         logger.debug("Trying to resolve {}", fqn); | ||||
|         try { | ||||
|             Class<?> clazz = this.classLoader.loadClass(ResolveUtil.convertCanonicalNameToBinaryName(fqn)); | ||||
|             final var classNode = ResolveUtil.getClassNode(clazz); | ||||
|             return Either.right(classNode); | ||||
|         } catch (ClassNotFoundException classNotFoundException) { | ||||
|             return Either.left( | ||||
|                     new ClassNodeResolveException( | ||||
|                             this.compileUnit, | ||||
|                             fqn, | ||||
|                             "Could not find class " + fqn + " with classLoader " + | ||||
|                                     this.classLoader, | ||||
|                             classNotFoundException | ||||
|                     ) | ||||
|             ); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public Either<ClassNodeResolveException, ClassNode> getClassForFqn(String fqn) { | ||||
|         return super.getClassForFqn(fqn).flatMapLeft(ignored -> { | ||||
|             final var classLoaderResult = this.resolveWithClassLoader(fqn); | ||||
|             if (classLoaderResult.isRight()) { | ||||
|                 this.addClassNode(classLoaderResult.getRight()); | ||||
|             } | ||||
|             return classLoaderResult; | ||||
|         }); | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -1,42 +0,0 @@ | ||||
| package groowt.view.component.web.transpile.resolve; | ||||
| 
 | ||||
| import groowt.util.fp.either.Either; | ||||
| import groowt.view.component.web.compiler.WebViewComponentTemplateCompileException; | ||||
| import groowt.view.component.web.compiler.WebViewComponentTemplateCompileUnit; | ||||
| import org.codehaus.groovy.ast.ClassNode; | ||||
| 
 | ||||
| public interface ComponentClassNodeResolver { | ||||
| 
 | ||||
|     final class ClassNodeResolveException extends WebViewComponentTemplateCompileException { | ||||
| 
 | ||||
|         private final String identifier; | ||||
| 
 | ||||
|         public ClassNodeResolveException( | ||||
|                 WebViewComponentTemplateCompileUnit compileUnit, | ||||
|                 String identifier, | ||||
|                 String message | ||||
|         ) { | ||||
|             super(compileUnit, message); | ||||
|             this.identifier = identifier; | ||||
|         } | ||||
| 
 | ||||
|         public ClassNodeResolveException( | ||||
|                 WebViewComponentTemplateCompileUnit compileUnit, | ||||
|                 String identifier, | ||||
|                 String message, | ||||
|                 Throwable cause | ||||
|         ) { | ||||
|             super(compileUnit, message, cause); | ||||
|             this.identifier = identifier; | ||||
|         } | ||||
| 
 | ||||
|         public String getIdentifier() { | ||||
|             return this.identifier; | ||||
|         } | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
|     Either<ClassNodeResolveException, ClassNode> getClassForFqn(String fqn); | ||||
|     Either<ClassNodeResolveException, ClassNode> getClassForNameWithoutPackage(String nameWithoutPackage); | ||||
| 
 | ||||
| } | ||||
| @ -1,84 +0,0 @@ | ||||
| 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 java.util.Objects; | ||||
| 
 | ||||
| public class ModuleNodeComponentClassNodeResolver extends CachingComponentClassNodeResolver { | ||||
| 
 | ||||
|     private ModuleNode moduleNode; | ||||
| 
 | ||||
|     public ModuleNodeComponentClassNodeResolver(WebViewComponentTemplateCompileUnit compileUnit) { | ||||
|         super(compileUnit); | ||||
|     } | ||||
| 
 | ||||
|     public ModuleNode getModuleNode() { | ||||
|         return Objects.requireNonNull(this.moduleNode); | ||||
|     } | ||||
| 
 | ||||
|     public void setModuleNode(ModuleNode moduleNode) { | ||||
|         this.moduleNode = Objects.requireNonNull(moduleNode); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public Either<ClassNodeResolveException, ClassNode> getClassForNameWithoutPackage(String nameWithoutPackage) { | ||||
|         return super.getClassForNameWithoutPackage(nameWithoutPackage).flatMapLeft(ignored -> { | ||||
|             // try regular imports first | ||||
|             final var importedClassNode = this.getModuleNode().getImportType(nameWithoutPackage); | ||||
|             if (importedClassNode != null) { | ||||
|                 this.addClassNode(importedClassNode); | ||||
|                 return Either.right(importedClassNode); | ||||
|             } | ||||
| 
 | ||||
|             // try star imports | ||||
|             final var starImports = this.getModuleNode().getStarImports(); | ||||
|             for (final var starImport : starImports) { | ||||
|                 final var packageName = starImport.getPackageName(); | ||||
|                 final String fqn; | ||||
|                 if (!packageName.equals(".") && packageName.endsWith(".")) { | ||||
|                     fqn = packageName + nameWithoutPackage; | ||||
|                 } else { | ||||
|                     fqn = packageName + "." + nameWithoutPackage; | ||||
|                 } | ||||
|                 final var withPackage = this.getClassForFqn(fqn); | ||||
|                 if (withPackage.isRight()) { | ||||
|                     return withPackage; | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             // try pre-pending package and asking for fqn | ||||
|             final String moduleNodePackageName = this.getModuleNode().getPackageName(); | ||||
|             final String packageName; | ||||
|             if (moduleNodePackageName != null) { | ||||
|                 packageName = moduleNodePackageName; | ||||
|             } else { | ||||
|                 packageName = ""; | ||||
|             } | ||||
| 
 | ||||
|             final String fqn; | ||||
|             if (packageName.equals(".") || packageName.isEmpty()) { | ||||
|                 fqn = nameWithoutPackage; | ||||
|             } else if (packageName.endsWith(".")) { | ||||
|                 fqn = packageName + nameWithoutPackage; | ||||
|             } else { | ||||
|                 fqn = packageName + "." + nameWithoutPackage; | ||||
|             } | ||||
| 
 | ||||
|             final var withPackage = this.getClassForFqn(fqn); | ||||
|             if (withPackage.isRight()) { | ||||
|                 return withPackage; | ||||
|             } else { | ||||
|                 return Either.left(new ClassNodeResolveException( | ||||
|                         this.compileUnit, | ||||
|                         nameWithoutPackage, | ||||
|                         "Cannot resolve " + nameWithoutPackage | ||||
|                                 + " from imports, package-local classes, or pre-added classes." | ||||
|                 )); | ||||
|             } | ||||
|         }); | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -1,31 +0,0 @@ | ||||
| package groowt.view.component.web.transpile.resolve; | ||||
| 
 | ||||
| import org.codehaus.groovy.ast.ClassHelper; | ||||
| import org.codehaus.groovy.ast.ClassNode; | ||||
| 
 | ||||
| import java.util.regex.Pattern; | ||||
| 
 | ||||
| public final class ResolveUtil { | ||||
| 
 | ||||
|     private static final Pattern packageSplitter = Pattern.compile("^(?<package>(?>\\p{Ll}[^.]*\\.)*)(?<top>\\p{Lu}[^.]*)(?<members>(?>\\.\\p{Lu}[^.]*)*)$"); | ||||
| 
 | ||||
|     public static ClassNode getClassNode(Class<?> clazz) { | ||||
|         return ClassHelper.makeCached(clazz); | ||||
|     } | ||||
| 
 | ||||
|     public static String convertCanonicalNameToBinaryName(String canonicalName) { | ||||
|         final var matcher = packageSplitter.matcher(canonicalName); | ||||
|         if (matcher.matches()) { | ||||
|             return new StringBuilder() | ||||
|                     .append(matcher.group("package")) | ||||
|                     .append(matcher.group("top")) | ||||
|                     .append(matcher.group("members").replaceAll("\\.", "\\$")) | ||||
|                     .toString(); | ||||
|         } else { | ||||
|             throw new IllegalArgumentException("Cannot split apart " + canonicalName); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private ResolveUtil() {} | ||||
| 
 | ||||
| } | ||||
| @ -1,5 +1,16 @@ | ||||
| package groowt.view.component.web.groovyc | ||||
| 
 | ||||
| import org.codehaus.groovy.control.CompilerConfiguration | ||||
| import org.codehaus.groovy.control.customizers.ImportCustomizer | ||||
| 
 | ||||
| (configuration as CompilerConfiguration).tap { | ||||
|     pluginFactory = new WebViewComponentParserPluginFactory() | ||||
|     addCompilationCustomizers(new ImportCustomizer().tap { | ||||
|         addStarImports( | ||||
|                 "groowt.view.component.web.lib", | ||||
|                 "groowt.view.component.web.runtime", | ||||
|                 "groowt.view.component.runtime" | ||||
|         ) | ||||
|     }) | ||||
| } | ||||
| 
 | ||||
| (configuration as CompilerConfiguration).pluginFactory = new WebViewComponentParserPluginFactory() | ||||
|  | ||||
| @ -3,7 +3,6 @@ package groowt.view.component.web.transpiler; | ||||
| import groowt.view.component.web.compiler.WebViewComponentTemplateCompileUnit; | ||||
| import groowt.view.component.web.transpile.SimpleTranspilerConfiguration; | ||||
| import groowt.view.component.web.transpile.TranspilerConfiguration; | ||||
| import groowt.view.component.web.transpile.resolve.CachingComponentClassNodeResolver; | ||||
| import org.codehaus.groovy.ast.ModuleNode; | ||||
| 
 | ||||
| public class DefaultBodyTranspilerTests extends BodyTranspilerTests { | ||||
| @ -13,7 +12,7 @@ public class DefaultBodyTranspilerTests extends BodyTranspilerTests { | ||||
|             WebViewComponentTemplateCompileUnit compileUnit, | ||||
|             ModuleNode moduleNode | ||||
|     ) { | ||||
|         return SimpleTranspilerConfiguration.withDefaults(new CachingComponentClassNodeResolver(compileUnit)); | ||||
|         return SimpleTranspilerConfiguration.withDefaults(); | ||||
|     } | ||||
| 
 | ||||
| } | ||||
|  | ||||
| @ -1,25 +0,0 @@ | ||||
| package groowt.view.component.web.transpiler; | ||||
| 
 | ||||
| import org.junit.jupiter.api.Test; | ||||
| 
 | ||||
| import static groowt.view.component.web.transpile.resolve.ResolveUtil.convertCanonicalNameToBinaryName; | ||||
| import static org.junit.jupiter.api.Assertions.*; | ||||
| 
 | ||||
| public class ResolveUtilTests { | ||||
| 
 | ||||
|     @Test | ||||
|     public void abcABC() { | ||||
|         assertEquals("a.b.c.A$B$C", convertCanonicalNameToBinaryName("a.b.c.A.B.C")); | ||||
|     } | ||||
| 
 | ||||
|     @Test | ||||
|     public void ABC() { | ||||
|         assertEquals("A$B$C", convertCanonicalNameToBinaryName("A.B.C")); | ||||
|     } | ||||
| 
 | ||||
|     @Test | ||||
|     public void abcA() { | ||||
|         assertEquals("a.b.c.A", convertCanonicalNameToBinaryName("a.b.c.A")); | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -2,7 +2,6 @@ package groowt.view.component.web.transpiler; | ||||
| 
 | ||||
| import groovy.lang.Tuple2; | ||||
| import groowt.view.component.compiler.ComponentTemplateCompileException; | ||||
| import groowt.view.component.compiler.DefaultComponentTemplateCompilerConfiguration; | ||||
| import groowt.view.component.compiler.source.StringSource; | ||||
| import groowt.view.component.web.BaseWebViewComponent; | ||||
| import groowt.view.component.web.antlr.ParserUtil; | ||||
| @ -55,7 +54,6 @@ public abstract class GroovyTranspilerTests { | ||||
|         final var cuNode = astBuilder.buildCompilationUnit(parseResult.getCompilationUnitContext()); | ||||
|         try { | ||||
|             this.transpiler.transpile( | ||||
|                     new DefaultComponentTemplateCompilerConfiguration(), | ||||
|                     new DefaultWebViewComponentTemplateCompileUnit( | ||||
|                             "<anonymous string source>", | ||||
|                             AnonymousWebViewComponent.class, | ||||
|  | ||||
| @ -9,6 +9,7 @@ import org.apache.logging.log4j.core.LoggerContext; | ||||
| import org.codehaus.groovy.control.CompilationFailedException; | ||||
| import org.codehaus.groovy.control.CompilationUnit; | ||||
| import org.codehaus.groovy.control.CompilerConfiguration; | ||||
| import org.codehaus.groovy.control.customizers.ImportCustomizer; | ||||
| import picocli.CommandLine; | ||||
| import picocli.CommandLine.Command; | ||||
| 
 | ||||
| @ -71,6 +72,15 @@ public final class GroovyWvcCompiler implements Callable<Integer> { | ||||
| 
 | ||||
|     public Integer doCompile() { | ||||
|         final CompilerConfiguration configuration = new CompilerConfiguration(); | ||||
| 
 | ||||
|         final var addGroowtImports = new ImportCustomizer(); | ||||
|         addGroowtImports.addStarImports( | ||||
|                 "groowt.view.component.web.lib", | ||||
|                 "groowt.view.component.web.runtime", | ||||
|                 "groowt.view.component.runtime" | ||||
|         ); | ||||
|         configuration.addCompilationCustomizers(addGroowtImports); | ||||
| 
 | ||||
|         configuration.setPluginFactory(new WebViewComponentParserPluginFactory()); | ||||
|         final CompilationUnit compilationUnit = new CompilationUnit(configuration); | ||||
| 
 | ||||
|  | ||||
| @ -4,13 +4,14 @@ import groowt.view.component.ViewComponent; | ||||
| import groowt.view.component.compiler.AbstractComponentTemplateCompileUnit; | ||||
| import groowt.view.component.compiler.ComponentTemplateCompileException; | ||||
| import groowt.view.component.compiler.ComponentTemplateCompileResult; | ||||
| import groowt.view.component.compiler.ComponentTemplateCompilerConfiguration; | ||||
| import groowt.view.component.compiler.source.ComponentTemplateSource; | ||||
| import groowt.view.component.compiler.source.FileSource; | ||||
| import groowt.view.component.compiler.source.URISource; | ||||
| import groowt.view.component.compiler.source.URLSource; | ||||
| import org.codehaus.groovy.control.CompilationUnit; | ||||
| import org.codehaus.groovy.control.CompilerConfiguration; | ||||
| import org.codehaus.groovy.control.Janitor; | ||||
| import org.codehaus.groovy.control.customizers.ImportCustomizer; | ||||
| import org.codehaus.groovy.control.io.ReaderSource; | ||||
| import org.jetbrains.annotations.Nullable; | ||||
| 
 | ||||
| @ -20,8 +21,20 @@ import java.net.URI; | ||||
| public class DefaultWebViewComponentTemplateCompileUnit extends AbstractComponentTemplateCompileUnit | ||||
|         implements ReaderSource, WebViewComponentTemplateCompileUnit { | ||||
| 
 | ||||
|     private static CompilationUnit getCompilationUnit() { | ||||
|         final var configuration = new CompilerConfiguration(); | ||||
|         final var addGroowtImports = new ImportCustomizer(); | ||||
|         addGroowtImports.addStarImports( | ||||
|                 "groowt.view.component.web.lib", | ||||
|                 "groowt.view.component.web.runtime", | ||||
|                 "groowt.view.component.runtime" | ||||
|         ); | ||||
|         configuration.addCompilationCustomizers(addGroowtImports); | ||||
|         return new CompilationUnit(configuration); | ||||
|     } | ||||
| 
 | ||||
|     private final String defaultPackageName; | ||||
|     private final CompilationUnit groovyCompilationUnit = new CompilationUnit(); | ||||
|     private final CompilationUnit groovyCompilationUnit; | ||||
| 
 | ||||
|     public DefaultWebViewComponentTemplateCompileUnit( | ||||
|             String descriptiveName, | ||||
| @ -35,6 +48,7 @@ public class DefaultWebViewComponentTemplateCompileUnit extends AbstractComponen | ||||
|         } else { | ||||
|             this.defaultPackageName = defaultPackageName; | ||||
|         } | ||||
|         this.groovyCompilationUnit = getCompilationUnit(); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
| @ -53,9 +67,8 @@ public class DefaultWebViewComponentTemplateCompileUnit extends AbstractComponen | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public ComponentTemplateCompileResult compile(ComponentTemplateCompilerConfiguration configuration) | ||||
|             throws ComponentTemplateCompileException { | ||||
|         final WebViewComponentTemplateCompiler compiler = WebViewComponentTemplateCompiler.get(configuration); | ||||
|     public ComponentTemplateCompileResult compile() throws ComponentTemplateCompileException { | ||||
|         final WebViewComponentTemplateCompiler compiler = WebViewComponentTemplateCompiler.get(); | ||||
|         return compiler.compile(this); | ||||
|     } | ||||
| 
 | ||||
|  | ||||
| @ -9,10 +9,10 @@ public interface WebViewComponentTemplateCompiler | ||||
|         extends ComponentTemplateCompiler<WebViewComponentTemplateCompileUnit> { | ||||
| 
 | ||||
|     static WebViewComponentTemplateCompiler get() { | ||||
|         return get(new DefaultComponentTemplateCompilerConfiguration()); | ||||
|         return get(new WebViewComponentTemplateCompilerConfiguration()); | ||||
|     } | ||||
| 
 | ||||
|     static WebViewComponentTemplateCompiler get(ComponentTemplateCompilerConfiguration configuration) { | ||||
|     static WebViewComponentTemplateCompiler get(WebViewComponentTemplateCompilerConfiguration configuration) { | ||||
|         final ServiceLoader<WebViewComponentTemplateCompilerFactory> factoryServiceLoader = | ||||
|                 ServiceLoader.load(WebViewComponentTemplateCompilerFactory.class); | ||||
|         final var factory = factoryServiceLoader.findFirst() | ||||
|  | ||||
| @ -0,0 +1,17 @@ | ||||
| package groowt.view.component.web.compiler; | ||||
| 
 | ||||
| import org.codehaus.groovy.control.CompilePhase; | ||||
| 
 | ||||
| public class WebViewComponentTemplateCompilerConfiguration { | ||||
| 
 | ||||
|     private CompilePhase compilePhase = CompilePhase.CLASS_GENERATION; | ||||
| 
 | ||||
|     public CompilePhase getCompilePhase() { | ||||
|         return this.compilePhase; | ||||
|     } | ||||
| 
 | ||||
|     public void setCompilePhase(CompilePhase compilePhase) { | ||||
|         this.compilePhase = compilePhase; | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -1,7 +1,5 @@ | ||||
| package groowt.view.component.web.compiler; | ||||
| 
 | ||||
| import groowt.view.component.compiler.ComponentTemplateCompilerConfiguration; | ||||
| 
 | ||||
| public interface WebViewComponentTemplateCompilerFactory { | ||||
|     WebViewComponentTemplateCompiler create(ComponentTemplateCompilerConfiguration configuration); | ||||
|     WebViewComponentTemplateCompiler create(WebViewComponentTemplateCompilerConfiguration configuration); | ||||
| } | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user