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; 017import java.util.List; 018 019import org.microbean.assign.Matcher; 020 021import org.microbean.attributes.Attributes; 022 023import static org.microbean.bean.InterceptorBindings.anyInterceptorBinding; 024 025/** 026 * A {@link Matcher} encapsulating <a 027 * href="https://jakarta.ee/specifications/cdi/4.0/jakarta-cdi-spec-4.0#interceptors">CDI-compatible interceptor binding 028 * matching rules</a>. 029 * 030 * @author <a href="https://about.me/lairdnelson" target="_top">Laird Nelson</a> 031 * 032 * @see #test(Collection, Collection) 033 */ 034public class InterceptorBindingsMatcher implements Matcher<Collection<? extends Attributes>, Collection<? extends Attributes>> { 035 036 /** 037 * Creates a new {@link InterceptorBindingsMatcher}. 038 */ 039 public InterceptorBindingsMatcher() { 040 super(); 041 } 042 043 /** 044 * Returns {@code true} if and only if either (a) both the collection of {@linkplain 045 * InterceptorBindings#interceptorBindings(Collection) interceptor bindings present} in {@code receiverAttributes} and 046 * the collection of {@linkplain InterceptorBindings#interceptorBindings(Collection) interceptor bindings present} in 047 * {@code payloadAttributes} are {@linkplain Collection#isEmpty() empty}, or (b) if the collection of {@linkplain 048 * InterceptorBindings#interceptorBindings(Collection) interceptor bindings present} in {@code payloadAttributes} has 049 * only one element and that element {@linkplain InterceptorBindings#anyInterceptorBinding(Attributes) is the 050 * <dfn>any</dfn> interceptor binding}, or (c) the sizes of the collection of {@linkplain 051 * InterceptorBindings#interceptorBindings(Collection) interceptor bindings present} in {@code receiverAttributes} and 052 * the collection of {@linkplain InterceptorBindings#interceptorBindings(Collection) interceptor bindings present} in 053 * {@code payloadAttributes} are the same and the collection of {@linkplain 054 * InterceptorBindings#interceptorBindings(Collection) interceptor bindings present} in {@code receiverAttributes} 055 * {@linkplain Collection#containsAll(Collection) contains all} the collection of {@linkplain 056 * InterceptorBindings#interceptorBindings(Collection) interceptor bindings present} in {@code payloadAttributes} and 057 * the collection of {@linkplain InterceptorBindings#interceptorBindings(Collection) interceptor bindings present} in 058 * {@code payloadAttributes} {@linkplain Collection#containsAll(Collection) contains all} the collection of 059 * {@linkplain InterceptorBindings#interceptorBindings(Collection) interceptor bindings present} in {@code 060 * receiverAttributes}. 061 * 062 * @param receiverAttributes a {@link Collection} of {@link Attributes}s; must not be {@code null} 063 * 064 * @param payloadAttributes a {@link Collection} of {@link Attributes}s; must not be {@code null} 065 * 066 * @return {@code true} if and only if either (a) both the collection of {@linkplain 067 * InterceptorBindings#interceptorBindings(Collection) interceptor bindings present} in {@code receiverAttributes} and 068 * the collection of {@linkplain InterceptorBindings#interceptorBindings(Collection) interceptor bindings present} in 069 * {@code payloadAttributes} are {@linkplain Collection#isEmpty() empty}, or (b) if the collection of {@linkplain 070 * InterceptorBindings#interceptorBindings(Collection) interceptor bindings present} in {@code payloadAttributes} has 071 * only one element and that element {@linkplain InterceptorBindings#anyInterceptorBinding(Attributes) is the 072 * <dfn>any</dfn> interceptor binding}, or (c) the sizes of the collection of {@linkplain 073 * InterceptorBindings#interceptorBindings(Collection) interceptor bindings present} in {@code receiverAttributes} and 074 * the collection of {@linkplain InterceptorBindings#interceptorBindings(Collection) interceptor bindings present} in 075 * {@code payloadAttributes} are the same and the collection of {@linkplain 076 * InterceptorBindings#interceptorBindings(Collection) interceptor bindings present} in {@code receiverAttributes} 077 * {@linkplain Collection#containsAll(Collection) contains all} the collection of {@linkplain 078 * InterceptorBindings#interceptorBindings(Collection) interceptor bindings present} in {@code payloadAttributes} and 079 * the collection of {@linkplain InterceptorBindings#interceptorBindings(Collection) interceptor bindings present} in 080 * {@code payloadAttributes} {@linkplain Collection#containsAll(Collection) contains all} the collection of 081 * {@linkplain InterceptorBindings#interceptorBindings(Collection) interceptor bindings present} in {@code 082 * receiverAttributes} 083 * 084 * @exception NullPointerException if either {@code receiverAttributes} or {@code payloadAttributes} is {@code null} 085 */ 086 @Override // Matcher<Collection<? extends Attributes>, Collection<? extends Attributes>> 087 public final boolean test(final Collection<? extends Attributes> receiverAttributes, 088 final Collection<? extends Attributes> payloadAttributes) { 089 final Collection<? extends Attributes> payloadBindings = interceptorBindings(payloadAttributes); 090 if (payloadBindings.isEmpty()) { 091 return interceptorBindings(receiverAttributes).isEmpty(); 092 } else if (payloadBindings.size() == 1 && anyInterceptorBinding(payloadBindings.iterator().next())) { 093 return true; 094 } 095 final Collection<? extends Attributes> receiverBindings = interceptorBindings(receiverAttributes); 096 return 097 receiverBindings.size() == payloadBindings.size() && 098 receiverBindings.containsAll(payloadBindings) && 099 payloadBindings.containsAll(receiverBindings); 100 } 101 102 /** 103 * Given a {@link Collection} of {@link Attributes}s, returns an immutable {@link Collection} consisting of those 104 * {@link Attributes} instances that are deemed to be interceptor bindings. 105 * 106 * <p>The default implementation of this method returns the value of an invocation of the {@link 107 * InterceptorBindings#interceptorBindings(Collection)} method.</p> 108 * 109 * @param as a {@link Collection}; must not be {@code null} 110 * 111 * @return a {@link List} of interceptor bindings; never {@code null} 112 * 113 * @exception NullPointerException if {@code as} is {@code null} 114 */ 115 protected Collection<? extends Attributes> interceptorBindings(final Collection<? extends Attributes> as) { 116 return InterceptorBindings.interceptorBindings(as); 117 } 118 119}