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