blob: a5065df67df7a8e7836943c7730878730b7cdd3c [file] [log] [blame]
/*******************************************************************************
* Crown Copyright (c) 2006, 2011, Copyright (c) 2006, 2017 Kestral Computing P/L and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Kestral Computing P/L - initial implementation
*
*******************************************************************************/
package org.eclipse.uomo.ucum.impl;
import static org.eclipse.uomo.core.impl.OutputHelper.isConsoleOutput;
import static org.eclipse.uomo.core.impl.OutputHelper.println;
import static org.eclipse.uomo.ucum.model.ConceptKind.*;
import static org.eclipse.uomo.ucum.expression.Operator.*;
import java.math.BigDecimal;
import java.math.MathContext;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.uomo.core.UOMoRuntimeException;
import org.eclipse.uomo.ucum.expression.Component;
import org.eclipse.uomo.ucum.expression.Factor;
import org.eclipse.uomo.ucum.expression.Symbol;
import org.eclipse.uomo.ucum.expression.Term;
import org.eclipse.uomo.ucum.model.DefinedUnit;
import org.eclipse.uomo.ucum.model.UcumModel;
import org.eclipse.uomo.ucum.parsers.ExpressionComposer;
import org.eclipse.uomo.ucum.parsers.ExpressionParser;
import org.eclipse.uomo.ucum.special.SpecialUnitHandler;
import org.eclipse.uomo.units.AbstractConverter;
import org.eclipse.uomo.util.Registry;
import org.unitsofmeasurement.unit.UnitConverter;
/**
* = [mu_0] = 4.[pi].10*-7.N/A2 = (g.m/s2)/(C/s)2? = g.m/s2/(C2/s2) =
* g.m/s2/C2.s2 = g.m/C2.s2/s2 = g.m/C2 = m.g.C-2
*
* @author Grahame Grieve
* @author Werner Keil
*/
class UcumConverter extends AbstractConverter {
/**
*
*/
private static final long serialVersionUID = -895607408258138526L;
private final UcumModel model;
@SuppressWarnings("rawtypes")
private final Registry<SpecialUnitHandler> handlers;
private final UnitConverter compound;
private Factor one = new Factor(1);
/**
* @param model
*/
public UcumConverter(UcumModel model, Registry handlers) {
super();
this.model = model;
this.handlers = handlers;
List<UnitConverter> compounds = getCompoundConverters();
compound = compounds.get(0);
}
public Canonical convert(Term term) throws UOMoRuntimeException {
return convertTerm(term);
}
private Canonical convertTerm(Term term) throws UOMoRuntimeException {
Canonical res = new Canonical(BigDecimal.ONE, new Term());
if (term.hasComp())
res.getUnit().setComp(convertComp(res, term.getComp()));
if (term.hasOp())
res.getUnit().setOp(term.getOp());
if (term.hasTerm()) {
Canonical t = convertTerm(term.getTerm());
res.setValue(res.getValue().multiply(t.getValue()));
if (t.hasUnit())
res.getUnit().setTermCheckOp(t.getUnit());
else
res.getUnit().setOp(null);
}
// normalise
debug("normalise", res.getUnit()); //$NON-NLS-1$
if (res.getUnit().hasOp() && res.getUnit().getOp() == DIVISION) {
res.getUnit().setOp(MULTIPLICATION);
flipExponents(res.getUnit().getTerm());
debug("flipped", res.getUnit()); //$NON-NLS-1$
}
if (!res.getUnit().hasComp() || res.getUnit().getComp() == one) {
res.setUnit(res.getUnit().getTerm());
debug("trimmed", res.getUnit()); //$NON-NLS-1$
}
// everything in scope is a multiplication operation. If comp is a term,
// then
// we are going to tack our term on the end of that term as a
// multiplication, and
// make comp our term
if (res.hasUnit() && res.getUnit().hasComp()
&& res.getUnit().getComp() instanceof Term) {
Term end = getEndTerm((Term) res.getUnit().getComp());
assert end.getOp() == null;
end.setOp(MULTIPLICATION);
end.setTermCheckOp(res.getUnit().getTerm());
res.setUnit((Term) res.getUnit().getComp());
debug("reorged", res.getUnit()); //$NON-NLS-1$
}
if (res.hasUnit()
&& (!res.getUnit().hasComp() || res.getUnit().getComp() == one)) {
res.setUnit(res.getUnit().getTerm());
debug("trimmed", res.getUnit()); //$NON-NLS-1$
}
// now we have a linear list of terms, each with one component.
// we scan through the list looking for common components to factor out
// we have to scan into the list because we may have deep duplicates
// from the previous flattening operation. we also remove anything
// that's
// ended up with an exponent of 0 during this operation
if (res.hasUnit())
res.setUnit(removeDuplicateComponents(res.getUnit()));
if (res.hasUnit() && !res.getUnit().hasTerm())
res.getUnit().setOp(null);
debug("norm finished", res.getUnit()); //$NON-NLS-1$
// System.out.println("value: "+res.getValue().toPlainString()+"; units: "+new
// ExpressionComposer().compose(res.getUnit()));
return res;
}
private void debug(String state, Term unit) {
if (isConsoleOutput()) { // avoiding any call if no debug output set
println(state + ": " + new ExpressionComposer().compose(unit)); //$NON-NLS-1$
}
}
private Term getEndTerm(Term term) {
if (term.hasTerm())
return getEndTerm(term.getTerm());
else
return term;
}
private Term removeDuplicateComponents(Term unit) {
if (unit == null)
return null;
assert unit.getComp() instanceof Symbol; // because that should be all
// that's left
Symbol symO = (Symbol) unit.getComp();
Term inner = findDuplicateCompOwner(unit.getTerm(), symO);
if (inner != null) {
Symbol symI = (Symbol) inner.getComp();
symI.setExponent(symI.getExponent() + symO.getExponent());
return removeDuplicateComponents(unit.getTerm());
}
if (symO.getExponent() == 0)
return removeDuplicateComponents(unit.getTerm());
unit.setTermCheckOp(removeDuplicateComponents(unit.getTerm()));
return unit;
}
private Term findDuplicateCompOwner(Term term, Symbol comp) {
if (term == null)
return null;
if (term.getComp() instanceof Symbol) {
Symbol sym = (Symbol) term.getComp();
if (sym.getPrefix() == comp.getPrefix() && // i.e. null
sym.getUnit() == comp.getUnit())
return term;
}
return findDuplicateCompOwner(term.getTerm(), comp);
}
private void flipExponents(Term term) {
if (term.getComp() instanceof Symbol) {
((Symbol) term.getComp()).invertExponent();
}
if (term.hasTerm()) {
flipExponents(term.getTerm());
}
}
private Component convertComp(Canonical ctxt, Component comp)
throws UOMoRuntimeException {
if (comp instanceof Term) {
Canonical t = convertTerm((Term) comp);
ctxt.multiplyValue(t.getValue());
return t.getUnit();
} else if (comp instanceof Factor) {
ctxt.multiplyValue(((Factor) comp).getValue());
return one; // nothing to convert
} else if (comp instanceof Symbol)
return convertSymbol(ctxt, (Symbol) comp);
else
throw new UOMoRuntimeException("unknown component type " //$NON-NLS-1$
+ comp.getClass().toString());
}
private Component convertSymbol(Canonical ctxt, Symbol comp)
throws UOMoRuntimeException {
if (comp.hasPrefix()) {
// ctxt.multiplyValue(comp.getPrefix().getValue());
ctxt.multiplyValue(((BigDecimal)comp.getPrefix().getValue()).pow(
comp.getExponent(), MathContext.DECIMAL128));
}
if (comp.getUnit().getKind() == BASEUNIT) {
Symbol res = new Symbol();
res.setUnit(comp.getUnit());
res.setExponent(comp.getExponent());
return res;
} else {
DefinedUnit unit = (DefinedUnit) comp.getUnit();
String u = unit.getValue().getCode();
if (unit.isSpecial()) {
if (!handlers.exists(unit.getCode()))
throw new UOMoRuntimeException("Not handled yet (special unit)"); //$NON-NLS-1$
else {
u = handlers.get(unit.getCode()).getUnits();
ctxt.multiplyValue(handlers.get(unit.getCode()).value());
}
} else
ctxt.multiplyValue(unit.getValue().value());
Term canonical = new ExpressionParser(model).parse(u);
if (canonical.hasComp() && !canonical.hasOp()
&& !canonical.hasTerm()) {
Component ret = convertComp(ctxt, canonical.getComp());
if (comp.getExponent() == 1)
return ret;
else if (ret instanceof Factor) {
((Factor) ret).setValue(comp.getExponent()
+ ((Factor) ret).getValue());
return ret;
} else if (ret instanceof Symbol) {
((Symbol) ret).setExponent(comp.getExponent()
* ((Symbol) ret).getExponent());
return ret;
} else if (ret instanceof Term) {
applyExponent((Term) ret, comp.getExponent());
return ret;
} else
throw new UOMoRuntimeException("unknown component type " //$NON-NLS-1$
+ comp.getClass().toString());
} else {
Canonical t1 = convertTerm(canonical);
Term ret = t1.getUnit();
if (comp.getExponent() == -1 && ret.hasComp() && ret.hasOp()
&& ret.hasTerm() && ret.getTerm().hasComp()
&& !ret.getTerm().hasOp() && !ret.getTerm().hasTerm()) {
Component t = ret.getTerm().getComp();
ret.getTerm().setComp(ret.getComp());
ret.setComp(t);
ctxt.divideValue(t1.getValue());
return ret;
} else if (comp.getExponent() != 1) {
ctxt.multiplyValue(t1.getValue());
// what we have to do is push the exponent into the all the
// symbols contained herein
applyExponent(ret, comp.getExponent());
return ret;
} else {
ctxt.multiplyValue(t1.getValue());
return ret;
}
}
}
}
private void applyExponent(Term term, int exponent) {
if (term == null)
return;
if (term.hasComp()) {
if (term.getComp() instanceof Term) {
applyExponent((Term) term.getComp(), exponent);
} else if (term.getComp() instanceof Symbol) {
Symbol sym = (Symbol) term.getComp();
sym.setExponent(sym.getExponent() * exponent);
}
}
applyExponent(term.getTerm(), exponent);
}
public UnitConverter concatenate(UnitConverter converter) {
return compound.concatenate(converter);
}
public double convert(double value) {
return compound.convert(value);
}
public BigDecimal convert(BigDecimal value, MathContext ctx)
throws ArithmeticException {
return compound.convert(value, ctx);
}
public List<UnitConverter> getCompoundConverters() {
final List<UnitConverter> compound = new ArrayList<UnitConverter>();
compound.add(this);
return compound;
}
public UnitConverter inverse() {
return compound.inverse();
}
public boolean isIdentity() {
return false;
}
public boolean isLinear() {
return false;
}
public Number convert(Number value) {
return compound.convert(value);
}
}