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.hl7.fhir.utilities.ucum; 013 014import java.io.File; 015import java.io.FileInputStream; 016import java.io.IOException; 017import java.io.InputStream; 018import java.text.DateFormat; 019import java.text.ParseException; 020import java.text.SimpleDateFormat; 021import java.util.Date; 022 023import org.hl7.fhir.exceptions.UcumException; 024import org.hl7.fhir.utilities.Utilities; 025import org.xmlpull.v1.XmlPullParser; 026import org.xmlpull.v1.XmlPullParserException; 027import org.xmlpull.v1.XmlPullParserFactory; 028 029 030/** 031 * Parses the file ucum-essense.xml 032 * 033 * @author Grahame Grieve 034 * 035 */ 036 037public class DefinitionParser { 038 039 public UcumModel parse(String filename) throws UcumException, XmlPullParserException, IOException, ParseException { 040 return parse(new FileInputStream(new File(filename))); 041 } 042 043 public UcumModel parse(InputStream stream) throws XmlPullParserException, IOException, ParseException, UcumException { 044 XmlPullParserFactory factory = XmlPullParserFactory.newInstance( 045 System.getProperty(XmlPullParserFactory.PROPERTY_NAME), null); 046 factory.setNamespaceAware(true); 047 XmlPullParser xpp = factory.newPullParser(); 048 049 xpp.setInput(stream, null); 050 051 int eventType = xpp.next(); 052 if (eventType != XmlPullParser.START_TAG) 053 throw new XmlPullParserException("Unable to process XML document"); 054 if (!xpp.getName().equals("root")) 055 throw new XmlPullParserException("Unable to process XML document: expected 'root' but found '"+xpp.getName()+"'"); 056 DateFormat fmt = new SimpleDateFormat("yyyy-MM-dd' 'HH:mm:ss' 'Z"); 057 Date date = fmt.parse(xpp.getAttributeValue(null, "revision-date").substring(7, 32)); 058 UcumModel root = new UcumModel(xpp.getAttributeValue(null, "version"), xpp.getAttributeValue(null, "revision"), date); 059 xpp.next(); 060 while (xpp.getEventType() != XmlPullParser.END_TAG) { 061 if (xpp.getEventType() == XmlPullParser.TEXT) { 062 if (Utilities.isWhitespace(xpp.getText())) 063 xpp.next(); 064 else 065 throw new XmlPullParserException("Unexpected text "+xpp.getText()); 066 } else if (xpp.getName().equals("prefix")) 067 root.getPrefixes().add(parsePrefix(xpp)); 068 else if (xpp.getName().equals("base-unit")) 069 root.getBaseUnits().add(parseBaseUnit(xpp)); 070 else if (xpp.getName().equals("unit")) 071 root.getDefinedUnits().add(parseUnit(xpp)); 072 else 073 throw new XmlPullParserException("unknown element name "+xpp.getName()); 074 } 075 return root; 076 } 077 078 private DefinedUnit parseUnit(XmlPullParser xpp) throws XmlPullParserException, IOException, UcumException { 079 DefinedUnit unit = new DefinedUnit(xpp.getAttributeValue(null, "Code"), xpp.getAttributeValue(null, "CODE")); 080 unit.setMetric("yes".equals(xpp.getAttributeValue(null, "isMetric"))); 081 unit.setSpecial("yes".equals(xpp.getAttributeValue(null, "isSpecial"))); 082 unit.setClass_(xpp.getAttributeValue(null, "class")); 083 xpp.next(); 084 skipWhitespace(xpp); 085 while (xpp.getEventType() == XmlPullParser.START_TAG && "name".equals(xpp.getName())) 086 unit.getNames().add(readElement(xpp, "name", "unit "+unit.getCode(), false)); 087 if (xpp.getEventType() == XmlPullParser.START_TAG && "printSymbol".equals(xpp.getName())) 088 unit.setPrintSymbol(readElement(xpp, "printSymbol", "unit "+unit.getCode(), true)); 089 unit.setProperty(readElement(xpp, "property", "unit "+unit.getCode(), false)); 090 unit.setValue(parseValue(xpp, "unit "+unit.getCode())); 091 xpp.next(); 092 skipWhitespace(xpp); 093 return unit; 094 } 095 096 private Value parseValue(XmlPullParser xpp, String context) throws XmlPullParserException, UcumException, IOException { 097 checkAtElement(xpp, "value", context); 098 Decimal val = null; 099 if (xpp.getAttributeValue(null, "value") != null) 100 try { 101 if (xpp.getAttributeValue(null, "value").contains(".")) 102 val = new Decimal(xpp.getAttributeValue(null, "value"), 24); // unlimited precision for these 103 else 104 val = new Decimal(xpp.getAttributeValue(null, "value")); 105 } catch (NumberFormatException e) { 106 throw new XmlPullParserException("Error reading "+context+": "+e.getMessage()); 107 } 108 Value value = new Value(xpp.getAttributeValue(null, "Unit"), xpp.getAttributeValue(null, "UNIT"), val); 109 value.setText(readElement(xpp, "value", context, true)); 110 return value; 111 } 112 113 private BaseUnit parseBaseUnit(XmlPullParser xpp) throws XmlPullParserException, IOException { 114 BaseUnit base = new BaseUnit(xpp.getAttributeValue(null, "Code"), xpp.getAttributeValue(null, "CODE")); 115 base.setDim(xpp.getAttributeValue(null, "dim").charAt(0)); 116 xpp.next(); 117 skipWhitespace(xpp); 118 base.getNames().add(readElement(xpp, "name", "base-unit "+base.getCode(), false)); 119 base.setPrintSymbol(readElement(xpp, "printSymbol", "base-unit "+base.getCode(), false)); 120 base.setProperty(readElement(xpp, "property", "base-unit "+base.getCode(), false)); 121 xpp.next(); 122 skipWhitespace(xpp); 123 return base; 124 } 125 126 private Prefix parsePrefix(XmlPullParser xpp) throws XmlPullParserException, IOException, UcumException { 127 Prefix prefix = new Prefix(xpp.getAttributeValue(null, "Code"), xpp.getAttributeValue(null, "CODE")); 128 xpp.next(); 129 skipWhitespace(xpp); 130 prefix.getNames().add(readElement(xpp, "name", "prefix "+prefix.getCode(), false)); 131 prefix.setPrintSymbol(readElement(xpp, "printSymbol", "prefix "+prefix.getCode(), false)); 132 checkAtElement(xpp, "value", "prefix "+prefix.getCode()); 133 prefix.setValue(new Decimal(xpp.getAttributeValue(null, "value"), 24)); 134 readElement(xpp, "value", "prefix "+prefix.getCode(), true); 135 xpp.next(); 136 skipWhitespace(xpp); 137 return prefix; 138 } 139 140 private String readElement(XmlPullParser xpp, String name, String context, boolean complex) throws XmlPullParserException, IOException { 141 checkAtElement(xpp, name, context); 142 xpp.next(); 143 skipWhitespace(xpp); 144 String val = null; 145 if (complex) { 146 val = readText(xpp); 147 } else if (xpp.getEventType() == XmlPullParser.TEXT) { 148 val = xpp.getText(); 149 xpp.next(); 150 skipWhitespace(xpp); 151 } 152 if (xpp.getEventType() != XmlPullParser.END_TAG) { 153 throw new XmlPullParserException("Unexpected content reading "+context); 154 } 155 xpp.next(); 156 skipWhitespace(xpp); 157 return val; 158 } 159 160 private String readText(XmlPullParser xpp) throws XmlPullParserException, IOException { 161 StringBuilder bldr = new StringBuilder(); 162 while (xpp.getEventType() != XmlPullParser.END_TAG) { 163 if (xpp.getEventType() == XmlPullParser.TEXT) { 164 bldr.append(xpp.getText()); 165 xpp.next(); 166 } else { 167 xpp.next(); 168 bldr.append(readText(xpp)); 169 xpp.next(); 170 skipWhitespace(xpp); 171 } 172 } 173 return bldr.toString(); 174 } 175 176 private void skipWhitespace(XmlPullParser xpp) throws XmlPullParserException, IOException { 177 while (xpp.getEventType() == XmlPullParser.TEXT && Utilities.isWhitespace(xpp.getText())) 178 xpp.next(); 179 } 180 181 private void checkAtElement(XmlPullParser xpp, String name, String context) throws XmlPullParserException { 182 if (xpp.getEventType() != XmlPullParser.START_TAG) 183 throw new XmlPullParserException("Unexpected state looking for "+name+": at "+Integer.toString(xpp.getEventType())+" reading "+context); 184 if (!xpp.getName().equals(name)) 185 throw new XmlPullParserException("Unexpected element looking for "+name+": found "+xpp.getName()+" reading "+context); 186 } 187}