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

Groovy example source code file (SingletonASTTransformation.java)

This example Groovy source code file (SingletonASTTransformation.java) 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

acc_public, acc_static, acc_static, annotatednode, annotatednode, annotationnode, argumentlistexpression, binaryexpression, blockstatement, blockstatement, classnode, returnstatement, returnstatement, util, variableexpression

The Groovy SingletonASTTransformation.java source code

/*
 * Copyright 2008 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.transform;

import org.codehaus.groovy.ast.*;
import org.codehaus.groovy.ast.expr.*;
import org.codehaus.groovy.ast.stmt.*;
import org.codehaus.groovy.control.CompilePhase;
import org.codehaus.groovy.control.SourceUnit;
import org.codehaus.groovy.syntax.Token;
import org.objectweb.asm.Opcodes;

import java.util.List;
import java.util.Iterator;

/**
 * Handles generation of code for the @Singleton annotation
 *
 * @author Alex Tkachman
 */
@GroovyASTTransformation(phase= CompilePhase.CANONICALIZATION)
public class SingletonASTTransformation implements ASTTransformation, Opcodes {

    /**
     *
     * @param nodes   the ast nodes
     * @param source  the source unit for the nodes
     */
    public void visit(ASTNode[] nodes, SourceUnit source) {
        if (!(nodes[0] instanceof AnnotationNode) || !(nodes[1] instanceof AnnotatedNode)) {
            throw new RuntimeException(
                String.format(
                    "Internal error: wrong types: %s / %s. Expected: AnnotationNode / AnnotatedNode", 
                    nodes[0].getClass(), 
                    nodes[1].getClass())
                );
        }

        AnnotatedNode parent = (AnnotatedNode) nodes[1];
        AnnotationNode node = (AnnotationNode) nodes[0];

        if (parent instanceof ClassNode) {
            ClassNode classNode = (ClassNode) parent;
            final Expression member = node.getMember("lazy");
            if(member instanceof ConstantExpression && ((ConstantExpression)member).getValue().equals(true))
               createLazy(classNode);
            else
               createNonLazy(classNode);
        }
    }

    private void createNonLazy(ClassNode classNode) {
        final FieldNode fieldNode = classNode.addField("instance", ACC_PUBLIC|ACC_FINAL|ACC_STATIC, classNode, new ConstructorCallExpression(classNode, new ArgumentListExpression()));
        createConstructor(classNode, fieldNode);

        final BlockStatement body = new BlockStatement();
        body.addStatement(new ReturnStatement(new VariableExpression(fieldNode)));
        classNode.addMethod("getInstance", ACC_STATIC|ACC_PUBLIC, classNode, Parameter.EMPTY_ARRAY, ClassNode.EMPTY_ARRAY, body);
    }

    private void createLazy(ClassNode classNode) {
        final FieldNode fieldNode = classNode.addField("instance", ACC_PRIVATE|ACC_STATIC|ACC_VOLATILE, classNode, null);
        createConstructor(classNode, fieldNode);

        final BlockStatement body = new BlockStatement();
        final Expression instanceExpression = new VariableExpression(fieldNode);
        body.addStatement(new IfStatement(
            new BooleanExpression(new BinaryExpression(instanceExpression, Token.newSymbol("!=",-1,-1), ConstantExpression.NULL)),
            new ReturnStatement(instanceExpression),
            new SynchronizedStatement(
                    new ClassExpression(classNode),
                    new IfStatement(
                            new BooleanExpression(new BinaryExpression(instanceExpression, Token.newSymbol("!=",-1,-1), ConstantExpression.NULL)),
                                new ReturnStatement(instanceExpression),
                                new ReturnStatement(new BinaryExpression(instanceExpression,Token.newSymbol("=",-1,-1), new ConstructorCallExpression(classNode, new ArgumentListExpression())))
                    )
            )
        ));
        classNode.addMethod("getInstance", ACC_STATIC|ACC_PUBLIC, classNode, Parameter.EMPTY_ARRAY, ClassNode.EMPTY_ARRAY, body);
    }

    private void createConstructor(ClassNode classNode, FieldNode field) {

        final List list = classNode.getDeclaredConstructors();
        MethodNode found = null;
        for (Iterator it = list.iterator(); it.hasNext(); ) {
            MethodNode mn = (MethodNode) it.next();
            final Parameter[] parameters = mn.getParameters();
            if (parameters == null || parameters.length == 0) {
                found = mn;
                break;
            }
        }

        if (found == null) {
            final BlockStatement body = new BlockStatement();
            body.addStatement(new IfStatement(
                    new BooleanExpression(new BinaryExpression(new VariableExpression(field), Token.newSymbol("!=",-1,-1), ConstantExpression.NULL)),
                new ThrowStatement(
                        new ConstructorCallExpression(ClassHelper.make(RuntimeException.class),
                                new ArgumentListExpression(
                                        new ConstantExpression("Can't instantiate singleton " + classNode.getName() + ". Use " + classNode.getName() + ".instance" )))),
                new EmptyStatement()));
            classNode.addConstructor(new ConstructorNode(ACC_PRIVATE, body));
        }
    }
}

Other Groovy examples (source code examples)

Here is a short list of links related to this Groovy SingletonASTTransformation.java 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.