Package org.grails.compiler.injection

Source Code of org.grails.compiler.injection.ArtefactTypeAstTransformation

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

import grails.artefact.Artefact;
import grails.build.logging.GrailsConsole;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;

import grails.compiler.ast.AllArtefactClassInjector;
import grails.compiler.ast.ClassInjector;
import grails.compiler.ast.GlobalClassInjector;
import grails.compiler.ast.GrailsArtefactClassInjector;
import grails.compiler.traits.TraitInjector;
import groovy.transform.CompilationUnitAware;

import org.codehaus.groovy.ast.ASTNode;
import org.codehaus.groovy.ast.AnnotatedNode;
import org.codehaus.groovy.ast.AnnotationNode;
import org.codehaus.groovy.ast.ClassNode;
import org.codehaus.groovy.ast.expr.ConstantExpression;
import org.codehaus.groovy.ast.expr.Expression;
import org.codehaus.groovy.control.CompilationUnit;
import org.codehaus.groovy.control.CompilePhase;
import org.codehaus.groovy.control.SourceUnit;
import org.codehaus.groovy.transform.GroovyASTTransformation;

/**
* A transformation used to apply transformers to classes not located in Grails
* directory structure. For example any class can be annotated with
* @Artefact("Controller") to make it into a controller no matter what the location.
*
* @author Graeme Rocher
* @since 2.0
*/
@GroovyASTTransformation(phase = CompilePhase.CANONICALIZATION)
public class ArtefactTypeAstTransformation extends AbstractArtefactTypeAstTransformation implements CompilationUnitAware {
    private static final ClassNode MY_TYPE = new ClassNode(Artefact.class);
   
    protected CompilationUnit compilationUnit;

    public void visit(ASTNode[] astNodes, SourceUnit sourceUnit) {
        AnnotatedNode parent = (AnnotatedNode) astNodes[1];
        AnnotationNode node = (AnnotationNode) astNodes[0];
       
        if (!(node instanceof AnnotationNode) || !(parent instanceof AnnotatedNode)) {
            throw new RuntimeException("Internal error: wrong types: $node.class / $parent.class");
        }

        if (!isArtefactAnnotationNode(node) || !(parent instanceof ClassNode)) {
            return;
        }

        ClassNode cNode = (ClassNode) parent;
        if (cNode.isInterface()) {
            throw new RuntimeException("Error processing interface '" + cNode.getName() + "'. @" +
                    getAnnotationType().getNameWithoutPackage() + " not allowed for interfaces.");
        }

        if(isApplied(cNode)) {
            return;
        }
       
        String artefactType = resolveArtefactType(sourceUnit, node, cNode);
        performInjectionOnArtefactType(sourceUnit, cNode, artefactType);
       
        performTraitInjectionOnArtefactType(sourceUnit, cNode, artefactType);
       
        postProcess(sourceUnit, node, cNode, artefactType);
       
        markApplied(cNode);       
    }

    protected void performTraitInjectionOnArtefactType(SourceUnit sourceUnit,
            ClassNode cNode, String artefactType) {
        if (compilationUnit != null) {
            try {
                // TODO this code is showing up in multiple places and should be centralized.  See ResourceTransform and EntityASTTransformation
                GrailsAwareTraitInjectionOperation grailsTraitInjector = new GrailsAwareTraitInjectionOperation(compilationUnit);
                List<TraitInjector> traitInjectors = grailsTraitInjector.getTraitInjectors();
                List<TraitInjector> injectorsToUse = new ArrayList<TraitInjector>();
                for (TraitInjector injector : traitInjectors) {
                    List<String> artefactTypes = Arrays.asList(injector.getArtefactTypes());
                    if (artefactTypes.contains(artefactType)) {
                        injectorsToUse.add(injector);
                    }
                }
                try {
                    if(injectorsToUse.size() > 0) {
                        grailsTraitInjector.performTraitInjection(sourceUnit, cNode, injectorsToUse);
                    }
                } catch (RuntimeException e) {
                    try {
                        GrailsConsole.getInstance().error("Error occurred calling Trait injector: "
                                        + e.getMessage(), e);
                    } catch (Throwable t) {
                        // ignore it
                    }
                    throw e;
                }
            } catch (Exception e) {
                try {
                    GrailsConsole.getInstance().error("Error occurred processing Trait injectors: "
                                    + e.getMessage(), e);
                } catch (Throwable t) {
                    // ignore it
                }
                throw new RuntimeException(e);
            }
        }
    }

    protected boolean isApplied(ClassNode cNode) {
        return GrailsASTUtils.isApplied(cNode, getAstAppliedMarkerClass());
    }

    protected void markApplied(ClassNode classNode) {
        GrailsASTUtils.markApplied(classNode, getAstAppliedMarkerClass());
    }

    protected Class<?> getAstAppliedMarkerClass() {
        return ArtefactTypeAstTransformation.class;
    }

    protected void postProcess(SourceUnit sourceUnit, AnnotationNode annotationNode, ClassNode classNode, String artefactType) {
        if(!getAnnotationType().equals(annotationNode.getClassNode())) {
            // add @Artefact annotation to resulting class so that "short cut" annotations like @TagLib
            // also produce an @Artefact annotation in the resulting class file
            AnnotationNode annotation=new AnnotationNode(getAnnotationType());
            annotation.addMember("value", new ConstantExpression(artefactType));
            classNode.addAnnotation(annotation);
        }
    }

    protected String resolveArtefactType(SourceUnit sourceUnit, AnnotationNode annotationNode, ClassNode classNode) {
        Expression value = annotationNode.getMember("value");

        if (value != null && (value instanceof ConstantExpression)) {
            ConstantExpression ce = (ConstantExpression) value;
            return ce.getText();
        }
        else {
            throw new RuntimeException("Class ["+classNode.getName()+"] contains an invalid @Artefact annotation. No artefact found for value specified.");
        }
    }

    protected boolean isArtefactAnnotationNode(AnnotationNode annotationNode) {
        return getAnnotationType().equals(annotationNode.getClassNode());
    }

    protected ClassNode getAnnotationType() {
        return new ClassNode(getAnnotationTypeClass());
    }

    protected Class getAnnotationTypeClass() {
        return MY_TYPE.getTypeClass();
    }

    public void performInjectionOnArtefactType(SourceUnit sourceUnit, ClassNode cNode, String artefactType) {
        doPerformInjectionOnArtefactType(sourceUnit, cNode, artefactType);
    }

    public static void doPerformInjectionOnArtefactType(SourceUnit sourceUnit, ClassNode cNode, String artefactType) {
        List<ClassInjector> injectors = findInjectors(artefactType, GrailsAwareInjectionOperation.getClassInjectors());
        performInjection(sourceUnit, cNode, injectors);
    }

    public static void performInjection(SourceUnit sourceUnit, ClassNode cNode, Collection<ClassInjector> injectors) {
        try {
            for (ClassInjector injector : injectors) {
                injector.performInjectionOnAnnotatedClass(sourceUnit, cNode);
            }
        } catch (RuntimeException e) {
            try {
                GrailsConsole.getInstance().error("Error occurred calling AST injector: " + e.getMessage(), e);
            } catch (Throwable t) {
                // ignore it
            }
            throw e;
        }
    }

    public static List<ClassInjector> findInjectors(String artefactType, ClassInjector[] classInjectors) {
        List<ClassInjector> injectors = new ArrayList<ClassInjector>();
        for (ClassInjector classInjector : classInjectors) {
            if (classInjector instanceof AllArtefactClassInjector) {
                injectors.add(classInjector);
            }
            else if(classInjector instanceof GlobalClassInjector) {
                injectors.add(classInjector);
            }
            else if (classInjector instanceof GrailsArtefactClassInjector) {
                GrailsArtefactClassInjector gace = (GrailsArtefactClassInjector) classInjector;

                if (hasArtefactType(artefactType,gace)) {
                    injectors.add(gace);
                }
            }
        }
        return injectors;
    }

    public static boolean hasArtefactType(String artefactType, GrailsArtefactClassInjector gace) {
        for (String _artefactType : gace.getArtefactTypes()) {
            if(_artefactType.equals("*")) return true;
            if (_artefactType.equals(artefactType)) {
                return true;
            }
        }
        return false;
    }

  @Override
  public void setCompilationUnit(CompilationUnit unit) {
    compilationUnit = unit;
  }
}
TOP

Related Classes of org.grails.compiler.injection.ArtefactTypeAstTransformation

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.