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.annotation.Annotation;
017
018import java.util.Collection;
019import java.util.List;
020import java.util.Set;
021
022import java.util.concurrent.CopyOnWriteArrayList;
023
024import javax.lang.model.element.AnnotationMirror;
025import javax.lang.model.element.Element;
026import javax.lang.model.element.ElementKind;
027import javax.lang.model.element.ElementVisitor;
028import javax.lang.model.element.Modifier;
029import javax.lang.model.element.Name;
030import javax.lang.model.element.VariableElement;
031
032import javax.lang.model.type.TypeMirror;
033
034import static java.util.Objects.requireNonNull;
035
036import static javax.lang.model.element.ElementKind.LOCAL_VARIABLE;
037
038/**
039 * An <strong>experimental</strong> {@link VariableElement} implementation that is a synthetic representation of a local
040 * variable.
041 *
042 * <p>{@link SyntheticLocalVariableElement} instances may be useful for capturing declaration annotations that really
043 * pertain to type usage. Such scenarios are often found in dependency injection systems.</p>
044 *
045 * @author <a href="https://about.me/lairdnelson" target="_top">Laird Nelson</a>
046 */
047public final class SyntheticLocalVariableElement implements VariableElement {
048
049  private static final Annotation[] EMPTY_ANNOTATION_ARRAY = new Annotation[0];
050  
051  private final List<AnnotationMirror> annotationMirrors;
052
053  private final Name name;
054  
055  private final TypeMirror type;
056
057  /**
058   * Creates a new {@link SyntheticLocalVariableElement}.
059   *
060   * @param type a non-{@code null} {@link TypeMirror} that a hypothetical local variable may bear; as of this writing
061   * no validation is performed on any argument supplied for this parameter
062   *
063   * @exception NullPointerException if {@code type} is {@code null}
064   *
065   * @see #SyntheticLocalVariableElement(Collection, TypeMirror, String)
066   */
067  public SyntheticLocalVariableElement(final TypeMirror type) {
068    this(List.of(), type, null);
069  }
070
071  /**
072   * Creates a new {@link SyntheticLocalVariableElement}.
073   *
074   * @param type a non-{@code null} {@link TypeMirror} that a hypothetical local variable may bear; as of this writing
075   * no validation is performed on any argument supplied for this parameter
076   *
077   * @param name the name of this {@link SyntheticLocalVariableElement}; may be {@code null}
078   *
079   * @exception NullPointerException if {@code type} is {@code null}
080   *
081   * @see #SyntheticLocalVariableElement(Collection, TypeMirror, String)
082   */
083  public SyntheticLocalVariableElement(final TypeMirror type, final String name) {
084    this(List.of(), type, name);
085  }
086
087  /**
088   * Creates a new {@link SyntheticLocalVariableElement}.
089   *
090   * @param as a non-{@code null} {@link Collection} of {@link AnnotationMirror}s
091   *
092   * @param type a non-{@code null} {@link TypeMirror} that a hypothetical local variable may bear; as of this writing
093   * no validation is performed on any argument supplied for this parameter
094   *
095   * @exception NullPointerException if {@code as} or {@code type} is {@code null}
096   *
097   * @see #SyntheticLocalVariableElement(Collection, TypeMirror, String)
098   */
099  public SyntheticLocalVariableElement(final Collection<? extends AnnotationMirror> as, final TypeMirror type) {
100    this(as, type, null);
101  }
102
103  /**
104   * Creates a new {@link SyntheticLocalVariableElement}.
105   *
106   * @param as a non-{@code null} {@link Collection} of {@link AnnotationMirror}s
107   *
108   * @param type a non-{@code null} {@link TypeMirror} that a hypothetical local variable may bear; as of this writing
109   * no validation is performed on any argument supplied for this parameter
110   *
111   * @param name the name of this {@link SyntheticLocalVariableElement}; may be {@code null}
112   *
113   * @exception NullPointerException if {@code as} or {@code type} is {@code null}
114   */
115  public SyntheticLocalVariableElement(final Collection<? extends AnnotationMirror> as,
116                                       final TypeMirror type,
117                                       final String name) {
118    super();
119    this.annotationMirrors = new CopyOnWriteArrayList<>(as);
120    this.name = new SyntheticName(name == null ? "" : name);
121    this.type = requireNonNull(type, "type");
122  }
123
124  @Override // VariableElement (Element)
125  public final <R, P> R accept(final ElementVisitor<R, P> v, final P p) {
126    return v.visitVariable(this, p);
127  }
128
129  @Override // VariableElement (Element)
130  public final TypeMirror asType() {
131    return this.type;
132  }
133
134  @Override // VariableElement (Object)
135  public final boolean equals(final Object other) {
136    return this == other || switch (other) {
137    case null -> false;
138    case SyntheticLocalVariableElement her when this.getClass() == her.getClass() -> this.type.equals(her.type) && this.name.contentEquals(her.name);
139    default -> false;
140    };
141  }
142  
143  @Override // VariableElement (AnnotatedConstruct)
144  public final List<AnnotationMirror> getAnnotationMirrors() {
145    return this.annotationMirrors;
146  }
147
148  @Deprecated
149  @Override // VariableElement (AnnotatedConstruct)
150  public final <A extends Annotation> A getAnnotation(final Class<A> annotationType) {
151    return null; // deliberate
152  }
153
154  @Deprecated
155  @Override // VariableElement (AnnotatedConstruct)
156  @SuppressWarnings("unchecked")
157  public final <A extends Annotation> A[] getAnnotationsByType(final Class<A> annotationType) {
158    return (A[])EMPTY_ANNOTATION_ARRAY; // deliberate
159  }
160  
161  @Override // VariableElement
162  public final Object getConstantValue() {
163    return null;
164  }
165
166  @Override // VariableElement (Element)
167  public final List<? extends Element> getEnclosedElements() {
168    return List.of();
169  }
170  
171  @Override // VariableElement (Element)
172  public final Element getEnclosingElement() {
173    return null; // deliberate
174  }
175
176  @Override // VariableElement (Element)
177  public final ElementKind getKind() {
178    return LOCAL_VARIABLE;
179  }
180
181  @Override // VariableElement (Element)
182  public final Set<Modifier> getModifiers() {
183    return Set.of();
184  }
185
186  @Override // VariableElement (Element)
187  public final Name getSimpleName() {
188    return this.name;
189  }
190
191  @Override // Object
192  public final int hashCode() {
193    return this.name.hashCode() ^ this.type.hashCode();
194  }
195  
196}