001/*
002 * Units of Measurement Common Library
003 * Copyright (c) 2005-2018, Jean-Marie Dautelle, 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-363 nor the names of its 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;
039
040import tech.uom.lib.common.util.NaturalQuantityComparator;
041
042/**
043 * Utility methods for operating on {@link Unit units} and {@link Quantity quantities} with Java 
044 * {@linkplain FunctionalInterface functional interfaces} like {@linkplain Function}. 
045 * 
046 * @author Otavio
047 * @author Werner
048 * @version 1.0.3
049 * @since 2.0
050 *
051 */
052@SuppressWarnings("rawtypes")
053public final class QuantityFunctions {
054
055  private QuantityFunctions() {
056          throw new Error("no instances");
057  }
058
059  /**
060   * Creates a comparator to sort by number, ignoring the unit.
061   * 
062   * @return <p>
063   *         <b>Given:</b>
064   *         <p>
065   *         <code>
066   * Quantity<Time> day = timeFactory.create(1, Units.DAY);<br>
067   * Quantity<Time> hours = timeFactory.create(18, Units.HOUR);<br>
068   * Quantity<Time> minutes = timeFactory.create(15, Units.HOUR);<br>
069   * Quantity<Time> seconds = timeFactory.create(100, Units.HOUR);<br>
070   * </code>
071   *         <p>
072   *         will return: <code>day, hours, minutes, seconds</code>
073   *         </p>
074   * @throws NullPointerException
075   */
076  public static <Q extends Quantity<Q>> Comparator<Quantity<Q>> sortNumber() {
077                return (q1, q2) -> Double.compare(q1.getValue().doubleValue(), q2.getValue().doubleValue());
078        }
079
080  /**
081   * Creates a comparator to sort by number descending, ignoring the unit.
082   * 
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
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   * @return <p>
106   *         <b>Given:</b>
107   *         <p>
108   *         <code>
109   * Quantity<Time> day = timeFactory.create(1, Units.DAY);<br>
110   * Quantity<Time> hours = timeFactory.create(18, Units.HOUR);<br>
111   * Quantity<Time> minutes = timeFactory.create(15, Units.HOUR);<br>
112   * Quantity<Time> seconds = timeFactory.create(100, Units.HOUR);<br>
113   * </code>
114   *         <p>
115   *         will return: <code>day, hours, minutes, seconds</code>
116   *         </p>
117   * @throws NullPointerException
118   */
119  public static <Q extends Quantity<Q>> Comparator<Quantity<Q>> sortSymbol() {
120                return (q1, q2) -> q1.getUnit().getSymbol().compareTo(q2.getUnit().getSymbol());
121        }
122
123  /**
124   * Creates a comparator to sort by name descending, ignoring the value.
125   * 
126   * @return <p>
127   *         <b>Given:</b>
128   *         </p>
129   *         <code>
130   * Quantity<Time> day = timeFactory.create(1, Units.DAY);<br>
131   * Quantity<Time> hours = timeFactory.create(18, Units.HOUR);<br>
132   * Quantity<Time> minutes = timeFactory.create(15, Units.HOUR);<br>
133   * Quantity<Time> seconds = timeFactory.create(100, Units.HOUR);<br>
134   * </code>
135   *         <p>
136   *         will return: <code>seconds, minutes, hour,  day</code>
137   *         </p>
138   * @throws NullPointerException
139   */
140  public static <Q extends Quantity<Q>> Comparator<Quantity<Q>> sortSymbolDesc() {
141    Comparator<Quantity<Q>> sortSymbol = sortSymbol();
142    return sortSymbol.reversed();
143  }
144
145  /**
146   * Creates a comparator to sort by natural order, looking to both the unit and the value.
147   * 
148   * @return <p>
149   *         <b>Given:</b>
150   *         </p>
151   *         <code>
152   * Quantity<Time> day = timeFactory.create(1, Units.DAY);<br>
153   * Quantity<Time> hours = timeFactory.create(18, Units.HOUR);<br>
154   * Quantity<Time> minutes = timeFactory.create(15, Units.HOUR);<br>
155   * Quantity<Time> seconds = timeFactory.create(100, Units.HOUR);<br>
156   * </code>
157   *         <p>
158   *         will return: <code>seconds, minutes, hours, day</code>
159   *         </p>
160   * @throws NullPointerException
161   */
162  @SuppressWarnings("unchecked")
163  public static <Q extends Quantity<Q>> Comparator<Quantity<Q>> sortNatural() {
164    return new NaturalQuantityComparator();
165  }
166
167  /**
168   * Creates a comparator to sort by natural order descending, looking to both the unit and the value.
169   * 
170   * @return <p>
171   *         <b>Given:</b>
172   *         </p>
173   *         <code>
174   * Quantity<Time> day = timeFactory.create(1, Units.DAY);<br>
175   * Quantity<Time> hours = timeFactory.create(18, Units.HOUR);<br>
176   * Quantity<Time> minutes = timeFactory.create(15, Units.HOUR);<br>
177   * Quantity<Time> seconds = timeFactory.create(100, Units.HOUR);<br>
178   * </code>
179   *         <p>
180   *         will return: <code>day, hour, minute, second</code>
181   *         </p>
182   * @throws NullPointerException
183   */
184  public static <Q extends Quantity<Q>> Comparator<Quantity<Q>> sortNaturalDesc() {
185    Comparator<Quantity<Q>> sortNatural = sortNatural();
186    return sortNatural.reversed();
187  }
188
189  /**
190   * Creates a BinaryOperator to calculate the minimum Quantity
191   * 
192   * @return the min BinaryOperator, not null.
193   */
194  public static <Q extends Quantity<Q>> BinaryOperator<Quantity<Q>> min() {
195
196                return (q1, q2) -> {
197                        double d1 = q1.getValue().doubleValue();
198                        double d2 = q2.to(q1.getUnit()).getValue().doubleValue();
199                        double min = Double.min(d1, d2);
200                        if (min == d1) {
201                                return q1;
202                        }
203                        return q2;
204                };
205        }
206
207  /**
208   * Creates a BinaryOperator to calculate the maximum Quantity
209   * 
210   * @return the max BinaryOperator, not null.
211   */
212  public static <Q extends Quantity<Q>> BinaryOperator<Quantity<Q>> max() {
213
214                return (q1, q2) -> {
215                        double d1 = q1.getValue().doubleValue();
216                        double d2 = q2.to(q1.getUnit()).getValue().doubleValue();
217                        double min = Double.max(d1, d2);
218                        if (min == d1) {
219                                return q1;
220                        }
221                        return q2;
222                };
223        }
224
225  /**
226   * Creates a BinaryOperator to sum.
227   * 
228   * @return the sum BinaryOperator
229   */
230  public static <Q extends Quantity<Q>> BinaryOperator<Quantity<Q>> sum() {
231                return Quantity::add;
232        }
233
234  /**
235   * Creates a BinaryOperator to sum converting to unit
236   * 
237   * @param unit
238   *          unit to be converting
239   * @return the sum BinaryOperator converting to unit
240   */
241  public static <Q extends Quantity<Q>> BinaryOperator<Quantity<Q>> sum(Unit<Q> unit) {
242                return (q1, q2) -> q1.to(unit).add(q2.to(unit));
243        }
244
245  /**
246   * Predicate to filter to one or more units
247   * 
248   * @param units
249   *          - units to be filtered (optional)
250   * @return A predicate to filter one or more units
251   */
252  @SafeVarargs
253        public static <Q extends Quantity<Q>> Predicate<Quantity<Q>> fiterByUnit(Unit<Q>... units) {
254
255                if (Objects.isNull(units) || units.length == 0) {
256                        return q -> true;
257                }
258                Predicate<Quantity<Q>> predicate = null;
259                for (Unit<Q> u : units) {
260                        if (Objects.isNull(predicate)) {
261                                predicate = q -> q.getUnit().equals(u);
262                        } else {
263                                predicate = predicate.or(q -> q.getUnit().equals(u));
264                        }
265                }
266                return predicate;
267        }
268
269  /**
270   * Predicate to filter excluding these units
271   * 
272   * @param units
273   *          - units to be filtered (optional)
274   * @return A predicate to filter to not be these units
275   */
276  @SafeVarargs
277        public static <Q extends Quantity<Q>> Predicate<Quantity<Q>> fiterByExcludingUnit(Unit<Q>... units) {
278                if (Objects.isNull(units) || units.length == 0) {
279                        return q -> true;
280                }
281                return fiterByUnit(units).negate();
282        }
283
284  /**
285   * creates a Filter to greater than number, ignoring units
286   * 
287   * @param value
288   *          - the value to be used in Predicate
289   * @return the Predicate greater than this number, ignoring units
290   */
291  public static <Q extends Quantity<Q>> Predicate<Quantity<Q>> isGreaterThan(Number value) {
292                return q -> q.getValue().doubleValue() > value.doubleValue();
293        }
294
295  /**
296   * creates a filter to greater than the quantity measure
297   * 
298   * @param quantity
299   *          - the measure to be used in filter
300   * @return the Predicate greater than this measure
301   */
302  public static <Q extends Quantity<Q>> Predicate<Quantity<Q>> isGreaterThan(Quantity<Q> quantity) {
303                return q -> q.to(quantity.getUnit()).getValue().doubleValue() > quantity.getValue().doubleValue();
304        }
305
306  /**
307   * creates a Filter to greater or equals than number, ignoring units
308   * 
309   * @param value
310   *          - the value to be used in Predicate
311   * @return the Predicate greater or equals than this number, ignoring units
312   */
313  public static <Q extends Quantity<Q>> Predicate<Quantity<Q>> isGreaterThanOrEqualTo(Number value) {
314                return q -> q.getValue().doubleValue() >= value.doubleValue();
315        }
316
317  /**
318   * creates a filter to greater or equals than the quantity measure
319   * 
320   * @param quantity
321   *          - the measure to be used in filter
322   * @return the Predicate greater or equals than this measure
323   */
324  public static <Q extends Quantity<Q>> Predicate<Quantity<Q>> isGreaterThanOrEqualTo(Quantity<Q> quantity) {
325                return q -> q.to(quantity.getUnit()).getValue().doubleValue() >= quantity.getValue().doubleValue();
326        }
327
328  /**
329   * creates a Filter to lesser than number, ignoring units
330   * 
331   * @param value
332   *          - the value to be used in Predicate
333   * @return the Predicate greater than this number, ignoring units
334   */
335  public static <Q extends Quantity<Q>> Predicate<Quantity<Q>> isLessThan(Number value) {
336                return q -> q.getValue().doubleValue() < value.doubleValue();
337        }
338
339  /**
340   * creates a filter to lesser than the quantity measure
341   * 
342   * @param quantity
343   *          - the measure to be used in filter
344   * @return the Predicate lesser than this measure
345   */
346  public static <Q extends Quantity<Q>> Predicate<Quantity<Q>> isLessThan(Quantity<Q> quantity) {
347                return q -> q.to(quantity.getUnit()).getValue().doubleValue() < quantity.getValue().doubleValue();
348        }
349
350  /**
351   * creates a Filter to lesser or equals than number, ignoring units
352   * 
353   * @param value
354   *          - the value to be used in Predicate
355   * @return the Predicate lesser or equals than this number, ignoring units
356   */
357  public static <Q extends Quantity<Q>> Predicate<Quantity<Q>> isLessThanOrEqualTo(Number value) {
358                return q -> q.getValue().doubleValue() <= value.doubleValue();
359        }
360
361  /**
362   * creates a filter to lesser or equals than the quantity measure
363   * 
364   * @param quantity
365   *          - the measure to be used in filter
366   * @return the Predicate lesser or equals than this measure
367   */
368  public static <Q extends Quantity<Q>> Predicate<Quantity<Q>> isLessThanOrEqualTo(Quantity<Q> quantity) {
369                return q -> q.to(quantity.getUnit()).getValue().doubleValue() <= quantity.getValue().doubleValue();
370        }
371
372  /**
373   * creates a Filter to between, lesser or equals and greater or equals, than number, ignoring units
374   * 
375   * @param min
376   *          - the min value to be used in Predicate
377   * @param max
378   *          - the max value to be used in Predicate
379   * @return the Predicate lesser or equals than this number, ignoring units
380   */
381  public static <Q extends Quantity<Q>> Predicate<Quantity<Q>> isBetween(Number min, Number max) {
382    Predicate<Quantity<Q>> minFilter = isGreaterThanOrEqualTo(min);
383    Predicate<Quantity<Q>> maxFilter = isLessThanOrEqualTo(max);
384    return minFilter.and(maxFilter);
385  }
386
387  /**
388   * creates a filter to between, lesser or equals and greater or equals, than the quantity measure
389   * 
390   * @param min
391   *          - the min value to be used in Predicate
392   * @param max
393   *          - the max value to be used in Predicate
394   * @return the Predicate lesser or equals than this measure
395   */
396  public static <Q extends Quantity<Q>> Predicate<Quantity<Q>> isBetween(Quantity<Q> min, Quantity<Q> max) {
397    return isGreaterThanOrEqualTo(min).and(isLessThanOrEqualTo(max));
398  }
399
400  /**
401   * creates a function to group quantities by their {@link Unit unit}.
402   * 
403   * @return the Function of {@link Quantity} grouped by {@link Unit}
404   */
405  public static <Q extends Quantity<Q>> Function<Quantity<Q>, Unit<Q>> groupByUnit() {
406                return Quantity::getUnit;
407        }
408}