io.micronaut.annotation.processing.visitor.JavaMethodElement Maven / Gradle / Ivy
/*
* Copyright 2017-2021 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.annotation.processing.visitor;
import io.micronaut.core.annotation.AccessorsStyle;
import io.micronaut.core.annotation.AnnotationMetadata;
import io.micronaut.core.annotation.Internal;
import io.micronaut.core.annotation.NonNull;
import io.micronaut.core.naming.NameUtils;
import io.micronaut.core.util.ArrayUtils;
import io.micronaut.core.util.CollectionUtils;
import io.micronaut.inject.ast.ClassElement;
import io.micronaut.inject.ast.ElementQuery;
import io.micronaut.inject.ast.GenericPlaceholderElement;
import io.micronaut.inject.ast.MethodElement;
import io.micronaut.inject.ast.ParameterElement;
import io.micronaut.inject.ast.PrimitiveElement;
import javax.lang.model.element.Element;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.VariableElement;
import javax.lang.model.type.DeclaredType;
import javax.lang.model.type.TypeKind;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.type.WildcardType;
import java.util.*;
import java.util.stream.Collectors;
/**
* A method element returning data from a {@link ExecutableElement}.
*
* @author James Kleeh
* @since 1.0
*/
@Internal
public class JavaMethodElement extends AbstractJavaElement implements MethodElement {
protected final JavaClassElement declaringClass;
protected final ExecutableElement executableElement;
protected final JavaVisitorContext visitorContext;
private ClassElement resolvedDeclaringClass;
private ParameterElement[] parameters;
private ParameterElement continuationParameter;
private ClassElement genericReturnType;
private ClassElement returnType;
/**
* @param declaringClass The declaring class
* @param executableElement The {@link ExecutableElement}
* @param annotationMetadata The annotation metadata
* @param visitorContext The visitor context
*/
public JavaMethodElement(
JavaClassElement declaringClass,
ExecutableElement executableElement,
AnnotationMetadata annotationMetadata,
JavaVisitorContext visitorContext) {
super(executableElement, annotationMetadata, visitorContext);
this.executableElement = executableElement;
this.visitorContext = visitorContext;
this.declaringClass = declaringClass;
}
@Override
public Optional getReceiverType() {
final TypeMirror receiverType = executableElement.getReceiverType();
if (receiverType != null) {
if (receiverType.getKind() != TypeKind.NONE) {
final ClassElement classElement = mirrorToClassElement(receiverType,
visitorContext,
declaringClass.getGenericTypeInfo());
return Optional.of(classElement);
}
}
return Optional.empty();
}
@Override
@NonNull
public ClassElement[] getThrownTypes() {
final List extends TypeMirror> thrownTypes = executableElement.getThrownTypes();
if (!thrownTypes.isEmpty()) {
return thrownTypes.stream()
.map(tm -> mirrorToClassElement(
tm,
visitorContext,
declaringClass.getGenericTypeInfo()
)).toArray(ClassElement[]::new);
}
return ClassElement.ZERO_CLASS_ELEMENTS;
}
@Override
public boolean isDefault() {
return executableElement.isDefault();
}
@Override
public boolean overrides(MethodElement methodElement) {
if (methodElement instanceof JavaMethodElement) {
return visitorContext.getElements().overrides(
executableElement,
((JavaMethodElement) methodElement).executableElement,
declaringClass.classElement
);
}
return false;
}
@NonNull
@Override
public ClassElement getGenericReturnType() {
if (this.genericReturnType == null) {
this.genericReturnType = returnType(declaringClass.getGenericTypeInfo());
}
return this.genericReturnType;
}
@Override
@NonNull
public ClassElement getReturnType() {
if (this.returnType == null) {
this.returnType = returnType(Collections.emptyMap());
}
return this.returnType;
}
@Override
public List extends GenericPlaceholderElement> getDeclaredTypeVariables() {
return executableElement.getTypeParameters().stream()
.map(tpe -> (GenericPlaceholderElement) mirrorToClassElement(tpe.asType(), visitorContext))
.collect(Collectors.toList());
}
@Override
public boolean isSuspend() {
getParameters();
return this.continuationParameter != null;
}
@Override
public ParameterElement[] getParameters() {
if (this.parameters == null) {
List extends VariableElement> parameters = executableElement.getParameters();
List elts = new ArrayList<>(parameters.size());
for (Iterator extends VariableElement> i = parameters.iterator(); i.hasNext();) {
VariableElement variableElement = i.next();
if (! i.hasNext() && isSuspend(variableElement)) {
this.continuationParameter = newParameterElement(variableElement, AnnotationMetadata.EMPTY_METADATA);
continue;
}
AnnotationMetadata annotationMetadata = visitorContext.getAnnotationUtils()
.getAnnotationMetadata(getFieldElementForWriter(), variableElement);
JavaParameterElement javaParameterElement = newParameterElement(variableElement, annotationMetadata);
if (annotationMetadata.hasDeclaredAnnotation("org.jetbrains.annotations.Nullable")) {
javaParameterElement.annotate("javax.annotation.Nullable").getAnnotationMetadata();
}
elts.add(javaParameterElement);
}
this.parameters = elts.toArray(new ParameterElement[0]);
}
return this.parameters;
}
@Override
public MethodElement withNewParameters(ParameterElement... newParameters) {
return new JavaMethodElement(declaringClass, executableElement, getAnnotationMetadata(), visitorContext) {
@Override
public ParameterElement[] getParameters() {
return ArrayUtils.concat(super.getParameters(), newParameters);
}
};
}
@Override
public ParameterElement[] getSuspendParameters() {
ParameterElement[] parameters = getParameters();
if (isSuspend()) {
return ArrayUtils.concat(parameters, continuationParameter);
} else {
return parameters;
}
}
/**
* Creates a new parameter element for the given args.
* @param variableElement The variable element
* @param annotationMetadata The annotation metadata
* @return The parameter element
*/
@NonNull
protected JavaParameterElement newParameterElement(@NonNull VariableElement variableElement, @NonNull AnnotationMetadata annotationMetadata) {
return new JavaParameterElement(declaringClass, variableElement, annotationMetadata, visitorContext);
}
@Override
public ClassElement getDeclaringType() {
if (resolvedDeclaringClass == null) {
Element enclosingElement = executableElement.getEnclosingElement();
if (enclosingElement instanceof TypeElement) {
TypeElement te = (TypeElement) enclosingElement;
if (declaringClass.getName().equals(te.getQualifiedName().toString())) {
resolvedDeclaringClass = declaringClass;
} else {
resolvedDeclaringClass = mirrorToClassElement(te.asType(), visitorContext, declaringClass.getGenericTypeInfo());
}
} else {
return declaringClass;
}
}
return resolvedDeclaringClass;
}
@Override
public ClassElement getOwningType() {
return declaringClass;
}
/**
* The return type for the given info.
* @param info The info
* @return The return type
*/
protected ClassElement returnType(Map> info) {
VariableElement varElement = CollectionUtils.last(executableElement.getParameters());
if (isSuspend(varElement)) {
DeclaredType dType = (DeclaredType) varElement.asType();
TypeMirror tm = dType.getTypeArguments().iterator().next();
if (tm.getKind() == TypeKind.WILDCARD) {
tm = ((WildcardType) tm).getSuperBound();
}
// check Void
if ((tm instanceof DeclaredType) && sameType("kotlin.Unit", (DeclaredType) tm)) {
return PrimitiveElement.VOID;
} else {
return mirrorToClassElement(tm, visitorContext, info, true);
}
}
final TypeMirror returnType = executableElement.getReturnType();
return mirrorToClassElement(returnType, visitorContext, info, true);
}
private static boolean sameType(String type, DeclaredType dt) {
Element elt = dt.asElement();
return (elt instanceof TypeElement) && type.equals(((TypeElement) elt).getQualifiedName().toString());
}
private boolean isSuspend(VariableElement ve) {
if (ve != null && ve.asType() instanceof DeclaredType) {
DeclaredType dt = (DeclaredType) ve.asType();
return sameType("kotlin.coroutines.Continuation", dt);
}
return false;
}
private Element getFieldElementForWriter() {
String[] writerPrefixes = getAnnotationMetadata()
.getValue(AccessorsStyle.class, "writePrefixes", String[].class)
.orElse(new String[]{AccessorsStyle.DEFAULT_WRITE_PREFIX});
final String methodName = getName();
if (!NameUtils.isWriterName(methodName, writerPrefixes) || executableElement.getParameters().size() != 1) {
return null; // not a writer
}
Element classElement = executableElement.getEnclosingElement();
if (!(classElement instanceof TypeElement)) {
return null; // not within a class
}
final String fieldName = NameUtils.getPropertyNameForSetter(methodName, writerPrefixes);
// Return the field corresponding to this writer.
return (Element) getDeclaringType()
.getEnclosedElement(ElementQuery.ALL_FIELDS.named(fieldName::equals))
.map(io.micronaut.inject.ast.Element::getNativeType)
.orElse(null);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy