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 surface value and unit.
23   */
24  public class SurfaceFormatter extends MeasureFormatter<Surface, SurfaceUnit> {
25      /**
26       * Square millimeter symbol.
27       */
28      public static final String SQUARE_MILLIMETER = "mm²";
29  
30      /**
31       * Square centimeter symbol.
32       */
33      public static final String SQUARE_CENTIMETER = "cm²";
34  
35      /**
36       * Square meter symbol.
37       */
38      public static final String SQUARE_METER = "m²";
39  
40      /**
41       * Square kilometer symbol.
42       */
43      public static final String SQUARE_KILOMETER = "Km²";
44  
45      /**
46       * Square inch symbol.
47       */
48      public static final String SQUARE_INCH = "sq in";
49  
50      /**
51       * Square foot symbol.
52       */
53      public static final String SQUARE_FOOT = "sq ft";
54  
55      /**
56       * Square yard symbol.
57       */
58      public static final String SQUARE_YARD = "sq yd";
59  
60      /**
61       * Square mile symbol.
62       */
63      public static final String SQUARE_MILE = "sq mi";
64  
65      /**
66       * Centiare symbol.
67       */
68      public static final String CENTIARE = "ca";
69  
70      /**
71       * Are symbol.
72       */
73      public static final String ARE = "a";
74  
75      /**
76       * Decare symbol.
77       */
78      public static final String DECARE = "daa";
79  
80      /**
81       * Hectare symbol.
82       */
83      public static final String HECTARE = "ha";
84  
85      /**
86       * Acre symbol.
87       */
88      public static final String ACRE = "acre";
89  
90      /**
91       * Constructor.
92       */
93      @SuppressWarnings("WeakerAccess")
94      public SurfaceFormatter() {
95          super();
96      }
97  
98      /**
99       * Constructor with locale.
100      *
101      * @param locale locale.
102      * @throws IllegalArgumentException if locale is null.
103      */
104     @SuppressWarnings("WeakerAccess")
105     public SurfaceFormatter(final Locale locale) {
106         super(locale);
107     }
108 
109     /**
110      * Copy constructor.
111      *
112      * @param formatter input instance to copy from.
113      * @throws NullPointerException if provided formatter is null.
114      */
115     public SurfaceFormatter(final SurfaceFormatter formatter) {
116         this(formatter.getLocale());
117     }
118 
119     /**
120      * Determines if two distance formatters are equal by comparing all of its
121      * internal parameters.
122      *
123      * @param obj another object to compare.
124      * @return true if provided object is assumed to be equal to this instance.
125      */
126     @Override
127     public boolean equals(final Object obj) {
128         final var equals = super.equals(obj);
129         return (obj instanceof SurfaceFormatter) && equals;
130     }
131 
132     /**
133      * Hash code generated for this instance.
134      * Hash codes can be internally used by some collections to coarsely compare objects.
135      * This implementation only calls parent implementation to avoid static analyzer warning.
136      *
137      * @return hash code.
138      */
139     @Override
140     public int hashCode() {
141         return super.hashCode();
142     }
143 
144     /**
145      * Gets unit system for detected unit into provided string representation
146      * of a measurement.
147      *
148      * @param source a measurement string representation to be checked.
149      * @return a unit system (either metric or imperial) or null if unit
150      * cannot be determined.
151      */
152     @Override
153     public UnitSystem getUnitSystem(final String source) {
154         final var unit = findUnit(source);
155         return unit != null ? SurfaceUnit.getUnitSystem(unit) : null;
156     }
157 
158     /**
159      * Parses provided string and tries to determine a surface value and unit.
160      *
161      * @param source text to be parsed.
162      * @return a surface containing a value and unit.
163      * @throws ParseException       if provided string cannot be parsed.
164      * @throws UnknownUnitException if unit cannot be determined.
165      */
166     @Override
167     public Surface parse(final String source) throws ParseException, UnknownUnitException {
168         return internalParse(source, new Surface());
169     }
170 
171     /**
172      * Attempts to determine a surface unit within a measurement string
173      * representation.
174      *
175      * @param source a surface measurement string representation.
176      * @return a surface unit, or null if none can be determined.
177      */
178     @Override
179     public SurfaceUnit findUnit(final String source) {
180         if (source.contains(SQUARE_MILLIMETER + " ") || source.endsWith(SQUARE_MILLIMETER)) {
181             return SurfaceUnit.SQUARE_MILLIMETER;
182         }
183         if (source.contains(SQUARE_CENTIMETER + " ") || source.endsWith(SQUARE_CENTIMETER)) {
184             return SurfaceUnit.SQUARE_CENTIMETER;
185         }
186         if (source.contains(SQUARE_KILOMETER + " ") || source.endsWith(SQUARE_KILOMETER)) {
187             return SurfaceUnit.SQUARE_KILOMETER;
188         }
189         if (source.contains(SQUARE_INCH + " ") || source.endsWith(SQUARE_INCH)) {
190             return SurfaceUnit.SQUARE_INCH;
191         }
192         if (source.contains(SQUARE_FOOT + " ") || source.endsWith(SQUARE_FOOT)) {
193             return SurfaceUnit.SQUARE_FOOT;
194         }
195         if (source.contains(SQUARE_YARD + " ") || source.endsWith(SQUARE_YARD)) {
196             return SurfaceUnit.SQUARE_YARD;
197         }
198         if (source.contains(SQUARE_MILE + " ") || source.endsWith(SQUARE_MILE)) {
199             return SurfaceUnit.SQUARE_MILE;
200         }
201         if (source.contains(CENTIARE + " ") || source.endsWith(CENTIARE)) {
202             return SurfaceUnit.CENTIARE;
203         }
204         if (source.contains(DECARE + " ") || source.endsWith(DECARE)) {
205             return SurfaceUnit.DECARE;
206         }
207         if (source.contains(HECTARE + " ") || source.endsWith(HECTARE)) {
208             return SurfaceUnit.HECTARE;
209         }
210         if (source.contains(ACRE + " ") || source.endsWith(ACRE)) {
211             return SurfaceUnit.ACRE;
212         }
213 
214         if (source.contains(ARE + " ") || source.endsWith(ARE)) {
215             return SurfaceUnit.ARE;
216         }
217         if (source.contains(SQUARE_METER + " ") || source.endsWith(SQUARE_METER)) {
218             return SurfaceUnit.SQUARE_METER;
219         }
220         return null;
221     }
222 
223     /**
224      * Formats and converts provided surface value and unit using provided
225      * unit system.
226      * If provided value is too large for provided unit, this method will
227      * convert it to a more appropriate unit using provided unit system (either
228      * metric or imperial).
229      *
230      * @param value  a surface value.
231      * @param unit   a surface unit.
232      * @param system system unit to convert surface to.
233      * @return a string representation of surface value and unit.
234      */
235     @Override
236     public String formatAndConvert(final Number value, final SurfaceUnit unit, final UnitSystem system) {
237         if (system == UnitSystem.IMPERIAL) {
238             return formatAndConvertImperial(value, unit);
239         } else {
240             return formatAndConvertMetric(value, unit);
241         }
242     }
243 
244     /**
245      * Formats and converts provided surface value and unit using metric unit
246      * system.
247      * If provided surface value is too large for provided surface unit,
248      * this method will convert it to a more appropriate unit.
249      *
250      * @param value a surface value.
251      * @param unit  a surface unit.
252      * @return a string representation of surface value and unit using metric
253      * unit system.
254      */
255     public String formatAndConvertMetric(final Number value, final SurfaceUnit unit) {
256         final var v = value.doubleValue();
257 
258         final var squareMillimeters = SurfaceConverter.convert(v, unit, SurfaceUnit.SQUARE_MILLIMETER);
259         if (Math.abs(squareMillimeters) < (SurfaceConverter.SQUARE_METERS_PER_SQUARE_CENTIMETER
260                 / SurfaceConverter.SQUARE_METERS_PER_SQUARE_MILLIMETER)) {
261             return format(squareMillimeters, SurfaceUnit.SQUARE_MILLIMETER);
262         }
263 
264         final var squareCentimeters = SurfaceConverter.convert(v, unit, SurfaceUnit.SQUARE_CENTIMETER);
265         if (Math.abs(squareCentimeters) < (1.0 / SurfaceConverter.SQUARE_METERS_PER_SQUARE_CENTIMETER)) {
266             return format(squareCentimeters, SurfaceUnit.SQUARE_CENTIMETER);
267         }
268 
269         final var squareMeters = SurfaceConverter.convert(v, unit, SurfaceUnit.SQUARE_METER);
270         if (Math.abs(squareMeters) < SurfaceConverter.SQUARE_METERS_PER_SQUARE_KILOMETER) {
271             return format(squareMeters, SurfaceUnit.SQUARE_METER);
272         }
273 
274         final var squareKilometers = SurfaceConverter.convert(v, unit, SurfaceUnit.SQUARE_KILOMETER);
275         return format(squareKilometers, SurfaceUnit.SQUARE_KILOMETER);
276     }
277 
278     /**
279      * Formats and converts provided surface value and unit using imperial unit
280      * system.
281      * If provided surface value is too large for provided surface unit,
282      * this method will convert it to a more appropriate unit.
283      *
284      * @param value a surface value.
285      * @param unit  a surface unit.
286      * @return a string representation of surface value and unit using imperial
287      * unit system.
288      */
289     public String formatAndConvertImperial(final Number value, final SurfaceUnit unit) {
290         final var v = value.doubleValue();
291 
292         final var squareInches = SurfaceConverter.convert(v, unit, SurfaceUnit.SQUARE_INCH);
293         if (Math.abs(squareInches) < (SurfaceConverter.SQUARE_METERS_PER_SQUARE_FOOT
294                 / SurfaceConverter.SQUARE_METERS_PER_SQUARE_INCH)) {
295             return format(squareInches, SurfaceUnit.SQUARE_INCH);
296         }
297 
298         final var squareFeet = SurfaceConverter.convert(v, unit, SurfaceUnit.SQUARE_FOOT);
299         if (Math.abs(squareFeet) < (SurfaceConverter.SQUARE_METERS_PER_SQUARE_YARD
300                 / SurfaceConverter.SQUARE_METERS_PER_SQUARE_FOOT)) {
301             return format(squareFeet, SurfaceUnit.SQUARE_FOOT);
302         }
303 
304         final var squareYards = SurfaceConverter.convert(v, unit, SurfaceUnit.SQUARE_YARD);
305         if (Math.abs(squareYards) < (SurfaceConverter.SQUARE_METERS_PER_SQUARE_MILE
306                 / SurfaceConverter.SQUARE_METERS_PER_SQUARE_YARD)) {
307             return format(squareYards, SurfaceUnit.SQUARE_YARD);
308         }
309 
310         final var squareMiles = SurfaceConverter.convert(v, unit, SurfaceUnit.SQUARE_MILE);
311         return format(squareMiles, SurfaceUnit.SQUARE_MILE);
312     }
313 
314     /**
315      * Returns unit string representation.
316      *
317      * @param unit a measure unit.
318      * @return its string representation.
319      */
320     @Override
321     public String getUnitSymbol(final SurfaceUnit unit) {
322         return switch (unit) {
323             case SQUARE_MILLIMETER -> SQUARE_MILLIMETER;
324             case SQUARE_CENTIMETER -> SQUARE_CENTIMETER;
325             case SQUARE_KILOMETER -> SQUARE_KILOMETER;
326             case SQUARE_INCH -> SQUARE_INCH;
327             case SQUARE_FOOT -> SQUARE_FOOT;
328             case SQUARE_YARD -> SQUARE_YARD;
329             case SQUARE_MILE -> SQUARE_MILE;
330             case CENTIARE -> CENTIARE;
331             case ARE -> ARE;
332             case DECARE -> DECARE;
333             case HECTARE -> HECTARE;
334             case ACRE -> ACRE;
335             default -> SQUARE_METER;
336         };
337     }
338 }