001/* -*- mode: Java; c-basic-offset: 2; indent-tabs-mode: nil; coding: utf-8-unix -*- 002 * 003 * Copyright © 2025–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.construct.element; 015 016import java.lang.constant.Constable; 017import java.lang.constant.ConstantDesc; 018import java.lang.constant.DynamicConstantDesc; 019import java.lang.constant.MethodTypeDesc; 020 021import java.util.ArrayList; 022import java.util.Collection; 023import java.util.Collections; 024import java.util.LinkedHashMap; 025import java.util.List; 026import java.util.Map; 027import java.util.Map.Entry; 028import java.util.Optional; 029 030import java.util.function.Supplier; 031 032import javax.lang.model.element.AnnotationMirror; 033import javax.lang.model.element.AnnotationValue; 034import javax.lang.model.element.ExecutableElement; 035 036import org.microbean.construct.PrimordialDomain; 037 038import org.microbean.construct.type.UniversalType; 039 040import static java.lang.constant.ConstantDescs.BSM_INVOKE; 041 042import static java.lang.constant.DirectMethodHandleDesc.Kind.STATIC; 043 044import static java.lang.constant.MethodHandleDesc.ofMethod; 045 046import static java.util.Objects.requireNonNull; 047 048/** 049 * An {@link AnnotationMirror} implementation. 050 * 051 * @author <a href="https://about.me/lairdnelson" target="_top">Laird Nelson</a> 052 * 053 * @see AnnotationMirror 054 */ 055public final class UniversalAnnotation implements AnnotationMirror, Constable { 056 057 058 /* 059 * Instance fields. 060 */ 061 062 063 // Eventually this should become a lazy constant/stable value 064 // volatile not needed 065 private Supplier<? extends AnnotationMirror> delegateSupplier; 066 067 private final PrimordialDomain domain; 068 069 070 /* 071 * Constructors. 072 */ 073 074 075 /** 076 * Creates a new {@link UniversalAnnotation}. 077 * 078 * @param delegate an {@link AnnotationMirror} to which operations will be delegated; must not be {@code null} 079 * 080 * @param domain a {@link PrimordialDomain}; must not be {@code null} 081 * 082 * @exception NullPointerException if either argument is {@code null} 083 */ 084 @SuppressWarnings("try") 085 public UniversalAnnotation(final AnnotationMirror delegate, final PrimordialDomain domain) { 086 super(); 087 this.domain = requireNonNull(domain, "domain"); 088 final AnnotationMirror unwrappedDelegate = unwrap(requireNonNull(delegate, "delegate")); 089 if (unwrappedDelegate == delegate) { 090 // No unwrapping happened so do symbol completion early; most common case 091 this.delegateSupplier = () -> { 092 try (var lock = domain.lock()) { 093 unwrappedDelegate.getElementValues(); // should take care of any symbol completion 094 this.delegateSupplier = () -> unwrappedDelegate; 095 } 096 return unwrappedDelegate; 097 }; 098 } else { 099 assert delegate instanceof UniversalAnnotation; 100 // Symbol completion already happened 101 this.delegateSupplier = () -> unwrappedDelegate; 102 } 103 } 104 105 106 /* 107 * Instance methods. 108 */ 109 110 111 /** 112 * Returns the delegate to which operations are delegated. 113 * 114 * @return a non-{@code null} delegate 115 * 116 * @see AnnotationMirror 117 */ 118 public final AnnotationMirror delegate() { 119 final AnnotationMirror delegate = this.delegateSupplier.get(); 120 assert !(delegate instanceof UniversalAnnotation); 121 return delegate; 122 } 123 124 @Override // Constable 125 public final Optional<DynamicConstantDesc<UniversalAnnotation>> describeConstable() { 126 // TODO: this.delegate() is never going to be a Constable. It's debatable whether this class should implement 127 // Constable at all. 128 return (this.domain instanceof Constable c0 ? c0.describeConstable() : Optional.<ConstantDesc>empty()) 129 .flatMap(primordialDomainDesc -> (this.delegate() instanceof Constable c1 ? c1.describeConstable() : Optional.<ConstantDesc>empty()) 130 .map(delegateDesc -> DynamicConstantDesc.ofNamed(BSM_INVOKE, 131 this.getAnnotationType().asElement().getSimpleName().toString(), 132 UniversalAnnotation.class.describeConstable().orElseThrow(), 133 ofMethod(STATIC, 134 UniversalAnnotation.class.describeConstable().orElseThrow(), 135 "of", 136 MethodTypeDesc.of(UniversalAnnotation.class.describeConstable().orElseThrow(), 137 AnnotationMirror.class.describeConstable().orElseThrow(), 138 PrimordialDomain.class.describeConstable().orElseThrow())), 139 delegateDesc, 140 primordialDomainDesc))); 141 } 142 143 /** 144 * Returns the {@link PrimordialDomain} supplied at construction time. 145 * 146 * @return the non-{@code null} {@link PrimordialDomain} supplied at construction time 147 */ 148 public final PrimordialDomain domain() { 149 return this.domain; 150 } 151 152 @Override // Object 153 @SuppressWarnings("try") 154 public final boolean equals(final Object other) { 155 return this == other || switch (other) { 156 case null -> false; 157 // No lock needed; see 158 // https://github.com/openjdk/jdk/blob/jdk-26%2B25/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Attribute.java#L45 159 case UniversalAnnotation ar when this.getClass() == ar.getClass() -> this.delegate().equals(ar.delegate()); 160 default -> false; 161 }; 162 } 163 164 @Override // AnnotationMirror 165 @SuppressWarnings("try") 166 public final UniversalType getAnnotationType() { 167 // No lock needed; see 168 // https://github.com/openjdk/jdk/blob/jdk-26%2B25/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Attribute.java#L285-L288 169 return UniversalType.of(this.delegate().getAnnotationType(), this.domain()); 170 } 171 172 @Override // AnnotationMirror 173 @SuppressWarnings("try") 174 public final Map<? extends UniversalElement, ? extends UniversalAnnotationValue> getElementValues() { 175 final Map<UniversalElement, UniversalAnnotationValue> map = new LinkedHashMap<>(17); 176 final PrimordialDomain d = this.domain(); 177 // TODO: is this lock actually needed, given how delegateSupplier works? 178 // try (var lock = d.lock()) { 179 for (final Entry<? extends ExecutableElement, ? extends AnnotationValue> e : this.delegate().getElementValues().entrySet()) { 180 map.put(UniversalElement.of(e.getKey(), d), UniversalAnnotationValue.of(e.getValue(), d)); 181 } 182 // } 183 return Collections.unmodifiableMap(map); 184 } 185 186 @Override // AnnotationMirror 187 public final int hashCode() { 188 // No lock needed; see 189 // https://github.com/openjdk/jdk/blob/jdk-26%2B25/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Attribute.java#L45 190 return this.delegate().hashCode(); 191 } 192 193 194 /* 195 * Static methods. 196 */ 197 198 199 /** 200 * Returns a non-{@code null} {@link UniversalAnnotation} that is either the supplied {@link AnnotationMirror} (if it 201 * itself is an {@link UniversalAnnotation}) or one that wraps it. 202 * 203 * @param a an {@link AnnotationMirror}; must not be {@code null} 204 * 205 * @param d a {@link PrimordialDomain}; must not be {@code null} 206 * 207 * @return a non-{@code null} {@link UniversalAnnotation} 208 * 209 * @exception NullPointerException if either argument is {@code null} 210 * 211 * @see #UniversalAnnotation(AnnotationMirror, PrimordialDomain) 212 */ 213 public static final UniversalAnnotation of(final AnnotationMirror a, final PrimordialDomain d) { 214 return a instanceof UniversalAnnotation ar ? ar : new UniversalAnnotation(a, d); 215 } 216 217 /** 218 * Returns a non-{@code null}, immutable {@link List} of {@link UniversalAnnotation}s whose elements wrap the supplied 219 * {@link List}'s elements. 220 * 221 * @param as a {@link Collection} of {@link AnnotationMirror}s; must not be {@code null} 222 * 223 * @param domain a {@link PrimordialDomain}; must not be {@code null} 224 * 225 * @return a non-{@code null}, immutable {@link List} of {@link UniversalAnnotation}s 226 * 227 * @exception NullPointerException if either argument is {@code null} 228 */ 229 public static final List<UniversalAnnotation> of(final Collection<? extends AnnotationMirror> as, 230 final PrimordialDomain domain) { 231 if (as.isEmpty()) { 232 return List.of(); 233 } 234 final List<UniversalAnnotation> newAs = new ArrayList<>(as.size()); 235 for (final AnnotationMirror a : as) { 236 newAs.add(UniversalAnnotation.of(a, domain)); 237 } 238 return Collections.unmodifiableList(newAs); 239 } 240 241 /** 242 * <dfn>Unwraps</dfn> the supplied {@link AnnotationMirror} implementation such that the returned value is not an 243 * instance of {@link UniversalAnnotation}. 244 * 245 * @param a an {@link AnnotationMirror}; may be {@code null} 246 * 247 * @return an {@link AnnotationMirror} that is guaranteed not to be an instance of {@link UniversalAnnotation} 248 * 249 * @see #delegate() 250 */ 251 public static final AnnotationMirror unwrap(AnnotationMirror a) { 252 while (a instanceof UniversalAnnotation ua) { 253 a = ua.delegate(); 254 } 255 return a; 256 } 257 258 259}