/* * Rapid Beans Framework: TypeRapidQuantityConversionTable.java * * Copyright (C) 2009 Martin Bluemel * * Creation Date: 11/14/2005 * * This program is free software; you can redistribute it and/or modify it under the terms of the * GNU Lesser General Public License as published by the Free Software Foundation; * either version 3 of the License, or (at your option) any later version. * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU Lesser General Public License for more details. * You should have received a copies of the GNU Lesser General Public License and the * GNU General Public License along with this program; if not, see <http://www.gnu.org/licenses/>. */ package org.rapidbeans.core.type; import java.math.BigDecimal; import java.math.MathContext; import java.util.List; import java.util.StringTokenizer; import java.util.logging.Level; import java.util.logging.Logger; import org.rapidbeans.core.basic.RapidEnum; import org.rapidbeans.core.exception.RapidBeansRuntimeException; /** * A helper class for converting quanitiest. * * @author Martin Bluemel */ public final class TypeRapidQuantityConversionTable { private static final Logger log = Logger.getLogger(TypeRapidQuantityConversionTable.class.getName()); /** * the "normal" unit with conversion factor 1 */ private RapidEnum normUnit = null; /** * specifies the quantitie's units. */ private RapidEnum[] units = null; /** * the table containing all conversion factors. */ private BigDecimal[][] conversionFactorTable = null; /** * the table containing reciprocal flags for all conversion factors. */ private boolean[][] conversionFactorTableReciprocalFlag = null; /** * computing strategy. */ private MathContext mathContext = MathContext.DECIMAL32; /** * constructor for a conversion conversionFactorTable. * * @param argUnitType * the enum type wich is the quantity type's unit. * @param argDescr * the String containing the conversion factors of each unit. * There are two different formats accepted to describe the * factors:<br/> * 1) XML: 2) Proprietary: */ public TypeRapidQuantityConversionTable(final TypeRapidEnum argUnitType, final String argDescr) { log.fine("parsing \"" + argDescr + "\""); List<RapidEnum> enums = argUnitType.getElements(); this.units = new RapidEnum[enums.size()]; int i; for (i = 0; i < this.units.length; i++) { this.units[i] = enums.get(i); } this.conversionFactorTable = new BigDecimal[this.units.length + 1][this.units.length]; this.conversionFactorTableReciprocalFlag = new boolean[this.units.length + 1][this.units.length]; // fill the factor column int j; StringTokenizer st = new StringTokenizer(argDescr, ","); String[] sa = new String[st.countTokens()]; StringBuffer sbUnitName = new StringBuffer(); StringBuffer sbFactor = new StringBuffer(); int state, len; String s; String sFactor; String unitName = null; RapidEnum unit = null; BigDecimal factor; char c, sign; for (i = 0; i < sa.length; i++) { s = st.nextToken(); len = s.length(); state = 0; sign = '?'; for (j = 0; j < len; j++) { c = s.charAt(j); switch (state) { case 0: switch (c) { case '/': case '*': unitName = sbUnitName.toString(); sbUnitName.setLength(0); unit = argUnitType.elementOf(unitName); sign = c; state = 1; break; default: sbUnitName.append(c); break; } break; case 1: sbFactor.append(c); break; default: break; } } sFactor = sbFactor.toString(); sbFactor.setLength(0); factor = new BigDecimal(sFactor); if (sign == '/') { // instead of storing the reciprocal value here // divide instead of always multiplying later // this avoids numerical failure // factor = BigDecimal.ONE.divide(factor); this.conversionFactorTableReciprocalFlag[this.units.length][unit.ordinal()] = true; } else { this.conversionFactorTableReciprocalFlag[this.units.length][unit.ordinal()] = false; } // enter normal conversion this.conversionFactorTable[this.units.length][unit.ordinal()] = factor; log.fine("Normal Factor[" + unit + "," + this.units.length + "," + unit.ordinal() + "] = " + factor); log.fine(" recip = [" + unit + "," + this.units.length + "," + unit.ordinal() + "] = " + this.conversionFactorTableReciprocalFlag[this.units.length][unit.ordinal()]); if (this.normUnit == null && factor.equals(BigDecimal.ONE)) { this.normUnit = unit; } } // fill the conversion matrix out of the factor column BigDecimal f1, f2; boolean r1, r2; for (i = 0; i < this.units.length; i++) { f1 = this.conversionFactorTable[this.units.length][i]; r1 = this.conversionFactorTableReciprocalFlag[this.units.length][i]; for (j = i + 1; j < this.units.length; j++) { f2 = this.conversionFactorTable[this.units.length][j]; r2 = this.conversionFactorTableReciprocalFlag[this.units.length][j]; if (f1 != null && f2 != null) { if ((!r1) && (!r2)) { switch (f1.compareTo(f2)) { case 1: this.conversionFactorTable[j][i] = f1.divide(f2, this.mathContext); this.conversionFactorTableReciprocalFlag[j][i] = true; this.conversionFactorTable[i][j] = f1.divide(f2, this.mathContext); this.conversionFactorTableReciprocalFlag[i][j] = false; break; case -1: this.conversionFactorTable[j][i] = f2.divide(f1, this.mathContext); this.conversionFactorTableReciprocalFlag[j][i] = false; this.conversionFactorTable[i][j] = f2.divide(f1, this.mathContext); this.conversionFactorTableReciprocalFlag[i][j] = true; break; case 0: this.conversionFactorTable[j][i] = BigDecimal.ONE; this.conversionFactorTableReciprocalFlag[j][i] = false; this.conversionFactorTable[i][j] = BigDecimal.ONE; this.conversionFactorTableReciprocalFlag[i][j] = false; break; default: throw new RapidBeansRuntimeException("compare unexpectedly deliverd value " + f1.compareTo(f2)); } } else if ((!r1) && r2) { if (f1.equals(BigDecimal.ONE.divide(f2))) { this.conversionFactorTable[j][i] = BigDecimal.ONE; this.conversionFactorTableReciprocalFlag[j][i] = false; this.conversionFactorTable[i][j] = BigDecimal.ONE; this.conversionFactorTableReciprocalFlag[i][j] = false; } else { this.conversionFactorTable[j][i] = f1.multiply(f2, this.mathContext); this.conversionFactorTableReciprocalFlag[j][i] = true; this.conversionFactorTable[i][j] = f1.multiply(f2, this.mathContext); this.conversionFactorTableReciprocalFlag[i][j] = false; } } else if (r1 && (!r2)) { if (f2.equals(BigDecimal.ONE.divide(f1))) { this.conversionFactorTable[j][i] = BigDecimal.ONE; this.conversionFactorTableReciprocalFlag[j][i] = false; this.conversionFactorTable[i][j] = BigDecimal.ONE; this.conversionFactorTableReciprocalFlag[i][j] = false; } else { this.conversionFactorTable[j][i] = f1.multiply(f2, this.mathContext); this.conversionFactorTableReciprocalFlag[j][i] = false; this.conversionFactorTable[i][j] = f1.multiply(f2, this.mathContext); this.conversionFactorTableReciprocalFlag[i][j] = true; } } else { // (r1 && r2) switch (f1.compareTo(f2)) { case 1: this.conversionFactorTable[j][i] = f1.divide(f2, this.mathContext); this.conversionFactorTableReciprocalFlag[j][i] = false; this.conversionFactorTable[i][j] = f1.divide(f2, this.mathContext); this.conversionFactorTableReciprocalFlag[i][j] = true; break; case -1: this.conversionFactorTable[j][i] = f2.divide(f1, this.mathContext); this.conversionFactorTableReciprocalFlag[j][i] = true; this.conversionFactorTable[i][j] = f2.divide(f1, this.mathContext); this.conversionFactorTableReciprocalFlag[i][j] = false; break; case 0: this.conversionFactorTable[j][i] = BigDecimal.ONE; this.conversionFactorTableReciprocalFlag[j][i] = false; this.conversionFactorTable[i][j] = BigDecimal.ONE; this.conversionFactorTableReciprocalFlag[i][j] = false; break; default: throw new RapidBeansRuntimeException("compare unexpectedly deliverd value " + f1.compareTo(f2)); } } } else { this.conversionFactorTable[j][i] = null; this.conversionFactorTable[j][i] = null; } if (log.getLevel() == Level.FINE || log.getLevel() == Level.FINEST) { String sRecip = "*"; if (this.conversionFactorTableReciprocalFlag[i][j]) { sRecip = "/"; } log.fine("factor [" + i + "][" + j + "] = " + sRecip + this.conversionFactorTable[j][i]); sRecip = "*"; if (this.conversionFactorTableReciprocalFlag[j][i]) { sRecip = "/"; } log.fine("factor [" + j + "][" + i + "] = " + sRecip + this.conversionFactorTable[i][j]); } } } } /** * retrieve a certain conversion factor. * * @param fromUnit * the source unit of the conversion * @param toUnit * the target unit of the conversion * @return the conversion factor */ public BigDecimal getConversionFactor(final RapidEnum fromUnit, final RapidEnum toUnit) { return this.conversionFactorTable[fromUnit.ordinal()][toUnit.ordinal()]; } /** * retrieve a certain conversion factor reciprocal state. * * @param fromUnit * the source unit of the conversion * @param toUnit * the target unit of the conversion * @return if the conversion factor is a reciprocal value */ public boolean getConversionFactorReciprocalFlag(final RapidEnum fromUnit, final RapidEnum toUnit) { return this.conversionFactorTableReciprocalFlag[fromUnit.ordinal()][toUnit.ordinal()]; } public void setConversionFactor(final RapidEnum fromUnit, final RapidEnum toUnit, final BigDecimal factor) { this.conversionFactorTable[fromUnit.ordinal()][toUnit.ordinal()] = factor; this.conversionFactorTableReciprocalFlag[fromUnit.ordinal()][toUnit.ordinal()] = false; this.conversionFactorTable[toUnit.ordinal()][fromUnit.ordinal()] = factor; this.conversionFactorTableReciprocalFlag[toUnit.ordinal()][fromUnit.ordinal()] = true; } /** * for tracing and testing purposes. */ protected void dump() { System.out.print("unitenum name=\"" + this.units[0].getType().getName() + "\"<"); int i, j; for (i = 0; i < this.units.length; i++) { if (i == 0) { System.out.print(this.units[i].name()); } else { System.out.print("," + this.units[i].name()); } } System.out.println(">"); for (i = 0; i < this.units.length; i++) { for (j = 0; j < this.units.length; j++) { if (j == 0) { System.out.print(this.conversionFactorTable[i][j]); } else { System.out.print("," + this.conversionFactorTable[i][j]); } } System.out.println(); } } /** * @return the normUnit */ public RapidEnum getNormUnit() { return normUnit; } }