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