/*
 * Copyright 2017-2020 original authors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package io.micronaut.core.annotation;

import io.micronaut.core.util.StringUtils;

import java.lang.annotation.*;
import java.lang.reflect.AnnotatedElement;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

Internal utility methods for annotations. For Internal and framework use only. Do not use in application code.
Author:Graeme Rocher
Since:1.0
/** * Internal utility methods for annotations. For Internal and framework use only. Do not use in application code. * * @author Graeme Rocher * @since 1.0 */
@Internal public class AnnotationUtil {
Constant for Kotlin metadata.
/** * Constant for Kotlin metadata. */
public static final String KOTLIN_METADATA = "kotlin.Metadata"; public static final List<String> INTERNAL_ANNOTATION_NAMES = Arrays.asList( Retention.class.getName(), "javax.annotation.meta.TypeQualifier", "javax.annotation.meta.TypeQualifierNickname", "kotlin.annotation.Retention", Inherited.class.getName(), SuppressWarnings.class.getName(), Override.class.getName(), Repeatable.class.getName(), Documented.class.getName(), "kotlin.annotation.MustBeDocumented", Target.class.getName(), "kotlin.annotation.Target", KOTLIN_METADATA );
Packages excludes from stereotype processing.
/** * Packages excludes from stereotype processing. */
public static final List<String> STEREOTYPE_EXCLUDES = Arrays.asList( "javax.annotation", "edu.umd.cs.findbugs.annotations" );
Constant indicating an zero annotation.
/** * Constant indicating an zero annotation. */
public static final Annotation[] ZERO_ANNOTATIONS = new Annotation[0];
Constant indicating an zero annotation.
/** * Constant indicating an zero annotation. */
public static final AnnotatedElement[] ZERO_ANNOTATED_ELEMENTS = new AnnotatedElement[0];
An empty re-usable element.
/** * An empty re-usable element. */
public static final AnnotatedElement EMPTY_ANNOTATED_ELEMENT = new AnnotatedElement() { @Override public <T extends Annotation> T getAnnotation(Class<T> annotationClass) { return null; } @Override public Annotation[] getAnnotations() { return ZERO_ANNOTATIONS; } @Override public Annotation[] getDeclaredAnnotations() { return ZERO_ANNOTATIONS; } };
Simple Annotation name used for nullable.
/** * Simple Annotation name used for nullable. */
public static final String NULLABLE = "javax.annotation.Nullable";
Simple Annotation name used for non-null.
/** * Simple Annotation name used for non-null. */
public static final String NON_NULL = "javax.annotation.Nonnull"; private static final Map<Integer, List<String>> INTERN_LIST_POOL = new ConcurrentHashMap<>(); private static final Map<String, Map<String, Object>> INTERN_MAP_POOL = new ConcurrentHashMap<>();
Converts the given objects into a set of potentially cached and interned strings contained within an internal pool of lists. See String.intern().

This method serves the purpose of reducing memory footprint by pooling common lists of annotations in compiled AnnotationMetadata

Params:
  • objects – The objects
Returns:A unmodifiable, pooled set of strings
/** * Converts the given objects into a set of potentially cached and interned strings contained within an internal pool of lists. See {@link String#intern()}. * * <p>This method serves the purpose of reducing memory footprint by pooling common lists of annotations in compiled {@link AnnotationMetadata}</p> * * @param objects The objects * @return A unmodifiable, pooled set of strings */
@SuppressWarnings({"unused"}) @UsedByGeneratedCode public static List<String> internListOf(Object... objects) { if (objects == null || objects.length == 0) { return Collections.emptyList(); } Integer hash = Arrays.hashCode(objects); return INTERN_LIST_POOL.computeIfAbsent(hash, integer -> StringUtils.internListOf(objects)); }
Converts the given objects into a map of potentially cached and interned strings where the keys and values are alternating entries in the passed array. See String.intern().

The values stored at even number positions will be converted to strings and interned.

Params:
  • values – The objects
See Also:
Returns:An unmodifiable set of strings
/** * Converts the given objects into a map of potentially cached and interned strings where the keys and values are alternating entries in the passed array. See {@link String#intern()}. * * <p>The values stored at even number positions will be converted to strings and interned.</p> * * @param values The objects * @return An unmodifiable set of strings * @see io.micronaut.core.util.CollectionUtils#mapOf(Object...) */
@SuppressWarnings("unused") @UsedByGeneratedCode public static Map<String, Object> internMapOf(Object... values) { if (values == null || values.length == 0) { return Collections.emptyMap(); } int len = values.length; if (len % 2 != 0) { throw new IllegalArgumentException("Number of arguments should be an even number representing the keys and values"); } // if the length is 2 then only a single annotation is defined, so tried use internal pool if (len == 2) { Object value = values[1]; if (value == Collections.EMPTY_MAP) { String key = values[0].toString(); return INTERN_MAP_POOL.computeIfAbsent(key, s -> Collections.singletonMap(s, Collections.emptyMap()) ); } else { return Collections.singletonMap( values[0].toString(), value ); } } else { return StringUtils.internMapOf(values); } }
Calculates the hash code of annotation values.
Params:
  • values – The map to calculate values' hash code
Returns:The hash code
/** * Calculates the hash code of annotation values. * * @param values The map to calculate values' hash code * @return The hash code */
@SuppressWarnings("MagicNumber") public static int calculateHashCode(Map<? extends CharSequence, Object> values) { int hashCode = 0; for (Map.Entry<? extends CharSequence, Object> member : values.entrySet()) { Object value = member.getValue(); int nameHashCode = member.getKey().hashCode(); int valueHashCode = !value.getClass().isArray() ? value.hashCode() : value.getClass() == boolean[].class ? Arrays.hashCode((boolean[]) value) : value.getClass() == byte[].class ? Arrays.hashCode((byte[]) value) : value.getClass() == char[].class ? Arrays.hashCode((char[]) value) : value.getClass() == double[].class ? Arrays.hashCode((double[]) value) : value.getClass() == float[].class ? Arrays.hashCode((float[]) value) : value.getClass() == int[].class ? Arrays.hashCode((int[]) value) : value.getClass() == long[].class ? Arrays.hashCode( (long[]) value ) : value.getClass() == short[].class ? Arrays .hashCode((short[]) value) : Arrays.hashCode((Object[]) value); hashCode += 127 * nameHashCode ^ valueHashCode; } return hashCode; }
Computes whether 2 annotation values are equal.
Params:
  • o1 – One object
  • o2 – Another object
Returns:Whether both objects are equal
/** * Computes whether 2 annotation values are equal. * * @param o1 One object * @param o2 Another object * @return Whether both objects are equal */
public static boolean areEqual(Object o1, Object o2) { return !o1.getClass().isArray() ? o1.equals(o2) : o1.getClass() == boolean[].class ? Arrays.equals((boolean[]) o1, (boolean[]) o2) : o1.getClass() == byte[].class ? Arrays.equals((byte[]) o1, (byte[]) o2) : o1.getClass() == char[].class ? Arrays.equals((char[]) o1, (char[]) o2) : o1.getClass() == double[].class ? Arrays.equals( (double[]) o1, (double[]) o2 ) : o1.getClass() == float[].class ? Arrays.equals( (float[]) o1, (float[]) o2 ) : o1.getClass() == int[].class ? Arrays.equals( (int[]) o1, (int[]) o2 ) : o1.getClass() == long[].class ? Arrays.equals( (long[]) o1, (long[]) o2 ) : o1.getClass() == short[].class ? Arrays.equals( (short[]) o1, (short[]) o2 ) : Arrays.equals( (Object[]) o1, (Object[]) o2 ); } }