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.util.ArrayList; 017import java.util.Collection; 018import java.util.Collections; 019import java.util.List; 020 021import java.util.function.Supplier; 022 023import javax.lang.model.element.AnnotationMirror; 024import javax.lang.model.element.AnnotationValue; 025import javax.lang.model.element.AnnotationValueVisitor; 026import javax.lang.model.element.VariableElement; 027 028import javax.lang.model.type.TypeMirror; 029 030import org.microbean.construct.PrimordialDomain; 031 032import org.microbean.construct.type.UniversalType; 033 034import static java.util.Objects.requireNonNull; 035 036/** 037 * An {@link AnnotationValue} implementation. 038 * 039 * @author <a href="https://about.me/lairdnelson" target="_top">Laird Nelson</a> 040 * 041 * @see AnnotationValue 042 */ 043public final class UniversalAnnotationValue implements AnnotationValue { 044 045 046 /* 047 * Instance fields. 048 */ 049 050 051 private final PrimordialDomain domain; 052 053 // Eventually this should become a lazy constant/stable value 054 // volatile not needed 055 // Treat as final 056 // Use delegate() to read; do not reference directly 057 private Supplier<? extends AnnotationValue> delegateSupplier; 058 059 060 /* 061 * Constructors. 062 */ 063 064 065 /** 066 * Creates a new {@link UniversalAnnotationValue}. 067 * 068 * @param delegate an {@link AnnotationValue} to which operations will be delegated; must not be {@code null} 069 * 070 * @param domain a {@link PrimordialDomain}; must not be {@code null} 071 * 072 * @exception NullPointerException if either argument is {@code null} 073 */ 074 @SuppressWarnings("try") 075 public UniversalAnnotationValue(final AnnotationValue delegate, final PrimordialDomain domain) { 076 super(); 077 this.domain = requireNonNull(domain, "domain"); 078 final AnnotationValue 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 // Should trigger symbol completion; see 084 // https://github.com/openjdk/jdk/blob/jdk-25%2B3/src/jdk.compiler/share/classes/com/sun/tools/javac/util/Constants.java#L49; 085 // any invocation of getTag() will do it. 086 unwrappedDelegate.toString(); // names will do it too 087 this.delegateSupplier = () -> unwrappedDelegate; // replace ourselves safely under lock 088 } 089 return unwrappedDelegate; 090 }; 091 } else { 092 assert delegate instanceof UniversalAnnotationValue; 093 // Symbol completion already happened; no lock needed 094 this.delegateSupplier = () -> unwrappedDelegate; 095 } 096 } 097 098 099 /* 100 * Instance methods. 101 */ 102 103 104 @Override // AnnotationValue 105 @SuppressWarnings("unchecked") 106 public final <R, P> R accept(final AnnotationValueVisitor<R, P> v, final P p) { 107 return switch (this.getValue()) { 108 case null -> v.visitUnknown(this, p); // ...or AssertionError? 109 case AnnotationMirror a -> v.visitAnnotation(a, p); 110 case List<?> l -> v.visitArray((List<? extends AnnotationValue>)l, p); 111 case TypeMirror t -> v.visitType(t, p); 112 case VariableElement e -> v.visitEnumConstant(e, p); 113 case Boolean b -> v.visitBoolean(b, p); 114 case Byte b -> v.visitByte(b, p); 115 case Character c -> v.visitChar(c, p); 116 case Double d -> v.visitDouble(d, p); 117 case Float f -> v.visitFloat(f, p); 118 case Integer i -> v.visitInt(i, p); 119 case Long l -> v.visitLong(l, p); 120 case Short s -> v.visitShort(s, p); 121 case String s -> v.visitString(s, p); 122 default -> v.visitUnknown(this, p); 123 }; 124 } 125 126 /** 127 * Returns the delegate to which operations are delegated. 128 * 129 * @return a non-{@code null} delegate 130 * 131 * @see AnnotationValue 132 */ 133 public final AnnotationValue delegate() { 134 final AnnotationValue delegate = this.delegateSupplier.get(); 135 assert !(delegate instanceof UniversalAnnotationValue); 136 return delegate; 137 } 138 139 /** 140 * Returns the {@link PrimordialDomain} supplied at construction time. 141 * 142 * @return the non-{@code null} {@link PrimordialDomain} supplied at construction time 143 */ 144 public final PrimordialDomain domain() { 145 return this.domain; 146 } 147 148 @Override // Object 149 @SuppressWarnings("try") 150 public final boolean equals(final Object other) { 151 return this == other || switch (other) { 152 case null -> false; 153 // No lock needed; see 154 // https://github.com/openjdk/jdk/blob/jdk-26%2B25/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Attribute.java#L45 155 case UniversalAnnotationValue uav when this.getClass() == uav.getClass() -> this.delegate().equals(uav.delegate()); 156 default -> false; 157 }; 158 } 159 160 @Override // AnnotationValue 161 @SuppressWarnings("unchecked") 162 public final Object getValue() { 163 final Object value = this.delegate().getValue(); 164 return switch (value) { 165 case null -> throw new AssertionError(); 166 case AnnotationMirror a -> UniversalAnnotation.of(a, this.domain()); 167 case List<?> l -> of((List<? extends AnnotationValue>)l, this.domain()); 168 case TypeMirror t -> UniversalType.of(t, this.domain()); 169 case VariableElement e -> UniversalElement.of(e, this.domain()); 170 default -> value; 171 }; 172 } 173 174 @Override // Object 175 public final int hashCode() { 176 return this.delegate().hashCode(); 177 } 178 179 @Override // AnnotationValue 180 public final String toString() { 181 return this.delegate().toString(); 182 } 183 184 185 /* 186 * Static methods. 187 */ 188 189 190 /** 191 * Returns a non-{@code null} {@link UniversalAnnotationValue} that is either the supplied {@link AnnotationValue} (if it 192 * itself is {@code null} or is an {@link UniversalAnnotationValue}) or one that wraps it. 193 * 194 * @param av an {@link AnnotationValue}; may be {@code null} 195 * 196 * @param domain a {@link PrimordialDomain}; must not be {@code null} 197 * 198 * @return an {@link UniversalAnnotationValue}, or {@code null} (if {@code av} is {@code null}) 199 * 200 * @exception NullPointerException if {@code domain} is {@code null} 201 * 202 * @see #UniversalAnnotationValue(AnnotationValue, PrimordialDomain) 203 */ 204 public static final UniversalAnnotationValue of(final AnnotationValue av, final PrimordialDomain domain) { 205 return switch (av) { 206 case null -> null; 207 case UniversalAnnotationValue uav -> uav; 208 default -> new UniversalAnnotationValue(av, domain); 209 }; 210 } 211 212 /** 213 * Returns a non-{@code null}, immutable {@link List} of {@link UniversalAnnotationValue}s whose elements wrap the 214 * supplied {@link List}'s elements. 215 * 216 * @param avs a {@link Collection} of {@link AnnotationValue}s; must not be {@code null} 217 * 218 * @param domain a {@link PrimordialDomain}; must not be {@code null} 219 * 220 * @return a non-{@code null}, immutable {@link List} of {@link UniversalAnnotationValue}s 221 * 222 * @exception NullPointerException if either argument is {@code null} 223 */ 224 public static final List<? extends UniversalAnnotationValue> of(final Collection<? extends AnnotationValue> avs, 225 final PrimordialDomain domain) { 226 if (avs.isEmpty()) { 227 return List.of(); 228 } 229 final List<UniversalAnnotationValue> newAvs = new ArrayList<>(avs.size()); 230 for (final AnnotationValue av : avs) { 231 newAvs.add(UniversalAnnotationValue.of(av, domain)); 232 } 233 return Collections.unmodifiableList(newAvs); 234 } 235 236 /** 237 * <dfn>Unwraps</dfn> the supplied {@link AnnotationValue} implementation such that the returned value is not an 238 * instance of {@link UniversalAnnotationValue}. 239 * 240 * @param a an {@link AnnotationValue}; may be {@code null} 241 * 242 * @return an {@link AnnotationValue} that is guaranteed not to be an instance of {@link UniversalAnnotationValue} 243 * 244 * @see #delegate() 245 */ 246 public static final AnnotationValue unwrap(AnnotationValue a) { 247 while (a instanceof UniversalAnnotationValue uav) { 248 a = uav.delegate(); 249 } 250 return a; 251 } 252 253}