001/*******************************************************************************
002 * Crown Copyright (c) 2006 - 2014, Copyright (c) 2006 - 2014 Kestral Computing P/L.
003 * All rights reserved. This program and the accompanying materials
004 * are made available under the terms of the Eclipse Public License v1.0
005 * which accompanies this distribution, and is available at
006 * http://www.eclipse.org/legal/epl-v10.html
007 * 
008 * Contributors:
009 *    Kestral Computing P/L - initial implementation
010 *******************************************************************************/
011
012package org.fhir.ucum;
013
014public class ExpressionParser {
015
016        private UcumModel model;
017        
018        /**
019         * @param model
020         */
021        public ExpressionParser(UcumModel model) {
022                super();
023                this.model = model;
024        }
025
026        public Term parse(String code) throws UcumException  {
027                Lexer lexer = new Lexer(code);
028                Term res = parseTerm(lexer, true);
029                if (!lexer.finished())
030                        throw new UcumException("Expression was not parsed completely. Syntax Error?");
031                return res;
032        }
033        
034        private Term parseTerm(Lexer lexer, boolean first) throws UcumException  {
035                Term res = new Term();
036                if (first && lexer.getType() == TokenType.NONE) {
037                        res.setComp(new Factor(1));
038                } else if (lexer.getType() == TokenType.SOLIDUS) {
039                        res.setOp(Operator.DIVISION);
040                        lexer.consume();
041                        res.setTerm(parseTerm(lexer, false));
042                } else {
043                  if (lexer.getType() == TokenType.ANNOTATION) {
044         res.setComp(new Factor(1)); // still lose the annotation
045         lexer.consume();
046                } else
047         res.setComp(parseComp(lexer));
048                        if (lexer.getType() != TokenType.NONE && lexer.getType() != TokenType.CLOSE) {
049                                if (lexer.getType() == TokenType.SOLIDUS) {
050                                        res.setOp(Operator.DIVISION);
051                                        lexer.consume();
052                                } else if (lexer.getType() == TokenType.PERIOD) {
053                                        res.setOp(Operator.MULTIPLICATION);
054                                        lexer.consume();
055                                } else if (lexer.getType() == TokenType.ANNOTATION)
056                                        res.setOp(Operator.MULTIPLICATION); // implicit
057                                else
058                                        lexer.error("Expected '/' or '.'");
059                                res.setTerm(parseTerm(lexer, false));
060                        }
061                } 
062                return res;
063        }
064
065        private Component parseComp(Lexer lexer) throws UcumException  {
066                if (lexer.getType() == TokenType.NUMBER) { 
067                        Factor fact = new Factor(lexer.getTokenAsInt());
068                        lexer.consume();
069                        return fact;
070                } else if (lexer.getType() == TokenType.SYMBOL)
071                        return parseSymbol(lexer);
072                else if  (lexer.getType() == TokenType.NONE)
073                        lexer.error("unexpected end of expression looking for a symbol or a number");
074                else if (lexer.getType() == TokenType.OPEN) {
075      lexer.consume();
076      Term res = parseTerm(lexer, true);
077      if (lexer.getType() == TokenType.CLOSE) 
078        lexer.consume();
079      else
080        lexer.error("Unexpected Token Type '"+lexer.getType().toString()+"' looking for a close bracket");
081      return res;
082                } else 
083                        lexer.error("unexpected token looking for a symbol or a number");
084                return null; // we never get to here
085        }
086
087        private Component parseSymbol(Lexer lexer) throws UcumException  {
088                Symbol symbol = new Symbol(); 
089                String sym = lexer.getToken();
090                
091                // now, can we pick a prefix that leaves behind a metric unit?
092                Prefix selected = null;
093                Unit unit = null;
094                for (Prefix prefix : model.getPrefixes()) {
095                        if (sym.startsWith(prefix.getCode())) {
096                                unit = model.getUnit(sym.substring(prefix.getCode().length()));
097                                if (unit != null && (unit.getKind() == ConceptKind.BASEUNIT || ((DefinedUnit) unit).isMetric())) {
098                                        selected = prefix;
099                                        break;
100                                };                              
101                        }
102                }
103
104                if (selected != null) {
105                        symbol.setPrefix(selected);
106                        symbol.setUnit(unit);
107                } else {
108                        unit = model.getUnit(sym);
109                        if (unit != null) 
110                                symbol.setUnit(unit);
111                        else if (!sym.equals("1"))
112                                lexer.error("The unit '"+sym+"' is unknown");
113                }
114                
115                lexer.consume();
116                if (lexer.getType() == TokenType.NUMBER) {
117                        symbol.setExponent(lexer.getTokenAsInt());
118                        lexer.consume();
119                } else
120                        symbol.setExponent(1);
121
122                return symbol;
123        }
124}