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}