DistanceFormatter.java
/*
* Copyright (C) 2018 Alberto Irurueta Carro (alberto@irurueta.com)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.irurueta.units;
import java.text.ParseException;
import java.util.Locale;
/**
* Formats and parses distance value and unit.
*/
public class DistanceFormatter extends MeasureFormatter<Distance, DistanceUnit> {
/**
* Millimeter symbol.
*/
public static final String MILLIMETER = "mm";
/**
* Centimeter symbol.
*/
public static final String CENTIMETER = "cm";
/**
* Meter symbol.
*/
public static final String METER = "m";
/**
* Kilometer symbol.
*/
public static final String KILOMETER = "Km";
/**
* Inch symbol.
*/
public static final String INCH = "in";
/**
* Foot symbol.
*/
public static final String FOOT = "ft";
/**
* Yard symbol.
*/
public static final String YARD = "yd";
/**
* Mile symbol.
*/
public static final String MILE = "mi";
/**
* Constructor.
*/
public DistanceFormatter() {
super();
}
/**
* Constructor with locale.
*
* @param locale locale.
* @throws IllegalArgumentException if locale is null.
*/
public DistanceFormatter(final Locale locale) {
super(locale);
}
/**
* Copy constructor.
*
* @param formatter input instance to copy from.
* @throws NullPointerException if provided formatter is null.
*/
public DistanceFormatter(final DistanceFormatter formatter) {
this(formatter.getLocale());
}
/**
* Determines if two distance formatters are equal by comparing all of their internal parameters.
*
* @param obj another object to compare.
* @return true if provided object is assumed to be equal to this instance.
*/
@Override
public boolean equals(final Object obj) {
final var equals = super.equals(obj);
return (obj instanceof DistanceFormatter) && equals;
}
/**
* Hash code generated for this instance.
* Hash codes can be internally used by some collections to coarsely compare objects.
* This implementation only calls parent implementation to avoid static analyzer warning.
*
* @return hash code.
*/
@Override
public int hashCode() {
return super.hashCode();
}
/**
* Gets unit system for detected unit into provided string representation
* of a measurement.
*
* @param source a measurement string representation to be checked.
* @return a unit system (either metric or imperial) or null if unit
* cannot be determined.
*/
@Override
public UnitSystem getUnitSystem(final String source) {
final var unit = findUnit(source);
return unit != null ? DistanceUnit.getUnitSystem(unit) : null;
}
/**
* Parses provided string and tries to determine a distance value and unit.
*
* @param source a string to be parsed.
* @return a distance containing a value and unit.
* @throws ParseException if provided string cannot be parsed.
* @throws UnknownUnitException if unit cannot be determined.
*/
@Override
public Distance parse(final String source) throws ParseException, UnknownUnitException {
return internalParse(source, new Distance());
}
/**
* Attempts to determine a distance unit within a measurement string
* representation.
*
* @param source a distance measurement string representation.
* @return a distance unit, or null if none can be determined.
*/
@Override
public DistanceUnit findUnit(final String source) {
if (source.contains(MILLIMETER + " ") || source.endsWith(MILLIMETER)) {
return DistanceUnit.MILLIMETER;
}
if (source.contains(CENTIMETER + " ") || source.endsWith(CENTIMETER)) {
return DistanceUnit.CENTIMETER;
}
if (source.contains(KILOMETER + " ") || source.endsWith(KILOMETER)) {
return DistanceUnit.KILOMETER;
}
if (source.contains(INCH + " ") || source.endsWith(INCH)) {
return DistanceUnit.INCH;
}
if (source.contains(FOOT + " ") || source.endsWith(FOOT)) {
return DistanceUnit.FOOT;
}
if (source.contains(YARD + " ") || source.endsWith(YARD)) {
return DistanceUnit.YARD;
}
if (source.contains(MILE + " ") || source.endsWith(MILE)) {
return DistanceUnit.MILE;
}
if (source.contains(METER + " ") || source.endsWith(METER)) {
return DistanceUnit.METER;
}
return null;
}
/**
* Formats and converts provided distance value and unit using provided
* unit system.
* If provided value is too large for provided unit, this method will
* convert it to a more appropriate unit using provided unit system (either
* metric or imperial).
*
* @param value a distance value.
* @param unit a distance unit.
* @param system system unit to convert distance to.
* @return a string representation of distance value and unit.
*/
@Override
public String formatAndConvert(
final Number value, final DistanceUnit unit,
final UnitSystem system) {
if (system == UnitSystem.IMPERIAL) {
return formatAndConvertImperial(value, unit);
} else {
return formatAndConvertMetric(value, unit);
}
}
/**
* Formats and converts provided distance value and unit using metric unit
* system.
* If provided distance value is too large for provided distance unit,
* this method will convert it to a more appropriate unit.
*
* @param value a distance value.
* @param unit a distance unit.
* @return a string representation of distance value and unit using metric
* unit system.
*/
public String formatAndConvertMetric(final Number value, final DistanceUnit unit) {
final var v = value.doubleValue();
final var millimeters = DistanceConverter.convert(v, unit, DistanceUnit.MILLIMETER);
if (Math.abs(millimeters) < (DistanceConverter.METERS_PER_CENTIMETER
/ DistanceConverter.METERS_PER_MILLIMETER)) {
return format(millimeters, DistanceUnit.MILLIMETER);
}
final var centimeters = DistanceConverter.convert(v, unit, DistanceUnit.CENTIMETER);
if (Math.abs(centimeters) < (1.0 / DistanceConverter.METERS_PER_CENTIMETER)) {
return format(centimeters, DistanceUnit.CENTIMETER);
}
final var meters = DistanceConverter.convert(v, unit, DistanceUnit.METER);
if (Math.abs(meters) < DistanceConverter.METERS_PER_KILOMETER) {
return format(meters, DistanceUnit.METER);
}
final var kilometers = DistanceConverter.convert(v, unit, DistanceUnit.KILOMETER);
return format(kilometers, DistanceUnit.KILOMETER);
}
/**
* Formats and converts provided distance value and unit using imperial unit
* system.
* If provided distance value is too large for provided distance unit,
* this method will convert it to a more appropriate unit.
*
* @param value a distance value.
* @param unit a distance unit.
* @return a string representation of distance value and unit using imperial
* unit system.
*/
public String formatAndConvertImperial(final Number value, final DistanceUnit unit) {
final var v = value.doubleValue();
final var inches = DistanceConverter.convert(v, unit, DistanceUnit.INCH);
if (Math.abs(inches) < (DistanceConverter.METERS_PER_FOOT / DistanceConverter.METERS_PER_INCH)) {
return format(inches, DistanceUnit.INCH);
}
final var feet = DistanceConverter.convert(v, unit, DistanceUnit.FOOT);
if (Math.abs(feet) < (DistanceConverter.METERS_PER_YARD / DistanceConverter.METERS_PER_FOOT)) {
return format(feet, DistanceUnit.FOOT);
}
final var yards = DistanceConverter.convert(v, unit, DistanceUnit.YARD);
if (Math.abs(yards) < (DistanceConverter.METERS_PER_MILE / DistanceConverter.METERS_PER_YARD)) {
return format(yards, DistanceUnit.YARD);
}
final var miles = DistanceConverter.convert(v, unit, DistanceUnit.MILE);
return format(miles, DistanceUnit.MILE);
}
/**
* Returns unit string representation.
*
* @param unit a distance unit.
* @return its string representation.
*/
@Override
public String getUnitSymbol(final DistanceUnit unit) {
return switch (unit) {
case MILLIMETER -> MILLIMETER;
case CENTIMETER -> CENTIMETER;
case KILOMETER -> KILOMETER;
case INCH -> INCH;
case FOOT -> FOOT;
case YARD -> YARD;
case MILE -> MILE;
default -> METER;
};
}
}