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}