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

grails.views.AbstractGroovyTemplateCompiler.groovy Maven / Gradle / Ivy

package grails.views

import grails.views.compiler.ViewsTransform
import grails.views.resolve.GenericGroovyTemplateResolver
import groovy.io.FileType
import groovy.transform.CompileStatic
import org.codehaus.groovy.control.CompilationUnit
import org.codehaus.groovy.control.CompilerConfiguration
import org.codehaus.groovy.control.SourceUnit
import org.codehaus.groovy.control.customizers.ASTTransformationCustomizer
import org.codehaus.groovy.control.customizers.ImportCustomizer
import org.codehaus.groovy.control.io.FileReaderSource
import java.util.concurrent.Callable
import java.util.concurrent.Executors
import java.util.concurrent.ExecutorService
import java.util.concurrent.TimeUnit
import java.util.concurrent.ExecutorCompletionService
import java.util.concurrent.CompletionService
import java.util.concurrent.Future
/**
 * A generic compiler for Groovy templates that are compiled into classes in production
 *
 * @author Graeme Rocher
 * @since 1.0
 */
@CompileStatic
abstract class AbstractGroovyTemplateCompiler {

    @Delegate CompilerConfiguration configuration = new CompilerConfiguration()

    String packageName = ""
    File sourceDir
    ViewConfiguration viewConfiguration

    AbstractGroovyTemplateCompiler(ViewConfiguration configuration, File sourceDir) {
        this.viewConfiguration = configuration
        this.packageName = configuration.packageName
        this.sourceDir = sourceDir
        configureCompiler(this.configuration)
    }

    AbstractGroovyTemplateCompiler() {
    }

    protected CompilerConfiguration configureCompiler(CompilerConfiguration configuration) {
        configuration.compilationCustomizers.clear()

        ImportCustomizer importCustomizer = new ImportCustomizer()
        importCustomizer.addStarImports( viewConfiguration.packageImports )
        importCustomizer.addStaticStars( viewConfiguration.staticImports )

        configuration.addCompilationCustomizers(importCustomizer)
        configuration.addCompilationCustomizers(new ASTTransformationCustomizer(newViewsTransform()))
        return configuration
    }

    protected ViewsTransform newViewsTransform() {
        new ViewsTransform(viewConfiguration.extension)
    }

    void compile(List sources) {
        
        ExecutorService threadPool = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors()*2)
        CompletionService completionService = new ExecutorCompletionService(threadPool);
        
        try {
            Integer collationLevel = Runtime.getRuntime().availableProcessors()*2
            if(sources.size() < collationLevel) {
                collationLevel = 1
            }
            configuration.setClasspathList(classpath)
            String pathToSourceDir = sourceDir.canonicalPath
            def collatedSources = sources.collate(collationLevel)
            List> futures = []
            for(int index=0;index < collatedSources.size();index++) {
                def sourceFiles = collatedSources[index]
                futures.add(completionService.submit({ ->
                    CompilerConfiguration configuration = new CompilerConfiguration(this.configuration)
                    for(int viewIndex=0;viewIndex < sourceFiles.size();viewIndex++) {
                        File source = sourceFiles[viewIndex]
                        configureCompiler(configuration)
                        CompilationUnit unit = new CompilationUnit(configuration)
                        String pathToSource = source.canonicalPath
                        String path = pathToSource - pathToSourceDir
                        String templateName = GenericGroovyTemplateResolver.resolveTemplateName(
                                packageName, path
                        )
                        unit.addSource(new SourceUnit(
                                templateName,
                                new FileReaderSource(source, configuration),
                                configuration,
                                unit.classLoader,
                                unit.errorCollector
                        ))
                        unit.compile()
                    }
                    return true
                } as Callable) as Future)
            }

            int pending = futures.size()
                
            while (pending > 0) {
                // Wait for up to 100ms to see if anything has completed.
                // The completed future is returned if one is found; otherwise null.
                // (Tune 100ms as desired)
                def completed = completionService.poll(100, TimeUnit.MILLISECONDS);
                if (completed != null) {
                    Boolean response = completed.get() as Boolean//need this to throw exceptions on main thread it seems
                    --pending;
                }
            } 
        }     
        finally {
                threadPool.shutdown()
        }
                
        

    }

    void compile(File...sources) {
        compile Arrays.asList(sources)
    }

    static void run(String[] args, Class configurationClass, Class compilerClass) {
        if(args.length != 7) {
            System.err.println("Invalid arguments: [${args.join(',')}]")
            System.err.println("""
Usage: java -cp CLASSPATH ${compilerClass.name} [srcDir] [destDir] [targetCompatibility] [packageImports] [packageName] [configFile] [encoding]
""")
            System.exit(1)
        }
        File srcDir = new File(args[0])
        File destinationDir = new File(args[1])
        String targetCompatibility = args[2]
        String[] packageImports = args[3].trim().split(',')
        String packageName = args[4].trim()
        File configFile = new File(args[5])
        String encoding = new File(args[6])

        GenericViewConfiguration configuration = configurationClass.getDeclaredConstructor().newInstance()
        configuration.packageName = packageName
        configuration.encoding = encoding
        configuration.packageImports = packageImports

        configuration.readConfiguration(configFile)

        AbstractGroovyTemplateCompiler compiler = compilerClass.getDeclaredConstructor(ViewConfiguration, File).newInstance(configuration, srcDir)
        compiler.setTargetDirectory( destinationDir )
        compiler.setSourceEncoding( configuration.encoding )
        if(targetCompatibility != null) {
            compiler.setTargetBytecode( targetCompatibility )
        }

        String fileExtension = configuration.extension
        compiler.setDefaultScriptExtension(fileExtension)

        List allFiles = []
        srcDir.eachFileRecurse(FileType.FILES) { File f ->
            if(f.name.endsWith(fileExtension)) {
                allFiles.add(f)
            }
        }
        compiler.compile(allFiles)
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy