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