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