001/* 002 * ============================================================================ 003 * Copyright © 2002-2020 by Thomas Thrien. 004 * All Rights Reserved. 005 * ============================================================================ 006 * Licensed to the public under the agreements of the GNU Lesser General Public 007 * License, version 3.0 (the "License"). You may obtain a copy of the License at 008 * 009 * http://www.gnu.org/licenses/lgpl.html 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, WITHOUT 013 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 014 * License for the specific language governing permissions and limitations 015 * under the License. 016 */ 017 018package org.tquadrat.foundation.util.internal; 019 020import static java.lang.Integer.signum; 021import static org.apiguardian.api.API.Status.INTERNAL; 022import static org.tquadrat.foundation.lang.Objects.requireNonNullArgument; 023 024import java.util.Comparator; 025import java.util.IdentityHashMap; 026import java.util.Map; 027 028import org.apiguardian.api.API; 029import org.tquadrat.foundation.annotation.ClassVersion; 030import org.tquadrat.foundation.util.Comparators.KeyProvider; 031 032/** 033 * Sometimes a special sort key is used to order elements, and instead of 034 * hiding the generation of that key inside the method 035 * {@link Comparator#compare(Object, Object) compare()} 036 * of an implementation of 037 * {@link Comparator}, 038 * this implementation expects an instance of 039 * {@link KeyProvider} 040 * as an argument to its 041 * {@linkplain #KeyBasedComparator(KeyProvider,Comparator,boolean) constructor}. 042 * 043 * @param <T> The type to order. 044 * @param <K> The key type that is used to determine the order; this may be 045 * the same as the type itself. 046 * 047 * @extauthor Thomas Thrien - thomas.thrien@tquadrat.org 048 * @version $Id: KeyBasedComparator.java 820 2020-12-29 20:34:22Z tquadrat $ 049 * @since 0.0.5 050 * 051 * @UMLGraph.link 052 */ 053@ClassVersion( sourceVersion = "$Id: KeyBasedComparator.java 820 2020-12-29 20:34:22Z tquadrat $" ) 054@API( status = INTERNAL, since = "0.0.5" ) 055public class KeyBasedComparator<T,K> implements Comparator<T> 056{ 057 /*------------*\ 058 ====** Attributes **======================================================= 059 \*------------*/ 060 /** 061 * A flag that determines whether the keys are to be cached internally; 062 * {@code true} means they are kept, {@code false} means that the keys are 063 * generated newly for each comparison. 064 */ 065 private final boolean m_CacheKeys; 066 067 /** 068 * The cache for the keys. 069 */ 070 private final Map<T,K> m_KeyCache = new IdentityHashMap<>(); 071 072 /** 073 * The comparator that determines the sort order for the keys. 074 */ 075 private final Comparator<K> m_KeyComparator; 076 077 /** 078 * The method that generates the key for the given element. 079 */ 080 private final KeyProvider<T,K> m_KeyProvider; 081 082 /*--------------*\ 083 ====** Constructors **===================================================== 084 \*--------------*/ 085 /** 086 * Creates a new {@code KeyBasedComparator} instance. 087 * 088 * @param keyProvider The method that generates the key for a given 089 * element. 090 * @param keyComparator The comparator that determines the sort order 091 * for the keys. 092 * @param cacheKeys A flag that determines whether the keys are to be 093 * cached internally; {@code true} means they are kept, {@code false} 094 * means that the keys are generated newly for each comparison. 095 */ 096 public KeyBasedComparator( final KeyProvider<T,K> keyProvider, final Comparator<K> keyComparator, final boolean cacheKeys ) 097 { 098 m_CacheKeys = cacheKeys; 099 m_KeyComparator = requireNonNullArgument( keyComparator, "keyComparator" ); 100 m_KeyProvider = requireNonNullArgument( keyProvider, "keyProvider" ); 101 } // KeyBasedComparator<T,K>() 102 103 /*---------*\ 104 ====** Methods **========================================================== 105 \*---------*/ 106 /** 107 * {@inheritDoc} 108 */ 109 @Override 110 public final int compare( final T o1, final T o2 ) 111 { 112 final var k1 = retrieveKey( o1 ); 113 final var k2 = retrieveKey( o2 ); 114 115 final var retValue = signum( m_KeyComparator.compare( k1, k2 ) ); 116 117 //---* Done *---------------------------------------------------------- 118 return retValue; 119 } // compare() 120 121 /** 122 * Provides the key for the given element, either from the cache or 123 * freshly generated. 124 * 125 * @param o The element. 126 * @return The sort key for the element. 127 */ 128 private final K retrieveKey( final T o ) 129 { 130 final var retValue = m_CacheKeys 131 ? m_KeyCache.computeIfAbsent( o, m_KeyProvider::getKey ) 132 : m_KeyProvider.getKey( o ); 133 134 //---* Done *---------------------------------------------------------- 135 return retValue; 136 } // retrieveKey() 137} 138// class KeyBasedComparator 139 140/* 141 * End of File 142 */