001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.juneau.commons;
018
019import static org.apache.juneau.commons.function.Suppliers.*;
020import static org.apache.juneau.commons.utils.CollectionUtils.*;
021import static org.apache.juneau.commons.utils.Shorts.*;
022import static org.apache.juneau.commons.utils.ThrowableUtils.*;
023
024import java.lang.annotation.*;
025import java.lang.reflect.*;
026import java.util.*;
027import java.util.function.*;
028
029import org.apache.juneau.commons.utils.*;
030
031/**
032 * A concrete implementation of a Java annotation that can be created programmatically at runtime.
033 *
034 * <p>
035 * This class provides a base for creating annotation instances without requiring them to be declared on
036 * program elements at compile-time. It allows annotations to be constructed using a builder pattern and
037 * follows all standard Java annotation semantics for equality, hashcode, and string representation.
038 *
039 * <h5 class='section'>Overview:</h5>
040 * <p>
041 * Java annotations are typically declared statically on classes, methods, fields, etc. at compile-time:
042 * <p class='bjava'>
043 *    <ja>@Marshalled</ja>(sort=<jk>true</jk>)
044 *    <jk>public class</jk> MyClass {...}
045 * </p>
046 *
047 * <p>
048 * This class allows you to create those same annotations programmatically:
049 * <p class='bjava'>
050 *    Bean <jv>annotation</jv> = MarshalledAnnotation
051 *       .<jsm>create</jsm>()
052 *       .sort(<jk>true</jk>)
053 *       .build();
054 * </p>
055 *
056 * <h5 class='section'>Equality and Hashcode:</h5>
057 * <p>
058 * Follows the standard Java conventions for annotation equality and hashcode calculation as defined in
059 * {@link Annotation#equals(Object)} and {@link Annotation#hashCode()}. This ensures that programmatically-created
060 * annotations are equivalent to compile-time declared annotations if they have the same type and properties.
061 *
062 * <p class='bjava'>
063 *    <jc>// These two annotations are equal:</jc>
064 *    <ja>@Marshalled</ja>(sort=<jk>true</jk>)
065 *    <jk>class</jk> MyClass {}
066 *
067 *    Bean <jv>declared</jv> = MyClass.<jk>class</jk>.getAnnotation(Bean.<jk>class</jk>);
068 *    Bean <jv>programmatic</jv> = MarshalledAnnotation.<jsm>create</jsm>().sort(<jk>true</jk>).build();
069 *
070 *    <jsm>assertEquals</jsm>(<jv>declared</jv>, <jv>programmatic</jv>);  <jc>// true</jc>
071 *    <jsm>assertEquals</jsm>(<jv>declared</jv>.hashCode(), <jv>programmatic</jv>.hashCode());  <jc>// true</jc>
072 * </p>
073 *
074 * <h5 class='section'>Hashcode Caching:</h5>
075 * <p>
076 * For performance reasons, the hashcode is calculated once and cached on first access.
077 * The hash is computed lazily when {@link #hashCode()} is first called and then stored for subsequent calls.
078 *
079 * <p class='bjava'>
080 *    <jk>public</jk> MyAnnotation(Builder <jv>builder</jv>) {
081 *       <jk>super</jk>(<jv>builder</jv>);
082 *       <jk>this</jk>.<jf>myField</jf> = <jv>builder</jv>.<jf>myField</jf>;
083 *    }
084 * </p>
085 *
086 * <h5 class='section'>Builder Pattern:</h5>
087 * <p>
088 * Subclasses should provide a nested {@link Builder} class that extends {@link AnnotationObject.Builder}
089 * to construct instances using a fluent builder pattern. The builder should:
090 * <ul class='spaced-list'>
091 *    <li>Provide setter methods for each annotation property
092 *    <li>Return <c>this</c> (or the builder type) from each setter for method chaining
093 *    <li>Provide a {@code build()} method that constructs the final annotation object
094 * </ul>
095 *
096 * <h5 class='section'>Example Implementation:</h5>
097 * <p class='bjava'>
098 *    <jk>public class</jk> MyAnnotationObject <jk>extends</jk> AnnotationObject <jk>implements</jk> MyAnnotation {
099 *
100 *       <jk>private final</jk> String <jf>value</jf>;
101 *
102 *       <jk>public static class</jk> Builder <jk>extends</jk> AnnotationObject.Builder {
103 *          String <jf>value</jf> = <js>""</js>;
104 *
105 *          <jk>public</jk> Builder() {
106 *             <jk>super</jk>(MyAnnotation.<jk>class</jk>);
107 *          }
108 *
109 *          <jk>public</jk> Builder value(String <jv>value</jv>) {
110 *             <jk>this</jk>.<jf>value</jf> = <jv>value</jv>;
111 *             <jk>return this</jk>;
112 *          }
113 *
114 *          <jk>public</jk> MyAnnotation build() {
115 *             <jk>return new</jk> MyAnnotationObject(<jk>this</jk>);
116 *          }
117 *       }
118 *
119 *       <jk>public</jk> MyAnnotationObject(Builder <jv>builder</jv>) {
120 *          <jk>super</jk>(<jv>builder</jv>);
121 *          <jk>this</jk>.<jf>value</jf> = <jv>builder</jv>.<jf>value</jf>;
122 *       }
123 *
124 *       <ja>@Override</ja>
125 *       <jk>public</jk> String value() {
126 *          <jk>return</jk> <jf>value</jf>;
127 *       }
128 *    }
129 * </p>
130 *
131 * <h5 class='section'>See Also:</h5><ul>
132 *    <li class='jc'>{@link AppliedAnnotationObject} - For annotations with dynamic targeting support
133 *    <li class='link'><a class="doclink" href="../../../../../overview-summary.html#juneau-commons.Annotations">Overview &gt; juneau-commons &gt; Annotations</a>
134 * </ul>
135 */
136@SuppressWarnings({
137   "java:S1192" // Duplicated literals (argument/property names) read more clearly inline than as constants
138})
139public class AnnotationObject implements Annotation {
140
141   //-----------------------------------------------------------------------------------------------------------------
142   // Static
143   //-----------------------------------------------------------------------------------------------------------------
144
145   /**
146    * Builder for {@link AnnotationObject} objects.
147    */
148   public static class Builder {
149
150      private final Class<? extends Annotation> annotationType;
151
152      /**
153       * Constructor.
154       *
155       * @param annotationType The annotation type of the annotation implementation class.  Must not be <jk>null</jk>.
156       */
157      public Builder(Class<? extends Annotation> annotationType) {
158         this.annotationType = reqnn("annotationType", annotationType);
159      }
160
161      /**
162       * Returns the annotation type being built.
163       *
164       * @return The annotation type being built.
165       */
166      public Class<? extends Annotation> getAnnotationType() { return annotationType; }
167   }
168
169   //-----------------------------------------------------------------------------------------------------------------
170   // Instance
171   //-----------------------------------------------------------------------------------------------------------------
172
173   private final Class<? extends Annotation> annotationType;
174   private final Supplier<Integer> hashCode = memoize(() -> AnnotationUtils.hash(this));
175
176   /**
177    * Constructor.
178    *
179    * @param b The builder used to instantiate the fields of this class.  Must not be <jk>null</jk>.
180    */
181   public AnnotationObject(Builder b) {
182      reqnn("b", b);
183      annotationType = b.getAnnotationType();
184   }
185
186   /**
187    * Implements the {@link Annotation#annotationType()} method for child classes.
188    *
189    * @return This class.
190    */
191   @Override /* Overridden from Annotation */
192   public Class<? extends Annotation> annotationType() {
193      return annotationType;
194   }
195
196   @Override /* Overridden from Object */
197   public boolean equals(Object o) {
198      return o instanceof Annotation o2 && annotationType.isInstance(o) && eq(this, o2);
199   }
200
201   @Override /* Overridden from Object */
202   public int hashCode() {
203      return hashCode.get();
204   }
205
206   /**
207    * Returns this annotation as a map of key/value pairs.
208    *
209    * <p>
210    * Useful for debugging.
211    *
212    * @return This annotation as a map of key/value pairs.
213    */
214   protected Map<String,Object> propertyMap() {
215      // @formatter:off
216      var m = mapb_so().sorted().build();
217      stream(annotationType().getDeclaredMethods())
218         // Note: isAnnotation() check is defensive code. For properly-formed AnnotationObject instances,
219         // annotationType() always returns an annotation interface, so this condition is always true.
220         .filter(x->x.getParameterCount() == 0 && x.getDeclaringClass().isAnnotation()) // HTT - false branches unreachable for well-formed annotation types
221         .sorted(Comparator.comparing(Method::getName))
222         .forEach(x -> m.put(x.getName(), safeSupplier(()->x.invoke(this))));
223      return m;
224      // @formatter:on
225   }
226
227   @Override /* Overridden from Object */
228   public String toString() {
229      return r(propertyMap());
230   }
231}