Search in sources :

Example 1 with JavaModelUtils

use of io.micronaut.inject.processing.JavaModelUtils in project micronaut-core by micronaut-projects.

the class JavaClassElement method getEnclosedElements.

@Override
public <T extends io.micronaut.inject.ast.Element> List<T> getEnclosedElements(@NonNull ElementQuery<T> query) {
    Objects.requireNonNull(query, "Query cannot be null");
    ElementQuery.Result<T> result = query.result();
    ElementKind kind = getElementKind(result.getElementType());
    List<T> resultingElements = new ArrayList<>();
    List<Element> enclosedElements = new ArrayList<>(getDeclaredEnclosedElements());
    boolean onlyDeclared = result.isOnlyDeclared();
    boolean onlyAbstract = result.isOnlyAbstract();
    boolean onlyConcrete = result.isOnlyConcrete();
    boolean onlyInstance = result.isOnlyInstance();
    if (!onlyDeclared) {
        Elements elements = visitorContext.getElements();
        TypeMirror superclass = classElement.getSuperclass();
        // traverse the super class true and add elements that are not overridden
        while (superclass instanceof DeclaredType) {
            DeclaredType dt = (DeclaredType) superclass;
            TypeElement element = (TypeElement) dt.asElement();
            // reached non-accessible class like Object, Enum, Record etc.
            if (element.getQualifiedName().toString().startsWith("java.lang.")) {
                break;
            }
            List<? extends Element> superElements = element.getEnclosedElements();
            List<Element> elementsToAdd = new ArrayList<>(superElements.size());
            superElements: for (Element superElement : superElements) {
                ElementKind superKind = superElement.getKind();
                if (superKind == kind) {
                    for (Element enclosedElement : enclosedElements) {
                        if (elements.hides(enclosedElement, superElement)) {
                            continue superElements;
                        } else if (enclosedElement.getKind() == ElementKind.METHOD && superElement.getKind() == ElementKind.METHOD) {
                            final ExecutableElement methodCandidate = (ExecutableElement) superElement;
                            if (elements.overrides((ExecutableElement) enclosedElement, methodCandidate, this.classElement)) {
                                continue superElements;
                            }
                        }
                    }
                    // dependency injection method resolution requires extended overrides checks
                    if (result.isOnlyInjected() && superElement.getKind() == ElementKind.METHOD) {
                        final ExecutableElement methodCandidate = (ExecutableElement) superElement;
                        // check for extended override
                        final String thisClassName = this.classElement.getQualifiedName().toString();
                        final String declaringClassName = element.getQualifiedName().toString();
                        boolean isParent = !declaringClassName.equals(thisClassName);
                        final ModelUtils javaModelUtils = visitorContext.getModelUtils();
                        final ExecutableElement overridingMethod = javaModelUtils.overridingOrHidingMethod(methodCandidate, this.classElement, false).orElse(methodCandidate);
                        TypeElement overridingClass = javaModelUtils.classElementFor(overridingMethod);
                        boolean overridden = isParent && overridingClass != null && !overridingClass.getQualifiedName().toString().equals(declaringClassName);
                        boolean isPackagePrivate = javaModelUtils.isPackagePrivate(methodCandidate);
                        boolean isPrivate = methodCandidate.getModifiers().contains(Modifier.PRIVATE);
                        if (overridden && !(isPrivate || isPackagePrivate)) {
                            // bail out if the method has been overridden, since it will have already been handled
                            continue;
                        }
                        if (isParent && overridden) {
                            boolean overriddenInjected = overridden && visitorContext.getAnnotationUtils().getAnnotationMetadata(overridingMethod).hasDeclaredAnnotation(AnnotationUtil.INJECT);
                            String packageOfOverridingClass = visitorContext.getElements().getPackageOf(overridingMethod).getQualifiedName().toString();
                            String packageOfDeclaringClass = visitorContext.getElements().getPackageOf(element).getQualifiedName().toString();
                            boolean isPackagePrivateAndPackagesDiffer = overridden && isPackagePrivate && !packageOfOverridingClass.equals(packageOfDeclaringClass);
                            if (!overriddenInjected && !isPackagePrivateAndPackagesDiffer && !isPrivate) {
                                // and is not annotated with @Inject
                                continue;
                            }
                        }
                    }
                    if (onlyAbstract && !superElement.getModifiers().contains(Modifier.ABSTRACT)) {
                        continue;
                    } else if (onlyConcrete && superElement.getModifiers().contains(Modifier.ABSTRACT)) {
                        continue;
                    } else if (onlyInstance && superElement.getModifiers().contains(Modifier.STATIC)) {
                        continue;
                    }
                    elementsToAdd.add(superElement);
                }
            }
            enclosedElements.addAll(elementsToAdd);
            superclass = element.getSuperclass();
        }
        if (kind == ElementKind.METHOD) {
            // if the element kind is interfaces then we need to go through interfaces as well
            Set<TypeElement> allInterfaces = visitorContext.getModelUtils().getAllInterfaces(this.classElement);
            List<Element> elementsToAdd = new ArrayList<>(allInterfaces.size());
            for (TypeElement itfe : allInterfaces) {
                List<? extends Element> interfaceElements = itfe.getEnclosedElements();
                interfaceElements: for (Element interfaceElement : interfaceElements) {
                    if (interfaceElement.getKind() == ElementKind.METHOD) {
                        ExecutableElement ee = (ExecutableElement) interfaceElement;
                        if (onlyAbstract && ee.getModifiers().contains(Modifier.DEFAULT)) {
                            continue;
                        } else if (onlyConcrete && !ee.getModifiers().contains(Modifier.DEFAULT)) {
                            continue;
                        }
                        for (Element enclosedElement : enclosedElements) {
                            if (enclosedElement.getKind() == ElementKind.METHOD) {
                                if (elements.overrides((ExecutableElement) enclosedElement, ee, this.classElement)) {
                                    continue interfaceElements;
                                }
                            }
                        }
                        elementsToAdd.add(interfaceElement);
                    }
                }
            }
            enclosedElements.addAll(elementsToAdd);
            elementsToAdd.clear();
        }
    }
    boolean onlyAccessible = result.isOnlyAccessible();
    if (kind == ElementKind.METHOD) {
        if (onlyAbstract) {
            if (isInterface()) {
                enclosedElements.removeIf((e) -> e.getModifiers().contains(Modifier.DEFAULT));
            } else {
                enclosedElements.removeIf((e) -> !e.getModifiers().contains(Modifier.ABSTRACT));
            }
        } else if (onlyConcrete) {
            if (isInterface()) {
                enclosedElements.removeIf((e) -> !e.getModifiers().contains(Modifier.DEFAULT));
            } else {
                enclosedElements.removeIf((e) -> e.getModifiers().contains(Modifier.ABSTRACT));
            }
        }
    }
    if (onlyInstance) {
        enclosedElements.removeIf((e) -> e.getModifiers().contains(Modifier.STATIC));
    }
    List<Predicate<Set<ElementModifier>>> modifierPredicates = result.getModifierPredicates();
    List<Predicate<String>> namePredicates = result.getNamePredicates();
    List<Predicate<AnnotationMetadata>> annotationPredicates = result.getAnnotationPredicates();
    final List<Predicate<ClassElement>> typePredicates = result.getTypePredicates();
    boolean hasNamePredicates = !namePredicates.isEmpty();
    boolean hasModifierPredicates = !modifierPredicates.isEmpty();
    boolean hasAnnotationPredicates = !annotationPredicates.isEmpty();
    boolean hasTypePredicates = !typePredicates.isEmpty();
    final JavaElementFactory elementFactory = visitorContext.getElementFactory();
    elementLoop: for (Element enclosedElement : enclosedElements) {
        ElementKind enclosedElementKind = enclosedElement.getKind();
        if (enclosedElementKind == kind || (enclosedElementKind == ElementKind.ENUM && kind == ElementKind.CLASS)) {
            String elementName = enclosedElement.getSimpleName().toString();
            if (onlyAccessible) {
                // exclude private members
                if (enclosedElement.getModifiers().contains(Modifier.PRIVATE)) {
                    continue;
                } else if (elementName.startsWith("$")) {
                    // exclude synthetic members or bridge methods that start with $
                    continue;
                } else {
                    Element enclosingElement = enclosedElement.getEnclosingElement();
                    final ClassElement onlyAccessibleFrom = result.getOnlyAccessibleFromType().orElse(this);
                    Object accessibleFrom = onlyAccessibleFrom.getNativeType();
                    // we need to check if it package private and within a different package so it can be excluded
                    if (enclosingElement != accessibleFrom && visitorContext.getModelUtils().isPackagePrivate(enclosedElement)) {
                        if (enclosingElement instanceof TypeElement) {
                            Name qualifiedName = ((TypeElement) enclosingElement).getQualifiedName();
                            String packageName = NameUtils.getPackageName(qualifiedName.toString());
                            if (!packageName.equals(onlyAccessibleFrom.getPackageName())) {
                                continue;
                            }
                        }
                    }
                }
            }
            if (hasModifierPredicates) {
                Set<ElementModifier> modifiers = enclosedElement.getModifiers().stream().map(m -> ElementModifier.valueOf(m.name())).collect(Collectors.toSet());
                for (Predicate<Set<ElementModifier>> modifierPredicate : modifierPredicates) {
                    if (!modifierPredicate.test(modifiers)) {
                        continue elementLoop;
                    }
                }
            }
            if (hasNamePredicates) {
                for (Predicate<String> namePredicate : namePredicates) {
                    if (!namePredicate.test(elementName)) {
                        continue elementLoop;
                    }
                }
            }
            final AnnotationMetadata metadata = visitorContext.getAnnotationUtils().getAnnotationMetadata(enclosedElement);
            if (hasAnnotationPredicates) {
                for (Predicate<AnnotationMetadata> annotationPredicate : annotationPredicates) {
                    if (!annotationPredicate.test(metadata)) {
                        continue elementLoop;
                    }
                }
            }
            T element;
            switch(enclosedElementKind) {
                case METHOD:
                    final ExecutableElement executableElement = (ExecutableElement) enclosedElement;
                    // noinspection unchecked
                    element = (T) elementFactory.newMethodElement(this, executableElement, metadata, genericTypeInfo);
                    break;
                case FIELD:
                    // noinspection unchecked
                    element = (T) elementFactory.newFieldElement(this, (VariableElement) enclosedElement, metadata);
                    break;
                case CONSTRUCTOR:
                    // noinspection unchecked
                    element = (T) elementFactory.newConstructorElement(this, (ExecutableElement) enclosedElement, metadata);
                    break;
                case CLASS:
                case ENUM:
                    // noinspection unchecked
                    element = (T) elementFactory.newClassElement((TypeElement) enclosedElement, metadata);
                    break;
                default:
                    element = null;
            }
            if (element != null) {
                if (hasTypePredicates) {
                    for (Predicate<ClassElement> typePredicate : typePredicates) {
                        ClassElement classElement;
                        if (element instanceof ConstructorElement) {
                            classElement = this;
                        } else if (element instanceof MethodElement) {
                            classElement = ((MethodElement) element).getGenericReturnType();
                        } else if (element instanceof ClassElement) {
                            classElement = (ClassElement) element;
                        } else {
                            classElement = ((FieldElement) element).getGenericField();
                        }
                        if (!typePredicate.test(classElement)) {
                            continue elementLoop;
                        }
                    }
                }
                List<Predicate<T>> elementPredicates = result.getElementPredicates();
                if (!elementPredicates.isEmpty()) {
                    for (Predicate<T> elementPredicate : elementPredicates) {
                        if (!elementPredicate.test(element)) {
                            continue elementLoop;
                        }
                    }
                }
                resultingElements.add(element);
            }
        }
    }
    return Collections.unmodifiableList(resultingElements);
}
Also used : ElementKind(javax.lang.model.element.ElementKind) 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) FieldElement(io.micronaut.inject.ast.FieldElement) 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) Predicate(java.util.function.Predicate) Name(javax.lang.model.element.Name) ElementQuery(io.micronaut.inject.ast.ElementQuery) TypeMirror(javax.lang.model.type.TypeMirror) ModelUtils(io.micronaut.annotation.processing.ModelUtils) JavaModelUtils(io.micronaut.inject.processing.JavaModelUtils) List(java.util.List) ArrayList(java.util.ArrayList) ConstructorElement(io.micronaut.inject.ast.ConstructorElement) TypeElement(javax.lang.model.element.TypeElement) ElementModifier(io.micronaut.inject.ast.ElementModifier) 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