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}