WeightFormatter.java
/*
* Copyright (C) 2020 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 weight value and unit.
*/
public class WeightFormatter extends MeasureFormatter<Weight, WeightUnit> {
/**
* Picogram symbol.
*/
public static final String PICOGRAM = "pg";
/**
* Nanogram symbol.
*/
public static final String NANOGRAM = "ng";
/**
* Microgram symbol.
*/
public static final String MICROGRAM = "µg";
/**
* Milligram symbol.
*/
public static final String MILLIGRAM = "mg";
/**
* Gram symbol.
*/
public static final String GRAM = "g";
/**
* Kilogram symbol.
*/
public static final String KILOGRAM = "Kg";
/**
* Metric tonne symbol.
*/
public static final String TONNE = "t";
/**
* Megatonne symbol.
*/
public static final String MEGATONNE = "Mt";
/**
* U.S. OR U.K. ton symbol.
*/
public static final String US_UK_TON = "ton";
/**
* Pound symbol.
*/
public static final String POUND = "lb";
/**
* Ounce symbol.
*/
public static final String OUNCE = "oz";
/**
* Constructor.
*/
public WeightFormatter() {
super();
}
/**
* Constructor with locale.
*
* @param locale locale.
* @throws IllegalArgumentException if locale is null.
*/
public WeightFormatter(final Locale locale) {
super(locale);
}
/**
* Copy constructor.
*
* @param formatter input instance to copy from.
* @throws NullPointerException if provided formatter is null.
*/
public WeightFormatter(final WeightFormatter formatter) {
this(formatter.getLocale());
}
/**
* Determines if two weight 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 WeightFormatter) && 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 ? WeightUnit.getUnitSystem(unit) : null;
}
/**
* Parses provided string and tries to determine a weight value and unit.
*
* @param source a string to be parsed.
* @return a weight containing a value and unit.
* @throws ParseException if provided string cannot be parsed.
* @throws UnknownUnitException if unit cannot be determined.
*/
@Override
public Weight parse(final String source) throws ParseException, UnknownUnitException {
return internalParse(source, new Weight());
}
/**
* Attempts to determine a weight unit within a measurement string
* representation.
*
* @param source a weight measurement string representation.
* @return a weight unit, or null if none can be determined.
*/
@Override
public WeightUnit findUnit(final String source) {
if (source.contains(PICOGRAM + " ") || source.endsWith(PICOGRAM)) {
return WeightUnit.PICOGRAM;
}
if (source.contains(NANOGRAM + " ") || source.endsWith(NANOGRAM)) {
return WeightUnit.NANOGRAM;
}
if (source.contains(MICROGRAM + " ") || source.endsWith(MICROGRAM)) {
return WeightUnit.MICROGRAM;
}
if (source.contains(MILLIGRAM + " ") || source.endsWith(MILLIGRAM)) {
return WeightUnit.MILLIGRAM;
}
if (source.contains(KILOGRAM + " ") || source.endsWith(KILOGRAM)) {
return WeightUnit.KILOGRAM;
}
if (source.contains(MEGATONNE + " ") || source.endsWith(MEGATONNE)) {
return WeightUnit.MEGATONNE;
}
if (source.contains(POUND + " ") || source.endsWith(POUND)) {
return WeightUnit.POUND;
}
if (source.contains(OUNCE + " ") || source.endsWith(OUNCE)) {
return WeightUnit.OUNCE;
}
if (source.contains(US_UK_TON + " ") || source.endsWith(US_UK_TON)) {
final var locale = getLocale();
if (Locale.UK.getCountry().equals(locale.getCountry())) {
// UK
return WeightUnit.UK_TON;
} else {
// US
return WeightUnit.US_TON;
}
}
if (source.contains(TONNE + " ") || source.endsWith(TONNE)) {
return WeightUnit.TONNE;
}
if (source.contains(GRAM + " ") || source.endsWith(GRAM)) {
return WeightUnit.GRAM;
}
return null;
}
/**
* Formats and converts provided weight 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 weight value.
* @param unit a weight unit.
* @param system system unit to convert measurement to.
* @return a string representation of weight value and unit.
*/
@Override
public String formatAndConvert(final Number value, final WeightUnit unit, final UnitSystem system) {
if (system == UnitSystem.IMPERIAL) {
return formatAndConvertImperial(value, unit);
} else {
return formatAndConvertMetric(value, unit);
}
}
/**
* Formats and converts provided weight value and unit using metric unit
* system.
* If provided weight value is too large for provided weight unit,
* this method will convert to a more appropriate unit.
*
* @param value a weight value.
* @param unit a weight unit.
* @return a string representation of weight value and unit using metric
* unit system.
*/
public String formatAndConvertMetric(final Number value, final WeightUnit unit) {
final var v = value.doubleValue();
final var picogram = WeightConverter.convert(v, unit, WeightUnit.PICOGRAM);
if (Math.abs(picogram) < (WeightConverter.GRAMS_PER_NANOGRAM / WeightConverter.GRAMS_PER_PICOGRAM)) {
return format(picogram, WeightUnit.PICOGRAM);
}
final var nanogram = WeightConverter.convert(v, unit, WeightUnit.NANOGRAM);
if (Math.abs(nanogram) < (WeightConverter.GRAMS_PER_MICROGRAM / WeightConverter.GRAMS_PER_NANOGRAM)) {
return format(nanogram, WeightUnit.NANOGRAM);
}
final var microgram = WeightConverter.convert(v, unit, WeightUnit.MICROGRAM);
if (Math.abs(microgram) < (WeightConverter.GRAMS_PER_MILLIGRAM / WeightConverter.GRAMS_PER_MICROGRAM)) {
return format(microgram, WeightUnit.MICROGRAM);
}
final var milligram = WeightConverter.convert(v, unit, WeightUnit.MILLIGRAM);
if (Math.abs(milligram) < (1.0 / WeightConverter.GRAMS_PER_MILLIGRAM)) {
return format(milligram, WeightUnit.MILLIGRAM);
}
final var gram = WeightConverter.convert(v, unit, WeightUnit.GRAM);
if (Math.abs(gram) < WeightConverter.GRAMS_PER_KILOGRAM) {
return format(gram, WeightUnit.GRAM);
}
final var kilogram = WeightConverter.convert(v, unit, WeightUnit.KILOGRAM);
if (Math.abs(kilogram) < (WeightConverter.GRAMS_PER_TONNE / WeightConverter.GRAMS_PER_KILOGRAM)) {
return format(kilogram, WeightUnit.KILOGRAM);
}
final var tonne = WeightConverter.convert(v, unit, WeightUnit.TONNE);
if (Math.abs(tonne) < (WeightConverter.GRAMS_PER_MEGATONNE / WeightConverter.GRAMS_PER_TONNE)) {
return format(tonne, WeightUnit.TONNE);
}
final var megatonne = WeightConverter.convert(v, unit, WeightUnit.MEGATONNE);
return format(megatonne, WeightUnit.MEGATONNE);
}
/**
* Formats and converts provided weight value and unit using imperial unit
* system.
* If provided weight value is too large for provided weight unit,
* this method will convert to a more appropriate unit.
*
* @param value a weight value.
* @param unit a weight unit.
* @return a string representation of weight value and unit using imperial
* unit system.
*/
public String formatAndConvertImperial(final Number value, final WeightUnit unit) {
final var v = value.doubleValue();
final var ounce = WeightConverter.convert(v, unit, WeightUnit.OUNCE);
if (Math.abs(ounce) < (WeightConverter.GRAMS_PER_POUND / WeightConverter.GRAMS_PER_OUNCE)) {
return format(ounce, WeightUnit.OUNCE);
}
final var pound = WeightConverter.convert(v, unit, WeightUnit.POUND);
final var locale = getLocale();
if (Locale.UK.getCountry().equals(locale.getCountry())) {
// UK
if (Math.abs(pound) < (WeightConverter.GRAMS_PER_UK_TON / WeightConverter.GRAMS_PER_POUND)) {
return format(pound, WeightUnit.POUND);
}
final var ton = WeightConverter.convert(v, unit, WeightUnit.UK_TON);
return format(ton, WeightUnit.UK_TON);
} else {
// US
if (Math.abs(pound) < (WeightConverter.GRAMS_PER_US_TON / WeightConverter.GRAMS_PER_POUND)) {
return format(pound, WeightUnit.POUND);
}
final var ton = WeightConverter.convert(v, unit, WeightUnit.US_TON);
return format(ton, WeightUnit.US_TON);
}
}
/**
* Returns unit string representation.
*
* @param unit a weight unit.
* @return its string representation.
*/
@Override
public String getUnitSymbol(final WeightUnit unit) {
return switch (unit) {
case PICOGRAM -> PICOGRAM;
case NANOGRAM -> NANOGRAM;
case MICROGRAM -> MICROGRAM;
case MILLIGRAM -> MILLIGRAM;
case KILOGRAM -> KILOGRAM;
case TONNE -> TONNE;
case MEGATONNE -> MEGATONNE;
case US_TON, UK_TON -> US_UK_TON;
case POUND -> POUND;
case OUNCE -> OUNCE;
default -> GRAM;
};
}
}