001/*
002 * Units of Measurement Reference Implementation
003 * Copyright (c) 2005-2023, Jean-Marie Dautelle, Werner Keil, Otavio Santana.
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.units.indriya.quantity;
031
032import static javax.measure.Quantity.Scale.ABSOLUTE;
033
034import java.math.BigDecimal;
035import java.math.BigInteger;
036import java.util.Objects;
037
038import javax.measure.Quantity;
039import javax.measure.Quantity.Scale;
040import javax.measure.Unit;
041
042import tech.units.indriya.ComparableQuantity;
043import tech.units.indriya.format.SimpleQuantityFormat;
044import tech.units.indriya.function.MixedRadix;
045
046/**
047 * Facade to access {@link Quantity} instances.
048 * 
049 * @version 2.4, June 5, 2023
050 * @author Werner Keil
051 * @author Otavio Santana
052 * @since 1.0
053 */
054public final class Quantities {
055        /**
056         * Private singleton constructor.
057         */
058        private Quantities() {
059        }
060
061        /**
062         * Returns the scalar quantity of unknown type corresponding to the specified
063         * representation. This method can be used to parse {@link MixedQuantity mixed}
064         * quantities. All of these expressions:<br>
065         * <code>
066         *     Quantity&lt;Length&gt; height      =  Quantities.getQuantity("1.70 m").asType(Length.class);<br>
067         *     Quantity&lt;Length&gt; heightinCm  =  Quantities.getQuantity("170 cm").asType(Length.class);<br>
068         *     Quantity&lt;Length&gt; heightMixed = Quantities.getQuantity("1 m 70 cm").asType(Length.class);
069         * </code>
070         * are equally supported.
071         * 
072         * <p>
073         * <b>Note:</b> This method handles only <code>Locale</code>-neutral quantity formatting and parsing
074         * are handled by the {@link SimpleQuantityFormat} class.<br>
075         * Due to the versatile parsing of this method recognizing both single and mixed quantities, a unit must be provided, otherwise it'll fail. 
076         * If you need to parse a unit-less quantity, please use the <code>parse()</code> method of {@link tech.units.indriya.AbstractQuantity AbstractQuantity} instead.
077         * </p>
078         *
079         * @param csq the decimal value(s) and unit(s) separated by space(s).
080         * @return <code>SimpleQuantityFormat.getInstance("n u~ ").parse(csq)</code>
081         * @throws IllegalArgumentException if no unit part was provided to parse
082         */
083        public static Quantity<?> getQuantity(CharSequence csq) {
084                //try {
085                        return SimpleQuantityFormat.getInstance("n u~ ").parse(csq);
086                //} catch (MeasurementParseException e) {
087//                      throw new IllegalArgumentException(e.getParsedString());
088        //      }
089        }
090
091        /**
092         * Returns the scalar quantity. When the {@link Number} was {@link BigDecimal}
093         * or {@link BigInteger} will uses {@link DecimalQuantity}, when the
094         * {@link Number} was {@link Double} will {@link DoubleQuantity} otherwise will
095         * {@link NumberQuantity}. in the specified unit.
096         * 
097         * @param value the measurement value.
098         * @param unit  the measurement unit.
099         * @param scale the measurement scale.
100         * @return the corresponding <code>numeric</code> quantity.
101         * @throws NullPointerException if value, unit or scale were null
102         * @since 2.0
103         */
104        public static <Q extends Quantity<Q>> ComparableQuantity<Q> getQuantity(Number value, Unit<Q> unit, Scale scale) {
105                Objects.requireNonNull(value);
106                Objects.requireNonNull(unit);
107                Objects.requireNonNull(scale);
108                return new NumberQuantity<>(value, unit, scale);
109        }
110
111        /**
112         * Returns the scalar quantity. When the {@link Number} was {@link BigDecimal}
113         * or {@link BigInteger} will uses {@link DecimalQuantity}, when the
114         * {@link Number} was {@link Double} will {@link DoubleQuantity} otherwise will
115         * {@link NumberQuantity}. in the specified unit.
116         * 
117         * @param value the measurement value.
118         * @param unit  the measurement unit.
119         * @return the corresponding <code>numeric</code> quantity.
120         * @throws NullPointerException when value or unit were null
121         */
122        public static <Q extends Quantity<Q>> ComparableQuantity<Q> getQuantity(Number value, Unit<Q> unit) {
123                return getQuantity(value, unit, ABSOLUTE);
124        }
125
126        /**
127         * Returns the mixed radix values and units combined into a single quantity.
128         * When the {@link Number} was {@link BigDecimal} or {@link BigInteger} will
129         * uses {@link DecimalQuantity}, when the {@link Number} was {@link Double} will
130         * {@link DoubleQuantity} otherwise will {@link NumberQuantity}. in the
131         * specified unit.
132         * 
133         * @param values the measurement values.
134         * @param units  the measurement units.
135         * @param scale  the measurement scale.
136         * @return the corresponding quantity.
137         * @throws NullPointerException     if values or scale were null
138         * @throws IllegalArgumentException if the size of the values array does not
139         *                                  match that of units.
140         * @since 2.0
141         */
142        public static <Q extends Quantity<Q>> Quantity<Q> getQuantity(Number[] values, Unit<Q>[] units, Scale scale) {
143                Objects.requireNonNull(values);
144                Objects.requireNonNull(units);
145                if (values.length == units.length) {
146                        return MixedRadix.of(units).createQuantity(values, scale);
147                } else {
148                        throw new IllegalArgumentException(
149                                        String.format("%s values don't match %s units", values.length, units.length));
150                }
151        }
152
153        /**
154         * Returns the mixed radix values and units combined into a single quantity in
155         * the {@code ABSOLUTE} scale.
156         * 
157         * @param values the measurement values.
158         * @param units  the measurement units.
159         * @return the corresponding quantity.
160         * @throws NullPointerException     if values or units were null
161         * @throws IllegalArgumentException if the size of the values array does not
162         *                                  match that of units.
163         * @since 2.0
164         */
165        @SafeVarargs
166        public static <Q extends Quantity<Q>> Quantity<Q> getQuantity(Number[] values, Unit<Q>... units) {
167                return getQuantity(values, units, ABSOLUTE);
168        }
169
170        /**
171         * Returns the mixed radix values and units as {@link MixedQuantity} in the
172         * specified scale.
173         * 
174         * @param values the measurement values.
175         * @param units  the measurement units.
176         * @param scale  the measurement scale.
177         * @return the corresponding mixed quantity.
178         * @throws NullPointerException     if values, units or scale were null
179         * @throws IllegalArgumentException if the size of the values array does not
180         *                                  match that of units.
181         * @since 2.1.2
182         */
183        public static <Q extends Quantity<Q>> MixedQuantity<Q> getMixedQuantity(Number[] values, Unit<Q>[] units,
184                        Scale scale) {
185                Objects.requireNonNull(values);
186                Objects.requireNonNull(units);
187                if (values.length == units.length) {
188                        return MixedRadix.of(units).createMixedQuantity(values, scale);
189                } else {
190                        throw new IllegalArgumentException(
191                                        String.format("%s values don't match %s units", values.length, units.length));
192                }
193        }
194
195        /**
196         * Returns the mixed radix values and units as {@link MixedQuantity} in the
197         * {@code ABSOLUTE} scale.
198         * 
199         * @param values the measurement values.
200         * @param units  the measurement units.
201         * @return the corresponding mixed quantity.
202         * @throws NullPointerException     if values, units or scale were null
203         * @throws IllegalArgumentException if the size of the values array does not
204         *                                  match that of units.
205         * @since 2.1.2
206         */
207        public static <Q extends Quantity<Q>> MixedQuantity<Q> getMixedQuantity(final Number[] values,
208                        final Unit<Q>[] units) {
209                return getMixedQuantity(values, units, ABSOLUTE);
210        }
211}