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

grails.cli.generator.AbstractGenerator.groovy Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2022-2024 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License")
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package grails.cli.generator

import groovy.transform.stc.ClosureParams
import groovy.transform.stc.FromString
import org.springframework.util.FileSystemUtils

import grails.build.logging.GrailsConsole
import grails.dev.commands.io.FileSystemInteraction
import grails.dev.commands.io.FileSystemInteractionImpl
import grails.dev.commands.template.TemplateRenderer
import grails.dev.commands.template.TemplateRendererImpl
import org.grails.build.parsing.CommandLine
import org.grails.config.CodeGenConfig
import org.grails.io.support.DefaultResourceLoader
import org.grails.io.support.Resource
import org.grails.io.support.SpringIOUtils

/**
 * @author Michael Yan
 * @since 2023.2.0
 */
class AbstractGenerator implements Generator {

    protected GenerationContext generationContext
    protected GrailsConsole console
    protected CommandLine commandLine

    protected File baseDir

    protected TemplateRenderer templateRenderer
    protected FileSystemInteraction fileSystemInteraction

    @Override
    void init(GenerationContext generationContext) {
        this.generationContext = generationContext
        this.console = generationContext.console
        this.commandLine = generationContext.commandLine
        this.baseDir = generationContext.baseDir
        this.templateRenderer = new TemplateRendererImpl(this.baseDir)
        this.fileSystemInteraction = new FileSystemInteractionImpl(this.baseDir, new DefaultResourceLoader())
    }

    protected CodeGenConfig loadApplicationConfig() {
        CodeGenConfig config = new CodeGenConfig()
        File applicationYml = new File(getBaseDir(), "app/conf/application.yml")
        File applicationGroovy = new File(getBaseDir(), "app/conf/application.groovy")
        if (applicationYml.exists()) {
            config.loadYml(applicationYml)
        }
        if (applicationGroovy.exists()) {
            config.loadGroovy(applicationGroovy)
        }
        config
    }

    protected File getBaseDir() {
        this.baseDir
    }

    protected String getTemplateRoot() {
        "templates/generators/$name"
    }

    protected TemplateRenderer getTemplateRenderer() {
        this.templateRenderer
    }

    protected FileSystemInteraction getFileSystemInteraction() {
        this.fileSystemInteraction
    }

    protected String getDefaultPackageName() {
        loadApplicationConfig().getProperty('grails.codegen.defaultPackage')
    }

    protected String getDefaultPackagePath() {
        getDefaultPackageName()?.replace('.', '/')
    }

    protected void createFile(String source, String destination, Map model, boolean overwrite) {
        this.templateRenderer.render(this.templateRenderer.template(getTemplateRoot(), source),
                fileSystemInteraction.file(destination), model, overwrite)
    }

    void template(String source, String destination) {
        template(source, destination, new HashMap(), true)
    }

    void template(String source, String destination, Map model) {
        this.templateRenderer.render(this.templateRenderer.template(getTemplateRoot(), source), file(destination), model, true)
    }

    void template(String source, String destination, Map model, boolean overwrite) {
        this.templateRenderer.render(this.templateRenderer.template(getTemplateRoot(), source), file(destination), model, overwrite)
    }

    void copyFile(String source, String destination) {
        this.console.addStatus('create '.padLeft(13), destination, 'GREEN')
        SpringIOUtils.copy(this.templateRenderer.template(getTemplateRoot(), source), file(destination))
    }

    void removeFile(String destination) {
        this.console.addStatus('remove '.padLeft(13), destination, 'YELLOW')
        File file = new File(getBaseDir(), destination)
        if (file.exists()) {
            file.delete()
        }
    }

    void prependToFile(String destination, String text, Map args = [:]) {
        this.console.addStatus('append '.padLeft(13), destination, 'GREEN')
        File file = new File(getBaseDir(), destination)
        if (file.canWrite()) {
            file.append(text)
        }
    }

    File file(Object path) {
        if (path instanceof File) {
            return (File) path
        }
        else if (path instanceof Resource) {
            return ((Resource) path).file
        }
        new File(getBaseDir(), path.toString())
    }

    /**
     * Creates a file with the specified name and the text contents using the system default encoding.
     * @param name name of the file to be created
     * @param contents the contents of the file, written using the system default encoding
     * @return the file being created
     */
    File file(String name, CharSequence contents) {
        new File(getBaseDir(), name) << contents
    }

    /**
     * Creates a file with the specified name and the specified binary contents
     * @param name name of the file to be created
     * @param contents the contents of the file
     * @return the file being created
     */
    File file(String name, byte[] contents) {
        new File(getBaseDir(), name) << contents
    }

    /**
     * Creates a file with the specified name and the contents from the source file (copy).
     * @param name name of the file to be created
     * @param contents the contents of the file
     * @return the file being created
     */
    File file(String name, File source) {
        file(name, source.bytes)
    }

    /**
     * Creates a new file in the current directory, whose contents is going to be generated in the
     * closure. The delegate of the closure is the file being created.
     * @param name name of the file to create
     * @param spec closure for generating the file contents
     * @return the created file
     */
    File file(String name, @ClosureParams(value = FromString, options = 'File') @DelegatesTo(value = File, strategy = Closure.DELEGATE_FIRST) Closure spec) {
        def file = new File(getBaseDir(), name)
        file.tap(spec)
    }

    /**
     * Creates a new empty directory
     * @param name the name of the directory to create
     * @return the created directory
     */
    File dir(String name) {
        this.console.addStatus('create '.padLeft(13), name, 'GREEN')
        def file = new File(getBaseDir(), name)
        file.mkdirs()
        file
    }

    /**
     * Creates a new directory and allows to specify a subdirectory structure using the closure as a specification
     * @param name name of the directory to be created
     * @param spec specification of the subdirectory structure
     * @return the created directory
     */
    File dir(String name, @DelegatesTo(value = FileTreeBuilder, strategy = Closure.DELEGATE_FIRST) Closure spec) {
        def oldBase = this.baseDir
        def newBase = dir(name)
        try {
            this.baseDir = newBase
            def clone = (Closure) spec.clone()
            clone.delegate = this
            clone.resolveStrategy = Closure.DELEGATE_FIRST
            clone()
        }
        finally {
            this.baseDir = oldBase
        }
        newBase
    }

    File call(@DelegatesTo(value = FileTreeBuilder, strategy = Closure.DELEGATE_FIRST) Closure spec) {
        def clone = (Closure) spec.clone()
        clone.delegate = this
        clone.resolveStrategy = Closure.DELEGATE_FIRST
        clone.call()
        this.baseDir
    }

    def methodMissing(String name, Object args) {
        if (args instanceof Object[] && args.length == 1) {
            def arg = args[0]
            if (arg instanceof Closure) {
                dir(name, (Closure) arg)
            }
            else if (arg instanceof CharSequence) {
                file(name, arg.toString())
            }
            else if (arg instanceof byte[]) {
                file(name, (byte[]) arg)
            }
            else if (arg instanceof File) {
                file(name, (File) arg)
            }
        }
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy