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 speed value and unit.
23   */
24  public class SpeedFormatter extends MeasureFormatter<Speed, SpeedUnit> {
25  
26      /**
27       * Meters per second symbol.
28       */
29      public static final String METERS_PER_SECOND = "m/s";
30  
31      /**
32       * Kilometers per hour symbol.
33       */
34      public static final String KILOMETERS_PER_HOUR = "Km/h";
35  
36      /**
37       * Kilometers per second symbol.
38       */
39      public static final String KILOMETERS_PER_SECOND = "Km/s";
40  
41      /**
42       * Feet per second symbol.
43       */
44      public static final String FEET_PER_SECOND = "ft/s";
45  
46      /**
47       * Miles per hour symbol.
48       */
49      public static final String MILES_PER_HOUR = "mph";
50  
51      /**
52       * Constructor.
53       */
54      public SpeedFormatter() {
55          super();
56      }
57  
58      /**
59       * Constructor with locale.
60       *
61       * @param locale locale.
62       * @throws IllegalArgumentException if locale is null.
63       */
64      public SpeedFormatter(final Locale locale) {
65          super(locale);
66      }
67  
68      /**
69       * Copy constructor.
70       *
71       * @param formatter input instance to copy from.
72       * @throws NullPointerException if provided formatter is null.
73       */
74      public SpeedFormatter(final SpeedFormatter formatter) {
75          this(formatter.getLocale());
76      }
77  
78      /**
79       * Determines if two speed formatters are equal by comparing all of their internal
80       * parameters.
81       *
82       * @param obj another object to compare.
83       * @return true if provided object is assumed to be equal to this instance.
84       */
85      @Override
86      public boolean equals(final Object obj) {
87          final var equals = super.equals(obj);
88          return (obj instanceof SpeedFormatter) && equals;
89      }
90  
91      /**
92       * Hash code generated for this instance.
93       * Hash codes can be internally used by some collections to coarsely compare objects.
94       * This implementation only calls parent implementation to avoid static analyzer warning.
95       *
96       * @return hash code.
97       */
98      @Override
99      public int hashCode() {
100         return super.hashCode();
101     }
102 
103     /**
104      * Gets unit system for detected unit into provided string representation
105      * of a measurement.
106      *
107      * @param source a measurement string representation to be checked.
108      * @return a unit system (either metric or imperial) or null if unit
109      * cannot be determined.
110      */
111     @Override
112     public UnitSystem getUnitSystem(final String source) {
113         final var unit = findUnit(source);
114         return unit != null ? SpeedUnit.getUnitSystem(unit) : null;
115     }
116 
117     /**
118      * Parses provided string and tries to determine speed value and unit.
119      *
120      * @param source a string to be parsed.
121      * @return a speed containing a value and unit.
122      * @throws ParseException       if provided string cannot be parsed.
123      * @throws UnknownUnitException if unit cannot be determined.
124      */
125     @Override
126     public Speed parse(final String source) throws ParseException, UnknownUnitException {
127         return internalParse(source, new Speed());
128     }
129 
130     /**
131      * Attempts to determine a speed unit within a measurement string representation.
132      *
133      * @param source a measurement string representation.
134      * @return a speed unit, or null if none can be determined.
135      */
136     @Override
137     public SpeedUnit findUnit(final String source) {
138         if (source.contains(KILOMETERS_PER_HOUR + " ") || source.endsWith(KILOMETERS_PER_HOUR)) {
139             return SpeedUnit.KILOMETERS_PER_HOUR;
140         }
141         if (source.contains(KILOMETERS_PER_SECOND + " ") || source.endsWith(KILOMETERS_PER_SECOND)) {
142             return SpeedUnit.KILOMETERS_PER_SECOND;
143         }
144         if (source.contains(FEET_PER_SECOND + " ") || source.endsWith(FEET_PER_SECOND)) {
145             return SpeedUnit.FEET_PER_SECOND;
146         }
147         if (source.contains(MILES_PER_HOUR + " ") || source.endsWith(MILES_PER_HOUR)) {
148             return SpeedUnit.MILES_PER_HOUR;
149         }
150         if (source.contains(METERS_PER_SECOND + " ") || source.endsWith(METERS_PER_SECOND)) {
151             return SpeedUnit.METERS_PER_SECOND;
152         }
153         return null;
154     }
155 
156     /**
157      * Formats and converts provided speed value and unit using provided
158      * unit system.
159      * If provided value is too large for provided unit, this method will
160      * convert it to a more appropriate unit using provided unit system (either
161      * metric or imperial).
162      *
163      * @param value  a speed value.
164      * @param unit   a speed unit.
165      * @param system system unit to convert speed to.
166      * @return a string representation of speed value and unit.
167      */
168     @Override
169     public String formatAndConvert(final Number value, final SpeedUnit unit, final UnitSystem system) {
170         if (system == UnitSystem.IMPERIAL) {
171             return formatAndConvertImperial(value, unit);
172         } else {
173             return formatAndConvertMetric(value, unit);
174         }
175     }
176 
177     /**
178      * Formats and converts provided speed value and unit using metric unit
179      * system.
180      * If provided speed value is too large for provided speed unit,
181      * this method will convert it to a more appropriate unit.
182      *
183      * @param value a speed value.
184      * @param unit  a speed unit.
185      * @return a string representation of speed value and unit using metric
186      * unit system.
187      */
188     public String formatAndConvertMetric(final Number value, final SpeedUnit unit) {
189         final var v = value.doubleValue();
190 
191         final var metersPerSecond = SpeedConverter.convert(v, unit, SpeedUnit.METERS_PER_SECOND);
192         if (Math.abs(metersPerSecond) < SpeedConverter.METERS_PER_KILOMETER / SpeedConverter.SECONDS_PER_HOUR) {
193             return format(metersPerSecond, SpeedUnit.METERS_PER_SECOND);
194         }
195 
196         final var kilometersPerHour = SpeedConverter.convert(v, unit, SpeedUnit.KILOMETERS_PER_HOUR);
197 
198         if (Math.abs(kilometersPerHour) < SpeedConverter.SECONDS_PER_HOUR) {
199             return format(kilometersPerHour, SpeedUnit.KILOMETERS_PER_HOUR);
200         }
201 
202         final var kilometersPerSecond = SpeedConverter.convert(v, unit, SpeedUnit.KILOMETERS_PER_SECOND);
203         return format(kilometersPerSecond, SpeedUnit.KILOMETERS_PER_SECOND);
204     }
205 
206     /**
207      * Formats and converts provided speed value and unit using imperial unit
208      * system.
209      * If provided speed value is too large for provided speed unit,
210      * this method will convert it to a more appropriate unit.
211      *
212      * @param value a speed value.
213      * @param unit  a speed unit.
214      * @return a string representation of speed value and unit using imperial
215      * unit system.
216      */
217     public String formatAndConvertImperial(final Number value, final SpeedUnit unit) {
218         final var v = value.doubleValue();
219 
220         final double feetPerSecond = SpeedConverter.convert(v, unit, SpeedUnit.FEET_PER_SECOND);
221         if (Math.abs(feetPerSecond) < SpeedConverter.METERS_PER_MILE
222                 / SpeedConverter.SECONDS_PER_HOUR / SpeedConverter.METERS_PER_FOOT) {
223             return format(feetPerSecond, SpeedUnit.FEET_PER_SECOND);
224         }
225 
226         final var milesPerHour = SpeedConverter.convert(v, unit, SpeedUnit.MILES_PER_HOUR);
227         return format(milesPerHour, SpeedUnit.MILES_PER_HOUR);
228     }
229 
230     /**
231      * Returns unit string representation.
232      *
233      * @param unit a speed unit.
234      * @return its string representation.
235      */
236     @SuppressWarnings("DuplicatedCode")
237     @Override
238     public String getUnitSymbol(final SpeedUnit unit) {
239         return switch (unit) {
240             case KILOMETERS_PER_HOUR -> KILOMETERS_PER_HOUR;
241             case KILOMETERS_PER_SECOND -> KILOMETERS_PER_SECOND;
242             case FEET_PER_SECOND -> FEET_PER_SECOND;
243             case MILES_PER_HOUR -> MILES_PER_HOUR;
244             default -> METERS_PER_SECOND;
245         };
246     }
247 }