001/* -*- mode: Java; c-basic-offset: 2; indent-tabs-mode: nil; coding: utf-8-unix -*-
002 *
003 * Copyright © 2024–2026 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 javax.lang.model.AnnotatedConstruct;
020
021import javax.lang.model.element.AnnotationMirror;
022import javax.lang.model.element.Element;
023
024import javax.lang.model.type.ArrayType;
025import javax.lang.model.type.DeclaredType;
026import javax.lang.model.type.TypeMirror;
027import javax.lang.model.type.TypeVariable;
028
029import org.microbean.assign.Annotated;
030import org.microbean.assign.Matcher;
031
032import static java.util.Objects.requireNonNull;
033
034/**
035 * A {@link Matcher} that tests an {@link Id} to see if it <dfn>matches</dfn> an {@link AnnotatedConstruct}.
036 *
037 * @author <a href="https://about.me/lairdnelson" target="_top">Laird Nelson</a>
038 *
039 * @see #test(Annotated, Id)
040 *
041 * @see BeanQualifiersMatcher
042 *
043 * @see BeanTypeMatcher
044 *
045 * @see Matcher
046 *
047 * @see Id
048 *
049 * @see Beans#typesafeFilteringSelectable(Collection, Matcher)
050 */
051// Convenience. Nothing actually refers to this class. See Selectables#typesafeFiltering(Collection, Matcher)
052public final class IdMatcher implements Matcher<Annotated<? extends AnnotatedConstruct>, Id> {
053
054  private final BeanTypeMatcher tm;
055
056  private final BeanQualifiersMatcher qm;
057
058  private final Matcher<? super Annotated<? extends AnnotatedConstruct>, ? super Id> other;
059
060  /**
061   * Creates a new {@link IdMatcher}.
062   *
063   * @param tm a {@link BeanTypeMatcher}; must not be {@code null}
064   *
065   * @param qm a {@link BeanQualifiersMatcher}; must not be {@code null}
066   *
067   * @exception NullPointerException} if {@code tm} or {@code qm} is {@code null}
068   *
069   * @see #IdMatcher(BeanTypeMatcher, BeanQualifiersMatcher, Matcher)
070   */
071  public IdMatcher(final BeanTypeMatcher tm, final BeanQualifiersMatcher qm) {
072    this(tm, qm, null);
073  }
074
075  /**
076   * Creates a new {@link IdMatcher}.
077   *
078   * @param tm a {@link BeanTypeMatcher}; must not be {@code null}
079   *
080   * @param qm a {@link BeanQualifiersMatcher}; must not be {@code null}
081   *
082   * @param other a supplementary {@link Matcher}; may be {@code null}
083   *
084   * @exception NullPointerException} if {@code tm} or {@code qm} is {@code null}
085   */
086  public IdMatcher(final BeanTypeMatcher tm,
087                   final BeanQualifiersMatcher qm,
088                   final Matcher<? super Annotated<? extends AnnotatedConstruct>, ? super Id> other) {
089    super();
090    this.tm = requireNonNull(tm, "tm");
091    this.qm = requireNonNull(qm, "qm");
092    this.other = other == null ? (ac, id) -> true : other;
093  }
094
095  /*
096   * Tests the supplied {@link Id} to see if it <dfn>matches</dfn> the supplied {@link AnnotatedConstruct} and returns the
097   * result.
098   *
099   * @param ac an {@link AnnotatedConstruct}; must not be {@code null}
100   *
101   * @param id an {@link Id}; must not be {@code null}
102   *
103   * @return {@code true} if {@code id} matches {@code ac}; {@code false} otherwise
104   *
105   * @exception NullPointerException if any argument is {@code null}
106   *
107   * @see #test(Annotated, Id)
108   *
109   * @see Annotated#of(AnnotatedConstruct, Predicate)
110   *
111   * @see BeanQualifiersMatcher#test(Collection, Collection)
112   *
113   * @see #test(TypeMirror, Iterable)
114   *
115   * @see BeanTypeMatcher#test(TypeMirror, TypeMirror)
116   */
117  // @Deprecated(forRemoval = true) // Annotated.of(AnnotatedConstruct) exists
118  // public final boolean test(final AnnotatedConstruct ac, final Id id) {
119  //   return this.test(Annotated.of(ac, this.qm.annotationElementInclusionPredicate()), id);
120  // }
121
122  /**
123   * Tests the supplied {@link Id} to see if it <dfn>matches</dfn> the supplied {@link Annotated Annotated&lt;? extends
124   * AnnotatedConstruct&gt;} and returns the result.
125   *
126   * @param aac an {@link Annotated Annotated&lt;? extends AnnotatedConstruct&gt;}; must not be {@code null}
127   *
128   * @param id an {@link Id}; must not be {@code null}
129   *
130   * @return {@code true} if {@code id} matches {@code aac}; {@code false} otherwise
131   *
132   * @exception NullPointerException if any argument is {@code null}
133   *
134   * @see Annotated#of(AnnotatedConstruct, Predicate)
135   *
136   * @see BeanQualifiersMatcher#test(Collection, Collection)
137   *
138   * @see #test(TypeMirror, Iterable)
139   *
140   * @see BeanTypeMatcher#test(TypeMirror, TypeMirror)
141   */
142  @Override // Matcher<Annotated<? extends AnnotatedConstruct>, Id>
143  public final boolean test(final Annotated<? extends AnnotatedConstruct> aac, final Id id) {
144    final AnnotatedConstruct ac = aac.annotated();
145    return
146      this.test(ac instanceof Element e ? e.asType() : (TypeMirror)ac, id.types()) &&
147      this.qm.test(aac.annotations(), id.annotations()) &&
148      this.other.test(aac, id);
149  }
150
151  /**
152   * Tests the supplied {@link Iterable} of {@link TypeMirror}s to see if at least one {@link TypeMirror} it yields
153   * <dfn>matches</dfn> the supplied {@link TypeMirror} and returns the result.
154   *
155   * <p>A {@link TypeMirror} <em>t</em> from the supplied {@link Iterable} <dfn>matches</dfn> the supplied {@code type}
156   * argument if an invocation of the {@link BeanTypeMatcher#test(TypeMirror, TypeMirror)} method invoked on the
157   * {@linkplain #IdMatcher(BeanTypeMatcher, BeanQualifiersMatcher, InterceptorBindingsMatcher)
158   * <code>BeanTypeManager</code> supplied at construction time} supplied with {@code type} and <em>t</em> returns
159   * {@code true}.</p>
160   *
161   * @param type a {@link TypeMirror} to test against; must not be {@code null}
162   *
163   * @param candidatess an {@link Iterable} of {@link TypeMirror}s; must not be {@code null}
164   *
165   * @return {@code true} if at least one {@link TypeMirror} from {@code candidates} matches {@code type}
166   *
167   * @exception NullPointerException if any argument is {@code null}
168   *
169   * @see BeanTypeMatcher#test(TypeMirror, TypeMirror)
170   */
171  private final boolean test(final TypeMirror type, final Iterable<? extends TypeMirror> candidates) {
172    requireNonNull(type, "type");
173    for (final TypeMirror candidate : candidates) {
174      if (this.tm.test(type, candidate)) {
175        return true;
176      }
177    }
178    return false;
179  }
180
181}