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.qualifier.NamedAttributeMap;
022
023import static org.microbean.bean.InterceptorBindings.anyInterceptorBinding;
024import static org.microbean.bean.InterceptorBindings.interceptorBindings;
025
026/**
027 * A {@link Matcher} encapsulating <a
028 * href="https://jakarta.ee/specifications/cdi/4.0/jakarta-cdi-spec-4.0#interceptors">CDI-compatible interceptor binding
029 * 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 InterceptorBindingsMatcher implements Matcher<Collection<? extends NamedAttributeMap<?>>, Collection<? extends NamedAttributeMap<?>>> {
036
037  /**
038   * Creates a new {@link InterceptorBindingsMatcher}.
039   */
040  public InterceptorBindingsMatcher() {
041    super();
042  }
043
044  /**
045   * Returns {@code true} if and only if either (a) both the collection of {@linkplain
046   * InterceptorBindings#interceptorBindings(Collection) interceptor bindings present} in {@code receiverAttributes} and
047   * the collection of {@linkplain InterceptorBindings#interceptorBindings(Collection) interceptor bindings present} in
048   * {@code payloadAttributes} are {@linkplain Collection#isEmpty() empty}, or (b) if the collection of {@linkplain
049   * InterceptorBindings#interceptorBindings(Collection) interceptor bindings present} in {@code payloadAttributes} has
050   * only one element and that element {@linkplain InterceptorBindings#anyInterceptorBinding(NamedAttributeMap) is the
051   * <dfn>any</dfn> interceptor binding}, or (c) the sizes of the collection of {@linkplain
052   * InterceptorBindings#interceptorBindings(Collection) interceptor bindings present} in {@code receiverAttributes} and
053   * the collection of {@linkplain InterceptorBindings#interceptorBindings(Collection) interceptor bindings present} in
054   * {@code payloadAttributes} are the same and the collection of {@linkplain
055   * InterceptorBindings#interceptorBindings(Collection) interceptor bindings present} in {@code receiverAttributes}
056   * {@linkplain Collection#containsAll(Collection) contains all} the collection of {@linkplain
057   * InterceptorBindings#interceptorBindings(Collection) interceptor bindings present} in {@code payloadAttributes} and
058   * the collection of {@linkplain InterceptorBindings#interceptorBindings(Collection) interceptor bindings present} in
059   * {@code payloadAttributes} {@linkplain Collection#containsAll(Collection) contains all} the collection of
060   * {@linkplain InterceptorBindings#interceptorBindings(Collection) interceptor bindings present} in {@code
061   * receiverAttributes}.
062   *
063   * @param receiverAttributes a {@link Collection} of {@link NamedAttributeMap}s; must not be {@code null}
064   *
065   * @param payloadAttributes a {@link Collection} of {@link NamedAttributeMap}s; must not be {@code null}
066   *
067   * @return {@code true} if and only if either (a) both the collection of {@linkplain
068   * InterceptorBindings#interceptorBindings(Collection) interceptor bindings present} in {@code receiverAttributes} and
069   * the collection of {@linkplain InterceptorBindings#interceptorBindings(Collection) interceptor bindings present} in
070   * {@code payloadAttributes} are {@linkplain Collection#isEmpty() empty}, or (b) if the collection of {@linkplain
071   * InterceptorBindings#interceptorBindings(Collection) interceptor bindings present} in {@code payloadAttributes} has
072   * only one element and that element {@linkplain InterceptorBindings#anyInterceptorBinding(NamedAttributeMap) is the
073   * <dfn>any</dfn> interceptor binding}, or (c) the sizes of the collection of {@linkplain
074   * InterceptorBindings#interceptorBindings(Collection) interceptor bindings present} in {@code receiverAttributes} and
075   * the collection of {@linkplain InterceptorBindings#interceptorBindings(Collection) interceptor bindings present} in
076   * {@code payloadAttributes} are the same and the collection of {@linkplain
077   * InterceptorBindings#interceptorBindings(Collection) interceptor bindings present} in {@code receiverAttributes}
078   * {@linkplain Collection#containsAll(Collection) contains all} the collection of {@linkplain
079   * InterceptorBindings#interceptorBindings(Collection) interceptor bindings present} in {@code payloadAttributes} and
080   * the collection of {@linkplain InterceptorBindings#interceptorBindings(Collection) interceptor bindings present} in
081   * {@code payloadAttributes} {@linkplain Collection#containsAll(Collection) contains all} the collection of
082   * {@linkplain InterceptorBindings#interceptorBindings(Collection) interceptor bindings present} in {@code
083   * receiverAttributes}
084   *
085   * @exception NullPointerException if either {@code receiverAttributes} or {@code payloadAttributes} is {@code null}
086   */
087  @Override // Matcher<Collection<? extends NamedAttributeMap<?>>, Collection<? extends NamedAttributeMap<?>>>
088  public final boolean test(final Collection<? extends NamedAttributeMap<?>> receiverAttributes,
089                            final Collection<? extends NamedAttributeMap<?>> payloadAttributes) {
090    final List<? extends NamedAttributeMap<?>> payloadBindings = interceptorBindings(payloadAttributes);
091    if (payloadBindings.isEmpty()) {
092      return interceptorBindings(receiverAttributes).isEmpty();
093    } else if (payloadBindings.size() == 1 && anyInterceptorBinding(payloadBindings.get(0))) {
094      return true;
095    }
096    final List<? extends NamedAttributeMap<?>> receiverBindings = interceptorBindings(receiverAttributes);
097    return
098      receiverBindings.size() == payloadBindings.size() &&
099      receiverBindings.containsAll(payloadBindings) &&
100      payloadBindings.containsAll(receiverBindings);
101  }
102
103}