001/* 002 * ============================================================================ 003 * Copyright © 2014 by Dominic Fox. 004 * All Rights Reserved. 005 * ============================================================================ 006 * The MIT License (MIT) 007 * 008 * Permission is hereby granted, free of charge, to any person obtaining a copy 009 * of this software and associated documentation files (the "Software"), to 010 * deal in the Software without restriction, including without limitation the 011 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 012 * sell copies of the Software, and to permit persons to whom the Software is 013 * furnished to do so, subject to the following conditions: 014 * 015 * The above copyright notice and this permission notice shall be included in 016 * all copies or substantial portions of the Software. 017 * 018 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 019 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 020 * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE 021 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 022 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 023 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 024 * IN THE SOFTWARE. 025 */ 026 027package org.tquadrat.foundation.stream; 028 029import static org.apiguardian.api.API.Status.STABLE; 030import static org.tquadrat.foundation.lang.Objects.isNull; 031import static org.tquadrat.foundation.lang.Objects.requireNonNullArgument; 032 033import java.util.Comparator; 034import java.util.NoSuchElementException; 035import java.util.stream.Stream; 036 037import org.apiguardian.api.API; 038import org.tquadrat.foundation.annotation.ClassVersion; 039import org.tquadrat.foundation.annotation.UtilityClass; 040import org.tquadrat.foundation.exception.PrivateConstructorForStaticClassCalledError; 041import org.tquadrat.foundation.lang.Objects; 042 043/** 044 * Some useful implementations of the 045 * {@link Selector} 046 * interface. 047 * 048 * @author Dominic Fox 049 * @modified Thomas Thrien - thomas.thrien@tquadrat.org 050 * @version $Id: Selectors.java 1031 2022-04-07 22:43:02Z tquadrat $ 051 * @since 0.0.7 052 * 053 * @UMLGraph.link 054 */ 055@UtilityClass 056@ClassVersion( sourceVersion = "$Id: Selectors.java 1031 2022-04-07 22:43:02Z tquadrat $" ) 057public final class Selectors 058{ 059 /*--------------*\ 060 ====** Constructors **===================================================== 061 \*--------------*/ 062 /** 063 * No instance allowed for this class! 064 */ 065 private Selectors() { throw new PrivateConstructorForStaticClassCalledError( Selectors.class ); } 066 067 /*---------*\ 068 ====** Methods **========================================================== 069 \*---------*/ 070 /** 071 * Returns a "Round Robin" 072 * {@link Selector} 073 * implementation. It will return always the next index for the given 074 * array where the respective value is not {@code null}, or {@code null} 075 * if the array contains only {@code null} values. 076 * 077 * @param <T> The type of the values to select from. 078 * @return The selector. 079 */ 080 @SuppressWarnings( "OverlyComplexAnonymousInnerClass" ) 081 @API( status = STABLE, since = "0.0.7" ) 082 public static <T> Selector<T> roundRobin() 083 { 084 @SuppressWarnings( "AnonymousInnerClass" ) 085 final Selector<T> retValue = new Selector<>() 086 { 087 /** 088 * The start index. 089 */ 090 private int m_StartIndex = 0; 091 092 /** 093 * {@inheritDoc} 094 * 095 * @return The array index of the selected value, or {@code null} 096 * if all values in the provided array are {@code null}. 097 */ 098 @SuppressWarnings( "MethodWithMultipleReturnPoints" ) 099 @Override 100 public final Integer apply( final T [] options ) 101 { 102 var result = m_StartIndex; 103 while( isNull( options [result] ) ) 104 { 105 result = (result + 1) % options.length; 106 if( (result == m_StartIndex) && isNull( options [result] ) ) 107 { 108 //---* All values in options are null *---------------- 109 return null; 110 } 111 } 112 113 m_StartIndex = (result + 1) % options.length; 114 return Integer.valueOf( result ); 115 } // apply() 116 }; 117 118 //---* Done *---------------------------------------------------------- 119 return retValue; 120 } // roundRobin() 121 122 /** 123 * Returns a 124 * {@link Selector} 125 * implementation that will always return the index for the greatest 126 * value from the given array, based on the natural order of the values. 127 * 128 * @param <T> The type of the values to select from. 129 * @return The selector. 130 */ 131 @API( status = STABLE, since = "0.0.7" ) 132 public static <T extends Comparable<T>> Selector<T> takeMax() 133 { 134 final Selector<T> retValue = takeMax( Comparator.naturalOrder() ); 135 136 //---* Done *---------------------------------------------------------- 137 return retValue; 138 } // takeMax() 139 140 /** 141 * Returns a 142 * {@link Selector} 143 * implementation that will always return the index for the greatest 144 * value from the given array, imposed by the given comparator. 145 * 146 * @param <T> The type of the values to select from. 147 * @param comparator The comparator. 148 * @return The selector. 149 */ 150 @API( status = STABLE, since = "0.0.7" ) 151 public static <T> Selector<T> takeMax( final Comparator<? super T> comparator ) 152 { 153 final Selector<T> retValue = takeMin( requireNonNullArgument( comparator, "comparator" ).reversed() ); 154 155 //---* Done *---------------------------------------------------------- 156 return retValue; 157 } // takeMax() 158 159 /** 160 * Returns a 161 * {@link Selector} 162 * implementation that will always return the index for the smallest 163 * value from the given array, based on the natural order of the values. 164 * 165 * @param <T> The type of the values to select from. 166 * @return The selector. 167 */ 168 @API( status = STABLE, since = "0.0.7" ) 169 public static <T extends Comparable<T>> Selector<T> takeMin() 170 { 171 final Selector<T> retValue = takeMin( Comparator.naturalOrder() ); 172 173 //---* Done *---------------------------------------------------------- 174 return retValue; 175 } // takeMin() 176 177 /** 178 * Returns a 179 * {@link Selector} 180 * implementation that will always return the index for the smallest 181 * value from the given array, imposed by the given comparator. 182 * 183 * @param <T> The type of the values to select from. 184 * @param comparator The comparator. 185 * @return The selector. 186 */ 187 @API( status = STABLE, since = "0.0.7" ) 188 public static <T> Selector<T> takeMin( final Comparator<? super T> comparator ) 189 { 190 requireNonNullArgument( comparator, "comparator" ); 191 192 @SuppressWarnings( "AnonymousInnerClass" ) 193 final Selector<T> retValue = new Selector<>() 194 { 195 /** 196 * The start index. 197 */ 198 private int m_StartIndex = 0; 199 200 /** 201 * {@inheritDoc} 202 * 203 * @throws NoSuchElementException All elements of the given array 204 * are {@code null}. 205 */ 206 @SuppressWarnings( "OptionalGetWithoutIsPresent" ) 207 @Override 208 public final Integer apply( final T [] options ) 209 { 210 final var smallest = Stream.of( options ) 211 .filter( Objects::nonNull ) 212 .min( comparator ) 213 .get(); 214 215 var result = m_StartIndex; 216 while( isNull( options [result] ) || (comparator.compare( smallest, options [result] ) != 0) ) 217 { 218 result = (result + 1) % options.length; 219 } 220 221 m_StartIndex = (result + 1) % options.length; 222 return result; 223 } // apply() 224 }; 225 226 //---* Done *---------------------------------------------------------- 227 return retValue; 228 } // takeMin() 229} 230// class Selectors 231 232/* 233 * End of File 234 */