001/*
002 * Copyright 2002-2018 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.util.comparator;
018
019import java.io.Serializable;
020import java.util.ArrayList;
021import java.util.Comparator;
022import java.util.List;
023
024import org.springframework.lang.Nullable;
025import org.springframework.util.Assert;
026
027/**
028 * A comparator that chains a sequence of one or more Comparators.
029 *
030 * <p>A compound comparator calls each Comparator in sequence until a single
031 * Comparator returns a non-zero result, or the comparators are exhausted and
032 * zero is returned.
033 *
034 * <p>This facilitates in-memory sorting similar to multi-column sorting in SQL.
035 * The order of any single Comparator in the list can also be reversed.
036 *
037 * @author Keith Donald
038 * @author Juergen Hoeller
039 * @since 1.2.2
040 * @param <T> the type of objects that may be compared by this comparator
041 * @deprecated as of Spring Framework 5.0, in favor of the standard JDK 8
042 * {@link Comparator#thenComparing(Comparator)}
043 */
044@Deprecated
045@SuppressWarnings({"serial", "rawtypes"})
046public class CompoundComparator<T> implements Comparator<T>, Serializable {
047
048        private final List<InvertibleComparator> comparators;
049
050
051        /**
052         * Construct a CompoundComparator with initially no Comparators. Clients
053         * must add at least one Comparator before calling the compare method or an
054         * IllegalStateException is thrown.
055         */
056        public CompoundComparator() {
057                this.comparators = new ArrayList<>();
058        }
059
060        /**
061         * Construct a CompoundComparator from the Comparators in the provided array.
062         * <p>All Comparators will default to ascending sort order,
063         * unless they are InvertibleComparators.
064         * @param comparators the comparators to build into a compound comparator
065         * @see InvertibleComparator
066         */
067        @SuppressWarnings("unchecked")
068        public CompoundComparator(Comparator... comparators) {
069                Assert.notNull(comparators, "Comparators must not be null");
070                this.comparators = new ArrayList<>(comparators.length);
071                for (Comparator comparator : comparators) {
072                        addComparator(comparator);
073                }
074        }
075
076
077        /**
078         * Add a Comparator to the end of the chain.
079         * <p>The Comparator will default to ascending sort order,
080         * unless it is a InvertibleComparator.
081         * @param comparator the Comparator to add to the end of the chain
082         * @see InvertibleComparator
083         */
084        @SuppressWarnings("unchecked")
085        public void addComparator(Comparator<? extends T> comparator) {
086                if (comparator instanceof InvertibleComparator) {
087                        this.comparators.add((InvertibleComparator) comparator);
088                }
089                else {
090                        this.comparators.add(new InvertibleComparator(comparator));
091                }
092        }
093
094        /**
095         * Add a Comparator to the end of the chain using the provided sort order.
096         * @param comparator the Comparator to add to the end of the chain
097         * @param ascending the sort order: ascending (true) or descending (false)
098         */
099        @SuppressWarnings("unchecked")
100        public void addComparator(Comparator<? extends T> comparator, boolean ascending) {
101                this.comparators.add(new InvertibleComparator(comparator, ascending));
102        }
103
104        /**
105         * Replace the Comparator at the given index.
106         * <p>The Comparator will default to ascending sort order,
107         * unless it is a InvertibleComparator.
108         * @param index the index of the Comparator to replace
109         * @param comparator the Comparator to place at the given index
110         * @see InvertibleComparator
111         */
112        @SuppressWarnings("unchecked")
113        public void setComparator(int index, Comparator<? extends T> comparator) {
114                if (comparator instanceof InvertibleComparator) {
115                        this.comparators.set(index, (InvertibleComparator) comparator);
116                }
117                else {
118                        this.comparators.set(index, new InvertibleComparator(comparator));
119                }
120        }
121
122        /**
123         * Replace the Comparator at the given index using the given sort order.
124         * @param index the index of the Comparator to replace
125         * @param comparator the Comparator to place at the given index
126         * @param ascending the sort order: ascending (true) or descending (false)
127         */
128        public void setComparator(int index, Comparator<T> comparator, boolean ascending) {
129                this.comparators.set(index, new InvertibleComparator<>(comparator, ascending));
130        }
131
132        /**
133         * Invert the sort order of each sort definition contained by this compound
134         * comparator.
135         */
136        public void invertOrder() {
137                for (InvertibleComparator comparator : this.comparators) {
138                        comparator.invertOrder();
139                }
140        }
141
142        /**
143         * Invert the sort order of the sort definition at the specified index.
144         * @param index the index of the comparator to invert
145         */
146        public void invertOrder(int index) {
147                this.comparators.get(index).invertOrder();
148        }
149
150        /**
151         * Change the sort order at the given index to ascending.
152         * @param index the index of the comparator to change
153         */
154        public void setAscendingOrder(int index) {
155                this.comparators.get(index).setAscending(true);
156        }
157
158        /**
159         * Change the sort order at the given index to descending sort.
160         * @param index the index of the comparator to change
161         */
162        public void setDescendingOrder(int index) {
163                this.comparators.get(index).setAscending(false);
164        }
165
166        /**
167         * Returns the number of aggregated comparators.
168         */
169        public int getComparatorCount() {
170                return this.comparators.size();
171        }
172
173
174        @Override
175        @SuppressWarnings("unchecked")
176        public int compare(T o1, T o2) {
177                Assert.state(!this.comparators.isEmpty(),
178                                "No sort definitions have been added to this CompoundComparator to compare");
179                for (InvertibleComparator comparator : this.comparators) {
180                        int result = comparator.compare(o1, o2);
181                        if (result != 0) {
182                                return result;
183                        }
184                }
185                return 0;
186        }
187
188
189        @Override
190        @SuppressWarnings("unchecked")
191        public boolean equals(@Nullable Object other) {
192                return (this == other || (other instanceof CompoundComparator &&
193                                this.comparators.equals(((CompoundComparator<T>) other).comparators)));
194        }
195
196        @Override
197        public int hashCode() {
198                return this.comparators.hashCode();
199        }
200
201        @Override
202        public String toString() {
203                return "CompoundComparator: " + this.comparators;
204        }
205
206}