org.codehaus.groovy.grails.compiler.injection.DefaultGrailsDomainClassInjector.java Source code

Java tutorial

Introduction

Here is the source code for org.codehaus.groovy.grails.compiler.injection.DefaultGrailsDomainClassInjector.java

Source

/*
 * Copyright 2004-2005 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.grails.compiler.injection;

//import org.apache.commons.logging.Log;
//import org.apache.commons.logging.LogFactory;

import org.codehaus.groovy.ast.*;
import org.codehaus.groovy.ast.expr.*;
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.transform.ASTTransformation;
import org.codehaus.groovy.transform.GroovyASTTransformation;

import java.lang.reflect.Modifier;
import java.net.URL;
import java.util.*;

/**
 * This is substantially the same code from Grails, except some references de-referenced
 * and the macro class added.
 *
 * Default implementation of domain class injector interface that adds the 'id'
 * and 'version' properties and other previously boilerplate code
 *
 * @author Graeme Rocher
 *
 * @since 0.2
 *
 * Created: 20th June 2006
 */
@GroovyASTTransformation(phase = CompilePhase.CANONICALIZATION)
public class DefaultGrailsDomainClassInjector implements ASTTransformation {
    //GrailsDomainClassInjector {

    //private static final Log LOG  = LogFactory.getLog(DefaultGrailsDomainClassInjector.class);

    public void visit(ASTNode[] nodes, SourceUnit source) {
        performInjection((ClassNode) nodes[1]);
    }

    public void performInjection(ClassNode classNode) {
        if (shouldInject(classNode)) {
            injectIdProperty(classNode);

            injectVersionProperty(classNode);

            injectToStringMethod(classNode);

            injectAssociations(classNode);

        }
    }

    public boolean shouldInject(URL url) {
        return true; //return GrailsResourceUtils.isDomainClass(url);
    }

    private boolean shouldInject(ClassNode classNode) {
        //String fullName = GrailsASTUtils.getFullName(classNode);
        //String mappingFile = GrailsDomainConfigurationUtil.getMappingFileName(fullName);

        //if(getClass().getResource(mappingFile)!=null) {
        //if(LOG.isDebugEnabled()) {
        //LOG.debug("[GrailsDomainInjector] Mapping file ["+mappingFile+"] found. Skipping property injection.");
        //}
        //return false;
        //}
        return true;
    }

    private void injectAssociations(ClassNode classNode) {

        List properties = classNode.getProperties();
        List propertiesToAdd = new ArrayList();
        for (Iterator p = properties.iterator(); p.hasNext();) {
            PropertyNode pn = (PropertyNode) p.next();
            final boolean isHasManyProperty = pn.getName().equals(/*GrailsDomainClassProperty.*/RELATES_TO_MANY)
                    || pn.getName().equals(/*GrailsDomainClassProperty.*/HAS_MANY);
            if (isHasManyProperty) {
                Expression e = pn.getInitialExpression();
                propertiesToAdd.addAll(createPropertiesForHasManyExpression(e, classNode));
            }
            final boolean isBelongsTo = pn.getName().equals(/*GrailsDomainClassProperty.*/BELONGS_TO);
            if (isBelongsTo) {
                Expression e = pn.getInitialExpression();
                propertiesToAdd.addAll(createPropertiesForBelongsToExpression(e, classNode));
            }
        }
        injectAssociationProperties(classNode, propertiesToAdd);
    }

    private Collection createPropertiesForBelongsToExpression(Expression e, ClassNode classNode) {
        List properties = new ArrayList();
        if (e instanceof MapExpression) {
            MapExpression me = (MapExpression) e;
            List mapEntries = me.getMapEntryExpressions();
            for (Iterator i = mapEntries.iterator(); i.hasNext();) {
                MapEntryExpression mme = (MapEntryExpression) i.next();
                String key = mme.getKeyExpression().getText();

                String type = mme.getValueExpression().getText();

                properties.add(new PropertyNode(key, Modifier.PUBLIC, ClassHelper.make(type), classNode, null, null,
                        null));
            }
        }

        return properties;
    }

    private void injectAssociationProperties(ClassNode classNode, List propertiesToAdd) {
        for (Iterator i = propertiesToAdd.iterator(); i.hasNext();) {
            PropertyNode pn = (PropertyNode) i.next();
            if (!/*GrailsASTUtils.*/hasProperty(classNode, pn.getName())) {
                //if(LOG.isDebugEnabled()) {
                //    LOG.debug("[GrailsDomainInjector] Adding property [" + pn.getName() + "] to class [" + classNode.getName() + "]");
                //}
                classNode.addProperty(pn);
            }
        }
    }

    private List createPropertiesForHasManyExpression(Expression e, ClassNode classNode) {
        List properties = new ArrayList();
        if (e instanceof MapExpression) {
            MapExpression me = (MapExpression) e;
            List mapEntries = me.getMapEntryExpressions();
            for (Iterator j = mapEntries.iterator(); j.hasNext();) {
                MapEntryExpression mee = (MapEntryExpression) j.next();
                Expression keyExpression = mee.getKeyExpression();
                String key = keyExpression.getText();
                addAssociationForKey(key, properties, classNode);
            }
        }
        return properties;
    }

    private void addAssociationForKey(String key, List properties, ClassNode classNode) {
        properties
                .add(new PropertyNode(key, Modifier.PUBLIC, new ClassNode(Set.class), classNode, null, null, null));
    }

    private void injectToStringMethod(ClassNode classNode) {
        final boolean hasToString = /*GrailsASTUtils.*/implementsZeroArgMethod(classNode, "toString");

        if (!hasToString) {
            GStringExpression ge = new GStringExpression(classNode.getName() + " : ${id}");
            ge.addString(new ConstantExpression(classNode.getName() + " : "));
            ge.addValue(new VariableExpression("id"));
            Statement s = new ReturnStatement(ge);
            MethodNode mn = new MethodNode("toString", Modifier.PUBLIC, new ClassNode(String.class),
                    new Parameter[0], new ClassNode[0], s);
            //if(LOG.isDebugEnabled()) {
            //    LOG.debug("[GrailsDomainInjector] Adding method [toString()] to class [" + classNode.getName() + "]");
            //}
            classNode.addMethod(mn);
        }
    }

    private void injectVersionProperty(ClassNode classNode) {
        final boolean hasVersion = /*GrailsASTUtils.*/hasProperty(classNode, /*GrailsDomainClassProperty.*/VERSION);

        if (!hasVersion) {
            //if(LOG.isDebugEnabled()) {
            //    LOG.debug("[GrailsDomainInjector] Adding property [" + GrailsDomainClassProperty.VERSION + "] to class [" + classNode.getName() + "]");
            //}
            classNode.addProperty( /*GrailsDomainClassProperty.*/VERSION, Modifier.PUBLIC,
                    new ClassNode(Long.class), null, null, null);
        }
    }

    private void injectIdProperty(ClassNode classNode) {
        final boolean hasId = /*GrailsASTUtils.*/hasProperty(classNode, /*GrailsDomainClassProperty.*/IDENTITY);

        if (!hasId) {
            //if(LOG.isDebugEnabled()) {
            //    LOG.debug("[GrailsDomainInjector] Adding property [" + GrailsDomainClassProperty.IDENTITY + "] to class [" + classNode.getName() + "]");
            //}
            classNode.addProperty( /*GrailsDomainClassProperty.*/IDENTITY, Modifier.PUBLIC,
                    new ClassNode(Long.class), null, null, null);
        }
    }

    //***************************************************************
    // from GrailsASTUtils
    //***************************************************************
    /**
     * Returns whether a classNode has the specified property or not
     *
     * @param classNode The ClassNode
     * @param propertyName The name of the property
     * @return True if the property exists in the ClassNode
     */
    public static boolean hasProperty(ClassNode classNode, String propertyName) {
        if (classNode == null || propertyName == null || "".equals(propertyName.trim()))
            return false;

        List properties = classNode.getProperties();
        for (Iterator i = properties.iterator(); i.hasNext();) {
            PropertyNode pn = (PropertyNode) i.next();
            if (pn.getName().equals(propertyName))
                return true;
        }
        return false;
    }

    /**
     * Tests whether the ClasNode implements the specified method name
     *
     * @param classNode The ClassNode
     * @param methodName The method name
     * @return True if it does implement the method
     */
    public static boolean implementsZeroArgMethod(ClassNode classNode, String methodName) {
        return implementsMethod(classNode, methodName, new Class[0]);
    }

    /**
     * Tests whether the ClassNode implements the specified method name
     *
     * @param classNode The ClassNode
     * @param methodName The method name
     * @param argTypes
     * @return True if it implements the method
     */
    private static boolean implementsMethod(ClassNode classNode, String methodName, Class[] argTypes) {
        List methods = classNode.getMethods();
        if (argTypes == null || argTypes.length == 0) {
            for (Iterator i = methods.iterator(); i.hasNext();) {
                MethodNode mn = (MethodNode) i.next();
                boolean methodMatch = mn.getName().equals(methodName);
                if (methodMatch)
                    return true;
                // TODO Implement further parameter analysis
            }
        }
        return false;
    }

    //***************************************************************
    // from GrailsDomainClassProperty
    //***************************************************************
    private static final String RELATES_TO_MANY = "relatesToMany";
    private static final String BELONGS_TO = "belongsTo";
    private static final String HAS_MANY = "hasMany";
    private static final String IDENTITY = "id";
    private static final String VERSION = "version";
}