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