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.scopelet; 015 016import java.lang.constant.Constable; 017import java.lang.constant.ConstantDesc; 018import java.lang.constant.DynamicConstantDesc; 019 020import java.util.ArrayList; 021import java.util.List; 022import java.util.Map; 023import java.util.Optional; 024 025import javax.lang.model.element.AnnotationMirror; 026import javax.lang.model.element.Element; 027import javax.lang.model.element.Name; 028import javax.lang.model.element.VariableElement; 029 030import org.microbean.construct.Domain; 031 032import org.microbean.construct.element.SyntheticAnnotationMirror; 033import org.microbean.construct.element.SyntheticAnnotationTypeElement; 034import org.microbean.construct.element.SyntheticAnnotationValue; 035 036import static java.lang.constant.ConstantDescs.BSM_INVOKE; 037 038import static java.lang.constant.MethodHandleDesc.ofConstructor; 039 040import static java.util.Objects.requireNonNull; 041 042import static javax.lang.model.element.ElementKind.ENUM_CONSTANT; 043 044import static org.microbean.construct.element.AnnotationMirrors.sameAnnotation; 045 046/** 047 * A utility class for working with qualifiers. 048 * 049 * @author <a href="https://about.me/lairdnelson" target="_top">Laird Nelson</a> 050 */ 051public class Qualifiers implements Constable { 052 053 private final org.microbean.assign.Qualifiers baseQualifiers; 054 055 private final AnnotationMirror primordialMetaQualifier; 056 057 private final List<AnnotationMirror> primordialMetaQualifiers; 058 059 /** 060 * Creates a new {@link Qualifiers}. 061 * 062 * @param domain a non-{@code null} {@link Domain} 063 * 064 * @param baseQualifiers a non-{@code null} {@link org.microbean.assign.Qualifiers} 065 * 066 * @see #Qualifiers(Domain, org.microbean.assign.Qualifiers, AnnotationMirror) 067 */ 068 public Qualifiers(final Domain domain, 069 final org.microbean.assign.Qualifiers baseQualifiers) { 070 this(domain, baseQualifiers, null); 071 } 072 073 /** 074 * Creates a new {@link Qualifiers}. 075 * 076 * @param domain a {@link Domain}; may be {@code null} in which case {@code primordialMetaQualifier} must not be 077 * {@code null} 078 * 079 * @param baseQualifiers a non-{@code null} {@link org.microbean.assign.Qualifiers} 080 * 081 * @param primordialMetaQualifier an {@link AnnotationMirror} identifying the <dfn>primordial meta-qualifier</dfn>; 082 * may be {@code null} in which case {@code domain} must be non-{@code null} 083 * 084 * @exception NullPointerException if {@code baseQualifiers} is {@code null}, or if {@code domain} is {@code null} in 085 * certain circumstances, or if {@code primordialMetaQualifier} is {@code null} in certain circumstances 086 */ 087 public Qualifiers(final Domain domain, 088 final org.microbean.assign.Qualifiers baseQualifiers, 089 final AnnotationMirror primordialMetaQualifier) { 090 super(); 091 this.baseQualifiers = requireNonNull(baseQualifiers, "baseQualifiers"); 092 if (primordialMetaQualifier == null) { 093 final List<? extends AnnotationMirror> as = domain.typeElement("java.lang.annotation.Documented").getAnnotationMirrors(); 094 assert as.size() == 3; // @Documented, @Retention, @Target, in that order, all annotated in turn with each other 095 final List<SyntheticAnnotationValue> savs = new ArrayList<>(4); 096 for (final Element e : domain.typeElement("java.lang.annotation.ElementType").getEnclosedElements()) { 097 if (e.getKind() == ENUM_CONSTANT && e instanceof VariableElement ve) { 098 final Name n = e.getSimpleName(); 099 if (n.contentEquals("TYPE") || n.contentEquals("METHOD") || n.contentEquals("FIELD") || n.contentEquals("PARAMETER")) { 100 savs.add(new SyntheticAnnotationValue(ve)); 101 } 102 } 103 } 104 final AnnotationMirror targetAnnotation = 105 new SyntheticAnnotationMirror(domain.typeElement("java.lang.annotation.Target"), Map.of("value", savs)); 106 final List<AnnotationMirror> metaAnnotations = 107 List.of(baseQualifiers.metaQualifier(), 108 as.get(1), // @Retention 109 targetAnnotation, // @Target 110 as.get(0)); // @Documented 111 this.primordialMetaQualifier = 112 primordialMetaQualifier == null ? 113 new SyntheticAnnotationMirror(new SyntheticAnnotationTypeElement(metaAnnotations, "Primordial")) : 114 primordialMetaQualifier; 115 } else { 116 this.primordialMetaQualifier = primordialMetaQualifier; 117 } 118 this.primordialMetaQualifiers = List.of(this.primordialMetaQualifier); 119 } 120 121 @Override // Constable 122 public Optional<? extends ConstantDesc> describeConstable() { 123 return this.baseQualifiers instanceof Constable ? ((Constable)this.baseQualifiers).describeConstable() : Optional.<ConstantDesc>empty() 124 .flatMap(baseQualifiersDesc -> this.primordialMetaQualifier instanceof Constable c ? c.describeConstable() : Optional.<ConstantDesc>empty() 125 .map(primordialQualifierDesc -> DynamicConstantDesc.of(BSM_INVOKE, 126 ofConstructor(this.getClass().describeConstable().orElseThrow(), 127 org.microbean.assign.Qualifiers.class.describeConstable().orElseThrow(), 128 AnnotationMirror.class.describeConstable().orElseThrow()), 129 baseQualifiersDesc, 130 primordialQualifierDesc))); 131 } 132 133 /** 134 * Returns the non-{@code null}, determinate {@link AnnotationMirror} representing the <dfn>primordial 135 * meta-qualifier</dfn>. 136 * 137 * @return the non-{@code null}, determinate {@link AnnotationMirror} representing the <dfn>primordial 138 * meta-qualifier</dfn> 139 */ 140 public final AnnotationMirror primordialMetaQualifier() { 141 return this.primordialMetaQualifier; 142 } 143 144 /** 145 * Returns {@code true} if and only if the supplied {@link AnnotationMirror} is {@linkplain 146 * org.microbean.assign.Qualifiers#sameAnnotation(AnnotationMirror, AnnotationMirror) the same} as the {@linkplain 147 * #primordialMetaQualifier() <dfn>primordial meta-qualifier</dfn>}. 148 * 149 * @param a a non-{@code null} {@link AnnotationMirror} 150 * 151 * @return {@code true} if and only if the supplied {@link AnnotationMirror} is {@linkplain 152 * org.microbean.assign.Qualifiers#sameAnnotation(AnnotationMirror, AnnotationMirror) the same} as the {@linkplain 153 * #primordialMetaQualifier() <dfn>primordial meta-qualifier</dfn>} 154 * 155 * @exception NullPointerException if {@code a} is {@code null} 156 * 157 * @see #primordialMetaQualifier() 158 * 159 * @see org.microbean.assign.Qualifiers#sameAnnotation(AnnotationMirror, AnnotationMirror) 160 */ 161 public final boolean primordialMetaQualifier(final AnnotationMirror a) { 162 return this.baseQualifiers.sameAnnotation(this.primordialMetaQualifier, a); 163 } 164 165 /** 166 * Returns a non-{@code null}, determinate, immutable {@link List} housing only the {@linkplain 167 * #primordialMetaQualifier() <dfn>primordial meta-qualifier</dfn>}. 168 * 169 * @return a non-{@code null}, determinate, immutable {@link List} housing only the {@linkplain 170 * #primordialMetaQualifier() <dfn>primordial meta-qualifier</dfn>} 171 */ 172 public final List<AnnotationMirror> primordialMetaQualifiers() { 173 return this.primordialMetaQualifiers; 174 } 175 176}