001/* 002 * Units of Measurement Common Library for Java 003 * Copyright (c) 2005-2023, Werner Keil and others. 004 * 005 * All rights reserved. 006 * 007 * Redistribution and use in source and binary forms, with or without modification, 008 * are permitted provided that the following conditions are met: 009 * 010 * 1. Redistributions of source code must retain the above copyright notice, 011 * this list of conditions and the following disclaimer. 012 * 013 * 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions 014 * and the following disclaimer in the documentation and/or other materials provided with the distribution. 015 * 016 * 3. Neither the name of JSR-385, Indriya nor the names of their contributors may be used to endorse or promote products 017 * derived from this software without specific prior written permission. 018 * 019 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 020 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 021 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 022 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 023 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 024 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 025 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 026 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 027 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 028 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 029 */ 030package tech.uom.lib.common.function; 031 032import java.util.Comparator; 033import java.util.Objects; 034import java.util.function.BinaryOperator; 035import java.util.function.Function; 036import java.util.function.Predicate; 037import javax.measure.Quantity; 038import javax.measure.Unit; 039import tech.uom.lib.common.util.NaturalQuantityComparator; 040 041/** 042 * Utility methods for operating on {@link Unit units} and {@link Quantity quantities} with Java 043 * {@linkplain FunctionalInterface functional interfaces} like {@linkplain Function}. 044 * 045 * @author Otavio 046 * @author Werner 047 * @version 1.0.3 048 * @since 2.0 049 */ 050@SuppressWarnings("rawtypes") 051public final class QuantityFunctions { 052 053 private QuantityFunctions() { 054 throw new Error("no instances"); 055 } 056 057 /** 058 * Creates a comparator to sort by number, ignoring the unit. 059 * 060 * @param <Q> the type of quantity 061 * @return <p> 062 * <b>Given:</b> 063 * <p> 064 * <code> 065 * Quantity<Time> day = timeFactory.create(1, Units.DAY);<br> 066 * Quantity<Time> hours = timeFactory.create(18, Units.HOUR);<br> 067 * Quantity<Time> minutes = timeFactory.create(15, Units.HOUR);<br> 068 * Quantity<Time> seconds = timeFactory.create(100, Units.HOUR);<br> 069 * </code> 070 * <p> 071 * will return: <code>day, hours, minutes, seconds</code> 072 * </p> 073 * @throws NullPointerException if any of the values to compare is {@code null} 074 */ 075 public static <Q extends Quantity<Q>> Comparator<Quantity<Q>> sortNumber() { 076 return (q1, q2) -> Double.compare(q1.getValue().doubleValue(), q2.getValue().doubleValue()); 077 } 078 079 /** 080 * Creates a comparator to sort by number descending, ignoring the unit. 081 * 082 * @param <Q> the type of quantity 083 * @return <p> 084 * <b>Given:</b> 085 * <p> 086 * <code> 087 * Quantity<Time> day = timeFactory.create(1, Units.DAY);<br> 088 * Quantity<Time> hours = timeFactory.create(18, Units.HOUR);<br> 089 * Quantity<Time> minutes = timeFactory.create(15, Units.HOUR);<br> 090 * Quantity<Time> seconds = timeFactory.create(100, Units.HOUR);<br> 091 * </code> 092 * <p> 093 * will return: <code>seconds, hours, minutes, day</code> 094 * </p> 095 * @throws NullPointerException if any of the values to compare is {@code null} 096 */ 097 public static <Q extends Quantity<Q>> Comparator<Quantity<Q>> sortNumberDesc() { 098 Comparator<Quantity<Q>> sortNumber = sortNumber(); 099 return sortNumber.reversed(); 100 } 101 102 /** 103 * Creates a comparator to sort by name, ignoring the value. 104 * 105 * @param <Q> the type of quantity 106 * @return <p> 107 * <b>Given:</b> 108 * <p> 109 * <code> 110 * Quantity<Time> day = timeFactory.create(1, Units.DAY);<br> 111 * Quantity<Time> hours = timeFactory.create(18, Units.HOUR);<br> 112 * Quantity<Time> minutes = timeFactory.create(15, Units.HOUR);<br> 113 * Quantity<Time> seconds = timeFactory.create(100, Units.HOUR);<br> 114 * </code> 115 * <p> 116 * will return: <code>day, hours, minutes, seconds</code> 117 * </p> 118 * @throws NullPointerException if any of the values to compare is {@code null} 119 */ 120 public static <Q extends Quantity<Q>> Comparator<Quantity<Q>> sortSymbol() { 121 return (q1, q2) -> q1.getUnit().getSymbol().compareTo(q2.getUnit().getSymbol()); 122 } 123 124 /** 125 * Creates a comparator to sort by name descending, ignoring the value. 126 * 127 * @param <Q> the type of quantity 128 * @return <p> 129 * <b>Given:</b> 130 * </p> 131 * <code> 132 * Quantity<Time> day = timeFactory.create(1, Units.DAY);<br> 133 * Quantity<Time> hours = timeFactory.create(18, Units.HOUR);<br> 134 * Quantity<Time> minutes = timeFactory.create(15, Units.HOUR);<br> 135 * Quantity<Time> seconds = timeFactory.create(100, Units.HOUR);<br> 136 * </code> 137 * <p> 138 * will return: <code>seconds, minutes, hour, day</code> 139 * </p> 140 * @throws NullPointerException if any of the values to compare is {@code null} 141 */ 142 public static <Q extends Quantity<Q>> Comparator<Quantity<Q>> sortSymbolDesc() { 143 Comparator<Quantity<Q>> sortSymbol = sortSymbol(); 144 return sortSymbol.reversed(); 145 } 146 147 /** 148 * Creates a comparator to sort by natural order, looking to both the unit and the value. 149 * 150 * @param <Q> the type of quantity 151 * @return <p> 152 * <b>Given:</b> 153 * </p> 154 * <code> 155 * Quantity<Time> day = timeFactory.create(1, Units.DAY);<br> 156 * Quantity<Time> hours = timeFactory.create(18, Units.HOUR);<br> 157 * Quantity<Time> minutes = timeFactory.create(15, Units.HOUR);<br> 158 * Quantity<Time> seconds = timeFactory.create(100, Units.HOUR);<br> 159 * </code> 160 * <p> 161 * will return: <code>seconds, minutes, hours, day</code> 162 * </p> 163 * @throws NullPointerException if any of the values to compare is {@code null} 164 */ 165 @SuppressWarnings("unchecked") 166 public static <Q extends Quantity<Q>> Comparator<Quantity<Q>> sortNatural() { 167 return new NaturalQuantityComparator(); 168 } 169 170 /** 171 * Creates a comparator to sort by natural order descending, looking to both the unit and the value. 172 * 173 * @param <Q> the type of quantity 174 * @return <p> 175 * <b>Given:</b> 176 * </p> 177 * <code> 178 * Quantity<Time> day = timeFactory.create(1, Units.DAY);<br> 179 * Quantity<Time> hours = timeFactory.create(18, Units.HOUR);<br> 180 * Quantity<Time> minutes = timeFactory.create(15, Units.HOUR);<br> 181 * Quantity<Time> seconds = timeFactory.create(100, Units.HOUR);<br> 182 * </code> 183 * <p> 184 * will return: <code>day, hour, minute, second</code> 185 * </p> 186 * @throws NullPointerException if any of the values to compare is {@code null} 187 */ 188 public static <Q extends Quantity<Q>> Comparator<Quantity<Q>> sortNaturalDesc() { 189 Comparator<Quantity<Q>> sortNatural = sortNatural(); 190 return sortNatural.reversed(); 191 } 192 193 /** 194 * Creates a BinaryOperator to calculate the minimum Quantity 195 * 196 * @param <Q> the type of quantity 197 * @return the min BinaryOperator, not null. 198 */ 199 public static <Q extends Quantity<Q>> BinaryOperator<Quantity<Q>> min() { 200 201 return (q1, q2) -> { 202 double d1 = q1.getValue().doubleValue(); 203 double d2 = q2.to(q1.getUnit()).getValue().doubleValue(); 204 double min = Double.min(d1, d2); 205 if (min == d1) { 206 return q1; 207 } 208 return q2; 209 }; 210 } 211 212 /** 213 * Creates a BinaryOperator to calculate the maximum Quantity 214 * 215 * @param <Q> the type of quantity 216 * @return the max BinaryOperator, not null. 217 */ 218 public static <Q extends Quantity<Q>> BinaryOperator<Quantity<Q>> max() { 219 220 return (q1, q2) -> { 221 double d1 = q1.getValue().doubleValue(); 222 double d2 = q2.to(q1.getUnit()).getValue().doubleValue(); 223 double min = Double.max(d1, d2); 224 if (min == d1) { 225 return q1; 226 } 227 return q2; 228 }; 229 } 230 231 /** 232 * Creates a BinaryOperator to sum. 233 * 234 * @param <Q> the type of quantity 235 * @return the sum BinaryOperator 236 */ 237 public static <Q extends Quantity<Q>> BinaryOperator<Quantity<Q>> sum() { 238 return Quantity::add; 239 } 240 241 /** 242 * Creates a BinaryOperator to sum converting to unit 243 * 244 * @param unit unit to be converting 245 * @param <Q> the type of quantity 246 * @return the sum BinaryOperator converting to unit 247 */ 248 public static <Q extends Quantity<Q>> BinaryOperator<Quantity<Q>> sum(Unit<Q> unit) { 249 return (q1, q2) -> q1.to(unit).add(q2.to(unit)); 250 } 251 252 /** 253 * Predicate to filter to one or more units 254 * 255 * @param units - units to be filtered (optional) 256 * @param <Q> the type of quantity 257 * @return A predicate to filter one or more units 258 */ 259 @SafeVarargs 260 public static <Q extends Quantity<Q>> Predicate<Quantity<Q>> fiterByUnit(Unit<Q>... units) { 261 262 if (Objects.isNull(units) || units.length == 0) { 263 return q -> true; 264 } 265 Predicate<Quantity<Q>> predicate = null; 266 for (Unit<Q> u : units) { 267 if (Objects.isNull(predicate)) { 268 predicate = q -> q.getUnit().equals(u); 269 } else { 270 predicate = predicate.or(q -> q.getUnit().equals(u)); 271 } 272 } 273 return predicate; 274 } 275 276 /** 277 * Predicate to filter excluding these units 278 * 279 * @param units - units to be filtered (optional) 280 * @param <Q> the type of quantity 281 * @return A predicate to filter to not be these units 282 */ 283 @SafeVarargs 284 public static <Q extends Quantity<Q>> Predicate<Quantity<Q>> fiterByExcludingUnit(Unit<Q>... units) { 285 if (Objects.isNull(units) || units.length == 0) { 286 return q -> true; 287 } 288 return fiterByUnit(units).negate(); 289 } 290 291 /** 292 * creates a Filter to greater than number, ignoring units 293 * 294 * @param value - the value to be used in Predicate 295 * @param <Q> the type of quantity 296 * @return the Predicate greater than this number, ignoring units 297 */ 298 public static <Q extends Quantity<Q>> Predicate<Quantity<Q>> isGreaterThan(Number value) { 299 return q -> q.getValue().doubleValue() > value.doubleValue(); 300 } 301 302 /** 303 * creates a filter to greater than the quantity measure 304 * 305 * @param quantity - the measure to be used in filter 306 * @param <Q> the type of quantity 307 * @return the Predicate greater than this measure 308 */ 309 public static <Q extends Quantity<Q>> Predicate<Quantity<Q>> isGreaterThan(Quantity<Q> quantity) { 310 return q -> q.to(quantity.getUnit()).getValue().doubleValue() > quantity.getValue().doubleValue(); 311 } 312 313 /** 314 * creates a Filter to greater or equals than number, ignoring units 315 * 316 * @param value - the value to be used in Predicate 317 * @param <Q> the type of quantity 318 * @return the Predicate greater or equals than this number, ignoring units 319 */ 320 public static <Q extends Quantity<Q>> Predicate<Quantity<Q>> isGreaterThanOrEqualTo(Number value) { 321 return q -> q.getValue().doubleValue() >= value.doubleValue(); 322 } 323 324 /** 325 * creates a filter to greater or equals than the quantity measure 326 * 327 * @param quantity - the measure to be used in filter 328 * @param <Q> the type of quantity 329 * @return the Predicate greater or equals than this measure 330 */ 331 public static <Q extends Quantity<Q>> Predicate<Quantity<Q>> isGreaterThanOrEqualTo(Quantity<Q> quantity) { 332 return q -> q.to(quantity.getUnit()).getValue().doubleValue() >= quantity.getValue().doubleValue(); 333 } 334 335 /** 336 * creates a Filter to lesser than number, ignoring units 337 * 338 * @param value - the value to be used in Predicate 339 * @param <Q> the type of quantity 340 * @return the Predicate greater than this number, ignoring units 341 */ 342 public static <Q extends Quantity<Q>> Predicate<Quantity<Q>> isLessThan(Number value) { 343 return q -> q.getValue().doubleValue() < value.doubleValue(); 344 } 345 346 /** 347 * creates a filter to lesser than the quantity measure 348 * 349 * @param quantity - the measure to be used in filter 350 * @param <Q> the type of quantity 351 * @return the Predicate lesser than this measure 352 */ 353 public static <Q extends Quantity<Q>> Predicate<Quantity<Q>> isLessThan(Quantity<Q> quantity) { 354 return q -> q.to(quantity.getUnit()).getValue().doubleValue() < quantity.getValue().doubleValue(); 355 } 356 357 /** 358 * creates a Filter to lesser or equals than number, ignoring units 359 * 360 * @param value - the value to be used in Predicate 361 * @param <Q> the type of quantity 362 * @return the Predicate lesser or equals than this number, ignoring units 363 */ 364 public static <Q extends Quantity<Q>> Predicate<Quantity<Q>> isLessThanOrEqualTo(Number value) { 365 return q -> q.getValue().doubleValue() <= value.doubleValue(); 366 } 367 368 /** 369 * creates a filter to lesser or equals than the quantity measure 370 * 371 * @param quantity - the measure to be used in filter 372 * @param <Q> the type of quantity 373 * @return the Predicate lesser or equals than this measure 374 */ 375 public static <Q extends Quantity<Q>> Predicate<Quantity<Q>> isLessThanOrEqualTo(Quantity<Q> quantity) { 376 return q -> q.to(quantity.getUnit()).getValue().doubleValue() <= quantity.getValue().doubleValue(); 377 } 378 379 /** 380 * creates a Filter to between, lesser or equals and greater or equals, than number, ignoring units 381 * 382 * @param min - the min value to be used in Predicate 383 * @param max - the max value to be used in Predicate 384 * @param <Q> the type of quantity 385 * @return the Predicate lesser or equals than this number, ignoring units 386 */ 387 public static <Q extends Quantity<Q>> Predicate<Quantity<Q>> isBetween(Number min, Number max) { 388 Predicate<Quantity<Q>> minFilter = isGreaterThanOrEqualTo(min); 389 Predicate<Quantity<Q>> maxFilter = isLessThanOrEqualTo(max); 390 return minFilter.and(maxFilter); 391 } 392 393 /** 394 * creates a filter to between, lesser or equals and greater or equals, than the quantity measure 395 * 396 * @param min - the min value to be used in Predicate 397 * @param max - the max value to be used in Predicate 398 * @param <Q> the type of quantity 399 * @return the Predicate lesser or equals than this measure 400 */ 401 public static <Q extends Quantity<Q>> Predicate<Quantity<Q>> isBetween(Quantity<Q> min, Quantity<Q> max) { 402 return isGreaterThanOrEqualTo(min).and(isLessThanOrEqualTo(max)); 403 } 404 405 /** 406 * creates a function to group quantities by their {@link Unit unit}. 407 * 408 * @param <Q> the type of quantity 409 * @return the Function of {@link Quantity} grouped by {@link Unit} 410 */ 411 public static <Q extends Quantity<Q>> Function<Quantity<Q>, Unit<Q>> groupByUnit() { 412 return Quantity::getUnit; 413 } 414}