001/*
002 * Units of Measurement Reference Implementation
003 * Copyright (c) 2005-2024, 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 3.0, October 3, 2024
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 of type {@link NumberQuantity} in the specified unit and scale.
093         * 
094         * @param value the measurement value.
095         * @param unit  the measurement unit.
096         * @param scale the measurement scale.
097         * @return the corresponding <code>numeric</code> quantity.
098         * @throws NullPointerException if value, unit or scale were null
099         * @since 2.0
100         */
101        public static <Q extends Quantity<Q>> ComparableQuantity<Q> getQuantity(Number value, Unit<Q> unit, Scale scale) {
102                Objects.requireNonNull(value);
103                Objects.requireNonNull(unit);
104                Objects.requireNonNull(scale);
105                return new NumberQuantity<>(value, unit, scale);
106        }
107
108        /**
109         * Returns the scalar quantity of type {@link NumberQuantity} in the specified unit and {@code ABSOLUTE} scale.
110         * 
111         * @param value the measurement value.
112         * @param unit  the measurement unit.
113         * @return the corresponding <code>numeric</code> quantity.
114         * @throws NullPointerException when value or unit were null
115         */
116        public static <Q extends Quantity<Q>> ComparableQuantity<Q> getQuantity(Number value, Unit<Q> unit) {
117                return getQuantity(value, unit, ABSOLUTE);
118        }
119
120        /**
121         * Returns the mixed radix values and units combined into a single quantity of type {@link NumberQuantity} in the
122         * specified unit and scale.
123         * 
124         * @param values the measurement values.
125         * @param units  the measurement units.
126         * @param scale  the measurement scale.
127         * @return the corresponding quantity.
128         * @throws NullPointerException     if values or scale were null
129         * @throws IllegalArgumentException if the size of the values array does not
130         *                                  match that of units.
131         * @since 2.0
132         */
133        public static <Q extends Quantity<Q>> Quantity<Q> getQuantity(Number[] values, Unit<Q>[] units, Scale scale) {
134                Objects.requireNonNull(values);
135                Objects.requireNonNull(units);
136                if (values.length == units.length) {
137                        return MixedRadix.of(units).createQuantity(values, scale);
138                } else {
139                        throw new IllegalArgumentException(
140                                        String.format("%s values don't match %s units", values.length, units.length));
141                }
142        }
143
144        /**
145         * Returns the mixed radix values and units combined into a single quantity in
146         * the {@code ABSOLUTE} scale.
147         * 
148         * @param values the measurement values.
149         * @param units  the measurement units.
150         * @return the corresponding quantity.
151         * @throws NullPointerException     if values or units were null
152         * @throws IllegalArgumentException if the size of the values array does not
153         *                                  match that of units.
154         * @since 2.0
155         */
156        @SafeVarargs
157        public static <Q extends Quantity<Q>> Quantity<Q> getQuantity(Number[] values, Unit<Q>... units) {
158                return getQuantity(values, units, ABSOLUTE);
159        }
160
161        /**
162         * Returns the mixed radix values and units as {@link MixedQuantity} in the
163         * specified scale.
164         * 
165         * @param values the measurement values.
166         * @param units  the measurement units.
167         * @param scale  the measurement scale.
168         * @return the corresponding mixed quantity.
169         * @throws NullPointerException     if values, units or scale were null
170         * @throws IllegalArgumentException if the size of the values array does not
171         *                                  match that of units.
172         * @since 2.1.2
173         */
174        public static <Q extends Quantity<Q>> MixedQuantity<Q> getMixedQuantity(Number[] values, Unit<Q>[] units,
175                        Scale scale) {
176                Objects.requireNonNull(values);
177                Objects.requireNonNull(units);
178                if (values.length == units.length) {
179                        return MixedRadix.of(units).createMixedQuantity(values, scale);
180                } else {
181                        throw new IllegalArgumentException(
182                                        String.format("%s values don't match %s units", values.length, units.length));
183                }
184        }
185
186        /**
187         * Returns the mixed radix values and units as {@link MixedQuantity} in the
188         * {@code ABSOLUTE} scale.
189         * 
190         * @param values the measurement values.
191         * @param units  the measurement units.
192         * @return the corresponding mixed quantity.
193         * @throws NullPointerException     if values, units or scale were null
194         * @throws IllegalArgumentException if the size of the values array does not
195         *                                  match that of units.
196         * @since 2.1.2
197         */
198        public static <Q extends Quantity<Q>> MixedQuantity<Q> getMixedQuantity(final Number[] values,
199                        final Unit<Q>[] units) {
200                return getMixedQuantity(values, units, ABSOLUTE);
201        }
202}