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