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