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}