001/*
002 * Copyright 2002-2019 the original author or authors.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *      https://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017package org.springframework.core.annotation;
018
019import java.lang.reflect.AnnotatedElement;
020import java.util.Map;
021
022import org.springframework.core.annotation.MergedAnnotations.SearchStrategy;
023import org.springframework.lang.Nullable;
024import org.springframework.util.ConcurrentReferenceHashMap;
025
026/**
027 * General utility for determining the order of an object based on its type declaration.
028 * Handles Spring's {@link Order} annotation as well as {@link javax.annotation.Priority}.
029 *
030 * @author Stephane Nicoll
031 * @author Juergen Hoeller
032 * @since 4.1
033 * @see Order
034 * @see javax.annotation.Priority
035 */
036public abstract class OrderUtils {
037
038        /** Cache marker for a non-annotated Class. */
039        private static final Object NOT_ANNOTATED = new Object();
040
041        private static final String JAVAX_PRIORITY_ANNOTATION = "javax.annotation.Priority";
042
043        /** Cache for @Order value (or NOT_ANNOTATED marker) per Class. */
044        private static final Map<AnnotatedElement, Object> orderCache = new ConcurrentReferenceHashMap<>(64);
045
046
047        /**
048         * Return the order on the specified {@code type}, or the specified
049         * default value if none can be found.
050         * <p>Takes care of {@link Order @Order} and {@code @javax.annotation.Priority}.
051         * @param type the type to handle
052         * @return the priority value, or the specified default order if none can be found
053         * @since 5.0
054         * @see #getPriority(Class)
055         */
056        public static int getOrder(Class<?> type, int defaultOrder) {
057                Integer order = getOrder(type);
058                return (order != null ? order : defaultOrder);
059        }
060
061        /**
062         * Return the order on the specified {@code type}, or the specified
063         * default value if none can be found.
064         * <p>Takes care of {@link Order @Order} and {@code @javax.annotation.Priority}.
065         * @param type the type to handle
066         * @return the priority value, or the specified default order if none can be found
067         * @see #getPriority(Class)
068         */
069        @Nullable
070        public static Integer getOrder(Class<?> type, @Nullable Integer defaultOrder) {
071                Integer order = getOrder(type);
072                return (order != null ? order : defaultOrder);
073        }
074
075        /**
076         * Return the order on the specified {@code type}.
077         * <p>Takes care of {@link Order @Order} and {@code @javax.annotation.Priority}.
078         * @param type the type to handle
079         * @return the order value, or {@code null} if none can be found
080         * @see #getPriority(Class)
081         */
082        @Nullable
083        public static Integer getOrder(Class<?> type) {
084                return getOrderFromAnnotations(type, MergedAnnotations.from(type, SearchStrategy.TYPE_HIERARCHY));
085        }
086
087        /**
088         * Return the order from the specified annotations collection.
089         * <p>Takes care of {@link Order @Order} and
090         * {@code @javax.annotation.Priority}.
091         * @param element the source element
092         * @param annotations the annotation to consider
093         * @return the order value, or {@code null} if none can be found
094         */
095        @Nullable
096        static Integer getOrderFromAnnotations(AnnotatedElement element, MergedAnnotations annotations) {
097                if (!(element instanceof Class)) {
098                        return findOrder(annotations);
099                }
100                Object cached = orderCache.get(element);
101                if (cached != null) {
102                        return (cached instanceof Integer ? (Integer) cached : null);
103                }
104                Integer result = findOrder(annotations);
105                orderCache.put(element, result != null ? result : NOT_ANNOTATED);
106                return result;
107        }
108
109        @Nullable
110        private static Integer findOrder(MergedAnnotations annotations) {
111                MergedAnnotation<Order> orderAnnotation = annotations.get(Order.class);
112                if (orderAnnotation.isPresent()) {
113                        return orderAnnotation.getInt(MergedAnnotation.VALUE);
114                }
115                MergedAnnotation<?> priorityAnnotation = annotations.get(JAVAX_PRIORITY_ANNOTATION);
116                if (priorityAnnotation.isPresent()) {
117                        return priorityAnnotation.getInt(MergedAnnotation.VALUE);
118                }
119                return null;
120        }
121
122        /**
123         * Return the value of the {@code javax.annotation.Priority} annotation
124         * declared on the specified type, or {@code null} if none.
125         * @param type the type to handle
126         * @return the priority value if the annotation is declared, or {@code null} if none
127         */
128        @Nullable
129        public static Integer getPriority(Class<?> type) {
130                return MergedAnnotations.from(type, SearchStrategy.TYPE_HIERARCHY).get(JAVAX_PRIORITY_ANNOTATION)
131                                .getValue(MergedAnnotation.VALUE, Integer.class).orElse(null);
132        }
133
134}