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

Groovy example source code file (DelegateASTTransformation.java)

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

annotatednode, annotationnode, classnode, classnode, fieldnode, list, list, methodnode, methodnode, parameter, propertynode, reflection, string, string, util, variableexpression

The Groovy DelegateASTTransformation.java source code

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

import groovy.lang.GroovyObject;
import org.codehaus.groovy.GroovyBugError;
import org.codehaus.groovy.ast.*;
import org.codehaus.groovy.ast.expr.*;
import org.codehaus.groovy.ast.stmt.ExpressionStatement;
import org.codehaus.groovy.ast.stmt.ReturnStatement;
import org.codehaus.groovy.classgen.Verifier;
import org.codehaus.groovy.control.CompilePhase;
import org.codehaus.groovy.control.SourceUnit;
import org.codehaus.groovy.control.messages.SyntaxErrorMessage;
import org.codehaus.groovy.syntax.SyntaxException;
import org.codehaus.groovy.syntax.Token;
import org.codehaus.groovy.syntax.Types;
import org.objectweb.asm.Opcodes;

import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Set;

/**
 * Handles generation of code for the <code>@Delegate annotation
 *
 * @author Alex Tkachman
 * @author Guillaume Laforge
 * @author Paul King
 */
@GroovyASTTransformation(phase = CompilePhase.CANONICALIZATION)
public class DelegateASTTransformation implements ASTTransformation, Opcodes {
    private static final ClassNode DEPRECATED_TYPE = ClassHelper.make(Deprecated.class);
    private static final ClassNode GROOVYOBJECT_TYPE = ClassHelper.make(GroovyObject.class);

    public void visit(ASTNode[] nodes, SourceUnit source) {
        if (nodes.length != 2 || !(nodes[0] instanceof AnnotationNode) || !(nodes[1] instanceof AnnotatedNode)) {
            throw new GroovyBugError("Internal error: expecting [AnnotationNode, AnnotatedNode] but got: " + Arrays.asList(nodes));
        }

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

        if (parent instanceof FieldNode) {
            FieldNode fieldNode = (FieldNode) parent;
            final ClassNode type = fieldNode.getType();
            final ClassNode owner = fieldNode.getOwner();
            if (type.equals(ClassHelper.OBJECT_TYPE) || type.equals(GROOVYOBJECT_TYPE)) {
                addError("@Delegate field '" + fieldNode.getName() + "' has an inappropriate type: " + type.getName() +
                        ". Please add an explicit type but not java.lang.Object or groovy.lang.GroovyObject.", parent, source);
                return;
            }
            if (type.equals(owner)) {
                addError("@Delegate field '" + fieldNode.getName() + "' has an inappropriate type: " + type.getName() +
                        ". Delegation to own type not supported. Please use a different type.", parent, source);
                return;
            }
            final List<MethodNode> fieldMethods = getAllMethods(type);
            for (ClassNode next : type.getAllInterfaces()) {
                fieldMethods.addAll(getAllMethods(next));
            }
            final Expression deprecatedElement = node.getMember("deprecated");
            final boolean deprecated = hasBooleanValue(deprecatedElement, true);

            final List<MethodNode> ownerMethods = getAllMethods(owner);
            for (MethodNode mn : fieldMethods) {
                addDelegateMethod(fieldNode, owner, ownerMethods, mn, deprecated);
            }

            for (PropertyNode prop : type.getProperties()) {
                if (prop.isStatic() || !prop.isPublic())
                    continue;
                String name = prop.getName();
                addGetterIfNeeded(fieldNode, owner, prop, name);
                addSetterIfNeeded(fieldNode, owner, prop, name);
            }

            final Expression interfacesElement = node.getMember("interfaces");
            if (hasBooleanValue(interfacesElement, false)) return;

            final Set<ClassNode> allInterfaces = type.getAllInterfaces();
            final Set<ClassNode> ownerIfaces = owner.getAllInterfaces();
            for (ClassNode iface : allInterfaces) {
                if (Modifier.isPublic(iface.getModifiers()) && !ownerIfaces.contains(iface)) {
                    final ClassNode[] ifaces = owner.getInterfaces();
                    final ClassNode[] newIfaces = new ClassNode[ifaces.length + 1];
                    System.arraycopy(ifaces, 0, newIfaces, 0, ifaces.length);
                    newIfaces[ifaces.length] = iface;
                    owner.setInterfaces(newIfaces);
                }
            }
        }
    }

    private List<MethodNode> getAllMethods(ClassNode type) {
        ClassNode node = type;
        List<MethodNode> result = new ArrayList();
        while (node != null) {
            result.addAll(node.getMethods());
            node = node.getSuperClass();
        }
        return result;
    }

    private boolean hasBooleanValue(Expression expression, boolean bool) {
        return expression instanceof ConstantExpression && ((ConstantExpression) expression).getValue().equals(bool);
    }

    private void addSetterIfNeeded(FieldNode fieldNode, ClassNode owner, PropertyNode prop, String name) {
        String setterName = "set" + Verifier.capitalize(name);
        if ((prop.getModifiers() & ACC_FINAL) == 0 && owner.getSetterMethod(setterName) == null) {
            owner.addMethod(setterName,
                    ACC_PUBLIC,
                    ClassHelper.VOID_TYPE,
                    new Parameter[]{new Parameter(nonGeneric(prop.getType()), "value")},
                    null,
                    new ExpressionStatement(
                            new BinaryExpression(
                                    new PropertyExpression(
                                            new VariableExpression(fieldNode),
                                            name),
                                    Token.newSymbol(Types.EQUAL, -1, -1),
                                    new VariableExpression("value"))));
        }
    }

    private void addGetterIfNeeded(FieldNode fieldNode, ClassNode owner, PropertyNode prop, String name) {
        String getterName = "get" + Verifier.capitalize(name);
        if (owner.getGetterMethod(getterName) == null) {
            owner.addMethod(getterName,
                    ACC_PUBLIC,
                    nonGeneric(prop.getType()),
                    Parameter.EMPTY_ARRAY,
                    null,
                    new ReturnStatement(
                            new PropertyExpression(
                                    new VariableExpression(fieldNode),
                                    name)));
        }
    }

    private void addDelegateMethod(FieldNode fieldNode, ClassNode owner, List<MethodNode> ownMethods, MethodNode candidate, boolean deprecated) {
        if (!candidate.isPublic() || candidate.isStatic() || 0 != (candidate.getModifiers () & Opcodes.ACC_SYNTHETIC))
            return;

        if (!candidate.getAnnotations(DEPRECATED_TYPE).isEmpty() && !deprecated)
            return;

        // ignore methods from GroovyObject
        for (MethodNode mn : GROOVYOBJECT_TYPE.getMethods()) {
            if (mn.getTypeDescriptor().equals(candidate.getTypeDescriptor())) {
                return;
            }
        }

        // ignore methods already in owner
        for (MethodNode mn : owner.getMethods()) {
            if (mn.getTypeDescriptor().equals(candidate.getTypeDescriptor())) {
                return;
            }
        }

        // give precedence to methods of self (but not abstract or static superclass methods)
        // also allows abstract or static self methods to be selected for overriding but they are ignored later
        MethodNode existingNode = null;
        for (MethodNode mn : ownMethods) {
            if (mn.getTypeDescriptor().equals(candidate.getTypeDescriptor()) && !mn.isAbstract() && !mn.isStatic()) {
                existingNode = mn;
                break;
            }
        }
        if (existingNode == null || existingNode.getCode() == null) {
            final ArgumentListExpression args = new ArgumentListExpression();
            final Parameter[] params = candidate.getParameters();
            final Parameter[] newParams = new Parameter[params.length];
            for (int i = 0; i < newParams.length; i++) {
                Parameter newParam = new Parameter(nonGeneric(params[i].getType()), params[i].getName());
                newParam.setInitialExpression(params[i].getInitialExpression());
                newParams[i] = newParam;
                args.addExpression(new VariableExpression(newParam));
            }
            // addMethod will ignore attempts to override abstract or static methods with same signature on self
            MethodNode newMethod = owner.addMethod(candidate.getName(),
                    candidate.getModifiers() & (~ACC_ABSTRACT) & (~ACC_NATIVE),
                    nonGeneric(candidate.getReturnType()),
                    newParams,
                    candidate.getExceptions(),
                    new ExpressionStatement(
                            new MethodCallExpression(
                                    new VariableExpression(fieldNode),
                                    candidate.getName(),
                                    args)));
            newMethod.setGenericsTypes(candidate.getGenericsTypes());
        }
    }

    private ClassNode nonGeneric(ClassNode type) {
        if (type.isUsingGenerics()) {
            final ClassNode nonGen = ClassHelper.makeWithoutCaching(type.getName());
            nonGen.setRedirect(type);
            nonGen.setGenericsTypes(null);
            nonGen.setUsingGenerics(false);
            return nonGen;
        } else {
            return type;
        }
    }

    public void addError(String msg, ASTNode expr, SourceUnit source) {
        int line = expr.getLineNumber();
        int col = expr.getColumnNumber();
        source.getErrorCollector().addErrorAndContinue(
                new SyntaxErrorMessage(new SyntaxException(msg + '\n', line, col), source)
        );
    }
}

Other Groovy examples (source code examples)

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