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}