Length.java
001 /*
002  * Java GPX Library (jpx-3.1.0).
003  * Copyright (c) 2016-2023 Franz Wilhelmstötter
004  *
005  * Licensed under the Apache License, Version 2.0 (the "License");
006  * you may not use this file except in compliance with the License.
007  * You may obtain a copy of the License at
008  *
009  *      http://www.apache.org/licenses/LICENSE-2.0
010  *
011  * Unless required by applicable law or agreed to in writing, software
012  * distributed under the License is distributed on an "AS IS" BASIS,
013  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014  * See the License for the specific language governing permissions and
015  * limitations under the License.
016  *
017  * Author:
018  *    Franz Wilhelmstötter (franz.wilhelmstoetter@gmail.com)
019  */
020 package io.jenetics.jpx;
021 
022 import static java.lang.String.format;
023 import static java.util.Objects.requireNonNull;
024 
025 import java.io.DataInput;
026 import java.io.DataOutput;
027 import java.io.IOException;
028 import java.io.InvalidObjectException;
029 import java.io.ObjectInputStream;
030 import java.io.Serial;
031 import java.io.Serializable;
032 import java.text.NumberFormat;
033 import java.text.ParseException;
034 
035 /**
036  * Extent of something along its greatest dimension or the extent of space
037  * between two objects or places. The metric system unit for this quantity is
038  * "m" (metre).
039  *
040  @author <a href="mailto:franz.wilhelmstoetter@gmail.com">Franz Wilhelmstötter</a>
041  @version 1.2
042  @since 1.0
043  */
044 public final class Length
045     extends Number
046     implements
047         Comparable<Length>,
048         Serializable
049 {
050 
051     @Serial
052     private static final long serialVersionUID = 2L;
053 
054     /**
055      * Represents a given length unit.
056      */
057     public enum Unit {
058 
059         /**
060          * Represents a meter.
061          */
062         METER(1.0),
063 
064         /**
065          * Represents a kilometer: ≙ 1,000 m.
066          */
067         KILOMETER(1_000.0),
068 
069         /**
070          * Represents an inch: ≙ 0.0254 m.
071          */
072         INCH(127.0/5_000.0),
073 
074         /**
075          * Represents a foot: ≙ 0.3048 m.
076          */
077         FOOT(0.3048),
078 
079         /**
080          * Represents a yard: ≙ 0.9144 m.
081          */
082         YARD(1_143.0/1_250.0),
083 
084         /**
085          * Represents a statute mile: ≙ 1,609.344 m.
086          */
087         MILE(201_168.0/125.0),
088 
089         /**
090          * Represents a fathom: ≙ 1.853184 m.
091          */
092         FATHOM(1.853184),
093 
094         /**
095          * Represents a cable: ≙ 185.3184 m.
096          */
097         CABLE(185.3184),
098 
099         /**
100          * Represents a nautical mile: ≙ 1,853.184 m.
101          */
102         NAUTICAL_MILE(1_853.184);
103 
104         private final double _factor;
105 
106         Unit(final double factor) {
107             _factor = factor;
108         }
109 
110         /**
111          * Convert the given length value of the given {@code sourceUnit} into a
112          * length value of {@code this} length unit. The given example converts 3
113          * inches into yards.
114          *
115          <pre>{@code
116          * final double yards = YARD.convert(3, INCH);
117          * }</pre>
118          *
119          @param length the length value
120          @param sourceUnit the source length unit
121          @return the speed value of {@code this} length unit
122          */
123         public double convert(final double length, final Unit sourceUnit) {
124             requireNonNull(sourceUnit);
125 
126             if (this == sourceUnit) {
127                 return length;
128             else {
129                 final double meters = length*sourceUnit._factor;
130                 return meters/_factor;
131             }
132         }
133     }
134 
135     private final double _value;
136 
137     /**
138      * Create a new {@code Length} object with the given value in meters.
139      *
140      @param value the value (in meters) of the new {@code Length} object
141      */
142     private Length(final double value) {
143         _value = value;
144     }
145 
146     /**
147      * Return the length in meter.
148      *
149      @return the length in meter
150      */
151     @Override
152     public double doubleValue() {
153         return _value;
154     }
155 
156     /**
157      * Return the length in the desired unit.
158      *
159      @param unit the desired length unit
160      @return the length in the desired unit
161      @throws NullPointerException if the given length {@code unit} is
162      *         {@code null}
163      */
164     public double to(final Unit unit) {
165         return unit.convert(_value, Unit.METER);
166     }
167 
168     @Override
169     public int intValue() {
170         return (int)doubleValue();
171     }
172 
173     @Override
174     public long longValue() {
175         return (long)doubleValue();
176     }
177 
178     @Override
179     public float floatValue() {
180         return (float)doubleValue();
181     }
182 
183     @Override
184     public int compareTo(final Length other) {
185         return Double.compare(_value, other._value);
186     }
187 
188     @Override
189     public int hashCode() {
190         return Double.hashCode(_value);
191     }
192 
193     @Override
194     public boolean equals(final Object obj) {
195         return obj == this ||
196             obj instanceof Length &&
197             Double.compare(((Length)obj)._value, _value== 0;
198     }
199 
200     @Override
201     public String toString() {
202         return format("%s m", _value);
203     }
204 
205 
206     /* *************************************************************************
207      *  Static object creation methods
208      * ************************************************************************/
209 
210     /**
211      * Create a new {@code Length} object with the given length.
212      *
213      @param length the length
214      @param unit the length unit
215      @return a new {@code Length} object with the given length.
216      @throws NullPointerException if the given length {@code unit} is
217      *         {@code null}
218      */
219     public static Length of(final double length, final Unit unit) {
220         requireNonNull(unit);
221         return new Length(Unit.METER.convert(length, unit));
222     }
223 
224     static Length parse(final String value, final NumberFormat format) {
225         final Double length = parseDouble(value, format);
226         return length !=  null ? Length.of(length, Unit.METERnull;
227     }
228 
229     private static Double parseDouble(
230         final String value,
231         final NumberFormat format
232     ) {
233         final String length = Strings.trim(value);
234 
235         if (length != null) {
236             try {
237                 return format.parse(length).doubleValue();
238             catch (ParseException e) {
239                 throw new NumberFormatException("Unable to parse " + value);
240             }
241         else {
242             return null;
243         }
244     }
245 
246     /* *************************************************************************
247      *  Java object serialization
248      * ************************************************************************/
249 
250     @Serial
251     private Object writeReplace() {
252         return new SerialProxy(SerialProxy.LENGTH, this);
253     }
254 
255     @Serial
256     private void readObject(final ObjectInputStream stream)
257         throws InvalidObjectException
258     {
259         throw new InvalidObjectException("Serialization proxy required.");
260     }
261 
262     void write(final DataOutput outthrows IOException {
263         out.writeDouble(_value);
264     }
265 
266     static Length read(final DataInput inthrows IOException {
267         return new Length(in.readDouble());
268     }
269 
270 }