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 }