View Javadoc
1   /*
2    * Copyright (C) 2020 Alberto Irurueta Carro (alberto@irurueta.com)
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *         http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package com.irurueta.units;
17  
18  import java.text.ParseException;
19  import java.util.Locale;
20  
21  /**
22   * Formats and parses weight value and unit.
23   */
24  public class WeightFormatter extends MeasureFormatter<Weight, WeightUnit> {
25  
26      /**
27       * Picogram symbol.
28       */
29      public static final String PICOGRAM = "pg";
30  
31      /**
32       * Nanogram symbol.
33       */
34      public static final String NANOGRAM = "ng";
35  
36      /**
37       * Microgram symbol.
38       */
39      public static final String MICROGRAM = "µg";
40  
41      /**
42       * Milligram symbol.
43       */
44      public static final String MILLIGRAM = "mg";
45  
46      /**
47       * Gram symbol.
48       */
49      public static final String GRAM = "g";
50  
51      /**
52       * Kilogram symbol.
53       */
54      public static final String KILOGRAM = "Kg";
55  
56      /**
57       * Metric tonne symbol.
58       */
59      public static final String TONNE = "t";
60  
61      /**
62       * Megatonne symbol.
63       */
64      public static final String MEGATONNE = "Mt";
65  
66      /**
67       * U.S. OR U.K. ton symbol.
68       */
69      public static final String US_UK_TON = "ton";
70  
71      /**
72       * Pound symbol.
73       */
74      public static final String POUND = "lb";
75  
76      /**
77       * Ounce symbol.
78       */
79      public static final String OUNCE = "oz";
80  
81      /**
82       * Constructor.
83       */
84      public WeightFormatter() {
85          super();
86      }
87  
88      /**
89       * Constructor with locale.
90       *
91       * @param locale locale.
92       * @throws IllegalArgumentException if locale is null.
93       */
94      public WeightFormatter(final Locale locale) {
95          super(locale);
96      }
97  
98      /**
99       * Copy constructor.
100      *
101      * @param formatter input instance to copy from.
102      * @throws NullPointerException if provided formatter is null.
103      */
104     public WeightFormatter(final WeightFormatter formatter) {
105         this(formatter.getLocale());
106     }
107 
108     /**
109      * Determines if two weight formatters are equal by comparing all of their
110      * internal parameters.
111      *
112      * @param obj another object to compare.
113      * @return true if provided object is assumed to be equal to this instance.
114      */
115     @Override
116     public boolean equals(final Object obj) {
117         final var equals = super.equals(obj);
118         return (obj instanceof WeightFormatter) && equals;
119     }
120 
121     /**
122      * Hash code generated for this instance.
123      * Hash codes can be internally used by some collections to coarsely compare objects.
124      * This implementation only calls parent implementation to avoid static analyzer warning.
125      *
126      * @return hash code.
127      */
128     @Override
129     public int hashCode() {
130         return super.hashCode();
131     }
132 
133     /**
134      * Gets unit system for detected unit into provided string representation
135      * of a measurement.
136      *
137      * @param source a measurement string representation to be checked.
138      * @return a unit system (either metric or imperial) or null if unit
139      * cannot be determined.
140      */
141     @Override
142     public UnitSystem getUnitSystem(final String source) {
143         final var unit = findUnit(source);
144         return unit != null ? WeightUnit.getUnitSystem(unit) : null;
145     }
146 
147     /**
148      * Parses provided string and tries to determine a weight value and unit.
149      *
150      * @param source a string to be parsed.
151      * @return a weight containing a value and unit.
152      * @throws ParseException       if provided string cannot be parsed.
153      * @throws UnknownUnitException if unit cannot be determined.
154      */
155     @Override
156     public Weight parse(final String source) throws ParseException, UnknownUnitException {
157         return internalParse(source, new Weight());
158     }
159 
160     /**
161      * Attempts to determine a weight unit within a measurement string
162      * representation.
163      *
164      * @param source a weight measurement string representation.
165      * @return a weight unit, or null if none can be determined.
166      */
167     @Override
168     public WeightUnit findUnit(final String source) {
169         if (source.contains(PICOGRAM + " ") || source.endsWith(PICOGRAM)) {
170             return WeightUnit.PICOGRAM;
171         }
172         if (source.contains(NANOGRAM + " ") || source.endsWith(NANOGRAM)) {
173             return WeightUnit.NANOGRAM;
174         }
175         if (source.contains(MICROGRAM + " ") || source.endsWith(MICROGRAM)) {
176             return WeightUnit.MICROGRAM;
177         }
178         if (source.contains(MILLIGRAM + " ") || source.endsWith(MILLIGRAM)) {
179             return WeightUnit.MILLIGRAM;
180         }
181         if (source.contains(KILOGRAM + " ") || source.endsWith(KILOGRAM)) {
182             return WeightUnit.KILOGRAM;
183         }
184         if (source.contains(MEGATONNE + " ") || source.endsWith(MEGATONNE)) {
185             return WeightUnit.MEGATONNE;
186         }
187         if (source.contains(POUND + " ") || source.endsWith(POUND)) {
188             return WeightUnit.POUND;
189         }
190         if (source.contains(OUNCE + " ") || source.endsWith(OUNCE)) {
191             return WeightUnit.OUNCE;
192         }
193 
194         if (source.contains(US_UK_TON + " ") || source.endsWith(US_UK_TON)) {
195             final var locale = getLocale();
196             if (Locale.UK.getCountry().equals(locale.getCountry())) {
197                 // UK
198                 return WeightUnit.UK_TON;
199             } else {
200                 // US
201                 return WeightUnit.US_TON;
202             }
203         }
204 
205         if (source.contains(TONNE + " ") || source.endsWith(TONNE)) {
206             return WeightUnit.TONNE;
207         }
208         if (source.contains(GRAM + " ") || source.endsWith(GRAM)) {
209             return WeightUnit.GRAM;
210         }
211 
212         return null;
213     }
214 
215     /**
216      * Formats and converts provided weight value and unit using provided
217      * unit system.
218      * If provided value is too large for provided unit, this method will
219      * convert it to a more appropriate unit using provided unit system (either
220      * metric or imperial).
221      *
222      * @param value  a weight value.
223      * @param unit   a weight unit.
224      * @param system system unit to convert measurement to.
225      * @return a string representation of weight value and unit.
226      */
227     @Override
228     public String formatAndConvert(final Number value, final WeightUnit unit, final UnitSystem system) {
229         if (system == UnitSystem.IMPERIAL) {
230             return formatAndConvertImperial(value, unit);
231         } else {
232             return formatAndConvertMetric(value, unit);
233         }
234     }
235 
236     /**
237      * Formats and converts provided weight value and unit using metric unit
238      * system.
239      * If provided weight value is too large for provided weight unit,
240      * this method will convert to a more appropriate unit.
241      *
242      * @param value a weight value.
243      * @param unit  a weight unit.
244      * @return a string representation of weight value and unit using metric
245      * unit system.
246      */
247     public String formatAndConvertMetric(final Number value, final WeightUnit unit) {
248         final var v = value.doubleValue();
249 
250         final var picogram = WeightConverter.convert(v, unit, WeightUnit.PICOGRAM);
251         if (Math.abs(picogram) < (WeightConverter.GRAMS_PER_NANOGRAM / WeightConverter.GRAMS_PER_PICOGRAM)) {
252             return format(picogram, WeightUnit.PICOGRAM);
253         }
254 
255         final var nanogram = WeightConverter.convert(v, unit, WeightUnit.NANOGRAM);
256         if (Math.abs(nanogram) < (WeightConverter.GRAMS_PER_MICROGRAM / WeightConverter.GRAMS_PER_NANOGRAM)) {
257             return format(nanogram, WeightUnit.NANOGRAM);
258         }
259 
260         final var microgram = WeightConverter.convert(v, unit, WeightUnit.MICROGRAM);
261         if (Math.abs(microgram) < (WeightConverter.GRAMS_PER_MILLIGRAM / WeightConverter.GRAMS_PER_MICROGRAM)) {
262             return format(microgram, WeightUnit.MICROGRAM);
263         }
264 
265         final var milligram = WeightConverter.convert(v, unit, WeightUnit.MILLIGRAM);
266         if (Math.abs(milligram) < (1.0 / WeightConverter.GRAMS_PER_MILLIGRAM)) {
267             return format(milligram, WeightUnit.MILLIGRAM);
268         }
269 
270         final var gram = WeightConverter.convert(v, unit, WeightUnit.GRAM);
271         if (Math.abs(gram) < WeightConverter.GRAMS_PER_KILOGRAM) {
272             return format(gram, WeightUnit.GRAM);
273         }
274 
275         final var kilogram = WeightConverter.convert(v, unit, WeightUnit.KILOGRAM);
276         if (Math.abs(kilogram) < (WeightConverter.GRAMS_PER_TONNE / WeightConverter.GRAMS_PER_KILOGRAM)) {
277             return format(kilogram, WeightUnit.KILOGRAM);
278         }
279 
280         final var tonne = WeightConverter.convert(v, unit, WeightUnit.TONNE);
281         if (Math.abs(tonne) < (WeightConverter.GRAMS_PER_MEGATONNE / WeightConverter.GRAMS_PER_TONNE)) {
282             return format(tonne, WeightUnit.TONNE);
283         }
284 
285         final var megatonne = WeightConverter.convert(v, unit, WeightUnit.MEGATONNE);
286         return format(megatonne, WeightUnit.MEGATONNE);
287     }
288 
289 
290     /**
291      * Formats and converts provided weight value and unit using imperial unit
292      * system.
293      * If provided weight value is too large for provided weight unit,
294      * this method will convert to a more appropriate unit.
295      *
296      * @param value a weight value.
297      * @param unit  a weight unit.
298      * @return a string representation of weight value and unit using imperial
299      * unit system.
300      */
301     public String formatAndConvertImperial(final Number value, final WeightUnit unit) {
302         final var v = value.doubleValue();
303 
304         final var ounce = WeightConverter.convert(v, unit, WeightUnit.OUNCE);
305         if (Math.abs(ounce) < (WeightConverter.GRAMS_PER_POUND / WeightConverter.GRAMS_PER_OUNCE)) {
306             return format(ounce, WeightUnit.OUNCE);
307         }
308 
309         final var pound = WeightConverter.convert(v, unit, WeightUnit.POUND);
310 
311         final var locale = getLocale();
312         if (Locale.UK.getCountry().equals(locale.getCountry())) {
313             // UK
314             if (Math.abs(pound) < (WeightConverter.GRAMS_PER_UK_TON / WeightConverter.GRAMS_PER_POUND)) {
315                 return format(pound, WeightUnit.POUND);
316             }
317 
318             final var ton = WeightConverter.convert(v, unit, WeightUnit.UK_TON);
319             return format(ton, WeightUnit.UK_TON);
320         } else {
321             // US
322             if (Math.abs(pound) < (WeightConverter.GRAMS_PER_US_TON / WeightConverter.GRAMS_PER_POUND)) {
323                 return format(pound, WeightUnit.POUND);
324             }
325 
326             final var ton = WeightConverter.convert(v, unit, WeightUnit.US_TON);
327             return format(ton, WeightUnit.US_TON);
328         }
329     }
330 
331     /**
332      * Returns unit string representation.
333      *
334      * @param unit a weight unit.
335      * @return its string representation.
336      */
337     @Override
338     public String getUnitSymbol(final WeightUnit unit) {
339         return switch (unit) {
340             case PICOGRAM -> PICOGRAM;
341             case NANOGRAM -> NANOGRAM;
342             case MICROGRAM -> MICROGRAM;
343             case MILLIGRAM -> MILLIGRAM;
344             case KILOGRAM -> KILOGRAM;
345             case TONNE -> TONNE;
346             case MEGATONNE -> MEGATONNE;
347             case US_TON, UK_TON -> US_UK_TON;
348             case POUND -> POUND;
349             case OUNCE -> OUNCE;
350             default -> GRAM;
351         };
352     }
353 }