001/* -*- mode: Java; c-basic-offset: 2; indent-tabs-mode: nil; coding: utf-8-unix -*- 002 * 003 * Copyright © 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.assign; 015 016import java.lang.constant.Constable; 017import java.lang.constant.ConstantDesc; 018import java.lang.constant.DynamicConstantDesc; 019 020import java.util.ArrayList; 021import java.util.Collection; 022import java.util.List; 023import java.util.Optional; 024 025import java.util.function.Predicate; 026 027import javax.lang.model.element.AnnotationMirror; 028import javax.lang.model.element.ExecutableElement; 029import javax.lang.model.element.QualifiedNameable; 030import javax.lang.model.element.TypeElement; 031 032import javax.lang.model.type.ArrayType; 033 034import org.microbean.construct.Domain; 035 036import org.microbean.construct.element.AnnotationMirrors; 037import org.microbean.construct.element.SyntheticAnnotationMirror; 038import org.microbean.construct.element.SyntheticAnnotationTypeElement; 039import org.microbean.construct.element.UniversalElement; 040 041import static java.lang.constant.ConstantDescs.BSM_INVOKE; 042import static java.lang.constant.ConstantDescs.NULL; 043 044import static java.lang.constant.MethodHandleDesc.ofConstructor; 045 046import static java.util.Collections.unmodifiableList; 047 048import static java.util.Objects.requireNonNull; 049 050import static javax.lang.model.element.ElementKind.ANNOTATION_TYPE; 051 052/** 053 * A utility class for working with <dfn>qualifiers</dfn>. 054 * 055 * <p>This class is currently not used by other classes in this package. It may be useful in a variety of dependency 056 * injection systems.</p> 057 * 058 * @author <a href="https://about.me/lairdnelson" target="_top">Laird Nelson</a> 059 */ 060public class Qualifiers implements Constable { 061 062 063 /* 064 * Instance fields. 065 */ 066 067 068 private final Predicate<? super ExecutableElement> annotationElementInclusionPredicate; 069 070 private final AnnotationMirror metaQualifier; 071 072 private final List<AnnotationMirror> metaQualifiers; 073 074 075 /* 076 * Constructors. 077 */ 078 079 080 /** 081 * Creates a new {@link Qualifiers}. 082 * 083 * @param domain a non-{@code null} {@link Domain} 084 * 085 * @exception NullPointerException if {@code domain} is {@code null} 086 * 087 * @see #Qualifiers(Domain, AnnotationMirror, Predicate) 088 */ 089 public Qualifiers(final Domain domain) { 090 this(domain, null, null); 091 } 092 093 /** 094 * Creates a new {@link Qualifiers}. 095 * 096 * @param metaQualifier a non-{@code null} {@link AnnotationMirror} to serve as the {@linkplain #metaQualifier() meta-qualifier} 097 * 098 * @exception NullPointerException if {@code metaQualifier} is {@code null} 099 * 100 * @see #Qualifiers(Domain, AnnotationMirror, Predicate) 101 */ 102 public Qualifiers(final AnnotationMirror metaQualifier) { 103 this(null, requireNonNull(metaQualifier, "metaQualifier"), null); 104 } 105 106 /** 107 * Creates a new {@link Qualifiers}. 108 * 109 * @param metaQualifier a non-{@code null} {@link AnnotationMirror} to serve as the {@linkplain #metaQualifier() meta-qualifier} 110 * 111 * @param annotationElementInclusionPredicate a {@link Predicate} that returns {@code true} if a given {@link 112 * ExecutableElement}, representing an annotation element, is to be included in the computation; may be {@code null} 113 * in which case it is as if {@code e -> true} were supplied instead 114 * 115 * @exception NullPointerException if {@code metaQualifier} is {@code null} 116 * 117 * @see #Qualifiers(Domain, AnnotationMirror, Predicate) 118 */ 119 public Qualifiers(final AnnotationMirror metaQualifier, 120 final Predicate<? super ExecutableElement> annotationElementInclusionPredicate) { 121 this(null, requireNonNull(metaQualifier, "metaQualifier"), annotationElementInclusionPredicate); 122 } 123 124 /** 125 * Creates a new {@link Qualifiers}. 126 * 127 * @param domain a {@link Domain}; if {@code null}, then {@code metaQualifier} must not be {@code null} 128 * 129 * @param metaQualifier an {@link AnnotationMirror} to serve as the {@linkplain #metaQualifier() meta-qualifier}; may 130 * (commonly) be {@code null} in which case a synthetic meta-qualifier will be used instead; must not be {@code null} 131 * if {@code domain} is {@code null} 132 * 133 * @param annotationElementInclusionPredicate a {@link Predicate} that returns {@code true} if a given {@link 134 * ExecutableElement}, representing an annotation element, is to be included in the computation; may be {@code null} 135 * in which case it is as if {@code e -> true} were supplied instead 136 * 137 * @exception NullPointerException if {@code domain} is {@code null} in certain situations 138 */ 139 public Qualifiers(final Domain domain, 140 final AnnotationMirror metaQualifier, 141 final Predicate<? super ExecutableElement> annotationElementInclusionPredicate) { 142 super(); 143 if (metaQualifier == null) { 144 final List<? extends AnnotationMirror> as = domain.typeElement("java.lang.annotation.Documented").getAnnotationMirrors(); 145 assert as.size() == 3; // @Documented, @Retention, @Target, in that order, all annotated in turn with each other 146 this.metaQualifier = 147 new SyntheticAnnotationMirror(new SyntheticAnnotationTypeElement(List.of(as.get(0), // @Documented 148 as.get(1), // @Retention(RUNTIME) (happens fortuitously to be RUNTIME) 149 as.get(2)), // @Target(ANNOTATION_TYPE) (happens fortuitously to be ANNOTATION_TYPE) 150 "Qualifier")); 151 } else { 152 this.metaQualifier = metaQualifier; 153 } 154 this.annotationElementInclusionPredicate = annotationElementInclusionPredicate == null ? Qualifiers::returnTrue : annotationElementInclusionPredicate; 155 this.metaQualifiers = List.of(this.metaQualifier); 156 } 157 158 159 /* 160 * Instance methods. 161 */ 162 163 // The contains* methods below do not apply only to qualifiers. That makes them smell a little funky here but it's not 164 // really worth breaking out, I don't think. The whole annotationElementInclusionPredicate thing may belong in 165 // microbean-bean, but it really doesn't *have* to be bean-qualifiers-specific. 166 167 /** 168 * Returns {@code true} if and only if the supplied {@link Collection} of {@link AnnotationMirror}s contains an {@link 169 * AnnotationMirror} that is {@linkplain AnnotationMirrors#sameAnnotation(AnnotationMirror, AnnotationMirror, 170 * Predicate) the same} as the supplied {@link AnnotationMirror}. 171 * 172 * @param c a non-{@code null} {@link Collection} of {@link AnnotationMirror}s 173 * 174 * @param a a non-{@code null} {@link AnnotationMirror} 175 * 176 * @return {@code true} if and only if the supplied {@link Collection} of {@link AnnotationMirror}s contains an {@link 177 * AnnotationMirror} that is {@linkplain AnnotationMirrors#sameAnnotation(AnnotationMirror, AnnotationMirror, 178 * Predicate) the same} as the supplied {@link AnnotationMirror} 179 * 180 * @exception NullPointerException if {@code c} or {@code a} is {@code null} 181 * 182 * @see AnnotationMirrors#sameAnnotation(AnnotationMirror, AnnotationMirror, Predicate) 183 * 184 * @see AnnotationMirrors#contains(Collection, AnnotationMirror, Predicate) 185 * 186 * @see #Qualifiers(Domain, AnnotationMirror, Predicate) 187 */ 188 public final boolean contains(final Collection<? extends AnnotationMirror> c, final AnnotationMirror a) { 189 return AnnotationMirrors.contains(c, a, this.annotationElementInclusionPredicate); 190 } 191 192 /** 193 * Returns {@code true} if and only if {@code c0} contains all {@linkplain 194 * AnnotationMirrors#sameAnnotation(AnnotationMirror, AnnotationMirror, Predicate) the same} {@link AnnotationMirror}s 195 * as are found in {@code c1}, 196 * 197 * @param c0 a non-{@code null} {@link Collection} of {@link AnnotationMirror}s 198 * 199 * @param c1 a non-{@code null} {@link Collection} of {@link AnnotationMirror}s 200 * 201 * @return {@code true} if and only if {@code c0} contains all {@linkplain 202 * AnnotationMirrors#sameAnnotation(AnnotationMirror, AnnotationMirror, Predicate) the same} {@link AnnotationMirror}s 203 * as are found in {@code c1} 204 * 205 * @exception NullPointerException if either {@code c0} or {@code c1} is {@code null} 206 * 207 * @see AnnotationMirrors#sameAnnotation(AnnotationMirror, AnnotationMirror, Predicate) 208 * 209 * @see AnnotationMirrors#containsAll(Collection, Collection, Predicate) 210 * 211 * @see #Qualifiers(Domain, AnnotationMirror, Predicate) 212 */ 213 public final boolean containsAll(final Collection<? extends AnnotationMirror> c0, 214 final Collection<? extends AnnotationMirror> c1) { 215 return AnnotationMirrors.containsAll(c0, c1, this.annotationElementInclusionPredicate); 216 } 217 218 /** 219 * Returns a non-{@code null}, determinate {@link Optional} housing a {@link ConstantDesc} describing this {@link 220 * Qualifiers}, or an {@linkplain Optional#isEmpty() empty} {@link Optional} if it cannot be described. 221 * 222 * @return a non-{@code null}, determinate {@link Optional} housing a {@link ConstantDesc} describing this {@link 223 * Qualifiers}, or an {@linkplain Optional#isEmpty() empty} {@link Optional} if it cannot be described 224 */ 225 @Override // Constable 226 public Optional<? extends ConstantDesc> describeConstable() { 227 return (this.metaQualifier instanceof Constable c ? c.describeConstable() : Optional.<ConstantDesc>empty()) 228 .flatMap(mqDesc -> (this.annotationElementInclusionPredicate == null ? 229 Optional.of(NULL) : 230 this.annotationElementInclusionPredicate instanceof Constable c ? 231 c.describeConstable() : 232 Optional.<ConstantDesc>empty()) 233 .map(pDesc -> DynamicConstantDesc.of(BSM_INVOKE, 234 ofConstructor(this.getClass().describeConstable().orElseThrow(), 235 Domain.class.describeConstable().orElseThrow(), 236 AnnotationMirror.class.describeConstable().orElseThrow(), 237 Predicate.class.describeConstable().orElseThrow()), 238 NULL, 239 mqDesc, 240 pDesc))); 241 } 242 243 /** 244 * Returns a non-{@code null}, determinate {@link AnnotationMirror} that can be used to designate (meta-annotate) 245 * other annotations as <dfn>qualifiers</dfn>. 246 * 247 * @return a non-{@code null}, determinate {@link AnnotationMirror} that can be used to designate (meta-annotate) 248 * other annotations as <dfn>qualifiers</dfn> 249 */ 250 public final AnnotationMirror metaQualifier() { 251 return this.metaQualifier; 252 } 253 254 /** 255 * Returns {@code true} if and only if the supplied {@link AnnotationMirror} {@linkplain 256 * #sameAnnotation(AnnotationMirror, AnnotationMirror) is the same annotation as} the supplied {@link 257 * AnnotationMirror}. 258 * 259 * @param a a non-{@code null} {@link AnnotationMirror} 260 * 261 * @return {@code true} if and only if if and only if the supplied {@link AnnotationMirror} {@linkplain 262 * #sameAnnotation(AnnotationMirror, AnnotationMirror) is the same annotation as} the supplied {@link 263 * AnnotationMirror} 264 * 265 * @exception NullPointerException if {@code a} is {@code null} 266 * 267 * @see #sameAnnotation(AnnotationMirror, AnnotationMirror) 268 */ 269 public final boolean metaQualifier(final AnnotationMirror a) { 270 return this.sameAnnotation(this.metaQualifier(), a); 271 } 272 273 /** 274 * Returns a non-{@code null}, determinate, immutable {@link List} whose sole element is the {@linkplain 275 * #metaQualifier() meta-qualifier} annotation. 276 * 277 * @return a non-{@code null}, determinate, immutable {@link List} whose sole element is the {@linkplain 278 * #metaQualifier() meta-qualifier} annotation 279 */ 280 public final List<AnnotationMirror> metaQualifiers() { 281 return this.metaQualifiers; 282 } 283 284 /** 285 * Returns {@code true} if and only if the supplied {@link AnnotationMirror} has an {@linkplain 286 * AnnotationMirror#getAnnotationType() annotation type} declared by a {@link TypeElement} that is {@linkplain 287 * javax.lang.model.AnnotatedConstruct#getAnnotationMirrors() annotated with} at least one annotation {@linkplain 288 * #metaQualifier(AnnotationMirror) deemed to be the meta-qualifier}. 289 * 290 * @param a a non-{@code null} {@link AnnotationMirror} 291 * 292 * @return {@code true} if and only if the supplied {@link AnnotationMirror} has an {@linkplain 293 * AnnotationMirror#getAnnotationType() annotation type} declared by a {@link TypeElement} that is {@linkplain 294 * javax.lang.model.AnnotatedConstruct#getAnnotationMirrors() annotated with} at least one annotation {@linkplain 295 * #metaQualifier(AnnotationMirror) deemed to be the meta-qualifier} 296 * 297 * @exception NullPointerException if {@code a} is {@code null} 298 */ 299 public final boolean qualifier(final AnnotationMirror a) { 300 if (!this.metaQualifier(a)) { 301 final TypeElement annotationInterface = (TypeElement)a.getAnnotationType().asElement(); 302 if (annotationInterface.getKind() == ANNOTATION_TYPE) { 303 for (final AnnotationMirror ma : annotationInterface.getAnnotationMirrors()) { 304 if (this.metaQualifier(ma)) { 305 return true; 306 } 307 } 308 } 309 } 310 return false; 311 } 312 313 /** 314 * Returns a non-{@code null}, determinate, immutable {@link List} of {@link AnnotationMirror} instances drawn from 315 * the supplied {@link Collection} that were {@linkplain #qualifier(AnnotationMirror) deemed to be qualifiers}. 316 * 317 * @param as a non-{@code null} {@link Collection} of {@link AnnotationMirror}s 318 * 319 * @return a non-{@code null}, determinate, immutable {@link List} of {@link AnnotationMirror} instances drawn from 320 * the supplied {@link Collection} that were {@linkplain #qualifier(AnnotationMirror) deemed to be qualifiers} 321 * 322 * @exception NullPointerException if {@code as} is {@code null} 323 * 324 * @see #qualifier(AnnotationMirror) 325 */ 326 public List<AnnotationMirror> qualifiers(final Collection<? extends AnnotationMirror> as) { 327 if (as.isEmpty()) { 328 return List.of(); 329 } 330 final List<AnnotationMirror> l = new ArrayList<>(as.size()); 331 for (final AnnotationMirror a : as) { 332 if (this.qualifier(a)) { 333 l.add(a); 334 } 335 } 336 return l.isEmpty() ? List.of() : unmodifiableList(l); 337 } 338 339 /** 340 * Determines whether the two {@link AnnotationMirror}s represent the same (underlying, otherwise opaque) annotation. 341 * 342 * @param am0 an {@link AnnotationMirror}; may be {@code null} 343 * 344 * @param am1 an {@link AnnotationMirror}; may be {@code null} 345 * 346 * @return {@code true} if the supplied {@link AnnotationMirror}s represent the same (underlying, otherwise opaque) 347 * annotation; {@code false} otherwise 348 * 349 * @see AnnotationMirrors#sameAnnotation(AnnotationMirror, AnnotatonMirror, Predicate) 350 * 351 * @see #Qualifiers(Domain, AnnotationMirror, Predicate) 352 */ 353 public final boolean sameAnnotation(final AnnotationMirror am0, final AnnotationMirror am1) { 354 return AnnotationMirrors.sameAnnotation(am0, am1, this.annotationElementInclusionPredicate); 355 } 356 357 private static final <X> boolean returnTrue(final X ignored) { 358 return true; 359 } 360 361}