All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.micronaut.ast.groovy.visitor.LoadedVisitor.groovy Maven / Gradle / Ivy

/*
 * Copyright 2017-2020 original 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
 *
 * https://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 io.micronaut.ast.groovy.visitor

import io.micronaut.core.annotation.Nullable
import groovy.transform.CompileStatic
import io.micronaut.ast.groovy.utils.AstAnnotationUtils
import io.micronaut.core.annotation.AnnotationMetadata
import io.micronaut.core.annotation.Internal
import io.micronaut.core.order.Ordered
import io.micronaut.inject.ast.ClassElement
import io.micronaut.inject.ast.Element
import io.micronaut.inject.visitor.TypeElementVisitor
import org.codehaus.groovy.ast.AnnotatedNode
import org.codehaus.groovy.ast.ClassHelper
import org.codehaus.groovy.ast.ClassNode
import org.codehaus.groovy.ast.ConstructorNode
import org.codehaus.groovy.ast.FieldNode
import org.codehaus.groovy.ast.GenericsType
import org.codehaus.groovy.ast.MethodNode
import org.codehaus.groovy.ast.PropertyNode
import org.codehaus.groovy.control.CompilationUnit
import org.codehaus.groovy.control.SourceUnit

/**
 * Used to store a reference to an underlying {@link TypeElementVisitor} and
 * optionally invoke the visit methods on the visitor if it matches the
 * element being visited by the AST transformation.
 *
 * @author James Kleeh
 * @since 1.0
 */
@Internal
@CompileStatic
class LoadedVisitor implements Ordered {

    private final SourceUnit sourceUnit
    private final TypeElementVisitor visitor
    private final String classAnnotation
    private final String elementAnnotation
    private ClassElement currentClassElement
    private final CompilationUnit compilationUnit

    private static final String OBJECT_CLASS = Object.class.getName()

    LoadedVisitor(SourceUnit source, CompilationUnit compilationUnit, TypeElementVisitor visitor) {
        this.compilationUnit = compilationUnit
        this.sourceUnit = source
        this.visitor = visitor
        ClassNode classNode = ClassHelper.make(visitor.getClass())
        ClassNode definition = classNode.getAllInterfaces().find {
            it.name == TypeElementVisitor.class.name
        }
        GenericsType[] generics = definition.getGenericsTypes()
        if (generics) {
            String typeName = generics[0].type.name
            if (typeName == OBJECT_CLASS) {
                classAnnotation = visitor.getClassType()
            } else {
                classAnnotation = typeName
            }
            String elementName = generics[1].type.name
            if (elementName == OBJECT_CLASS) {
                elementAnnotation = visitor.getElementType()
            } else {
                elementAnnotation = elementName
            }
        } else {
            classAnnotation = ClassHelper.OBJECT
            elementAnnotation = ClassHelper.OBJECT
        }
    }

    TypeElementVisitor getVisitor() {
        visitor
    }

    @Override
    int getOrder() {
        return getVisitor().getOrder()
    }

    @Override
    boolean equals(o) {
        if (this.is(o)) return true
        if (getClass() != o.class) return false

        LoadedVisitor that = (LoadedVisitor) o

        if (visitor.getClass() != that.getClass() ) return false

        return true
    }

    @Override
    int hashCode() {
        return visitor.getClass().hashCode()
    }

    @Override
    String toString() {
        visitor.toString()
    }
    /**
     * @param classNode The class node
     * @return True if the class node should be visited
     */
    boolean matches(ClassNode classNode) {
        if (classAnnotation == ClassHelper.OBJECT) {
            return true
        }
        AnnotationMetadata annotationMetadata = AstAnnotationUtils.getAnnotationMetadata(sourceUnit, compilationUnit, classNode)
        return annotationMetadata.hasStereotype(classAnnotation)
    }

    /**
     * @param annotationMetadata The annotation data
     * @return True if the element should be visited
     */
    boolean matches(AnnotationMetadata annotationMetadata) {
        if (elementAnnotation == ClassHelper.OBJECT) {
            return true
        }
        return annotationMetadata.hasStereotype(elementAnnotation)
    }

    /**
     * Invoke the underlying visitor for the given node.
     *
     * @param annotatedNode The node to visit
     * @param annotationMetadata The annotation data for the node
     * @param visitorContext the Groovy visitor context
     */
    @Nullable Element visit(AnnotatedNode annotatedNode, AnnotationMetadata annotationMetadata, GroovyVisitorContext visitorContext) {
        switch (annotatedNode.getClass()) {
            case PropertyNode:
                def e = visitorContext.getElementFactory().newFieldElement((PropertyNode) annotatedNode, annotationMetadata)
                visitor.visitField(e, visitorContext)
                return e
            case FieldNode:
                def field = (FieldNode) annotatedNode;
                if (field.enum) {
                    def e = visitorContext.getElementFactory().newEnumConstantElement(currentClassElement, (FieldNode) annotatedNode, annotationMetadata)
                    visitor.visitEnumConstant(e, visitorContext)
                    return e
                } else {
                    def e = visitorContext.getElementFactory().newFieldElement(currentClassElement, (FieldNode) annotatedNode, annotationMetadata)
                    visitor.visitField(e, visitorContext)
                    return e
                }
            case ConstructorNode:
                def e = visitorContext.getElementFactory().newConstructorElement(currentClassElement, (ConstructorNode) annotatedNode, annotationMetadata)
                visitor.visitConstructor(e, visitorContext)
                return e
            case MethodNode:
                if (currentClassElement != null) {
                    def e = visitorContext.getElementFactory().newSourceMethodElement(currentClassElement, (MethodNode) annotatedNode, annotationMetadata)
                    visitor.visitMethod(e, visitorContext)
                    return e
                }
                break
            case ClassNode:
                ClassNode cn = (ClassNode) annotatedNode
                currentClassElement = visitorContext.getElementFactory().newSourceClassElement(cn, annotationMetadata)
                visitor.visitClass(currentClassElement, visitorContext)
                return currentClassElement
        }

        return null
    }

    void start(GroovyVisitorContext visitorContext) {
        visitor.start(visitorContext)
    }

    void finish(GroovyVisitorContext visitorContext) {
        visitor.finish(visitorContext)
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy