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}