001package org.fhir.ucum;
002
003/*******************************************************************************
004 * Crown Copyright (c) 2006 - 2014, Copyright (c) 2006 - 2014 Kestral Computing P/L.
005 * All rights reserved. This program and the accompanying materials
006 * are made available under the terms of the Eclipse Public License v1.0
007 * which accompanies this distribution, and is available at
008 * http://www.eclipse.org/legal/epl-v10.html
009 * 
010 * Contributors:
011 *    Kestral Computing P/L - initial implementation
012 *******************************************************************************/
013
014import java.util.Collections;
015import java.util.Comparator;
016
017import org.fhir.ucum.Canonical.CanonicalUnit;
018import org.fhir.ucum.special.Registry;
019
020public class Converter {
021
022        private UcumModel model;
023        private Registry handlers;
024        
025        /**
026         * @param model
027         */
028        public Converter(UcumModel model, Registry handlers) {
029                super();
030                this.model = model;
031                this.handlers = handlers;
032        }
033
034        
035        public Canonical convert(Term term) throws UcumException  {
036                return normalise("  ", term);
037        }
038        
039        private Canonical normalise(String indent, Term term) throws UcumException  {
040                Canonical result = new Canonical(new Decimal(1));
041                
042                debug(indent, "canonicalise", term);
043    boolean div = false;
044                Term t = term;
045                while (t != null) {
046        if (t.getComp() instanceof Term) {
047                Canonical temp = normalise(indent+"  ", (Term) t.getComp());
048                if (div) {
049                        result.divideValue(temp.getValue());
050                        for (CanonicalUnit c : temp.getUnits()) 
051                                c.setExponent(0-c.getExponent());
052                } else {
053                        result.multiplyValue(temp.getValue());
054                }
055                result.getUnits().addAll(temp.getUnits());
056        } else if (t.getComp() instanceof Factor) {
057                if (div)
058                        result.divideValue(((Factor) t.getComp()).getValue());
059                else
060                        result.multiplyValue(((Factor) t.getComp()).getValue());
061        } else if (t.getComp() instanceof Symbol) {
062                Symbol o = (Symbol) t.getComp();
063                        Canonical temp = normalise(indent, o);
064                if (div) {
065                        result.divideValue(temp.getValue());
066                        for (CanonicalUnit c : temp.getUnits()) 
067                                c.setExponent(0-c.getExponent());
068                } else {
069                        result.multiplyValue(temp.getValue());
070                }
071                result.getUnits().addAll(temp.getUnits());
072        }
073                        div = t.getOp() == Operator.DIVISION;
074                        t = t.getTerm();
075                }
076
077                debug(indent, "collate", result);
078
079                for (int i = result.getUnits().size()-1; i >= 0; i--) {
080                        CanonicalUnit sf = result.getUnits().get(i);
081                        for (int j = i-1; j >=0; j--) {
082                                CanonicalUnit st = result.getUnits().get(j);
083                                if (st.getBase() == sf.getBase()) {
084                                        st.setExponent(sf.getExponent()+st.getExponent());
085                                        result.getUnits().remove(i);
086                                        break;
087                                }
088                        }
089                }
090                for (int i = result.getUnits().size()-1; i >= 0; i--) {
091                        CanonicalUnit sf = result.getUnits().get(i);
092                        if (sf.getExponent() == 0)
093                                result.getUnits().remove(i);
094                }
095                
096                debug(indent, "sort", result);
097                Collections.sort(result.getUnits(), new Comparator<CanonicalUnit>(){
098                   @Override
099                   public int compare(final CanonicalUnit lhs, CanonicalUnit rhs) {
100                         return lhs.getBase().getCode().compareTo(rhs.getBase().getCode());
101                     }
102                 });
103                debug(indent, "done", result);
104                return result;
105        }
106
107  private Canonical normalise(String indent, Symbol sym) throws UcumException  {
108        Canonical result = new Canonical(new Decimal(1));
109        
110        if (sym.getUnit() instanceof BaseUnit) {
111                result.getUnits().add(new CanonicalUnit((BaseUnit) sym.getUnit(), sym.getExponent()));
112        } else {
113                        Canonical can = expandDefinedUnit(indent, (DefinedUnit) sym.getUnit());
114                        for (CanonicalUnit c : can.getUnits()) {
115                                c.setExponent(c.getExponent() *  sym.getExponent());
116                        }
117                        result.getUnits().addAll(can.getUnits());
118                        if (sym.getExponent() > 0) 
119                                for (int i = 0; i < sym.getExponent(); i++)
120                                        result.multiplyValue(can.getValue());
121                        else
122                                for (int i = 0; i > sym.getExponent(); i--)
123                                        result.divideValue(can.getValue());
124                } 
125                if (sym.getPrefix() != null) {
126                        if (sym.getExponent() > 0) 
127                                for (int i = 0; i < sym.getExponent(); i++)
128                                        result.multiplyValue(sym.getPrefix().getValue());
129                        else
130                                for (int i = 0; i > sym.getExponent(); i--)
131                                        result.divideValue(sym.getPrefix().getValue());
132                }
133                return result;
134  }
135
136        private Canonical expandDefinedUnit(String indent, DefinedUnit unit) throws UcumException  {
137                String u = unit.getValue().getUnit();
138                if (unit.isSpecial()) {
139                        if (!handlers.exists(unit.getCode())) 
140                                throw new UcumException("Not handled yet (special unit)");
141                        else 
142                                u = handlers.get(unit.getCode()).getUnits();
143                }
144                        
145                Term t = new ExpressionParser(model).parse(u);
146                debug(indent, "now handle", t);
147                Canonical result = normalise(indent+"  ", t);
148                result.multiplyValue(unit.getValue().getValue());
149                return result;
150  }
151
152
153        private void debug(String indent, String state, Term unit) {
154//              System.out.println(indent+state+": "+new ExpressionComposer().compose(unit));
155        }
156
157
158        private void debug(String indent, String state, Canonical can) {
159//               System.out.println(indent+state+": "+new ExpressionComposer().compose(can));
160        }
161
162
163
164}