001/* -*- mode: Java; c-basic-offset: 2; indent-tabs-mode: nil; coding: utf-8-unix -*-
002 *
003 * Copyright © 2023–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.ArrayList;
017import java.util.Collection;
018import java.util.Collections;
019import java.util.List;
020import java.util.Map;
021import java.util.Objects;
022
023import org.microbean.qualifier.NamedAttributeMap;
024
025/**
026 * A utility class providing methods that work with interceptor bindings.
027 *
028 * @author <a href="https://about.me/lairdnelson" target="_top">Laird Nelson</a>
029 */
030public final class InterceptorBindings {
031
032  private static final NamedAttributeMap<String> INTERCEPTOR_BINDING = new NamedAttributeMap<>("InterceptorBinding");
033
034  private static final List<NamedAttributeMap<String>> INTERCEPTOR_BINDING_LIST = List.of(INTERCEPTOR_BINDING);
035
036  private static final NamedAttributeMap<String> ANY_INTERCEPTOR_BINDING =
037    new NamedAttributeMap<>("Any", Map.of(), Map.of(), INTERCEPTOR_BINDING_LIST);
038
039  private InterceptorBindings() {
040    super();
041  }
042
043  /**
044   * Returns a {@link NamedAttributeMap} representing the <dfn>any</dfn> interceptor binding.
045   *
046   * @return a {@link NamedAttributeMap} representing the <dfn>any</dfn> interceptor binding; never {@code null}
047   */
048  public static final NamedAttributeMap<String> anyInterceptorBinding() {
049    return ANY_INTERCEPTOR_BINDING;
050  }
051
052  /**
053   * Returns {@code true} if and only if the supplied {@link NamedAttributeMap} represents the <dfn>any</dfn>
054   * interceptor binding.
055   *
056   * @param nam a {@link NamedAttributeMap}; may be {@code null} in which case {@code false} will be returned
057   *
058   * @return {@code true} if and only if the supplied {@link NamedAttributeMap} represents the <dfn>any</dfn>
059   * interceptor binding
060   *
061   * @see #anyInterceptorBinding()
062   */
063  public static final boolean anyInterceptorBinding(final NamedAttributeMap<?> nam) {
064    return ANY_INTERCEPTOR_BINDING.equals(nam) && interceptorBinding(nam);
065  }
066
067  /**
068   * Returns a {@link NamedAttributeMap} representing the <dfn>interceptor binding</dfn> (meta-) interceptor binding.
069   *
070   * @return a {@link NamedAttributeMap} representing the <dfn>interceptor binding</dfn> (meta-) interceptor binding;
071   * never {@code null}
072   */
073  public static final NamedAttributeMap<String> interceptorBinding() {
074    return INTERCEPTOR_BINDING;
075  }
076
077  /**
078   * Returns {@code true} if and only if the supplied {@link NamedAttributeMap} is itself a {@link NamedAttributeMap}
079   * that can be used to designate other {@link NamedAttributeMap}s as interceptor bindings, or a {@link
080   * NamedAttributeMap} so designated.
081   *
082   * @param a a {@link NamedAttributeMap}; may be {@code null} in which case {@code false} will be returned
083   *
084   * @return {@code true} if and only if the supplied {@link NamedAttributeMap} is itself a {@link NamedAttributeMap}
085   * that can be used to designate other {@link NamedAttributeMap}s as interceptor bindings, or a {@link
086   * NamedAttributeMap} so designated
087   *
088   * @see #interceptorBinding()
089   */
090  public static final boolean interceptorBinding(final NamedAttributeMap<?> a) {
091    return a != null && interceptorBinding(a.metadata());
092  }
093
094  private static final boolean interceptorBinding(final Iterable<? extends NamedAttributeMap<?>> mds) {
095    for (final NamedAttributeMap<?> md : mds) {
096      if (md.equals(INTERCEPTOR_BINDING) && md.metadata().isEmpty() || interceptorBinding(md)) {
097        return true;
098      }
099    }
100    return false;
101  }
102
103  /**
104   * Given a {@link Collection} of {@link NamedAttributeMap}s, returns an immutable {@link List} consisting of those
105   * {@link NamedAttributeMap} instances that are {@linkplain #interceptorBinding() deemed to be interceptor bindings}.
106   *
107   * @param c a {@link Collection}; must not be {@code null}
108   *
109   * @return a {@link List} of interceptor bindings
110   *
111   * @exception NullPointerException if {@code c} is {@code null}
112   */
113  public static final List<NamedAttributeMap<?>> interceptorBindings(final Collection<? extends NamedAttributeMap<?>> c) {
114    if (c.isEmpty()) {
115      return List.of();
116    }
117    final ArrayList<NamedAttributeMap<?>> list = new ArrayList<>(c.size());
118    for (final NamedAttributeMap<?> nam : c) {
119      if (interceptorBinding(nam)) {
120        list.add(nam);
121      }
122    }
123    list.trimToSize();
124    return Collections.unmodifiableList(list);
125  }
126
127  /**
128   * Returns a {@link NamedAttributeMap} representing a <dfn>target class</dfn> interceptor binding.
129   *
130   * @param type the target class name; must not be {@code null}
131   *
132   * @return a {@link NamedAttributeMap} representing a <dfn>target class</dfn> interceptor binding; never {@code null}
133   *
134   * @exception NullPointerException if {@code type} is {@code null}
135   */
136  public static final NamedAttributeMap<?> targetClassInterceptorBinding(final String type) {
137    return new NamedAttributeMap<String>("TargetClass", Map.of("class", Objects.requireNonNull(type, "type")), Map.of(), INTERCEPTOR_BINDING_LIST);
138  }
139
140  /**
141   * Returns {@code true} if and only if the supplied {@link NamedAttributeMap} is a <dfn>target class</dfn> interceptor
142   * binding.
143   *
144   * @param nam a {@link NamedAttributeMap}; must not be {@code null}
145   *
146   * @return {@code true} if and only if the supplied {@link NamedAttributeMap} is a <dfn>target class</dfn> interceptor
147   * binding
148   *
149   * @exception NullPointerException if {@code nam} is {@code null}
150   */
151  // Is nam a TargetClass interceptor binding?
152  public static final boolean targetClassInterceptorBinding(final NamedAttributeMap<?> nam) {
153    return nam.name().equals("TargetClass") && interceptorBinding(nam);
154  }
155
156}