    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