001// *************************************************************************************************************************** 002// * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file * 003// * distributed with this work for additional information regarding copyright ownership. The ASF licenses this file * 004// * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance * 005// * with the License. You may obtain a copy of the License at * 006// * * 007// * http://www.apache.org/licenses/LICENSE-2.0 * 008// * * 009// * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an * 010// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the * 011// * specific language governing permissions and limitations under the License. * 012// *************************************************************************************************************************** 013package org.apache.juneau.annotation; 014 015import static org.apache.juneau.collections.JsonMap.*; 016import static org.apache.juneau.common.internal.ThrowableUtils.*; 017import static java.util.Arrays.*; 018 019import java.lang.annotation.*; 020import java.lang.reflect.*; 021import java.util.*; 022 023import org.apache.juneau.collections.*; 024import org.apache.juneau.internal.*; 025 026/** 027 * A concrete implementation of an annotation. 028 * 029 * <p> 030 * Follows the standard Java conventions for equality and hashcode calculation for annotations. 031 * Equivalent annotations defined programmatically and declaratively should match for equality and hashcode calculation. 032 * 033 * <p> 034 * For performance reasons, the hashcode is calculated one time and cached at the end of object creation. 035 * Constructors must call the {@link #postConstruct()} method after all fields have been set to trigger this calculation. 036 * 037 * <h5 class='section'>See Also:</h5><ul> 038 * </ul> 039 */ 040public class AnnotationImpl implements Annotation { 041 042 private final Class<? extends Annotation> annotationType; 043 private int hashCode = -1; 044 045 /** 046 * Constructor. 047 * 048 * @param b The builder used to instantiate the fields of this class. 049 */ 050 public AnnotationImpl(AnnotationBuilder b) { 051 this.annotationType = b.annotationType; 052 } 053 054 /** 055 * This method must be called at the end of initialization to calculate the hashCode one time. 056 */ 057 protected void postConstruct() { 058 this.hashCode = AnnotationUtils.hashCode(this); 059 } 060 061 /** 062 * Implements the {@link Annotation#annotationType()} method for child classes. 063 * 064 * @return This class. 065 */ 066 @Override /* Annotation */ 067 public Class<? extends Annotation> annotationType() { 068 return annotationType; 069 } 070 071 @Override /* Object */ 072 public int hashCode() { 073 if (hashCode == -1) 074 throw new IllegalArgumentException("Programming error. postConstruct() was never called on annotation."); 075 return hashCode; 076 } 077 078 @Override /* Object */ 079 public boolean equals(Object o) { 080 if (! annotationType.isInstance(o)) 081 return false; 082 return AnnotationUtils.equals(this, (Annotation)o); 083 } 084 085 /** 086 * Returns this annotation as a map of key/value pairs. 087 * 088 * <p> 089 * Useful for debugging. 090 * 091 * @return This annotation as a map of key/value pairs. 092 */ 093 public JsonMap toMap() { 094 JsonMap m = create(); 095 stream(annotationType().getDeclaredMethods()) 096 .filter(x->x.getParameterCount() == 0 && x.getDeclaringClass().isAnnotation()) 097 .sorted(Comparator.comparing(Method::getName)) 098 .forEach(x -> m.append(x.getName(), safeSupplier(()->x.invoke(this)))); 099 return m; 100 } 101 102 @Override /* Object */ 103 public String toString() { 104 return toMap().asString(); 105 } 106}