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; 017 018import javax.lang.model.type.TypeMirror; 019 020import org.microbean.assign.AttributedType; 021import org.microbean.assign.Matcher; 022 023import org.microbean.attributes.Attributes; 024 025import static java.util.Objects.requireNonNull; 026 027/** 028 * A {@link Matcher} that tests an {@link Id} to see if it matches an {@link AttributedType}. 029 * 030 * @author <a href="https://about.me/lairdnelson" target="_top">Laird Nelson</a> 031 * 032 * @see #test(AttributedType, Id) 033 * 034 * @see BeanQualifiersMatcher 035 * 036 * @see BeanTypeMatcher 037 * 038 * @see Matcher 039 * 040 * @see Id 041 * 042 * @see Selectables#typesafeFiltering(Collection, Matcher) 043 */ 044// Convenience. Nothing actually refers to this class. See Selectables#typesafeFiltering(Collection, Matcher) 045public final class IdMatcher implements Matcher<AttributedType, Id> { 046 047 private final BeanTypeMatcher tm; 048 049 private final BeanQualifiersMatcher qm; 050 051 private final Matcher<AttributedType, Id> other; 052 053 /** 054 * Creates a new {@link IdMatcher}. 055 * 056 * @param tm a {@link BeanTypeMatcher}; must not be {@code null} 057 * 058 * @param qm a {@link BeanQualifiersMatcher}; must not be {@code null} 059 * 060 * @exception NullPointerException} if {@code tm} or {@code qm} is {@code null} 061 * 062 * @see #IdMatcher(BeanTypeMatcher, BeanQualifiersMatcher, Matcher) 063 */ 064 public IdMatcher(final BeanTypeMatcher tm, final BeanQualifiersMatcher qm) { 065 this(tm, qm, null); 066 } 067 068 /** 069 * Creates a new {@link IdMatcher}. 070 * 071 * @param tm a {@link BeanTypeMatcher}; must not be {@code null} 072 * 073 * @param qm a {@link BeanQualifiersMatcher}; must not be {@code null} 074 * 075 * @param other a supplementary {@link Matcher}; may be {@code null} 076 * 077 * @exception NullPointerException} if {@code tm} or {@code qm} is {@code null} 078 */ 079 public IdMatcher(final BeanTypeMatcher tm, 080 final BeanQualifiersMatcher qm, 081 final Matcher<AttributedType, Id> other) { 082 super(); 083 this.tm = requireNonNull(tm, "tm"); 084 this.qm = requireNonNull(qm, "qm"); 085 this.other = other == null ? (t, i) -> true : other; 086 } 087 088 /** 089 * Tests the supplied {@link Id} to see if it <dfn>matches</dfn> the supplied {@link AttributedType} and returns the 090 * result. 091 * 092 * <p>An {@link Id} <dfn>matches</dfn> an {@link AttributedType} if and only if all of the following are {@code 093 * true}:</p> 094 * 095 * <ol> 096 * 097 * <li>An invocation of the {@link BeanQualifiersMatcher#test(Collection, Collection)} method on the {@link 098 * BeanQualifiersMatcher} supplied at {@linkplain #IdMatcher(BeanTypeMatcher, BeanQualifiersMatcher, 099 * Matcher) construction time} supplied with the supplied {@linkplain AttributedType#attributes() 100 * <code>AttributedType</code>'s attributes} and the supplied {@linkplain Id#attributes() <code>Id</code>'s 101 * attributes} returns {@code true}</li> 102 * 103 * <li>An invocation of the {@link Matcher#test(Object, Object)} method on the supplementary {@link Matcher} supplied 104 * at {@linkplain #IdMatcher(BeanTypeMatcher, BeanQualifiersMatcher, Matcher) construction time} supplied with the 105 * supplied {@linkplain AttributedType} and the supplied {@link Id} returns {@code true}</li> 106 * 107 * <li>An invocation of this {@link IdMatcher}'s {@link #test(TypeMirror, Iterable)} method supplied with the supplied 108 * {@linkplain AttributedType#type() <code>AttributedType</code>'s type} and the supplied {@linkplain Id#attributes() 109 * <code>Id</code>'s attributes} returns {@code true}</li> 110 * 111 * </ol> 112 * 113 * @param t an {@link AttributedType}; must not be {@code null} 114 * 115 * @param id an {@link Id}; must not be {@code null} 116 * 117 * @return {@code true} if {@code id} matches {@code t}; {@code false} otherwise 118 * 119 * @exception NullPointerException if any argument is {@code null} 120 * 121 * @see BeanQualifiersMatcher#test(Collection, Collection) 122 * 123 * @see #test(TypeMirror, Iterable) 124 * 125 * @see BeanTypeMatcher#test(TypeMirror, TypeMirror) 126 */ 127 @Override // Matcher<AttributedType, Id> (BiPredicate<AttributedType, Id>) 128 public final boolean test(final AttributedType t, final Id id) { 129 return 130 this.test(t.type(), id.types()) && 131 this.qm.test(t.attributes(), id.attributes()) && 132 this.other.test(t, id); 133 } 134 135 /** 136 * Tests the supplied {@link Iterable} of {@link TypeMirror}s to see if at least one {@link TypeMirror} it yields 137 * <dfn>matches</dfn> the supplied {@link TypeMirror} and returns the result. 138 * 139 * <p>A {@link TypeMirror} <em>t</em> from the supplied {@link Iterable} <dfn>matches</dfn> the supplied {@code type} 140 * argument if an invocation of the {@link BeanTypeMatcher#test(TypeMirror, TypeMirror)} method invoked on the 141 * {@linkplain #IdMatcher(BeanTypeMatcher, BeanQualifiersMatcher, InterceptorBindingsMatcher) 142 * <code>BeanTypeManager</code> supplied at construction time} supplied with {@code type} and <em>t</em> returns 143 * {@code true}.</p> 144 * 145 * @param type a {@link TypeMirror} to test against; must not be {@code null} 146 * 147 * @param ts an {@link Iterable} of {@link TypeMirror}s; must not be {@code null} 148 * 149 * @return {@code true} if at least one {@link TypeMirror} from {@code ts} matches {@code t} 150 * 151 * @exception NullPointerException if any argument is {@code null} 152 * 153 * @see BeanTypeMatcher#test(TypeMirror, TypeMirror) 154 */ 155 private final boolean test(final TypeMirror type, final Iterable<? extends TypeMirror> ts) { 156 requireNonNull(type, "type"); 157 for (final TypeMirror t : ts) { 158 if (this.tm.test(type, t)) { 159 return true; 160 } 161 } 162 return false; 163 } 164 165}