001/* -*- mode: Java; c-basic-offset: 2; indent-tabs-mode: nil; coding: utf-8-unix -*- 002 * 003 * Copyright © 2025 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.Objects; 030import java.util.Optional; 031import java.util.Set; 032 033import java.util.function.Supplier; 034 035import javax.lang.model.element.AnnotationMirror; 036import javax.lang.model.element.AnnotationValue; 037import javax.lang.model.element.ExecutableElement; 038 039import org.microbean.construct.PrimordialDomain; 040import org.microbean.construct.UniversalConstruct; 041 042import org.microbean.construct.type.UniversalType; 043 044import static java.lang.constant.ConstantDescs.BSM_INVOKE; 045 046import static java.lang.constant.DirectMethodHandleDesc.Kind.STATIC; 047 048import static java.lang.constant.MethodHandleDesc.ofMethod; 049 050import static java.util.Objects.requireNonNull; 051 052/** 053 * An {@link AnnotationMirror} implementation. 054 * 055 * @author <a href="https://about.me/lairdnelson" target="_top">Laird Nelson</a> 056 * 057 * @see AnnotationMirror 058 */ 059public final class UniversalAnnotation implements AnnotationMirror, Constable { 060 061 062 /* 063 * Instance fields. 064 */ 065 066 067 // Eventually this should become a lazy constant/stable value 068 // volatile not needed 069 private Supplier<? extends AnnotationMirror> delegateSupplier; 070 071 private final PrimordialDomain domain; 072 073 074 /* 075 * Constructors. 076 */ 077 078 079 /** 080 * Creates a new {@link UniversalAnnotation}. 081 * 082 * @param delegate an {@link AnnotationMirror} to which operations will be delegated; must not be {@code null} 083 * 084 * @param domain a {@link PrimordialDomain}; must not be {@code null} 085 * 086 * @exception NullPointerException if either argument is {@code null} 087 */ 088 @SuppressWarnings("try") 089 public UniversalAnnotation(final AnnotationMirror delegate, final PrimordialDomain domain) { 090 super(); 091 this.domain = requireNonNull(domain, "domain"); 092 final AnnotationMirror unwrappedDelegate = unwrap(requireNonNull(delegate, "delegate")); 093 if (unwrappedDelegate == delegate) { 094 // No unwrapping happened so do symbol completion early; most common case 095 this.delegateSupplier = () -> { 096 try (var lock = domain.lock()) { 097 unwrappedDelegate.getElementValues(); // should take care of any symbol completion 098 this.delegateSupplier = () -> unwrappedDelegate; 099 } 100 return unwrappedDelegate; 101 }; 102 } else { 103 assert delegate instanceof UniversalAnnotation; 104 // Symbol completion already happened 105 this.delegateSupplier = () -> unwrappedDelegate; 106 } 107 } 108 109 110 /* 111 * Instance methods. 112 */ 113 114 115 /** 116 * Returns the delegate to which operations are delegated. 117 * 118 * @return a non-{@code null} delegate 119 * 120 * @see AnnotationMirror 121 */ 122 public final AnnotationMirror delegate() { 123 final AnnotationMirror delegate = this.delegateSupplier.get(); 124 assert !(delegate instanceof UniversalAnnotation); 125 return delegate; 126 } 127 128 @Override // Constable 129 public Optional<? extends ConstantDesc> describeConstable() { 130 return this.domain instanceof Constable c0 ? c0.describeConstable() : Optional.<ConstantDesc>empty() 131 .flatMap(primordialDomainDesc -> this.delegate() instanceof Constable c1 ? c1.describeConstable() : Optional.<ConstantDesc>empty() 132 .map(delegateDesc -> DynamicConstantDesc.of(BSM_INVOKE, 133 ofMethod(STATIC, 134 ClassDesc.of(this.getClass().getName()), 135 "of", 136 MethodTypeDesc.of(ClassDesc.of(this.getClass().getName()), 137 ClassDesc.of(AnnotationMirror.class.getName()), 138 ClassDesc.of(PrimordialDomain.class.getName()))), 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<? extends 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}