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