001/* -*- mode: Java; c-basic-offset: 2; indent-tabs-mode: nil; coding: utf-8-unix -*- 002 * 003 * Copyright © 2024–2025 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 org.microbean.assign.Matcher; 019 020import org.microbean.qualifier.NamedAttributeMap; 021 022import static org.microbean.assign.Qualifiers.anyAndDefaultQualifiers; 023import static org.microbean.assign.Qualifiers.defaultQualifier; 024import static org.microbean.assign.Qualifiers.qualifiers; 025 026/** 027 * A {@link Matcher} encapsulating <a 028 * href="https://jakarta.ee/specifications/cdi/4.0/jakarta-cdi-spec-4.0#observertypesafe_resolution">CDI-compatible bean 029 * qualifier matching rules</a>. 030 * 031 * @author <a href="https://about.me/lairdnelson" target="_top">Laird Nelson</a> 032 * 033 * @see #test(Collection, Collection) 034 */ 035public final class BeanQualifiersMatcher implements Matcher<Collection<? extends NamedAttributeMap<?>>, Collection<? extends NamedAttributeMap<?>>> { 036 037 038 /* 039 * Constructors. 040 */ 041 042 043 /** 044 * Creates a new {@link BeanQualifiersMatcher}. 045 */ 046 public BeanQualifiersMatcher() { 047 super(); 048 } 049 050 051 /* 052 * Instance methods. 053 */ 054 055 056 /** 057 * Returns {@code true} if and only if either (a) the collection of {@linkplain 058 * org.microbean.assign.Qualifiers#qualifiers(Collection) qualifiers present} in {@code receiverAttributes} is 059 * {@linkplain Collection#isEmpty() empty} and either the collection of {@linkplain 060 * org.microbean.assign.Qualifiers#qualifiers(Collection) qualifiers present} in {@code payloadAttributes} is also 061 * empty or contains the {@linkplain org.microbean.assign.Qualifiers#defaultQualifier() default qualifier}, or (b) if 062 * the collection of {@linkplain org.microbean.assign.Qualifiers#qualifiers(Collection) qualifiers present} in {@code 063 * payloadAttributes} {@linkplain Collection#isEmpty() is empty} or the return value of {@link 064 * org.microbean.assign.Qualifiers#anyAndDefaultQualifiers()} {@linkplain Collection#containsAll(Collection) contains 065 * all} of the {@linkplain org.microbean.assign.Qualifiers#qualifiers(Collection) qualifiers present} in {@code 066 * receiverAttributes}, or (c) if the collection of {@linkplain org.microbean.assign.Qualifiers#qualifiers(Collection) 067 * qualifiers present} in {@code payloadAttributes} {@linkplain Collection#containsAll(Collection) contains all} of 068 * the {@linkplain org.microbean.assign.Qualifiers#qualifiers(Collection) qualifiers present} in {@code 069 * receiverAttributes}. 070 * 071 * @param receiverAttributes a {@link Collection} of {@link NamedAttributeMap}s; must not be {@code null} 072 * 073 * @param payloadAttributes a {@link Collection} of {@link NamedAttributeMap}s; must not be {@code null} 074 * 075 * @return {@code true} if and only if either (a) the collection of {@linkplain 076 * org.microbean.assign.Qualifiers#qualifiers(Collection) qualifiers present} in {@code receiverAttributes} is 077 * {@linkplain Collection#isEmpty() empty} and either the collection of {@linkplain 078 * org.microbean.assign.Qualifiers#qualifiers(Collection) qualifiers present} in {@code payloadAttributes} is also 079 * empty or contains the {@linkplain org.microbean.assign.Qualifiers#defaultQualifier() default qualifier}, or (b) if 080 * the collection of {@linkplain org.microbean.assign.Qualifiers#qualifiers(Collection) qualifiers present} in {@code 081 * payloadAttributes} {@linkplain Collection#isEmpty() is empty} or the return value of {@link 082 * org.microbean.assign.Qualifiers#anyAndDefaultQualifiers()} {@linkplain Collection#containsAll(Collection) contains 083 * all} of the {@linkplain org.microbean.assign.Qualifiers#qualifiers(Collection) qualifiers present} in {@code 084 * receiverAttributes}, or (c) if the collection of {@linkplain org.microbean.assign.Qualifiers#qualifiers(Collection) 085 * qualifiers present} in {@code payloadAttributes} {@linkplain Collection#containsAll(Collection) contains all} of 086 * the {@linkplain org.microbean.assign.Qualifiers#qualifiers(Collection) qualifiers present} in {@code 087 * receiverAttributes} 088 * 089 * @exception NullPointerException if either {@code receiverAttributes} or {@code payloadAttributes} is {@code null} 090 */ 091 @Override // Matcher<Collection<? extends NamedAttributeMap<?>>, Collection<? extends NamedAttributeMap<?>>> 092 public final boolean test(final Collection<? extends NamedAttributeMap<?>> receiverAttributes, 093 final Collection<? extends NamedAttributeMap<?>> payloadAttributes) { 094 final Collection<? extends NamedAttributeMap<?>> receiverQualifiers = qualifiers(receiverAttributes); 095 Collection<? extends NamedAttributeMap<?>> payloadQualifiers = qualifiers(payloadAttributes); 096 // https://jakarta.ee/specifications/cdi/4.0/jakarta-cdi-spec-4.0#performing_typesafe_resolution 097 // "A bean is assignable to a given injection point if...the bean has all the required qualifiers. If no required 098 // qualifiers were explicitly specified, the container assumes the required qualifier @Default." 099 // 100 // https://jakarta.ee/specifications/cdi/4.0/jakarta-cdi-spec-4.0#builtin_qualifiers 101 // "Every bean has the built-in qualifier @Any, even if it does not explicitly declare this qualifier. If a bean 102 // does not explicitly declare a qualifier other than @Named or @Any, the bean has exactly one additional qualifier, 103 // of type @Default. This is called the default qualifier." 104 // 105 // https://jakarta.ee/specifications/cdi/4.0/jakarta-cdi-spec-4.0#injection_point_default_qualifier 106 // "If an injection point declares no qualifier, the injection point has exactly one qualifier, the default 107 // qualifier @Default." 108 return 109 receiverQualifiers.isEmpty() ? payloadQualifiers.isEmpty() || payloadQualifiers.contains(defaultQualifier()) : 110 (payloadQualifiers.isEmpty() ? anyAndDefaultQualifiers() : payloadQualifiers).containsAll(receiverQualifiers); 111 } 112 113}