View Javadoc
1   /*
2    * Copyright (C) 2018 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 distance value and unit.
23   */
24  public class DistanceFormatter extends MeasureFormatter<Distance, DistanceUnit> {
25  
26      /**
27       * Millimeter symbol.
28       */
29      public static final String MILLIMETER = "mm";
30  
31      /**
32       * Centimeter symbol.
33       */
34      public static final String CENTIMETER = "cm";
35  
36      /**
37       * Meter symbol.
38       */
39      public static final String METER = "m";
40  
41      /**
42       * Kilometer symbol.
43       */
44      public static final String KILOMETER = "Km";
45  
46      /**
47       * Inch symbol.
48       */
49      public static final String INCH = "in";
50  
51      /**
52       * Foot symbol.
53       */
54      public static final String FOOT = "ft";
55  
56      /**
57       * Yard symbol.
58       */
59      public static final String YARD = "yd";
60  
61      /**
62       * Mile symbol.
63       */
64      public static final String MILE = "mi";
65  
66      /**
67       * Constructor.
68       */
69      public DistanceFormatter() {
70          super();
71      }
72  
73      /**
74       * Constructor with locale.
75       *
76       * @param locale locale.
77       * @throws IllegalArgumentException if locale is null.
78       */
79      public DistanceFormatter(final Locale locale) {
80          super(locale);
81      }
82  
83      /**
84       * Copy constructor.
85       *
86       * @param formatter input instance to copy from.
87       * @throws NullPointerException if provided formatter is null.
88       */
89      public DistanceFormatter(final DistanceFormatter formatter) {
90          this(formatter.getLocale());
91      }
92  
93      /**
94       * Determines if two distance formatters are equal by comparing all of their internal parameters.
95       *
96       * @param obj another object to compare.
97       * @return true if provided object is assumed to be equal to this instance.
98       */
99      @Override
100     public boolean equals(final Object obj) {
101         final var equals = super.equals(obj);
102         return (obj instanceof DistanceFormatter) && equals;
103     }
104 
105     /**
106      * Hash code generated for this instance.
107      * Hash codes can be internally used by some collections to coarsely compare objects.
108      * This implementation only calls parent implementation to avoid static analyzer warning.
109      *
110      * @return hash code.
111      */
112     @Override
113     public int hashCode() {
114         return super.hashCode();
115     }
116 
117     /**
118      * Gets unit system for detected unit into provided string representation
119      * of a measurement.
120      *
121      * @param source a measurement string representation to be checked.
122      * @return a unit system (either metric or imperial) or null if unit
123      * cannot be determined.
124      */
125     @Override
126     public UnitSystem getUnitSystem(final String source) {
127         final var unit = findUnit(source);
128         return unit != null ? DistanceUnit.getUnitSystem(unit) : null;
129     }
130 
131     /**
132      * Parses provided string and tries to determine a distance value and unit.
133      *
134      * @param source a string to be parsed.
135      * @return a distance containing a value and unit.
136      * @throws ParseException       if provided string cannot be parsed.
137      * @throws UnknownUnitException if unit cannot be determined.
138      */
139     @Override
140     public Distance parse(final String source) throws ParseException, UnknownUnitException {
141         return internalParse(source, new Distance());
142     }
143 
144     /**
145      * Attempts to determine a distance unit within a measurement string
146      * representation.
147      *
148      * @param source a distance measurement string representation.
149      * @return a distance unit, or null if none can be determined.
150      */
151     @Override
152     public DistanceUnit findUnit(final String source) {
153         if (source.contains(MILLIMETER + " ") || source.endsWith(MILLIMETER)) {
154             return DistanceUnit.MILLIMETER;
155         }
156         if (source.contains(CENTIMETER + " ") || source.endsWith(CENTIMETER)) {
157             return DistanceUnit.CENTIMETER;
158         }
159         if (source.contains(KILOMETER + " ") || source.endsWith(KILOMETER)) {
160             return DistanceUnit.KILOMETER;
161         }
162         if (source.contains(INCH + " ") || source.endsWith(INCH)) {
163             return DistanceUnit.INCH;
164         }
165         if (source.contains(FOOT + " ") || source.endsWith(FOOT)) {
166             return DistanceUnit.FOOT;
167         }
168         if (source.contains(YARD + " ") || source.endsWith(YARD)) {
169             return DistanceUnit.YARD;
170         }
171         if (source.contains(MILE + " ") || source.endsWith(MILE)) {
172             return DistanceUnit.MILE;
173         }
174 
175         if (source.contains(METER + " ") || source.endsWith(METER)) {
176             return DistanceUnit.METER;
177         }
178         return null;
179     }
180 
181     /**
182      * Formats and converts provided distance value and unit using provided
183      * unit system.
184      * If provided value is too large for provided unit, this method will
185      * convert it to a more appropriate unit using provided unit system (either
186      * metric or imperial).
187      *
188      * @param value  a distance value.
189      * @param unit   a distance unit.
190      * @param system system unit to convert distance to.
191      * @return a string representation of distance value and unit.
192      */
193     @Override
194     public String formatAndConvert(
195             final Number value, final DistanceUnit unit,
196             final UnitSystem system) {
197         if (system == UnitSystem.IMPERIAL) {
198             return formatAndConvertImperial(value, unit);
199         } else {
200             return formatAndConvertMetric(value, unit);
201         }
202     }
203 
204     /**
205      * Formats and converts provided distance value and unit using metric unit
206      * system.
207      * If provided distance value is too large for provided distance unit,
208      * this method will convert it to a more appropriate unit.
209      *
210      * @param value a distance value.
211      * @param unit  a distance unit.
212      * @return a string representation of distance value and unit using metric
213      * unit system.
214      */
215     public String formatAndConvertMetric(final Number value, final DistanceUnit unit) {
216         final var v = value.doubleValue();
217 
218         final var millimeters = DistanceConverter.convert(v, unit, DistanceUnit.MILLIMETER);
219         if (Math.abs(millimeters) < (DistanceConverter.METERS_PER_CENTIMETER
220                 / DistanceConverter.METERS_PER_MILLIMETER)) {
221             return format(millimeters, DistanceUnit.MILLIMETER);
222         }
223 
224         final var centimeters = DistanceConverter.convert(v, unit, DistanceUnit.CENTIMETER);
225         if (Math.abs(centimeters) < (1.0 / DistanceConverter.METERS_PER_CENTIMETER)) {
226             return format(centimeters, DistanceUnit.CENTIMETER);
227         }
228 
229         final var meters = DistanceConverter.convert(v, unit, DistanceUnit.METER);
230         if (Math.abs(meters) < DistanceConverter.METERS_PER_KILOMETER) {
231             return format(meters, DistanceUnit.METER);
232         }
233 
234         final var kilometers = DistanceConverter.convert(v, unit, DistanceUnit.KILOMETER);
235         return format(kilometers, DistanceUnit.KILOMETER);
236     }
237 
238     /**
239      * Formats and converts provided distance value and unit using imperial unit
240      * system.
241      * If provided distance value is too large for provided distance unit,
242      * this method will convert it to a more appropriate unit.
243      *
244      * @param value a distance value.
245      * @param unit  a distance unit.
246      * @return a string representation of distance value and unit using imperial
247      * unit system.
248      */
249     public String formatAndConvertImperial(final Number value, final DistanceUnit unit) {
250         final var v = value.doubleValue();
251 
252         final var inches = DistanceConverter.convert(v, unit, DistanceUnit.INCH);
253         if (Math.abs(inches) < (DistanceConverter.METERS_PER_FOOT / DistanceConverter.METERS_PER_INCH)) {
254             return format(inches, DistanceUnit.INCH);
255         }
256 
257         final var feet = DistanceConverter.convert(v, unit, DistanceUnit.FOOT);
258         if (Math.abs(feet) < (DistanceConverter.METERS_PER_YARD / DistanceConverter.METERS_PER_FOOT)) {
259             return format(feet, DistanceUnit.FOOT);
260         }
261 
262         final var yards = DistanceConverter.convert(v, unit, DistanceUnit.YARD);
263         if (Math.abs(yards) < (DistanceConverter.METERS_PER_MILE / DistanceConverter.METERS_PER_YARD)) {
264             return format(yards, DistanceUnit.YARD);
265         }
266 
267         final var miles = DistanceConverter.convert(v, unit, DistanceUnit.MILE);
268         return format(miles, DistanceUnit.MILE);
269     }
270 
271     /**
272      * Returns unit string representation.
273      *
274      * @param unit a distance unit.
275      * @return its string representation.
276      */
277     @Override
278     public String getUnitSymbol(final DistanceUnit unit) {
279         return switch (unit) {
280             case MILLIMETER -> MILLIMETER;
281             case CENTIMETER -> CENTIMETER;
282             case KILOMETER -> KILOMETER;
283             case INCH -> INCH;
284             case FOOT -> FOOT;
285             case YARD -> YARD;
286             case MILE -> MILE;
287             default -> METER;
288         };
289     }
290 }