io.micronaut.ast.groovy.annotation.GroovyAnnotationMetadataBuilder 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.annotation;
import groovy.lang.GroovyObject;
import groovy.lang.GroovyObjectSupport;
import groovy.lang.Script;
import io.micronaut.ast.groovy.utils.AstGenericUtils;
import io.micronaut.core.annotation.NonNull;
import io.micronaut.ast.groovy.utils.AstMessageUtils;
import io.micronaut.ast.groovy.utils.ExtendedParameter;
import io.micronaut.ast.groovy.visitor.GroovyVisitorContext;
import io.micronaut.core.annotation.AnnotationClassValue;
import io.micronaut.core.annotation.AnnotationValue;
import io.micronaut.core.convert.ConversionService;
import io.micronaut.core.io.service.SoftServiceLoader;
import io.micronaut.core.reflect.ClassUtils;
import io.micronaut.core.reflect.ReflectionUtils;
import io.micronaut.core.util.CollectionUtils;
import io.micronaut.core.util.StringUtils;
import io.micronaut.core.value.OptionalValues;
import io.micronaut.inject.annotation.AbstractAnnotationMetadataBuilder;
import io.micronaut.inject.annotation.AnnotatedElementValidator;
import io.micronaut.inject.visitor.VisitorContext;
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.MethodNode;
import org.codehaus.groovy.ast.ModuleNode;
import org.codehaus.groovy.ast.PackageNode;
import org.codehaus.groovy.ast.Parameter;
import org.codehaus.groovy.ast.PropertyNode;
import org.codehaus.groovy.ast.Variable;
import org.codehaus.groovy.ast.expr.AnnotationConstantExpression;
import org.codehaus.groovy.ast.expr.ClassExpression;
import org.codehaus.groovy.ast.expr.ConstantExpression;
import org.codehaus.groovy.ast.expr.Expression;
import org.codehaus.groovy.ast.expr.ListExpression;
import org.codehaus.groovy.ast.expr.PropertyExpression;
import org.codehaus.groovy.ast.expr.VariableExpression;
import org.codehaus.groovy.ast.stmt.ExpressionStatement;
import org.codehaus.groovy.ast.stmt.ReturnStatement;
import org.codehaus.groovy.ast.stmt.Statement;
import org.codehaus.groovy.control.CompilationUnit;
import org.codehaus.groovy.control.SourceUnit;
import java.lang.annotation.Annotation;
import java.lang.annotation.Inherited;
import java.lang.annotation.Repeatable;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
/**
* Groovy implementation of {@link AbstractAnnotationMetadataBuilder}.
*
* @author Graeme Rocher
* @since 1.0
*/
public class GroovyAnnotationMetadataBuilder extends AbstractAnnotationMetadataBuilder {
public static final ClassNode ANN_OVERRIDE = ClassHelper.make(Override.class);
public static final String VALIDATOR_KEY = "io.micronaut.VALIDATOR";
final SourceUnit sourceUnit;
final AnnotatedElementValidator elementValidator;
final CompilationUnit compilationUnit;
public GroovyAnnotationMetadataBuilder(SourceUnit sourceUnit, CompilationUnit compilationUnit) {
this.compilationUnit = compilationUnit;
this.sourceUnit = sourceUnit;
if (sourceUnit != null) {
final ModuleNode ast = sourceUnit.getAST();
if (ast != null) {
Object validator = ast.getNodeMetaData(VALIDATOR_KEY);
if (validator instanceof AnnotatedElementValidator) {
elementValidator = (AnnotatedElementValidator) validator;
} else {
this.elementValidator = SoftServiceLoader.load(AnnotatedElementValidator.class).firstAvailable().orElse(null);
ast.putNodeMetaData(VALIDATOR_KEY, this.elementValidator);
}
} else {
this.elementValidator = null;
}
} else {
this.elementValidator = null;
}
}
@Override
protected boolean isValidationRequired(AnnotatedNode member) {
if (member != null) {
final List annotations = member.getAnnotations();
if (CollectionUtils.isNotEmpty(annotations)) {
return annotations.stream().anyMatch((it) -> it.getClassNode().getName().startsWith("javax.validation"));
}
}
return false;
}
@Override
protected boolean isExcludedAnnotation(@NonNull AnnotatedNode element, @NonNull String annotationName) {
if (element instanceof ClassNode && ((ClassNode) element).isAnnotationDefinition() && annotationName.startsWith("java.lang.annotation")) {
return false;
} else {
return super.isExcludedAnnotation(element, annotationName);
}
}
@Override
protected AnnotatedNode getAnnotationMember(AnnotatedNode originatingElement, CharSequence member) {
if (originatingElement instanceof ClassNode) {
final List methods = ((ClassNode) originatingElement).getMethods(member.toString());
if (CollectionUtils.isNotEmpty(methods)) {
return methods.iterator().next();
}
}
return null;
}
@Override
protected RetentionPolicy getRetentionPolicy(@NonNull AnnotatedNode annotation) {
List annotations = annotation.getAnnotations();
for (AnnotationNode ann : annotations) {
if (ann.getClassNode().getName().equals(Retention.class.getName())) {
final Iterator i = ann.getMembers().values().iterator();
if (i.hasNext()) {
final Expression expr = i.next();
if (expr instanceof PropertyExpression) {
PropertyExpression pe = (PropertyExpression) expr;
try {
return RetentionPolicy.valueOf(pe.getPropertyAsString());
} catch (Throwable e) {
// should never happen
return RetentionPolicy.RUNTIME;
}
}
}
}
}
return RetentionPolicy.RUNTIME;
}
@Override
protected AnnotatedElementValidator getElementValidator() {
return this.elementValidator;
}
@Override
protected void addError(@NonNull AnnotatedNode originatingElement, @NonNull String error) {
AstMessageUtils.error(sourceUnit, originatingElement, error);
}
@Override
protected void addWarning(@NonNull AnnotatedNode originatingElement, @NonNull String warning) {
AstMessageUtils.warning(sourceUnit, originatingElement, warning);
}
@Override
protected boolean isMethodOrClassElement(AnnotatedNode element) {
return element instanceof ClassNode || element instanceof MethodNode;
}
@Override
protected String getDeclaringType(@NonNull AnnotatedNode element) {
if (element instanceof ClassNode) {
return ((ClassNode) element).getName();
}
final ClassNode declaringClass = element.getDeclaringClass();
if (declaringClass != null) {
return declaringClass.getName();
}
return null;
}
@Override
protected boolean hasAnnotation(AnnotatedNode element, Class extends Annotation> annotation) {
return !element.getAnnotations(ClassHelper.makeCached(annotation)).isEmpty();
}
@Override
protected boolean hasAnnotation(AnnotatedNode element, String annotation) {
for (AnnotationNode ann: element.getAnnotations()) {
if (ann.getClassNode().getName().equals(annotation)) {
return true;
}
}
return false;
}
@Override
protected boolean hasAnnotations(AnnotatedNode element) {
return CollectionUtils.isNotEmpty(element.getAnnotations());
}
@Override
protected VisitorContext createVisitorContext() {
return new GroovyVisitorContext(sourceUnit, compilationUnit);
}
@Override
protected AnnotatedNode getTypeForAnnotation(AnnotationNode annotationMirror) {
return annotationMirror.getClassNode();
}
@Override
protected String getRepeatableName(AnnotationNode annotationMirror) {
return getRepeatableNameForType(annotationMirror.getClassNode());
}
@Override
protected String getRepeatableNameForType(AnnotatedNode annotationType) {
List annotationNodes = annotationType.getAnnotations(ClassHelper.makeCached(Repeatable.class));
if (CollectionUtils.isNotEmpty(annotationNodes)) {
Expression expression = annotationNodes.get(0).getMember("value");
if (expression instanceof ClassExpression) {
return expression.getType().getName();
}
}
return null;
}
@Override
protected Optional getAnnotationMirror(String annotationName) {
ClassNode cn = ClassUtils.forName(annotationName, GroovyAnnotationMetadataBuilder.class.getClassLoader())
.map(ClassHelper::make)
.orElseGet(() -> ClassHelper.make(annotationName));
if (!cn.getName().equals(ClassHelper.OBJECT)) {
return Optional.of(cn);
} else {
return Optional.empty();
}
}
@Override
protected String getAnnotationTypeName(AnnotationNode annotationMirror) {
return annotationMirror.getClassNode().getName();
}
@Override
protected String getElementName(AnnotatedNode element) {
if (element instanceof ClassNode) {
return ((ClassNode) element).getName();
} else if (element instanceof MethodNode) {
return ((MethodNode) element).getName();
} else if (element instanceof FieldNode) {
return ((FieldNode) element).getName();
} else if (element instanceof PropertyNode) {
return ((PropertyNode) element).getName();
} else if (element instanceof PackageNode) {
return ((PackageNode) element).getName();
}
throw new IllegalArgumentException("Cannot establish name for node type: " + element.getClass().getName());
}
@Override
protected List extends AnnotationNode> getAnnotationsForType(AnnotatedNode element) {
List annotations = element.getAnnotations();
List expanded = new ArrayList<>(annotations.size());
for (AnnotationNode node: annotations) {
Expression value = node.getMember("value");
boolean repeatable = false;
if (value instanceof ListExpression) {
for (Expression expression: ((ListExpression) value).getExpressions()) {
if (expression instanceof AnnotationConstantExpression) {
String name = getRepeatableNameForType(expression.getType());
if (name != null && name.equals(node.getClassNode().getName())) {
repeatable = true;
expanded.add((AnnotationNode) ((AnnotationConstantExpression) expression).getValue());
}
}
}
}
if (!repeatable || node.getMembers().size() > 1) {
expanded.add(node);
}
}
return expanded;
}
@Override
protected List buildHierarchy(AnnotatedNode element, boolean inheritTypeAnnotations, boolean declaredOnly) {
if (declaredOnly) {
return Collections.singletonList(element);
} else if (element instanceof ClassNode) {
List hierarchy = new ArrayList<>();
ClassNode cn = (ClassNode) element;
hierarchy.add(cn);
if (cn.isAnnotationDefinition()) {
return hierarchy;
}
populateTypeHierarchy(cn, hierarchy);
Collections.reverse(hierarchy);
return hierarchy;
} else if (element instanceof MethodNode) {
MethodNode mn = (MethodNode) element;
List hierarchy;
if (inheritTypeAnnotations) {
hierarchy = buildHierarchy(mn.getDeclaringClass(), false, declaredOnly);
} else {
hierarchy = new ArrayList<>();
}
if (!mn.getAnnotations(ANN_OVERRIDE).isEmpty()) {
hierarchy.addAll(findOverriddenMethods(mn));
}
hierarchy.add(mn);
return hierarchy;
} else if (element instanceof ExtendedParameter) {
ExtendedParameter p = (ExtendedParameter) element;
List hierarchy = new ArrayList<>();
MethodNode methodNode = p.getMethodNode();
if (!methodNode.getAnnotations(ANN_OVERRIDE).isEmpty()) {
int variableIdx = Arrays.asList(methodNode.getParameters()).indexOf(p.getParameter());
for (MethodNode overridden : findOverriddenMethods(methodNode)) {
hierarchy.add(new ExtendedParameter(overridden, overridden.getParameters()[variableIdx]));
}
}
hierarchy.add(p);
return hierarchy;
} else {
if (element == null) {
return new ArrayList<>();
} else {
return Collections.singletonList(element);
}
}
}
@Override
protected void readAnnotationRawValues(
AnnotatedNode originatingElement,
String annotationName,
AnnotatedNode member,
String memberName,
Object annotationValue,
Map annotationValues) {
if (!annotationValues.containsKey(memberName)) {
final Object v = readAnnotationValue(originatingElement, member, memberName, annotationValue);
if (v != null) {
validateAnnotationValue(originatingElement, annotationName, member, memberName, v);
annotationValues.put(memberName, v);
}
}
}
@Override
protected Map extends AnnotatedNode, ?> readAnnotationDefaultValues(String annotationName, AnnotatedNode annotationType) {
Map defaultValues = new LinkedHashMap<>();
if (annotationType instanceof ClassNode) {
ClassNode classNode = (ClassNode) annotationType;
List methods = new ArrayList<>(classNode.getMethods());
// TODO: Remove this branch of the code after upgrading to Groovy 3.0
// https://issues.apache.org/jira/browse/GROOVY-8696
if (classNode.isResolved()) {
Class> resolved = classNode.getTypeClass();
for (MethodNode method : methods) {
try {
final Object defaultValue = resolved.getDeclaredMethod(method.getName()).getDefaultValue();
if (defaultValue != null) {
if (defaultValue instanceof Class) {
defaultValues.put(method, new ClassExpression(ClassHelper.makeCached((Class>) defaultValue)));
} else {
if (defaultValue instanceof String) {
if (StringUtils.isNotEmpty((String) defaultValue)) {
defaultValues.put(method, new ConstantExpression(defaultValue));
}
} else {
defaultValues.put(method, new ConstantExpression(defaultValue));
}
}
}
} catch (NoSuchMethodError | NoSuchMethodException e) {
// method no longer exists alias annotation
// ignore and continue
}
}
} else {
for (MethodNode method : methods) {
Statement stmt = method.getCode();
Expression expression = null;
if (stmt instanceof ReturnStatement) {
expression = ((ReturnStatement) stmt).getExpression();
} else if (stmt instanceof ExpressionStatement) {
expression = ((ExpressionStatement) stmt).getExpression();
}
if (expression instanceof ConstantExpression) {
ConstantExpression ce = (ConstantExpression) expression;
final Object v = ce.getValue();
if (v != null) {
if (v instanceof String) {
if (StringUtils.isNotEmpty((String) v)) {
defaultValues.put(method, new ConstantExpression(v));
}
} else {
defaultValues.put(method, expression);
}
}
}
}
}
}
return defaultValues;
}
@Override
protected boolean isInheritedAnnotation(@NonNull AnnotationNode annotationMirror) {
final List annotations = annotationMirror.getClassNode().getAnnotations();
if (CollectionUtils.isNotEmpty(annotations)) {
return annotations.stream().anyMatch((ann) ->
ann.getClassNode().getName().equals(Inherited.class.getName())
);
}
return false;
}
@Override
protected boolean isInheritedAnnotationType(@NonNull AnnotatedNode annotationType) {
final List annotations = annotationType.getAnnotations();
if (CollectionUtils.isNotEmpty(annotations)) {
return annotations.stream().anyMatch((ann) ->
ann.getClassNode().getName().equals(Inherited.class.getName())
);
}
return false;
}
@Override
protected Map getAnnotationMembers(String annotationType) {
final AnnotatedNode node = getAnnotationMirror(annotationType).orElse(null);
if (node instanceof ClassNode) {
final ClassNode cn = (ClassNode) node;
if (cn.isAnnotationDefinition()) {
return cn.getDeclaredMethodsMap();
}
}
return Collections.emptyMap();
}
@Override
protected boolean hasSimpleAnnotation(AnnotatedNode element, String simpleName) {
if (element != null) {
final List annotations = element.getAnnotations();
for (AnnotationNode ann : annotations) {
if (ann.getClassNode().getNameWithoutPackage().equalsIgnoreCase(simpleName)) {
return true;
}
}
}
return false;
}
@Override
protected Map extends AnnotatedNode, ?> readAnnotationDefaultValues(AnnotationNode annotationMirror) {
ClassNode classNode = annotationMirror.getClassNode();
String annotationName = classNode.getName();
return readAnnotationDefaultValues(annotationName, classNode);
}
@Override
protected Object readAnnotationValue(AnnotatedNode originatingElement, AnnotatedNode member, String memberName, Object annotationValue) {
if (annotationValue instanceof ConstantExpression) {
if (annotationValue instanceof AnnotationConstantExpression) {
AnnotationConstantExpression ann = (AnnotationConstantExpression) annotationValue;
AnnotationNode value = (AnnotationNode) ann.getValue();
final AnnotationValue> av = readNestedAnnotationValue(originatingElement, value);
if (member instanceof MethodNode && ((MethodNode) member).getReturnType().isArray()) {
return new AnnotationValue[] { av };
} else {
return av;
}
} else {
return ((ConstantExpression) annotationValue).getValue();
}
} else if (annotationValue instanceof PropertyExpression) {
PropertyExpression pe = (PropertyExpression) annotationValue;
if (pe.getObjectExpression() instanceof ClassExpression) {
ClassExpression ce = (ClassExpression) pe.getObjectExpression();
ClassNode propertyType = ce.getType();
if (propertyType.isEnum()) {
return pe.getPropertyAsString();
} else {
if (propertyType.isResolved()) {
Class typeClass = propertyType.getTypeClass();
try {
final Field f = ReflectionUtils.getRequiredField(typeClass, pe.getPropertyAsString());
f.setAccessible(true);
return f.get(typeClass);
} catch (Throwable e) {
// ignore
}
}
}
}
} else if (annotationValue instanceof ClassExpression) {
return new AnnotationClassValue<>(((ClassExpression) annotationValue).getType().getName());
} else if (annotationValue instanceof ListExpression) {
ListExpression le = (ListExpression) annotationValue;
List
© 2015 - 2025 Weber Informatics LLC | Privacy Policy