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