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