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}