001/* -*- mode: Java; c-basic-offset: 2; indent-tabs-mode: nil; coding: utf-8-unix -*- 002 * 003 * Copyright © 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.ClassDesc; 017import java.lang.constant.Constable; 018import java.lang.constant.ConstantDesc; 019import java.lang.constant.DynamicConstantDesc; 020 021import java.util.List; 022import java.util.Optional; 023 024import javax.lang.model.element.AnnotationMirror; 025import javax.lang.model.element.AnnotationValue; 026import javax.lang.model.element.AnnotationValueVisitor; 027import javax.lang.model.element.VariableElement; 028 029import javax.lang.model.type.TypeMirror; 030 031import org.microbean.construct.constant.Constables; 032 033import static java.lang.constant.ConstantDescs.BSM_INVOKE; 034import static java.lang.constant.ConstantDescs.CD_Object; 035 036import static java.lang.constant.MethodHandleDesc.ofConstructor; 037 038/** 039 * An <strong>experimental</strong> {@link AnnotationValue} implementation that is partially or wholly synthetic. 040 * 041 * @author <a href="https://about.me/lairdnelson" target="_top">Laird Nelson</a> 042 */ 043public final class SyntheticAnnotationValue implements AnnotationValue, Constable { 044 045 046 /* 047 * Instance fields. 048 */ 049 050 051 // Will be one of: 052 // 053 // * AnnotationMirror 054 // * List<SyntheticAnnotationValue> 055 // * TypeMirror (DeclaredType, PrimitiveType) 056 // * VariableElement (ENUM_CONSTANT) 057 // * Boolean 058 // * Byte 059 // * Character 060 // * Double 061 // * Float 062 // * Integer 063 // * Long 064 // * Short 065 // * String 066 private final Object value; 067 068 069 /* 070 * Constructors. 071 */ 072 073 074 /** 075 * Creates a new {@link SyntheticAnnotationValue}. 076 * 077 * @param value a value legal for an {@link AnnotationValue}; must not be {@code null} 078 * 079 * @exception NullPointerException if {@code value} is {@code null} 080 * 081 * @exception IllegalArgumentException if {@code value} is not legal for an {@link AnnotationValue} 082 * 083 * @see AnnotationValue 084 */ 085 public SyntheticAnnotationValue(final Object value) { 086 super(); 087 this.value = value(value); 088 } 089 090 091 /* 092 * Instance methods. 093 */ 094 095 096 @Override // AnnotationValue 097 @SuppressWarnings("unchecked") 098 public final <R, P> R accept(final AnnotationValueVisitor<R, P> v, final P p) { 099 return switch (this.value) { 100 case null -> v.visitUnknown(this, p); // ...or AssertionError? 101 case AnnotationMirror a -> v.visitAnnotation(a, p); 102 case List<?> l -> v.visitArray((List<? extends AnnotationValue>)l, p); 103 case TypeMirror t -> v.visitType(t, p); 104 case VariableElement e -> v.visitEnumConstant(e, p); 105 case Boolean b -> v.visitBoolean(b, p); 106 case Byte b -> v.visitByte(b, p); 107 case Character c -> v.visitChar(c, p); 108 case Double d -> v.visitDouble(d, p); 109 case Float f -> v.visitFloat(f, p); 110 case Integer i -> v.visitInt(i, p); 111 case Long l -> v.visitLong(l, p); 112 case Short s -> v.visitShort(s, p); 113 case String s -> v.visitString(s, p); 114 default -> v.visitUnknown(this, p); 115 }; 116 } 117 118 @Override // Constable 119 public final Optional<DynamicConstantDesc<SyntheticAnnotationValue>> describeConstable() { 120 final Optional<? extends ConstantDesc> valueDescOptional = switch (this.value) { 121 case Constable c -> c.describeConstable(); 122 case ConstantDesc cd -> Optional.of(cd); 123 case List<?> l -> Constables.describe(l); 124 default -> Optional.<ConstantDesc>empty(); 125 }; 126 return valueDescOptional.map(valueDesc -> DynamicConstantDesc.ofNamed(BSM_INVOKE, 127 this.getClass().getSimpleName(), 128 this.getClass().describeConstable().orElseThrow(), 129 ofConstructor(this.getClass().describeConstable().orElseThrow(), 130 CD_Object), 131 valueDesc)); 132 } 133 134 @Override // Object 135 public final boolean equals(final Object other) { 136 return this == other || switch (other) { 137 case null -> false; 138 case SyntheticAnnotationValue sav when this.getClass() == sav.getClass() -> this.value.equals(sav.value); 139 default -> false; 140 }; 141 } 142 143 @Override // AnnotationValue 144 public final Object getValue() { 145 return this.value; 146 } 147 148 @Override // Object 149 public final int hashCode() { 150 return this.value.hashCode(); 151 } 152 153 @Override // Object 154 public final String toString() { 155 return this.value.toString(); 156 } 157 158 159 /* 160 * Static methods. 161 */ 162 163 164 /** 165 * Returns a non-{@code null}, determinate {@link SyntheticAnnotationValue} that represents the supplied {@code 166 * value}. 167 * 168 * <p>If {@code value} is a {@link SyntheticAnnotationValue}, then it is returned unchanged.</p> 169 * 170 * @param value a value legal for an {@link AnnotationValue}; must not be {@code null} 171 * 172 * @return a non-{@code null}, determinate {@link SyntheticAnnotationValue} 173 * 174 * @exception NullPointerException if {@code value} is {@code null} 175 * 176 * @exception IllegalArgumentException if {@code value} is not legal for an {@link AnnotationValue} 177 * 178 * @see AnnotationValue 179 */ 180 public static final SyntheticAnnotationValue of(final Object value) { 181 return switch (value) { 182 case null -> throw new NullPointerException("value"); 183 case SyntheticAnnotationValue sav -> sav; 184 default -> new SyntheticAnnotationValue(value); 185 }; 186 } 187 188 private static final Object value(final Object value) { 189 return switch (value) { 190 case null -> throw new NullPointerException("value"); 191 192 case AnnotationValue av -> av.getValue(); // not part of the spec; just good hygiene 193 194 case List<?> l -> l.stream().map(SyntheticAnnotationValue::new).toList(); 195 196 case TypeMirror t -> switch (t.getKind()) { 197 case ARRAY, BOOLEAN, BYTE, CHAR, DECLARED, DOUBLE, FLOAT, INT, LONG, SHORT, VOID -> t; 198 default -> throw new IllegalArgumentException("value: " + value); 199 }; 200 201 case VariableElement e -> switch (e.getKind()) { 202 case ENUM_CONSTANT -> e; 203 default -> throw new IllegalArgumentException("value: " + value); 204 }; 205 206 case AnnotationMirror a -> a; 207 case Boolean b -> b; 208 case Byte b -> b; 209 case Character c -> c; 210 case Double d -> d; 211 case Float f -> f; 212 case Integer i -> i; 213 case Long l -> l; 214 case Short s -> s; 215 case String s -> s; 216 217 default -> throw new IllegalArgumentException("value: " + value); 218 }; 219 } 220 221}