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