alvinalexander.com | career | drupal | java | mac | mysql | perl | scala | uml | unix  

Groovy example source code file (AstBuilder.groovy)

This example Groovy source code file (AstBuilder.groovy) is included in the DevDaily.com "Java Source Code Warehouse" project. The intent of this project is to help you "Learn Java by Example" TM.

Java - Groovy tags/keywords

a, a, are, astbuilder, astspecificationcompiler, blockstatement, closure, illegalargumentexception, illegalargumentexception, illegalstateexception, list, list, string, string

The Groovy AstBuilder.groovy source code

/*
 * Copyright 2003-2010 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
 *
 *     http://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 org.codehaus.groovy.ast.builder

import org.codehaus.groovy.ast.ASTNode
import org.codehaus.groovy.control.CompilePhase
import org.codehaus.groovy.ast.stmt.BlockStatement

/**
 * The AstBuilder provides several ways to build an abstract syntax tree (AST) of Groovy code.
 *
 * You can convert a String into AST using the buildFromString method.
 * You can convert code into AST using the buildFromCode method.
 * You can use the AST DSL with the buildFromSpec method. 
 * 
 * For more information, see the resources on the Groovy wiki pages. 
 *
 * @author Hamlet D'Arcy
 */

public class AstBuilder {

    /**
     * Builds AST based on the code within the  {@link Closure}  parameter.
     *
     * This method <strong>must be invoked at compile time and never at runtime, because
     * an ASTTransformation must be run to support it. If you receive an IllegalStateException then
     * you most likely need to add stronger typing. For instance, this will not work:
     * <code>
     *      def builder = new AstBuilder()
     *      builder.buildFromCode {
     *             // some code
     *      }
     * </code>
     * While this code will:
     * <code>
     *      new AstBuilder().buildFromCode {
     *             // some code
     *      }
     * </code>
     *
     * The compiler rewrites buildFromCode invocations into  {@link AstBuilder#buildFromString(CompilePhase, boolean, String)}
     * invocations. An exception raised during AST generation will show a stack trace from  {@link AstBuilder#buildFromString(CompilePhase, boolean, String)}
     * and not from  {@link AstBuilder#buildFromCode(CompilePhase, boolean, Closure)} .
     *
     * The compiler saves the source code of the closure as a String within the Java class file. The String source
     * of the closure will be visible and un-obfuscated within the class file. If your Closure parameter contains
     * sensitive data such as a hard-coded password then that data is free to be seen by anyone with the class file.
     * Do not store sensitive data within the closure parameter.
     *
     * @param phase
     *      the  {@link CompilePhase}  the AST will be targeted towards. Default is  {@link CompilePhase#CLASS_GENERATION}
     * @param statementsOnly
     *      when true, only the script statements are returned. WHen false, you will
     *      receive back a Script class node also. Default is true.
     * @param block
     *      the code that will be converted
     * @returns a List of  {@link ASTNode} .
     * @throws IllegalStateException
     *      this method may not be invoked at runtime. It works via a compile-time transformation
     *      of the closure source code into a String, which is sent to the  {@link AstBuilder#buildFromString(CompilePhase, boolean, String)}
     *      method. The buildFromCode() method must be invoked against a strongly typed AstBuilder.
     */
    List<ASTNode> buildFromCode(CompilePhase phase = CompilePhase.CLASS_GENERATION, boolean statementsOnly = true, Closure block) {
        throw new IllegalStateException("""AstBuilder.build(CompilePhase, boolean, Closure):List<ASTNode> should never be called at runtime.
Are you sure you are using it correctly?
""")
    }


    /**
     * Builds AST based on the code within the String parameter.
     *
     * @param phase
     *      the  {@link CompilePhase}  the AST will be targeted towards. Default is  {@link CompilePhase#CLASS_GENERATION}
     * @param statementsOnly
     *      when true, only the script statements are returned. WHen false, you will
     *      receive back a Script class node also. Default is true.
     * @param source
     *      The source code String that will be compiled.
     * @returns a List of  {@link ASTNode} .
     * @throws IllegalArgumentException
     *      if source is null or empty
     */
    List<ASTNode> buildFromString(CompilePhase phase = CompilePhase.CLASS_GENERATION, boolean statementsOnly = true, String source) {
        if (!source || "" == source.trim()) throw new IllegalArgumentException("A source must be specified")
        return new AstStringCompiler().compile(source, phase, statementsOnly);
    }

    /**
     * Builds AST based on the code within the String parameter. The parameter is assumed to be 
     * a code block which is not legal Groovy code. A goto label is affixed to the block, compiled, 
     * and the resulting BlockStatement wrapper is removed before returning a result. 
     * @param phase
     *      the  {@link CompilePhase}  the AST will be targeted towards. Default is  {@link CompilePhase#CLASS_GENERATION}
     * @param statementsOnly
     *      when true, only the script statements are returned. WHen false, you will
     *      receive back a Script class node also. Default is true.
     * @param source
     *      The source code String that will be compiled. The string must be a block wrapped in curly braces. 
     * @returns a List of  {@link ASTNode} .
     * @throws IllegalArgumentException
     *      if source is null or empty
     */
    private List<ASTNode> buildFromBlock(CompilePhase phase = CompilePhase.CLASS_GENERATION, boolean statementsOnly = true, String source) {
        if (!source || "" == source.trim()) throw new IllegalArgumentException("A source must be specified")
        def labelledSource = "__synthesized__label__${System.currentTimeMillis()}__:" + source
        List<ASTNode> result = new AstStringCompiler().compile(labelledSource, phase, statementsOnly)
        // find the block statement from the result, and unwrap it from one level.
        result.collect { node ->
            if (node instanceof BlockStatement) {
                ((BlockStatement)node).statements[0] //unwrap the artifact of pre-pending the goto label
            } else {
                node
            }
        }
    }

    /**
     * Builds AST based on the DSL data within the Closure parameter.
     * @param specification
     *      the contents to create
     */
    List<ASTNode> buildFromSpec(Closure specification) {
        if (specification == null) throw new IllegalArgumentException('Null: specification')
        def properties = new AstSpecificationCompiler(specification)
        return properties.expression
    }
}

Other Groovy examples (source code examples)

Here is a short list of links related to this Groovy AstBuilder.groovy source code file:

... this post is sponsored by my books ...

#1 New Release!

FP Best Seller

 

new blog posts

 

Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.

A percentage of advertising revenue from
pages under the /java/jwarehouse URI on this website is
paid back to open source projects.