001/* -*- mode: Java; c-basic-offset: 2; indent-tabs-mode: nil; coding: utf-8-unix -*- 002 * 003 * Copyright © 2024 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.AnnotationRecord; 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 // volatile not needed 071 private Supplier<? extends T> delegateSupplier; 072 073 private volatile String s; 074 075 076 /* 077 * Constructors. 078 */ 079 080 081 /** 082 * Creates a new {@link AnnotatedConstruct}. 083 * 084 * @param delegate a delegate to which operations are delegated; must not be {@code null} 085 * 086 * @param domain; a {@link Domain} from which the supplied {@code delegate} is presumed to have originated; must not 087 * be {@code null} 088 * 089 * @exception NullPointerException if either argument is {@code null} 090 */ 091 @SuppressWarnings("try") 092 protected UniversalConstruct(final T delegate, final Domain domain) { 093 super(); 094 this.domain = Objects.requireNonNull(domain, "domain"); 095 final T unwrappedDelegate = unwrap(Objects.requireNonNull(delegate, "delegate")); 096 final Runnable symbolCompleter = switch (unwrappedDelegate) { 097 case null -> throw new IllegalArgumentException("delegate: " + delegate); 098 case Element e -> e::getKind; 099 case TypeMirror t -> t::getKind; 100 default -> UniversalConstruct::doNothing; 101 }; 102 this.delegateSupplier = () -> { 103 try (var lock = domain.lock()) { 104 symbolCompleter.run(); 105 this.delegateSupplier = () -> unwrappedDelegate; 106 } 107 return unwrappedDelegate; 108 }; 109 } 110 111 112 /* 113 * Instance methods. 114 */ 115 116 117 /** 118 * Returns the delegate to which operations are delegated. 119 * 120 * @return a non-{@code null} delegate 121 * 122 * @see Element 123 * 124 * @see TypeMirror 125 */ 126 public final T delegate() { 127 return this.delegateSupplier.get(); 128 } 129 130 @Override // Constable 131 public final Optional<? extends ConstantDesc> describeConstable() { 132 final T delegate = this.delegate(); 133 final Domain domain = this.domain(); 134 return Constables.describe(delegate, domain) 135 .map(delegateDesc -> DynamicConstantDesc.of(BSM_INVOKE, 136 MethodHandleDesc.ofConstructor(ClassDesc.of(this.getClass().getName()), 137 ClassDesc.of(delegate instanceof TypeMirror ? TypeMirror.class.getName() : Element.class.getName()), 138 ClassDesc.of(Domain.class.getName())), 139 delegateDesc, 140 ((Constable)domain).describeConstable().orElseThrow())); 141 } 142 143 /** 144 * Returns the {@link Domain} supplied at construction time. 145 * 146 * @return the non-{@code null} {@link Domain} supplied at construction time 147 */ 148 public final Domain domain() { 149 return this.domain; 150 } 151 152 @Override // AnnotatedConstruct 153 public final List<? extends AnnotationRecord> getAnnotationMirrors() { 154 return AnnotationRecord.of(this.delegate().getAnnotationMirrors(), this.domain()); 155 } 156 157 @Override // AnnotatedConstruct 158 @SuppressWarnings("try") 159 public final <A extends Annotation> A getAnnotation(final Class<A> annotationType) { 160 try (var lock = this.domain().lock()) { 161 return this.delegate().getAnnotation(annotationType); 162 } 163 } 164 165 @Override // AnnotatedConstruct 166 @SuppressWarnings("try") 167 public final <A extends Annotation> A[] getAnnotationsByType(final Class<A> annotationType) { 168 try (var lock = this.domain().lock()) { 169 return this.delegate().getAnnotationsByType(annotationType); 170 } 171 } 172 173 @Override // Object 174 @SuppressWarnings("try") 175 public final String toString() { 176 String s = this.s; // volatile read 177 if (s == null) { 178 try (var lock = this.domain().lock()) { 179 s = this.s = this.delegate().toString(); // volatile write, read 180 } 181 assert s != null; 182 } 183 return s; 184 } 185 186 187 /* 188 * Static methods. 189 */ 190 191 192 /** 193 * <dfn>Unwraps</dfn> the supplied {@link AnnotatedConstruct} implementation such that the returned value is not an 194 * instance of {@link UniversalConstruct}. 195 * 196 * @param <T> an {@link AnnotatedConstruct} subtype (possibly {@link UniversalElement} or {@link UniversalType}) 197 * 198 * @param t an {@link AnnotatedConstruct}; may be {@code null} 199 * 200 * @return an object of the appropriate type that is guaranteed not to be an instance of {@link UniversalConstruct} 201 * 202 * @see #delegate() 203 */ 204 @SuppressWarnings("unchecked") 205 public static final <T extends AnnotatedConstruct> T unwrap(T t) { 206 while (t instanceof UniversalConstruct<?> uc) { 207 t = (T)uc.delegate(); 208 } 209 return t; 210 } 211 212 private static final void doNothing() {} 213 214}