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.core.convert.converter; 018 019import java.util.Comparator; 020import java.util.Map; 021 022import org.springframework.core.convert.ConversionService; 023import org.springframework.util.Assert; 024import org.springframework.util.comparator.ComparableComparator; 025 026/** 027 * A {@link Comparator} that converts values before they are compared. The specified 028 * {@link Converter} will be used to convert each value before it passed to the underlying 029 * {@code Comparator}. 030 * 031 * @author Phillip Webb 032 * @since 3.2 033 * @param <S> the source type 034 * @param <T> the target type 035 */ 036public class ConvertingComparator<S, T> implements Comparator<S> { 037 038 private final Comparator<T> comparator; 039 040 private final Converter<S, T> converter; 041 042 043 /** 044 * Create a new {@link ConvertingComparator} instance. 045 * @param converter the converter 046 */ 047 @SuppressWarnings("unchecked") 048 public ConvertingComparator(Converter<S, T> converter) { 049 this(ComparableComparator.INSTANCE, converter); 050 } 051 052 /** 053 * Create a new {@link ConvertingComparator} instance. 054 * @param comparator the underlying comparator used to compare the converted values 055 * @param converter the converter 056 */ 057 public ConvertingComparator(Comparator<T> comparator, Converter<S, T> converter) { 058 Assert.notNull(comparator, "Comparator must not be null"); 059 Assert.notNull(converter, "Converter must not be null"); 060 this.comparator = comparator; 061 this.converter = converter; 062 } 063 064 /** 065 * Create a new {@link ComparableComparator} instance. 066 * @param comparator the underlying comparator 067 * @param conversionService the conversion service 068 * @param targetType the target type 069 */ 070 public ConvertingComparator( 071 Comparator<T> comparator, ConversionService conversionService, Class<? extends T> targetType) { 072 073 this(comparator, new ConversionServiceConverter<S, T>(conversionService, targetType)); 074 } 075 076 077 @Override 078 public int compare(S o1, S o2) { 079 T c1 = this.converter.convert(o1); 080 T c2 = this.converter.convert(o2); 081 return this.comparator.compare(c1, c2); 082 } 083 084 /** 085 * Create a new {@link ConvertingComparator} that compares {@link java.util.Map.Entry 086 * map * entries} based on their {@link java.util.Map.Entry#getKey() keys}. 087 * @param comparator the underlying comparator used to compare keys 088 * @return a new {@link ConvertingComparator} instance 089 */ 090 public static <K, V> ConvertingComparator<Map.Entry<K, V>, K> mapEntryKeys(Comparator<K> comparator) { 091 return new ConvertingComparator<Map.Entry<K,V>, K>(comparator, new Converter<Map.Entry<K, V>, K>() { 092 @Override 093 public K convert(Map.Entry<K, V> source) { 094 return source.getKey(); 095 } 096 }); 097 } 098 099 /** 100 * Create a new {@link ConvertingComparator} that compares {@link java.util.Map.Entry 101 * map entries} based on their {@link java.util.Map.Entry#getValue() values}. 102 * @param comparator the underlying comparator used to compare values 103 * @return a new {@link ConvertingComparator} instance 104 */ 105 public static <K, V> ConvertingComparator<Map.Entry<K, V>, V> mapEntryValues(Comparator<V> comparator) { 106 return new ConvertingComparator<Map.Entry<K,V>, V>(comparator, new Converter<Map.Entry<K, V>, V>() { 107 @Override 108 public V convert(Map.Entry<K, V> source) { 109 return source.getValue(); 110 } 111 }); 112 } 113 114 115 /** 116 * Adapts a {@link ConversionService} and <tt>targetType</tt> to a {@link Converter}. 117 */ 118 private static class ConversionServiceConverter<S, T> implements Converter<S, T> { 119 120 private final ConversionService conversionService; 121 122 private final Class<? extends T> targetType; 123 124 public ConversionServiceConverter(ConversionService conversionService, 125 Class<? extends T> targetType) { 126 Assert.notNull(conversionService, "ConversionService must not be null"); 127 Assert.notNull(targetType, "TargetType must not be null"); 128 this.conversionService = conversionService; 129 this.targetType = targetType; 130 } 131 132 @Override 133 public T convert(S source) { 134 return this.conversionService.convert(source, this.targetType); 135 } 136 } 137 138}