001/* -*- mode: Java; c-basic-offset: 2; indent-tabs-mode: nil; coding: utf-8-unix -*- 002 * 003 * Copyright © 2024–2026 microBean™. 004 * 005 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with 006 * the License. You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on 011 * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the 012 * specific language governing permissions and limitations under the License. 013 */ 014package org.microbean.bean; 015 016import java.util.Collection; 017 018import java.util.function.Predicate; 019 020import javax.lang.model.element.AnnotationMirror; 021import javax.lang.model.element.ExecutableElement; 022 023import org.microbean.assign.Matcher; 024 025import static java.util.Objects.requireNonNull; 026 027import static org.microbean.construct.element.AnnotationMirrors.sameAnnotation; 028import static org.microbean.construct.element.AnnotationMirrors.contains; 029import static org.microbean.construct.element.AnnotationMirrors.containsAll; 030 031/** 032 * A {@link Matcher} encapsulating <a 033 * href="https://jakarta.ee/specifications/cdi/4.1/jakarta-cdi-spec-4.0#observertypesafe_resolution">CDI-compatible bean 034 * qualifier matching rules</a>. 035 * 036 * @author <a href="https://about.me/lairdnelson" target="_top">Laird Nelson</a> 037 * 038 * @see #test(Collection, Collection) 039 */ 040public class BeanQualifiersMatcher 041 implements Matcher<Collection<? extends AnnotationMirror>, Collection<? extends AnnotationMirror>> { 042 043 044 /* 045 * Instance fields. 046 */ 047 048 049 private final org.microbean.assign.Qualifiers aq; 050 051 private final Qualifiers bq; 052 053 054 /* 055 * Constructors. 056 */ 057 058 059 /** 060 * Creates a new {@link BeanQualifiersMatcher}. 061 * 062 * @param aq a (@link org.microbean.assign.Qualifiers}; must not be {@code null} 063 * 064 * @param bq a {@link Qualifiers}; must not be {@code null} 065 * 066 * @exception NullPointerException if any argument is {@code null} 067 */ 068 public BeanQualifiersMatcher(final org.microbean.assign.Qualifiers aq, 069 final Qualifiers bq) { 070 super(); 071 this.aq = requireNonNull(aq, "aq"); 072 this.bq = requireNonNull(bq, "bq"); 073 } 074 075 076 /* 077 * Instance methods. 078 */ 079 080 081 /* 082 * Returns the non-{@code null}, determinate {@link Predicate} {@linkplain 083 * org.microbean.assign.Qualifiers#Qualifiers(Domain, AnnotationMirror, Predicate) supplied (indirectly) at 084 * construction time} that returns {@code true} if a given {@link ExecutableElement}, representing an annotation 085 * element, is to be included in any comparison operation. 086 * 087 * @return the non-{@code null}, determinate {@link Predicate} {@linkplain 088 * org.microbean.assign.Qualifiers#Qualifiers(Domain, AnnotationMirror, Predicate) supplied (indirectly) at 089 * construction time} that returns {@code true} if a given {@link ExecutableElement}, representing an annotation 090 * element, is to be included in any comparison operation 091 * 092 * @see #BeanQualifiersMatcher(Qualifiers) 093 * 094 * @see org.microbean.assign.Qualifiers#annotationElementInclusionPredicate() 095 */ 096 // public final Predicate<? super ExecutableElement> annotationElementInclusionPredicate() { 097 // return this.qualifiers.annotationElementInclusionPredicate(); 098 // } 099 100 @Override // Matcher<Collection<? extends AnnotationMirror>, Collection<? extends AnnotationMirror>> 101 public final boolean test(final Collection<? extends AnnotationMirror> receiverAnnotations, 102 final Collection<? extends AnnotationMirror> payloadAnnotations) { 103 final Collection<? extends AnnotationMirror> receiverQualifiers = this.aq.qualifiers(receiverAnnotations); 104 final Collection<? extends AnnotationMirror> payloadQualifiers = this.aq.qualifiers(payloadAnnotations); 105 // https://jakarta.ee/specifications/cdi/4.1/jakarta-cdi-spec-4.1#performing_typesafe_resolution "A bean is 106 // assignable to a given injection point if...the bean [payload] has all the required [receiver] qualifiers. If no 107 // required qualifiers were explicitly specified, the container assumes the required qualifier @Default." 108 // 109 // https://jakarta.ee/specifications/cdi/4.1/jakarta-cdi-spec-4.1#builtin_qualifiers "Every bean has the built-in 110 // qualifier @Any, even if it does not explicitly declare this qualifier. If a bean does not explicitly declare a 111 // qualifier other than @Named or @Any, the bean has exactly one additional qualifier, of type @Default. This is 112 // called the default qualifier." 113 // 114 // https://jakarta.ee/specifications/cdi/4.1/jakarta-cdi-spec-4.1#injection_point_default_qualifier 115 // "If an injection point declares no qualifier, the injection point has exactly one qualifier, the default 116 // qualifier @Default." 117 return 118 receiverQualifiers.isEmpty() ? 119 payloadQualifiers.isEmpty() || this.aq.contains(payloadQualifiers, this.bq.defaultQualifier()) : 120 this.aq.containsAll(payloadQualifiers.isEmpty() ? this.bq.anyAndDefaultQualifiers() : payloadQualifiers, 121 receiverQualifiers); 122 } 123 124}