Search in sources :

Example 1 with PublicMethodVisitor

use of io.micronaut.annotation.processing.PublicMethodVisitor in project micronaut-core by micronaut-projects.

the class JavaClassElement method getBeanProperties.

@Override
public List<PropertyElement> getBeanProperties() {
    if (this.beanProperties == null) {
        Map<String, BeanPropertyData> props = new LinkedHashMap<>();
        Map<String, VariableElement> fields = new LinkedHashMap<>();
        if (isRecord()) {
            classElement.asType().accept(new SuperclassAwareTypeVisitor<Object, Object>(visitorContext) {

                @Override
                protected boolean isAcceptable(Element element) {
                    return JavaModelUtils.isRecord(element);
                }

                @Override
                public Object visitDeclared(DeclaredType type, Object o) {
                    Element element = type.asElement();
                    if (isAcceptable(element)) {
                        List<? extends Element> enclosedElements = element.getEnclosedElements();
                        for (Element enclosedElement : enclosedElements) {
                            if (JavaModelUtils.isRecordComponent(enclosedElement) || enclosedElement instanceof ExecutableElement) {
                                if (enclosedElement.getKind() != ElementKind.CONSTRUCTOR) {
                                    accept(type, enclosedElement, o);
                                }
                            }
                        }
                    }
                    return o;
                }

                @Override
                protected void accept(DeclaredType type, Element element, Object o) {
                    String name = element.getSimpleName().toString();
                    if (element instanceof ExecutableElement) {
                        BeanPropertyData beanPropertyData = props.get(name);
                        if (beanPropertyData != null) {
                            beanPropertyData.getter = (ExecutableElement) element;
                        }
                    } else {
                        props.computeIfAbsent(name, propertyName -> {
                            BeanPropertyData beanPropertyData = new BeanPropertyData(propertyName);
                            beanPropertyData.declaringType = JavaClassElement.this;
                            beanPropertyData.type = mirrorToClassElement(element.asType(), visitorContext, genericTypeInfo, true);
                            return beanPropertyData;
                        });
                    }
                }
            }, null);
        } else {
            classElement.asType().accept(new PublicMethodVisitor<Object, Object>(visitorContext) {

                final String[] readPrefixes = getValue(AccessorsStyle.class, "readPrefixes", String[].class).orElse(new String[] { AccessorsStyle.DEFAULT_READ_PREFIX });

                final String[] writePrefixes = getValue(AccessorsStyle.class, "writePrefixes", String[].class).orElse(new String[] { AccessorsStyle.DEFAULT_WRITE_PREFIX });

                @Override
                protected boolean isAcceptable(javax.lang.model.element.Element element) {
                    if (element.getKind() == ElementKind.FIELD) {
                        return true;
                    }
                    if (element.getKind() == ElementKind.METHOD && element instanceof ExecutableElement) {
                        Set<Modifier> modifiers = element.getModifiers();
                        if (modifiers.contains(Modifier.PUBLIC) && !modifiers.contains(Modifier.STATIC)) {
                            ExecutableElement executableElement = (ExecutableElement) element;
                            String methodName = executableElement.getSimpleName().toString();
                            if (methodName.contains("$")) {
                                return false;
                            }
                            if (NameUtils.isReaderName(methodName, readPrefixes) && executableElement.getParameters().isEmpty()) {
                                return true;
                            } else {
                                return NameUtils.isWriterName(methodName, writePrefixes) && executableElement.getParameters().size() == 1;
                            }
                        }
                    }
                    return false;
                }

                @Override
                protected void accept(DeclaredType declaringType, javax.lang.model.element.Element element, Object o) {
                    if (element instanceof VariableElement) {
                        fields.put(element.getSimpleName().toString(), (VariableElement) element);
                        return;
                    }
                    ExecutableElement executableElement = (ExecutableElement) element;
                    String methodName = executableElement.getSimpleName().toString();
                    final TypeElement declaringTypeElement = (TypeElement) executableElement.getEnclosingElement();
                    if (NameUtils.isReaderName(methodName, readPrefixes) && executableElement.getParameters().isEmpty()) {
                        String propertyName = NameUtils.getPropertyNameForGetter(methodName, readPrefixes);
                        TypeMirror returnType = executableElement.getReturnType();
                        ClassElement getterReturnType;
                        if (returnType instanceof TypeVariable) {
                            TypeVariable tv = (TypeVariable) returnType;
                            final String tvn = tv.toString();
                            final ClassElement classElement = getTypeArguments().get(tvn);
                            if (classElement != null) {
                                getterReturnType = classElement;
                            } else {
                                getterReturnType = mirrorToClassElement(returnType, visitorContext, JavaClassElement.this.genericTypeInfo, true);
                            }
                        } else {
                            getterReturnType = mirrorToClassElement(returnType, visitorContext, JavaClassElement.this.genericTypeInfo, true);
                        }
                        BeanPropertyData beanPropertyData = props.computeIfAbsent(propertyName, BeanPropertyData::new);
                        configureDeclaringType(declaringTypeElement, beanPropertyData);
                        beanPropertyData.type = getterReturnType;
                        beanPropertyData.getter = executableElement;
                        if (beanPropertyData.setter != null) {
                            TypeMirror typeMirror = beanPropertyData.setter.getParameters().get(0).asType();
                            ClassElement setterParameterType = mirrorToClassElement(typeMirror, visitorContext, JavaClassElement.this.genericTypeInfo, true);
                            if (!setterParameterType.isAssignable(getterReturnType)) {
                                // not a compatible setter
                                beanPropertyData.setter = null;
                            }
                        }
                    } else if (NameUtils.isWriterName(methodName, writePrefixes) && executableElement.getParameters().size() == 1) {
                        String propertyName = NameUtils.getPropertyNameForSetter(methodName, writePrefixes);
                        TypeMirror typeMirror = executableElement.getParameters().get(0).asType();
                        ClassElement setterParameterType = mirrorToClassElement(typeMirror, visitorContext, JavaClassElement.this.genericTypeInfo, true);
                        BeanPropertyData beanPropertyData = props.computeIfAbsent(propertyName, BeanPropertyData::new);
                        configureDeclaringType(declaringTypeElement, beanPropertyData);
                        ClassElement propertyType = beanPropertyData.type;
                        if (propertyType != null) {
                            if (propertyType.getName().equals(setterParameterType.getName())) {
                                beanPropertyData.setter = executableElement;
                            }
                        } else {
                            beanPropertyData.setter = executableElement;
                        }
                    }
                }

                private void configureDeclaringType(TypeElement declaringTypeElement, BeanPropertyData beanPropertyData) {
                    if (beanPropertyData.declaringType == null && !classElement.equals(declaringTypeElement)) {
                        beanPropertyData.declaringType = mirrorToClassElement(declaringTypeElement.asType(), visitorContext, genericTypeInfo, true);
                    } else if (beanPropertyData.declaringType == null) {
                        beanPropertyData.declaringType = mirrorToClassElement(declaringTypeElement.asType(), visitorContext, genericTypeInfo, false);
                    }
                }
            }, null);
        }
        if (!props.isEmpty()) {
            this.beanProperties = new ArrayList<>(props.size());
            for (Map.Entry<String, BeanPropertyData> entry : props.entrySet()) {
                String propertyName = entry.getKey();
                BeanPropertyData value = entry.getValue();
                final VariableElement fieldElement = fields.get(propertyName);
                if (value.getter != null) {
                    final AnnotationMetadata annotationMetadata;
                    List<Element> parents = new ArrayList<>();
                    if (fieldElement != null) {
                        parents.add(fieldElement);
                    }
                    if (value.setter != null) {
                        parents.add(value.setter);
                    }
                    if (!parents.isEmpty()) {
                        annotationMetadata = visitorContext.getAnnotationUtils().getAnnotationMetadata(parents, value.getter);
                    } else {
                        annotationMetadata = visitorContext.getAnnotationUtils().newAnnotationBuilder().buildForMethod(value.getter);
                    }
                    JavaPropertyElement propertyElement = new JavaPropertyElement(value.declaringType == null ? this : value.declaringType, value.getter, annotationMetadata, propertyName, value.type, value.setter == null, visitorContext) {

                        @Override
                        public ClassElement getGenericType() {
                            TypeMirror propertyType = value.getter.getReturnType();
                            if (fieldElement != null) {
                                TypeMirror fieldType = fieldElement.asType();
                                if (visitorContext.getTypes().isAssignable(fieldType, propertyType)) {
                                    propertyType = fieldType;
                                }
                            }
                            Map<String, Map<String, TypeMirror>> declaredGenericInfo = getGenericTypeInfo();
                            return parameterizedClassElement(propertyType, visitorContext, declaredGenericInfo);
                        }

                        @Override
                        public Optional<String> getDocumentation() {
                            Elements elements = visitorContext.getElements();
                            String docComment = elements.getDocComment(value.getter);
                            return Optional.ofNullable(docComment);
                        }

                        @Override
                        public Optional<MethodElement> getWriteMethod() {
                            if (value.setter != null) {
                                return Optional.of(new JavaMethodElement(JavaClassElement.this, value.setter, visitorContext.getAnnotationUtils().newAnnotationBuilder().buildForMethod(value.setter), visitorContext));
                            }
                            return Optional.empty();
                        }

                        @Override
                        public Optional<MethodElement> getReadMethod() {
                            return Optional.of(new JavaMethodElement(JavaClassElement.this, value.getter, annotationMetadata, visitorContext));
                        }
                    };
                    beanProperties.add(propertyElement);
                }
            }
            this.beanProperties = Collections.unmodifiableList(beanProperties);
        } else {
            this.beanProperties = Collections.emptyList();
        }
    }
    return Collections.unmodifiableList(beanProperties);
}
Also used : ElementQuery(io.micronaut.inject.ast.ElementQuery) PackageElement(io.micronaut.inject.ast.PackageElement) AnnotationUtils(io.micronaut.annotation.processing.AnnotationUtils) Modifier(javax.lang.model.element.Modifier) FieldElement(io.micronaut.inject.ast.FieldElement) TypeElement(javax.lang.model.element.TypeElement) Internal(io.micronaut.core.annotation.Internal) WildcardElement(io.micronaut.inject.ast.WildcardElement) Elements(javax.lang.model.util.Elements) PublicMethodVisitor(io.micronaut.annotation.processing.PublicMethodVisitor) Map(java.util.Map) NameUtils(io.micronaut.core.naming.NameUtils) ClassUtils(io.micronaut.core.reflect.ClassUtils) GenericPlaceholderElement(io.micronaut.inject.ast.GenericPlaceholderElement) Predicate(java.util.function.Predicate) Collection(java.util.Collection) Set(java.util.Set) AccessorsStyle(io.micronaut.core.annotation.AccessorsStyle) Element(javax.lang.model.element.Element) Types(javax.lang.model.util.Types) Collectors(java.util.stream.Collectors) Objects(java.util.Objects) StringUtils(io.micronaut.core.util.StringUtils) SuperclassAwareTypeVisitor(io.micronaut.annotation.processing.SuperclassAwareTypeVisitor) List(java.util.List) AnnotationUtil(io.micronaut.core.annotation.AnnotationUtil) ModelUtils(io.micronaut.annotation.processing.ModelUtils) MethodElement(io.micronaut.inject.ast.MethodElement) TypeVariable(javax.lang.model.type.TypeVariable) Optional(java.util.Optional) JavaModelUtils(io.micronaut.inject.processing.JavaModelUtils) VariableElement(javax.lang.model.element.VariableElement) ClassElement(io.micronaut.inject.ast.ClassElement) ConstructorElement(io.micronaut.inject.ast.ConstructorElement) ArrayList(java.util.ArrayList) LinkedHashMap(java.util.LinkedHashMap) PropertyElement(io.micronaut.inject.ast.PropertyElement) ArrayableClassElement(io.micronaut.inject.ast.ArrayableClassElement) DeclaredType(javax.lang.model.type.DeclaredType) ElementModifier(io.micronaut.inject.ast.ElementModifier) Name(javax.lang.model.element.Name) ElementKind(javax.lang.model.element.ElementKind) Iterator(java.util.Iterator) ExecutableElement(javax.lang.model.element.ExecutableElement) TypeParameterElement(javax.lang.model.element.TypeParameterElement) NonNull(io.micronaut.core.annotation.NonNull) TypeMirror(javax.lang.model.type.TypeMirror) AnnotationMetadata(io.micronaut.core.annotation.AnnotationMetadata) Collections(java.util.Collections) Set(java.util.Set) PackageElement(io.micronaut.inject.ast.PackageElement) FieldElement(io.micronaut.inject.ast.FieldElement) TypeElement(javax.lang.model.element.TypeElement) WildcardElement(io.micronaut.inject.ast.WildcardElement) GenericPlaceholderElement(io.micronaut.inject.ast.GenericPlaceholderElement) Element(javax.lang.model.element.Element) MethodElement(io.micronaut.inject.ast.MethodElement) VariableElement(javax.lang.model.element.VariableElement) ClassElement(io.micronaut.inject.ast.ClassElement) ConstructorElement(io.micronaut.inject.ast.ConstructorElement) PropertyElement(io.micronaut.inject.ast.PropertyElement) ArrayableClassElement(io.micronaut.inject.ast.ArrayableClassElement) ExecutableElement(javax.lang.model.element.ExecutableElement) TypeParameterElement(javax.lang.model.element.TypeParameterElement) ExecutableElement(javax.lang.model.element.ExecutableElement) ArrayList(java.util.ArrayList) MethodElement(io.micronaut.inject.ast.MethodElement) ClassElement(io.micronaut.inject.ast.ClassElement) ArrayableClassElement(io.micronaut.inject.ast.ArrayableClassElement) VariableElement(javax.lang.model.element.VariableElement) Elements(javax.lang.model.util.Elements) AnnotationMetadata(io.micronaut.core.annotation.AnnotationMetadata) LinkedHashMap(java.util.LinkedHashMap) TypeMirror(javax.lang.model.type.TypeMirror) TypeVariable(javax.lang.model.type.TypeVariable) List(java.util.List) ArrayList(java.util.ArrayList) TypeElement(javax.lang.model.element.TypeElement) Element(javax.lang.model.element.Element) AccessorsStyle(io.micronaut.core.annotation.AccessorsStyle) Map(java.util.Map) LinkedHashMap(java.util.LinkedHashMap) DeclaredType(javax.lang.model.type.DeclaredType)

Aggregations

AnnotationUtils (io.micronaut.annotation.processing.AnnotationUtils)1 ModelUtils (io.micronaut.annotation.processing.ModelUtils)1 PublicMethodVisitor (io.micronaut.annotation.processing.PublicMethodVisitor)1 SuperclassAwareTypeVisitor (io.micronaut.annotation.processing.SuperclassAwareTypeVisitor)1 AccessorsStyle (io.micronaut.core.annotation.AccessorsStyle)1 AnnotationMetadata (io.micronaut.core.annotation.AnnotationMetadata)1 AnnotationUtil (io.micronaut.core.annotation.AnnotationUtil)1 Internal (io.micronaut.core.annotation.Internal)1 NonNull (io.micronaut.core.annotation.NonNull)1 NameUtils (io.micronaut.core.naming.NameUtils)1 ClassUtils (io.micronaut.core.reflect.ClassUtils)1 StringUtils (io.micronaut.core.util.StringUtils)1 ArrayableClassElement (io.micronaut.inject.ast.ArrayableClassElement)1 ClassElement (io.micronaut.inject.ast.ClassElement)1 ConstructorElement (io.micronaut.inject.ast.ConstructorElement)1 ElementModifier (io.micronaut.inject.ast.ElementModifier)1 ElementQuery (io.micronaut.inject.ast.ElementQuery)1 FieldElement (io.micronaut.inject.ast.FieldElement)1 GenericPlaceholderElement (io.micronaut.inject.ast.GenericPlaceholderElement)1 MethodElement (io.micronaut.inject.ast.MethodElement)1