001/* -*- mode: Java; c-basic-offset: 2; indent-tabs-mode: nil; coding: utf-8-unix -*-
002 *
003 * Copyright © 2024 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;
020import java.util.Map;
021import java.util.Map.Entry;
022import java.util.Objects;
023import java.util.Set;
024
025import javax.lang.model.element.AnnotationMirror;
026import javax.lang.model.element.AnnotationValue;
027import javax.lang.model.element.ExecutableElement;
028
029import org.microbean.construct.Domain;
030
031import org.microbean.construct.type.UniversalType;
032
033import static java.util.HashMap.newHashMap;
034
035/**
036 * An {@link AnnotationMirror} implementation.
037 *
038 * @param delegate an {@link AnnotationMirror} to which operations will be delegated; must not be {@code null}
039 *
040 * @param domain a {@link Domain}; must not be {@code null}
041 *
042 * @author <a href="https://about.me/lairdnelson" target="_top">Laird Nelson</a>
043 *
044 * @see AnnotationMirror
045 */
046public final record AnnotationRecord(AnnotationMirror delegate, Domain domain) implements AnnotationMirror {
047
048
049  /*
050   * Constructors.
051   */
052
053
054  /**
055   * Creates a new {@link AnnotationRecord}.
056   *
057   * @param delegate an {@link AnnotationMirror} to which operations will be delegated; must not be {@code null}
058   *
059   * @param domain a {@link Domain}; must not be {@code null}
060   *
061   * @exception NullPointerException if either argument is {@code null}
062   */
063  public AnnotationRecord {
064    Objects.requireNonNull(delegate, "delegate");
065    Objects.requireNonNull(domain, "domain");
066  }
067
068
069  /*
070   * Instance methods.
071   */
072
073
074  @Override // Object
075  @SuppressWarnings("try")
076  public final boolean equals(final Object other) {
077    return this == other || switch (other) {
078    case null -> false;
079    case AnnotationMirror a -> {
080      try (var lock = this.domain().lock()) {
081        yield this.delegate().equals(a instanceof AnnotationRecord ar ? ar.delegate() : a);
082      }
083    }
084    default -> false;
085    };
086  }
087
088  @Override // AnnotationMirror
089  @SuppressWarnings("try")
090  public final UniversalType getAnnotationType() {
091    final Domain d = this.domain();
092    try (var lock = d.lock()) {
093      return UniversalType.of(this.delegate().getAnnotationType(), d);
094    }
095  }
096
097  @Override // AnnotationMirror
098  @SuppressWarnings("try")
099  public final Map<? extends UniversalElement, ? extends AnnotationValueRecord> getElementValues() {
100    final Map<UniversalElement, AnnotationValueRecord> map = newHashMap(17);
101    final Domain d = this.domain();
102    try (var lock = d.lock()) {
103      for (final Entry<? extends ExecutableElement, ? extends AnnotationValue> e : this.delegate().getElementValues().entrySet()) {
104        map.put(UniversalElement.of(e.getKey(), d), AnnotationValueRecord.of(e.getValue(), d));
105      }
106    }
107    return Collections.unmodifiableMap(map);
108  }
109
110  @Override // AnnotationMirror
111  public final int hashCode() {
112    int hashCode = 37 * 17 + this.getAnnotationType().hashCode();
113    return 37 * hashCode + this.getElementValues().hashCode();
114  }
115
116
117  /*
118   * Static methods.
119   */
120
121
122  /**
123   * Returns a non-{@code null} {@link AnnotationRecord} that is either the supplied {@link AnnotationMirror} (if it
124   * itself is an {@link AnnotationRecord}) or one that wraps it.
125   *
126   * @param a an {@link AnnotationMirror}; must not be {@code null}
127   *
128   * @param d a {@link Domain}; must not be {@code null}
129   *
130   * @return a non-{@code null} {@link AnnotationRecord}
131   *
132   * @exception NullPointerException if either argument is {@code null}
133   *
134   * @see #AnnotationRecord(AnnotationMirror, Domain)
135   */
136  public static final AnnotationRecord of(final AnnotationMirror a, final Domain d) {
137    return a instanceof AnnotationRecord ar ? ar : new AnnotationRecord(a, d);
138  }
139
140  /**
141   * Returns a non-{@code null}, immutable {@link List} of {@link AnnotationRecord}s whose elements wrap the supplied
142   * {@link List}'s elements.
143   *
144   * @param as a {@link Collection} of {@link AnnotationMirror}s; must not be {@code null}
145   *
146   * @param domain a {@link Domain}; must not be {@code null}
147   *
148   * @return a non-{@code null}, immutable {@link List} of {@link AnnotationRecord}s
149   *
150   * @exception NullPointerException if either argument is {@code null}
151   */
152  public static final List<? extends AnnotationRecord> of(final Collection<? extends AnnotationMirror> as,
153                                                          final Domain domain) {
154    if (as.isEmpty()) {
155      return List.of();
156    }
157    final List<AnnotationRecord> newAs = new ArrayList<>(as.size());
158    for (final AnnotationMirror a : as) {
159      newAs.add(AnnotationRecord.of(a, domain));
160    }
161    return Collections.unmodifiableList(newAs);
162  }
163}