Package org.codehaus.groovy.transform

Source Code of org.codehaus.groovy.transform.AutoCloneASTTransformation

/*
* 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.transform.AutoClone;
import groovy.transform.AutoCloneStyle;
import org.codehaus.groovy.ast.ASTNode;
import org.codehaus.groovy.ast.AnnotatedNode;
import org.codehaus.groovy.ast.AnnotationNode;
import org.codehaus.groovy.ast.ClassHelper;
import org.codehaus.groovy.ast.ClassNode;
import org.codehaus.groovy.ast.FieldNode;
import org.codehaus.groovy.ast.Parameter;
import org.codehaus.groovy.ast.VariableScope;
import org.codehaus.groovy.ast.expr.*;
import org.codehaus.groovy.ast.stmt.BlockStatement;
import org.codehaus.groovy.ast.stmt.EmptyStatement;
import org.codehaus.groovy.ast.stmt.ExpressionStatement;
import org.codehaus.groovy.ast.stmt.IfStatement;
import org.codehaus.groovy.ast.stmt.ReturnStatement;
import org.codehaus.groovy.ast.stmt.Statement;
import org.codehaus.groovy.control.CompilePhase;
import org.codehaus.groovy.control.SourceUnit;
import org.codehaus.groovy.syntax.Token;
import org.codehaus.groovy.syntax.Types;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.util.List;

import static org.codehaus.groovy.transform.AbstractASTTransformUtil.assignStatement;
import static org.codehaus.groovy.transform.AbstractASTTransformUtil.getInstanceNonPropertyFields;
import static org.codehaus.groovy.transform.AbstractASTTransformUtil.getInstancePropertyFields;
import static org.codehaus.groovy.transform.AbstractASTTransformUtil.isInstanceOf;

/**
* Handles generation of code for the @AutoClone annotation.
*
* @author Paul King
*/
@GroovyASTTransformation(phase = CompilePhase.CANONICALIZATION)
public class AutoCloneASTTransformation extends AbstractASTTransformation {
    static final Class MY_CLASS = AutoClone.class;
    static final ClassNode MY_TYPE = ClassHelper.make(MY_CLASS);
    static final String MY_TYPE_NAME = "@" + MY_TYPE.getNameWithoutPackage();
    private static final ClassNode CLONEABLE_TYPE = ClassHelper.make(Cloneable.class);
    private static final ClassNode BAOS_TYPE = ClassHelper.make(ByteArrayOutputStream.class);
    private static final ClassNode BAIS_TYPE = ClassHelper.make(ByteArrayInputStream.class);
    private static final Token ASSIGN = Token.newSymbol(Types.ASSIGN, -1, -1);

    public void visit(ASTNode[] nodes, SourceUnit source) {
        init(nodes, source);
        AnnotatedNode parent = (AnnotatedNode) nodes[1];
        AnnotationNode anno = (AnnotationNode) nodes[0];
        if (!MY_TYPE.equals(anno.getClassNode())) return;

        if (parent instanceof ClassNode) {
            ClassNode cNode = (ClassNode) parent;
            checkNotInterface(cNode, MY_TYPE_NAME);
            cNode.addInterface(CLONEABLE_TYPE);
            boolean includeFields = memberHasValue(anno, "includeFields", true);
            AutoCloneStyle style = getStyle(anno, "style");
            List<String> excludes = tokenize((String) getMemberValue(anno, "excludes"));
            List<FieldNode> list = getInstancePropertyFields(cNode);
            if (includeFields) {
                list.addAll(getInstanceNonPropertyFields(cNode));
            }
            if (style == null) style = AutoCloneStyle.CLONE;
            switch (style) {
                case COPY_CONSTRUCTOR:
                    createCloneCopyConstructor(cNode, list, excludes);
                    break;
                case SERIALIZATION:
                    createCloneSerialization(cNode, list, excludes);
                    break;
                case CLONE:
                    createClone(cNode, list, excludes);
                    break;
            }
        }
    }

    private void createCloneSerialization(ClassNode cNode, List<FieldNode> list, List<String> excludes) {
        final BlockStatement body = new BlockStatement();
        // def baos = new ByteArrayOutputStream()
        final Expression baos = new VariableExpression("baos");
        body.addStatement(new ExpressionStatement(new DeclarationExpression(baos, ASSIGN, new ConstructorCallExpression(BAOS_TYPE, MethodCallExpression.NO_ARGUMENTS))));

        // baos.withObjectOutputStream{ it.writeObject(this) }
        BlockStatement writeClosureCode = new BlockStatement();
        final Expression it = new VariableExpression("it");
        writeClosureCode.addStatement(new ExpressionStatement(new MethodCallExpression(it, "writeObject", VariableExpression.THIS_EXPRESSION)));
        ClosureExpression writeClosure = new ClosureExpression(new Parameter[]{}, writeClosureCode);
        writeClosure.setVariableScope(new VariableScope());
        body.addStatement(new ExpressionStatement(new MethodCallExpression(baos, "withObjectOutputStream", new ArgumentListExpression(writeClosure))));

        // def bais = new ByteArrayInputStream(baos.toByteArray())
        final Expression bais = new VariableExpression("bais");
        ConstructorCallExpression bytes = new ConstructorCallExpression(BAIS_TYPE, new TupleExpression(new MethodCallExpression(baos, "toByteArray", MethodCallExpression.NO_ARGUMENTS)));
        body.addStatement(new ExpressionStatement(new DeclarationExpression(bais, ASSIGN, bytes)));

        // return bais.withObjectInputStream(getClass().classLoader){ it.readObject() }
        BlockStatement readClosureCode = new BlockStatement();
        readClosureCode.addStatement(new ExpressionStatement(new MethodCallExpression(it, "readObject", MethodCallExpression.NO_ARGUMENTS)));
        ClosureExpression readClosure = new ClosureExpression(new Parameter[]{}, readClosureCode);
        readClosure.setVariableScope(new VariableScope());
        Expression klass = new MethodCallExpression(VariableExpression.THIS_EXPRESSION, "getClass", MethodCallExpression.NO_ARGUMENTS);
        Expression classLoader = new MethodCallExpression(klass, "getClassLoader", MethodCallExpression.NO_ARGUMENTS);
        Expression result = new MethodCallExpression(bais, "withObjectInputStream", new ArgumentListExpression(classLoader, readClosure));
        body.addStatement(new ReturnStatement(result));

        ClassNode[] exceptions = {ClassHelper.make(CloneNotSupportedException.class)};
        cNode.addMethod("clone", ACC_PUBLIC, ClassHelper.OBJECT_TYPE, new Parameter[0], exceptions, body);
    }

    private void createCloneCopyConstructor(ClassNode cNode, List<FieldNode> list, List<String> excludes) {
        BlockStatement initBody = new BlockStatement();
        if (cNode.getDeclaredConstructors().size() == 0) {
            // add no-arg constructor
            initBody.addStatement(new EmptyStatement());
            cNode.addConstructor(ACC_PUBLIC, new Parameter[0], ClassNode.EMPTY_ARRAY, initBody);
            initBody = new BlockStatement();
        }
        Parameter initParam = new Parameter(cNode, "other");
        final Expression other = new VariableExpression(initParam);
        boolean hasParent = cNode.getSuperClass() != ClassHelper.OBJECT_TYPE;
        if (hasParent) {
            initBody.addStatement(new ExpressionStatement(new ConstructorCallExpression(ClassNode.SUPER, other)));
        }
        for (FieldNode fieldNode : list) {
            String name = fieldNode.getName();
            if (excludes.contains(name)) continue;
            PropertyExpression direct = new PropertyExpression(other, name);
            Expression cloned = new MethodCallExpression(direct, "clone", MethodCallExpression.NO_ARGUMENTS);
            Expression to = new PropertyExpression(VariableExpression.THIS_EXPRESSION, name);
            Statement assignCloned = assignStatement(to, cloned);
            Statement assignDirect = assignStatement(to, direct);
            initBody.addStatement(new IfStatement(isInstanceOf(direct, CLONEABLE_TYPE), assignCloned, assignDirect));
        }
        ClassNode[] exceptions = {ClassHelper.make(CloneNotSupportedException.class)};
        cNode.addConstructor(ACC_PROTECTED, new Parameter[]{initParam}, ClassNode.EMPTY_ARRAY, initBody);
        final BlockStatement cloneBody = new BlockStatement();
        cloneBody.addStatement(new ExpressionStatement(new ConstructorCallExpression(cNode, new ArgumentListExpression(VariableExpression.THIS_EXPRESSION))));
        cNode.addMethod("clone", ACC_PUBLIC, ClassHelper.OBJECT_TYPE, new Parameter[0], exceptions, cloneBody);
    }

    private void createClone(ClassNode cNode, List<FieldNode> list, List<String> excludes) {
        final BlockStatement body = new BlockStatement();
        final Expression result = new VariableExpression("_result");
        final Expression clone = new MethodCallExpression(VariableExpression.SUPER_EXPRESSION, "clone", MethodCallExpression.NO_ARGUMENTS);
        body.addStatement(new ExpressionStatement(new DeclarationExpression(result, ASSIGN, clone)));
        for (FieldNode fieldNode : list) {
            if (excludes.contains(fieldNode.getName())) continue;
            Expression fieldExpr = new VariableExpression(fieldNode);
            Expression from = new MethodCallExpression(fieldExpr, "clone", MethodCallExpression.NO_ARGUMENTS);
            Expression to = new PropertyExpression(result, fieldNode.getName());
            Statement doClone = assignStatement(to, from);
            Statement doNothing = new EmptyStatement();
            body.addStatement(new IfStatement(isInstanceOf(fieldExpr, CLONEABLE_TYPE), doClone, doNothing));
        }
        body.addStatement(new ReturnStatement(result));
        ClassNode[] exceptions = {ClassHelper.make(CloneNotSupportedException.class)};
        cNode.addMethod("clone", ACC_PUBLIC, ClassHelper.OBJECT_TYPE, new Parameter[0], exceptions, body);
    }

    private AutoCloneStyle getStyle(AnnotationNode node, String name) {
        final Expression member = node.getMember(name);
        if (member != null && member instanceof PropertyExpression) {
            PropertyExpression prop = (PropertyExpression) member;
            Expression oe = prop.getObjectExpression();
            if (oe instanceof ClassExpression) {
                ClassExpression ce = (ClassExpression) oe;
                if (ce.getType().getName().equals("groovy.transform.AutoCloneStyle")) {
                    return AutoCloneStyle.valueOf(prop.getPropertyAsString());
                }
            }
        }
        return null;
    }

}
TOP

Related Classes of org.codehaus.groovy.transform.AutoCloneASTTransformation

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.