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