001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.commons.collections4.comparators;
018
019import java.io.Serializable;
020import java.util.Comparator;
021import java.util.Objects;
022
023import org.apache.commons.collections4.ComparatorUtils;
024import org.apache.commons.collections4.Transformer;
025
026/**
027 * Decorates another Comparator with transformation behavior. That is, the
028 * return value from the transform operation will be passed to the decorated
029 * {@link Comparator#compare(Object,Object) compare} method.
030 * <p>
031 * This class is Serializable from Commons Collections 4.0.
032 * </p>
033 *
034 * @param <I> the type of the input to the function
035 * @param <O> the type of the result of the function
036 * @since 2.1
037 * @see org.apache.commons.collections4.Transformer
038 * @see org.apache.commons.collections4.comparators.ComparableComparator
039 */
040public class TransformingComparator<I, O> implements Comparator<I>, Serializable {
041
042    /** Serialization version from Collections 4.0. */
043    private static final long serialVersionUID = 3456940356043606220L;
044
045    /** The decorated comparator. */
046    private final Comparator<O> decorated;
047
048    /** The transformer being used. */
049    private final Transformer<? super I, ? extends O> transformer;
050
051    /**
052     * Constructs an instance with the given Transformer and a
053     * {@link ComparableComparator ComparableComparator}.
054     *
055     * @param transformer what will transform the arguments to {@code compare}
056     */
057    public TransformingComparator(final Transformer<? super I, ? extends O> transformer) {
058        this(transformer, ComparatorUtils.NATURAL_COMPARATOR);
059    }
060
061    /**
062     * Constructs an instance with the given Transformer and Comparator.
063     *
064     * @param transformer  what will transform the arguments to {@code compare}
065     * @param decorated  the decorated Comparator
066     */
067    public TransformingComparator(final Transformer<? super I, ? extends O> transformer,
068                                  final Comparator<O> decorated) {
069        this.decorated = decorated;
070        this.transformer = transformer;
071    }
072
073    /**
074     * Returns the result of comparing the values from the transform operation.
075     *
076     * @param obj1  the first object to transform then compare
077     * @param obj2  the second object to transform then compare
078     * @return negative if obj1 is less, positive if greater, zero if equal
079     */
080    @Override
081    public int compare(final I obj1, final I obj2) {
082        final O value1 = transformer.apply(obj1);
083        final O value2 = transformer.apply(obj2);
084        return decorated.compare(value1, value2);
085    }
086
087    /**
088     * Returns {@code true} iff <em>that</em> Object is
089     * a {@link Comparator} whose ordering is known to be
090     * equivalent to mine.
091     * <p>
092     * This implementation returns {@code true}
093     * iff {@code <em>that</em>} is a {@link TransformingComparator}
094     * whose attributes are equal to mine.
095     *
096     * @param object  the object to compare to
097     * @return true if equal
098     */
099    @Override
100    public boolean equals(final Object object) {
101        if (this == object) {
102            return true;
103        }
104        if (null == object) {
105            return false;
106        }
107        if (object.getClass().equals(this.getClass())) {
108            final TransformingComparator<?, ?> comp = (TransformingComparator<?, ?>) object;
109            return Objects.equals(decorated, comp.decorated) &&
110                   Objects.equals(transformer, comp.transformer);
111        }
112        return false;
113    }
114
115    /**
116     * Implement a hash code for this comparator that is consistent with
117     * {@link #equals(Object) equals}.
118     *
119     * @return a hash code for this comparator.
120     */
121    @Override
122    public int hashCode() {
123        int total = 17;
124        total = total * 37 + (decorated == null ? 0 : decorated.hashCode());
125        return total * 37 + (transformer == null ? 0 : transformer.hashCode());
126    }
127
128}
129