001/*
002 * Copyright 2002-2020 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;
018
019import java.util.Arrays;
020import java.util.Comparator;
021import java.util.List;
022
023import org.springframework.lang.Nullable;
024import org.springframework.util.ObjectUtils;
025
026/**
027 * {@link Comparator} implementation for {@link Ordered} objects, sorting
028 * by order value ascending, respectively by priority descending.
029 *
030 * <h3>{@code PriorityOrdered} Objects</h3>
031 * <p>{@link PriorityOrdered} objects will be sorted with higher priority than
032 * <em>plain</em> {@code Ordered} objects.
033 *
034 * <h3>Same Order Objects</h3>
035 * <p>Objects that have the same order value will be sorted with arbitrary
036 * ordering with respect to other objects with the same order value.
037 *
038 * <h3>Non-ordered Objects</h3>
039 * <p>Any object that does not provide its own order value is implicitly
040 * assigned a value of {@link Ordered#LOWEST_PRECEDENCE}, thus ending up
041 * at the end of a sorted collection in arbitrary order with respect to
042 * other objects with the same order value.
043 *
044 * @author Juergen Hoeller
045 * @author Sam Brannen
046 * @since 07.04.2003
047 * @see Ordered
048 * @see PriorityOrdered
049 * @see org.springframework.core.annotation.AnnotationAwareOrderComparator
050 * @see java.util.List#sort(java.util.Comparator)
051 * @see java.util.Arrays#sort(Object[], java.util.Comparator)
052 */
053public class OrderComparator implements Comparator<Object> {
054
055        /**
056         * Shared default instance of {@code OrderComparator}.
057         */
058        public static final OrderComparator INSTANCE = new OrderComparator();
059
060
061        /**
062         * Build an adapted order comparator with the given source provider.
063         * @param sourceProvider the order source provider to use
064         * @return the adapted comparator
065         * @since 4.1
066         */
067        public Comparator<Object> withSourceProvider(OrderSourceProvider sourceProvider) {
068                return (o1, o2) -> doCompare(o1, o2, sourceProvider);
069        }
070
071        @Override
072        public int compare(@Nullable Object o1, @Nullable Object o2) {
073                return doCompare(o1, o2, null);
074        }
075
076        private int doCompare(@Nullable Object o1, @Nullable Object o2, @Nullable OrderSourceProvider sourceProvider) {
077                boolean p1 = (o1 instanceof PriorityOrdered);
078                boolean p2 = (o2 instanceof PriorityOrdered);
079                if (p1 && !p2) {
080                        return -1;
081                }
082                else if (p2 && !p1) {
083                        return 1;
084                }
085
086                int i1 = getOrder(o1, sourceProvider);
087                int i2 = getOrder(o2, sourceProvider);
088                return Integer.compare(i1, i2);
089        }
090
091        /**
092         * Determine the order value for the given object.
093         * <p>The default implementation checks against the given {@link OrderSourceProvider}
094         * using {@link #findOrder} and falls back to a regular {@link #getOrder(Object)} call.
095         * @param obj the object to check
096         * @return the order value, or {@code Ordered.LOWEST_PRECEDENCE} as fallback
097         */
098        private int getOrder(@Nullable Object obj, @Nullable OrderSourceProvider sourceProvider) {
099                Integer order = null;
100                if (obj != null && sourceProvider != null) {
101                        Object orderSource = sourceProvider.getOrderSource(obj);
102                        if (orderSource != null) {
103                                if (orderSource.getClass().isArray()) {
104                                        for (Object source : ObjectUtils.toObjectArray(orderSource)) {
105                                                order = findOrder(source);
106                                                if (order != null) {
107                                                        break;
108                                                }
109                                        }
110                                }
111                                else {
112                                        order = findOrder(orderSource);
113                                }
114                        }
115                }
116                return (order != null ? order : getOrder(obj));
117        }
118
119        /**
120         * Determine the order value for the given object.
121         * <p>The default implementation checks against the {@link Ordered} interface
122         * through delegating to {@link #findOrder}. Can be overridden in subclasses.
123         * @param obj the object to check
124         * @return the order value, or {@code Ordered.LOWEST_PRECEDENCE} as fallback
125         */
126        protected int getOrder(@Nullable Object obj) {
127                if (obj != null) {
128                        Integer order = findOrder(obj);
129                        if (order != null) {
130                                return order;
131                        }
132                }
133                return Ordered.LOWEST_PRECEDENCE;
134        }
135
136        /**
137         * Find an order value indicated by the given object.
138         * <p>The default implementation checks against the {@link Ordered} interface.
139         * Can be overridden in subclasses.
140         * @param obj the object to check
141         * @return the order value, or {@code null} if none found
142         */
143        @Nullable
144        protected Integer findOrder(Object obj) {
145                return (obj instanceof Ordered ? ((Ordered) obj).getOrder() : null);
146        }
147
148        /**
149         * Determine a priority value for the given object, if any.
150         * <p>The default implementation always returns {@code null}.
151         * Subclasses may override this to give specific kinds of values a
152         * 'priority' characteristic, in addition to their 'order' semantics.
153         * A priority indicates that it may be used for selecting one object over
154         * another, in addition to serving for ordering purposes in a list/array.
155         * @param obj the object to check
156         * @return the priority value, or {@code null} if none
157         * @since 4.1
158         */
159        @Nullable
160        public Integer getPriority(Object obj) {
161                return null;
162        }
163
164
165        /**
166         * Sort the given List with a default OrderComparator.
167         * <p>Optimized to skip sorting for lists with size 0 or 1,
168         * in order to avoid unnecessary array extraction.
169         * @param list the List to sort
170         * @see java.util.List#sort(java.util.Comparator)
171         */
172        public static void sort(List<?> list) {
173                if (list.size() > 1) {
174                        list.sort(INSTANCE);
175                }
176        }
177
178        /**
179         * Sort the given array with a default OrderComparator.
180         * <p>Optimized to skip sorting for lists with size 0 or 1,
181         * in order to avoid unnecessary array extraction.
182         * @param array the array to sort
183         * @see java.util.Arrays#sort(Object[], java.util.Comparator)
184         */
185        public static void sort(Object[] array) {
186                if (array.length > 1) {
187                        Arrays.sort(array, INSTANCE);
188                }
189        }
190
191        /**
192         * Sort the given array or List with a default OrderComparator,
193         * if necessary. Simply skips sorting when given any other value.
194         * <p>Optimized to skip sorting for lists with size 0 or 1,
195         * in order to avoid unnecessary array extraction.
196         * @param value the array or List to sort
197         * @see java.util.Arrays#sort(Object[], java.util.Comparator)
198         */
199        public static void sortIfNecessary(Object value) {
200                if (value instanceof Object[]) {
201                        sort((Object[]) value);
202                }
203                else if (value instanceof List) {
204                        sort((List<?>) value);
205                }
206        }
207
208
209        /**
210         * Strategy interface to provide an order source for a given object.
211         * @since 4.1
212         */
213        @FunctionalInterface
214        public interface OrderSourceProvider {
215
216                /**
217                 * Return an order source for the specified object, i.e. an object that
218                 * should be checked for an order value as a replacement to the given object.
219                 * <p>Can also be an array of order source objects.
220                 * <p>If the returned object does not indicate any order, the comparator
221                 * will fall back to checking the original object.
222                 * @param obj the object to find an order source for
223                 * @return the order source for that object, or {@code null} if none found
224                 */
225                @Nullable
226                Object getOrderSource(Object obj);
227        }
228
229}