package org.andork.unit; import java.math.BigDecimal; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; public class Length extends UnitType<Length> { public static final Length type; public static final Unit<Length> kilometers; public static final Unit<Length> meters; public static final Unit<Length> centimeters; public static final Unit<Length> miles; public static final Unit<Length> yards; public static final Unit<Length> feet; public static final Unit<Length> inches; private static final Map<Unit<Length>, Map<Unit<Length>, BigDecimal>> bigDecimalConversions = new HashMap<>(); private static final Map<Unit<Length>, Map<Unit<Length>, Double>> doubleConversions = new HashMap<>(); private static final Set<Unit<Length>> imperialUnits = new HashSet<>(); private static final Set<Unit<Length>> metricUnits = new HashSet<>(); static { type = new Length(); type.addUnit(kilometers = new Unit<Length>(type, "km")); type.addUnit(meters = new Unit<Length>(type, "m")); type.addUnit(centimeters = new Unit<Length>(type, "cm")); type.addUnit(miles = new Unit<Length>(type, "mi")); type.addUnit(yards = new Unit<Length>(type, "yd")); type.addUnit(feet = new Unit<Length>(type, "ft")); type.addUnit(inches = new Unit<Length>(type, "in")); metricUnits.add(kilometers); metricUnits.add(meters); metricUnits.add(centimeters); imperialUnits.add(miles); imperialUnits.add(yards); imperialUnits.add(feet); imperialUnits.add(inches); for (Unit<Length> unit : type.units()) { Map<Unit<Length>, BigDecimal> bigDecimalConv = new HashMap<>(); // conversion from unit to itself bigDecimalConv.put(unit, BigDecimal.ONE); bigDecimalConversions.put(unit, bigDecimalConv); doubleConversions.put(unit, new HashMap<>()); } // set up conversions from meters to feet and all metric units Map<Unit<Length>, BigDecimal> meterConversions = bigDecimalConversions.get(meters); meterConversions.put(meters, BigDecimal.ONE); meterConversions .put(feet, BigDecimal.ONE.divide(new BigDecimal("0.3048"), 64, BigDecimal.ROUND_HALF_UP)); meterConversions.put(kilometers, new BigDecimal("0.001")); meterConversions.put(centimeters, new BigDecimal(100)); // set up conversions from other metric units to meters for (Unit<Length> unit : metricUnits) { if (unit == meters) { continue; } bigDecimalConversions.get(unit).put(meters, BigDecimal.ONE.divide(meterConversions.get(unit), 64, BigDecimal.ROUND_HALF_UP)); } // set up conversions from feet to meters and all imperial units Map<Unit<Length>, BigDecimal> feetConversions = bigDecimalConversions.get(feet); feetConversions.put(feet, BigDecimal.ONE); feetConversions.put(meters, new BigDecimal("0.3048")); feetConversions.put(miles, BigDecimal.ONE.divide(new BigDecimal(5280), 64, BigDecimal.ROUND_HALF_UP)); feetConversions.put(yards, BigDecimal.ONE.divide(new BigDecimal(3), 64, BigDecimal.ROUND_HALF_UP)); feetConversions.put(inches, new BigDecimal(12)); // set up conversions from other imperial units to feet for (Unit<Length> unit : imperialUnits) { if (unit == feet) { continue; } bigDecimalConversions.get(unit).put(feet, BigDecimal.ONE.divide(feetConversions.get(unit), 64, BigDecimal.ROUND_HALF_UP)); } // now go through all combinations of metric and imperial units and // compute the conversions for (Unit<Length> metricUnit : metricUnits) { Map<Unit<Length>, BigDecimal> metricConversions = bigDecimalConversions.get(metricUnit); for (Unit<Length> imperialUnit : imperialUnits) { if (metricUnit == meters && imperialUnit == feet) { continue; } // metricUnit/imperialUnit = metricUnit/m * m/ft * // ft/imperialUnit metricConversions.put(imperialUnit, metricConversions.get(meters) .multiply(meterConversions.get(feet)) .multiply(feetConversions.get(imperialUnit))); Map<Unit<Length>, BigDecimal> imperialConversions = bigDecimalConversions.get(imperialUnit); // imperialUnit/metricUnit = imperialUnit/ft * ft/m * // m/metricUnit imperialConversions.put(metricUnit, imperialConversions.get(feet) .multiply(feetConversions.get(meters)) .multiply(meterConversions.get(metricUnit))); } } // create the double conversions map by rounding all the BigDecimals for (Unit<Length> unit : type.units()) { Map<Unit<Length>, BigDecimal> bigDecimalConv = bigDecimalConversions.get(unit); Map<Unit<Length>, Double> doubleConv = doubleConversions.get(unit); for (Map.Entry<Unit<Length>, BigDecimal> entry : bigDecimalConv.entrySet()) { doubleConv.put(entry.getKey(), entry.getValue().doubleValue()); } } } private Length() { } @Override public double convert(double d, Unit<Length> from, Unit<Length> to) { return d * doubleConversions.get(from).get(to); } }