001/* -*- mode: Java; c-basic-offset: 2; indent-tabs-mode: nil; coding: utf-8-unix -*- 002 * 003 * Copyright © 2024–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; 015 016import java.lang.annotation.Annotation; 017 018import java.lang.constant.ClassDesc; 019import java.lang.constant.Constable; 020import java.lang.constant.ConstantDesc; 021import java.lang.constant.DynamicConstantDesc; 022import java.lang.constant.MethodHandleDesc; 023 024import java.util.List; 025import java.util.Objects; 026import java.util.Optional; 027 028import java.util.function.Supplier; 029 030import javax.lang.model.AnnotatedConstruct; 031 032import javax.lang.model.element.Element; 033 034import javax.lang.model.type.TypeMirror; 035 036import org.microbean.construct.constant.Constables; 037 038import org.microbean.construct.element.UniversalAnnotation; 039import org.microbean.construct.element.UniversalElement; 040 041import org.microbean.construct.type.UniversalType; 042 043import static java.lang.constant.ConstantDescs.BSM_INVOKE; 044 045/** 046 * An abstract implementation of {@link AnnotatedConstruct} from which only {@link UniversalElement} and {@link 047 * UniversalType} descend. 048 * 049 * @param <T> a type of {@link AnnotatedConstruct}, which may be only either {@link Element} or {@link TypeMirror} 050 * 051 * @author <a href="https://about.me/lairdnelson" target="_top">Laird Nelson</a> 052 * 053 * @see AnnotatedConstruct 054 * 055 * @see UniversalElement 056 * 057 * @see UniversalType 058 */ 059public abstract sealed class UniversalConstruct<T extends AnnotatedConstruct> implements AnnotatedConstruct, Constable 060 permits UniversalElement, UniversalType { 061 062 063 /* 064 * Instance fields. 065 */ 066 067 068 private final Domain domain; 069 070 // Eventually this should become a lazy constant/stable value 071 // volatile not needed 072 private Supplier<? extends T> delegateSupplier; 073 074 // Eventually this should become a lazy constant/stable value 075 private volatile String s; 076 077 078 /* 079 * Constructors. 080 */ 081 082 083 /** 084 * Creates a new {@link AnnotatedConstruct}. 085 * 086 * @param delegate a delegate to which operations are delegated; must not be {@code null} 087 * 088 * @param domain; a {@link Domain} from which the supplied {@code delegate} is presumed to have originated; must not 089 * be {@code null} 090 * 091 * @exception NullPointerException if either argument is {@code null} 092 */ 093 @SuppressWarnings("try") 094 protected UniversalConstruct(final T delegate, final Domain domain) { 095 super(); 096 this.domain = Objects.requireNonNull(domain, "domain"); 097 final T unwrappedDelegate = unwrap(Objects.requireNonNull(delegate, "delegate")); 098 if (unwrappedDelegate == delegate) { 099 // No unwrapping happened so do symbol completion early; most common case. 100 final Runnable symbolCompleter = switch (unwrappedDelegate) { 101 case Element e -> e::getModifiers; 102 case TypeMirror t -> t::getKind; 103 default -> UniversalConstruct::doNothing; 104 }; 105 this.delegateSupplier = () -> { 106 try (var lock = domain.lock()) { 107 symbolCompleter.run(); 108 this.delegateSupplier = () -> unwrappedDelegate; 109 } 110 return unwrappedDelegate; 111 }; 112 } else { 113 assert delegate instanceof UniversalConstruct<?>; 114 // Symbol completion already happened 115 this.delegateSupplier = () -> unwrappedDelegate; 116 } 117 } 118 119 120 /* 121 * Instance methods. 122 */ 123 124 125 /** 126 * Returns the delegate to which operations are delegated. 127 * 128 * <p>The delegate is guaranteed not to be an instance of {@link UniversalConstruct}.</p> 129 * 130 * @return a non-{@code null} delegate 131 * 132 * @see Element 133 * 134 * @see TypeMirror 135 */ 136 public final T delegate() { 137 final T delegate = this.delegateSupplier.get(); 138 assert !(delegate instanceof UniversalConstruct); 139 return delegate; 140 } 141 142 @Override // Constable 143 public final Optional<? extends ConstantDesc> describeConstable() { 144 final T delegate = this.delegate(); 145 final Domain domain = this.domain(); 146 return Constables.describe(delegate, domain) 147 .map(delegateDesc -> DynamicConstantDesc.of(BSM_INVOKE, 148 MethodHandleDesc.ofConstructor(ClassDesc.of(this.getClass().getName()), 149 ClassDesc.of(delegate instanceof TypeMirror ? TypeMirror.class.getName() : Element.class.getName()), 150 ClassDesc.of(Domain.class.getName())), 151 delegateDesc, 152 ((Constable)domain).describeConstable().orElseThrow())); 153 } 154 155 /** 156 * Returns the {@link Domain} supplied at construction time. 157 * 158 * @return the non-{@code null} {@link Domain} supplied at construction time 159 */ 160 public final Domain domain() { 161 return this.domain; 162 } 163 164 @Override // Object 165 public final boolean equals(final Object other) { 166 // Interesting; equality does not cause symbol completion. See: 167 // 168 // https://github.com/openjdk/jdk/blob/jdk-26%2B25/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Type.java#L553-L559 169 // (the only type that overrides this is ArrayType; see 170 // https://github.com/openjdk/jdk/blob/jdk-26%2B25/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Type.java#L1402-L1406) 171 // 172 // https://github.com/openjdk/jdk/blob/jdk-26%2B25/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Symbol.java 173 // (Symbol (Element) doesn't override it at all.) 174 return this == other || switch (other) { 175 case null -> false; 176 case UniversalConstruct<?> uc when this.getClass() == uc.getClass() -> this.delegate().equals(uc.delegate()); 177 default -> false; 178 }; 179 } 180 181 @Override // AnnotatedConstruct 182 public final List<? extends UniversalAnnotation> getAnnotationMirrors() { 183 return UniversalAnnotation.of(this.delegate().getAnnotationMirrors(), this.domain()); 184 } 185 186 @Override // AnnotatedConstruct 187 @SuppressWarnings("try") 188 public final <A extends Annotation> A getAnnotation(final Class<A> annotationType) { 189 // TODO: is this lock actually needed, given how delegateSupplier works? 190 try (var lock = this.domain().lock()) { 191 return this.delegate().getAnnotation(annotationType); 192 } 193 } 194 195 @Override // AnnotatedConstruct 196 @SuppressWarnings("try") 197 public final <A extends Annotation> A[] getAnnotationsByType(final Class<A> annotationType) { 198 // TODO: is this lock actually needed, given how delegateSupplier works? 199 try (var lock = this.domain().lock()) { 200 return this.delegate().getAnnotationsByType(annotationType); 201 } 202 } 203 204 @Override // Object 205 public final int hashCode() { 206 // Interesting; hashCode doesn't cause symbol completion. See: 207 // 208 // https://github.com/openjdk/jdk/blob/jdk-26%2B25/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Type.java#L565-L568 209 // (AnnoConstruct doesn't define it so super.hashCode() is Object.hashCode()) 210 // 211 // https://github.com/openjdk/jdk/blob/jdk-26%2B25/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Symbol.java 212 // (Symbol (Element) doesn't override it at all.) 213 return this.delegate().hashCode(); 214 } 215 216 @Override // Object 217 @SuppressWarnings("try") 218 public final String toString() { 219 String s = this.s; // volatile read 220 if (s == null) { 221 try (var lock = this.domain().lock()) { 222 s = this.s = this.delegate().toString(); // volatile write, read 223 } 224 assert s != null; 225 } 226 return s; 227 } 228 229 230 /* 231 * Static methods. 232 */ 233 234 235 /** 236 * <dfn>Unwraps</dfn> the supplied {@link AnnotatedConstruct} implementation such that the returned value is not an 237 * instance of {@link UniversalConstruct}. 238 * 239 * @param <T> an {@link AnnotatedConstruct} subtype (possibly {@link UniversalElement} or {@link UniversalType}) 240 * 241 * @param t an {@link AnnotatedConstruct}; may be {@code null} 242 * 243 * @return an object of the appropriate type that is guaranteed not to be an instance of {@link UniversalConstruct} 244 * 245 * @see #delegate() 246 */ 247 @SuppressWarnings("unchecked") 248 public static final <T extends AnnotatedConstruct> T unwrap(T t) { 249 while (t instanceof UniversalConstruct<?> uc) { 250 t = (T)uc.delegate(); 251 } 252 return t; 253 } 254 255 private static final void doNothing() {} 256 257}