1   
2   
3   
4   
5   
6   
7   
8   
9   
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
28  package ca.uhn.hl7v2.conf.parser;
29  
30  import java.io.BufferedReader;
31  import java.io.File;
32  import java.io.FileNotFoundException;
33  import java.io.FileReader;
34  import java.io.IOException;
35  import java.io.InputStream;
36  
37  import org.slf4j.Logger;
38  import org.slf4j.LoggerFactory;
39  import org.w3c.dom.DOMError;
40  import org.w3c.dom.DOMErrorHandler;
41  import org.w3c.dom.Document;
42  import org.w3c.dom.Element;
43  import org.w3c.dom.Node;
44  import org.w3c.dom.NodeList;
45  
46  import ca.uhn.hl7v2.conf.ProfileException;
47  import ca.uhn.hl7v2.conf.spec.MetaData;
48  import ca.uhn.hl7v2.conf.spec.RuntimeProfile;
49  import ca.uhn.hl7v2.conf.spec.message.AbstractComponent;
50  import ca.uhn.hl7v2.conf.spec.message.AbstractSegmentContainer;
51  import ca.uhn.hl7v2.conf.spec.message.Component;
52  import ca.uhn.hl7v2.conf.spec.message.DataValue;
53  import ca.uhn.hl7v2.conf.spec.message.Field;
54  import ca.uhn.hl7v2.conf.spec.message.ProfileStructure;
55  import ca.uhn.hl7v2.conf.spec.message.Seg;
56  import ca.uhn.hl7v2.conf.spec.message.SegGroup;
57  import ca.uhn.hl7v2.conf.spec.message.StaticDef;
58  import ca.uhn.hl7v2.conf.spec.message.SubComponent;
59  import ca.uhn.hl7v2.util.XMLUtils;
60  
61  
62  
63  
64  
65  
66  
67  
68  
69  
70  
71  
72  
73  
74  
75  
76  
77  
78  
79  
80  
81  
82  
83  
84  
85  
86  
87  
88  
89  public class ProfileParser {
90  
91  	private static final String PROFILE_XSD = "ca/uhn/hl7v2/conf/parser/message_profile.xsd";
92  
93  	private static final Logger log = LoggerFactory.getLogger(ProfileParser.class);
94  
95  	private final boolean alwaysValidate;
96  	private final DOMErrorHandler errorHandler;
97  
98  	
99  
100 
101 
102 
103 
104 	public ProfileParser(boolean alwaysValidate) {
105 
106 		this.alwaysValidate = alwaysValidate;
107 		this.errorHandler = error -> {
108             if (error.getSeverity() == DOMError.SEVERITY_WARNING) {
109                 log.warn("Warning: {}", error.getMessage());
110             } else {
111                 throw new RuntimeException((Exception) error.getRelatedException());
112             }
113             return true;
114         };
115 	}
116 
117 
118 	
119 
120 
121 
122 
123 
124 
125 
126 
127 
128 
129 	public RuntimeProfile parseClasspath(String classPath) throws ProfileException, IOException {
130 
131 		InputStream stream = Thread.currentThread().getContextClassLoader()
132 				.getResourceAsStream(classPath);
133 		if (stream == null) {
134 			throw new FileNotFoundException(classPath);
135 		}
136 
137 		StringBuilder profileString = new StringBuilder();
138 		byte[] buffer = new byte[1000];
139 		int bytesRead;
140 		while ((bytesRead = stream.read(buffer)) > 0) {
141 			profileString.append(new String(buffer, 0, bytesRead));
142 		}
143 
144 		RuntimeProfile profile = new RuntimeProfile();
145 		Document doc = parseIntoDOM(profileString.toString());
146 
147 		Element root = doc.getDocumentElement();
148 		profile.setHL7Version(root.getAttribute("HL7Version"));
149 
150 		
151 		NodeList nl = root.getElementsByTagName("HL7v2xStaticDef");
152 		Element staticDef = (Element) nl.item(0);
153 		StaticDef sd = parseStaticProfile(staticDef);
154 		profile.setMessage(sd);
155 		return profile;
156 	}
157 
158 	
159 
160 
161 	public RuntimeProfile parse(String profileString) throws ProfileException {
162 		RuntimeProfile profile = new RuntimeProfile();
163 		Document doc = parseIntoDOM(profileString);
164 
165 		Element root = doc.getDocumentElement();
166 		profile.setHL7Version(root.getAttribute("HL7Version"));
167 
168 		NodeList metadataList = root.getElementsByTagName("MetaData");
169 		if (metadataList.getLength() > 0) {
170 			Element metadata = (Element) metadataList.item(0);
171 			String name = metadata.getAttribute("Name");
172 			profile.setName(name);
173 		}
174 
175 		
176 		NodeList nl = root.getElementsByTagName("HL7v2xStaticDef");
177 		Element staticDef = (Element) nl.item(0);
178 		StaticDef sd = parseStaticProfile(staticDef);
179 		profile.setMessage(sd);
180 		return profile;
181 	}
182 
183 	private StaticDef parseStaticProfile(Element elem) throws ProfileException {
184 		StaticDef message = new StaticDef();
185 		message.setMsgType(elem.getAttribute("MsgType"));
186 		message.setEventType(elem.getAttribute("EventType"));
187 		message.setMsgStructID(elem.getAttribute("MsgStructID"));
188 		message.setOrderControl(elem.getAttribute("OrderControl"));
189 		message.setEventDesc(elem.getAttribute("EventDesc"));
190 		message.setIdentifier(elem.getAttribute("Identifier"));
191 		message.setRole(elem.getAttribute("Role"));
192 
193 		Element md = getFirstElementByTagName("MetaData", elem);
194 		if (md != null)
195 			message.setMetaData(parseMetaData(md));
196 
197 		message.setImpNote(getValueOfFirstElement("ImpNote", elem));
198 		message.setDescription(getValueOfFirstElement("Description", elem));
199 		message.setReference(getValueOfFirstElement("Reference", elem));
200 
201 		parseChildren(message, elem);
202 		return message;
203 	}
204 
205 	
206 	private MetaData parseMetaData(Element elem) {
207 		log.debug("ProfileParser.parseMetaData() has been called ... note that this method does nothing.");
208 		return null;
209 	}
210 
211 	
212 
213 
214 	private void parseChildren(AbstractSegmentContainer parent, Element elem)
215 			throws ProfileException {
216 		int childIndex = 1;
217 		NodeList children = elem.getChildNodes();
218 		for (int i = 0; i < children.getLength(); i++) {
219 			Node n = children.item(i);
220 			if (n.getNodeType() == Node.ELEMENT_NODE) {
221 				Element child = (Element) n;
222 				if (child.getNodeName().equalsIgnoreCase("SegGroup")) {
223 					SegGroup group = parseSegmentGroupProfile(child);
224 					parent.setChild(childIndex++, group);
225 				} else if (child.getNodeName().equalsIgnoreCase("Segment")) {
226 					Seg segment = parseSegmentProfile(child);
227 					parent.setChild(childIndex++, segment);
228 				}
229 			}
230 		}
231 	}
232 
233 	
234 	private SegGroup parseSegmentGroupProfile(Element elem) throws ProfileException {
235 		SegGroup group = new SegGroup();
236 		log.debug("Parsing segment group profile: " + elem.getAttribute("Name"));
237 
238 		parseProfileStuctureData(group, elem);
239 
240 		parseChildren(group, elem);
241 		return group;
242 	}
243 
244 	
245 	private Seg parseSegmentProfile(Element elem) throws ProfileException {
246 		Seg segment = new Seg();
247 		log.debug("Parsing segment profile: " + elem.getAttribute("Name"));
248 
249 		parseProfileStuctureData(segment, elem);
250 
251 		int childIndex = 1;
252 		NodeList children = elem.getChildNodes();
253 		for (int i = 0; i < children.getLength(); i++) {
254 			Node n = children.item(i);
255 			if (n.getNodeType() == Node.ELEMENT_NODE) {
256 				Element child = (Element) n;
257 				if (child.getNodeName().equalsIgnoreCase("Field")) {
258 					Field field = parseFieldProfile(child);
259 					segment.setField(childIndex++, field);
260 				}
261 			}
262 		}
263 
264 		return segment;
265 	}
266 
267 	
268 	private void parseProfileStuctureData(ProfileStructure struct, Element elem)
269 			throws ProfileException {
270 		struct.setName(elem.getAttribute("Name"));
271 		struct.setLongName(elem.getAttribute("LongName"));
272 		struct.setUsage(elem.getAttribute("Usage"));
273 		String min = elem.getAttribute("Min");
274 		String max = elem.getAttribute("Max");
275 		try {
276 			struct.setMin(Short.parseShort(min));
277 			if (max.indexOf('*') >= 0) {
278 				struct.setMax((short) -1);
279 			} else {
280 				struct.setMax(Short.parseShort(max));
281 			}
282 		} catch (NumberFormatException e) {
283 			throw new ProfileException("Min and max must be short integers: " + min + ", " + max, e);
284 		}
285 
286 		struct.setImpNote(getValueOfFirstElement("ImpNote", elem));
287 		struct.setDescription(getValueOfFirstElement("Description", elem));
288 		struct.setReference(getValueOfFirstElement("Reference", elem));
289 		struct.setPredicate(getValueOfFirstElement("Predicate", elem));
290 	}
291 
292 	
293 	private Field parseFieldProfile(Element elem) throws ProfileException {
294 		Field field = new Field();
295 		log.debug("  Parsing field profile: " + elem.getAttribute("Name"));
296 
297 		field.setUsage(elem.getAttribute("Usage"));
298 		String itemNo = elem.getAttribute("ItemNo");
299 		String min = elem.getAttribute("Min");
300 		String max = elem.getAttribute("Max");
301 
302 		try {
303 			if (itemNo.length() > 0) {
304 				field.setItemNo(Short.parseShort(itemNo));
305 			}
306 		} catch (NumberFormatException e) {
307 			throw new ProfileException("Invalid ItemNo: " + itemNo + "( for name "
308 					+ elem.getAttribute("Name") + ")", e);
309 		} 
310 
311 		try {
312 			field.setMin(Short.parseShort(min));
313 			if (max.indexOf('*') >= 0) {
314 				field.setMax((short) -1);
315 			} else {
316 				field.setMax(Short.parseShort(max));
317 			}
318 		} catch (NumberFormatException e) {
319 			throw new ProfileException("Min and max must be short integers: " + min + ", " + max, e);
320 		}
321 
322 		parseAbstractComponentData(field, elem);
323 
324 		int childIndex = 1;
325 		NodeList children = elem.getChildNodes();
326 		for (int i = 0; i < children.getLength(); i++) {
327 			Node n = children.item(i);
328 			if (n.getNodeType() == Node.ELEMENT_NODE) {
329 				Element child = (Element) n;
330 				if (child.getNodeName().equalsIgnoreCase("Component")) {
331 					Component comp = (Component) parseComponentProfile(child, false);
332 					field.setComponent(childIndex++, comp);
333 				}
334 			}
335 		}
336 
337 		return field;
338 	}
339 
340 	
341 	private AbstractComponent<?> parseComponentProfile(Element elem, boolean isSubComponent)
342 			throws ProfileException {
343 		AbstractComponent<?> comp;
344 		if (isSubComponent) {
345 			log.debug("      Parsing subcomp profile: " + elem.getAttribute("Name"));
346 			comp = new SubComponent();
347 		} else {
348 			log.debug("    Parsing comp profile: " + elem.getAttribute("Name"));
349 			comp = new Component();
350 
351 			int childIndex = 1;
352 			NodeList children = elem.getChildNodes();
353 			for (int i = 0; i < children.getLength(); i++) {
354 				Node n = children.item(i);
355 				if (n.getNodeType() == Node.ELEMENT_NODE) {
356 					Element child = (Element) n;
357 					if (child.getNodeName().equalsIgnoreCase("SubComponent")) {
358 						SubComponent subcomp = (SubComponent) parseComponentProfile(child, true);
359 						((Component) comp).setSubComponent(childIndex++, subcomp);
360 					}
361 				}
362 			}
363 		}
364 
365 		parseAbstractComponentData(comp, elem);
366 
367 		return comp;
368 	}
369 
370 	
371 
372 
373 	private void parseAbstractComponentData(AbstractComponent<?> comp, Element elem)
374 			throws ProfileException {
375 		comp.setName(elem.getAttribute("Name"));
376 		comp.setUsage(elem.getAttribute("Usage"));
377 		comp.setDatatype(elem.getAttribute("Datatype"));
378 		String length = elem.getAttribute("Length");
379 		if (length != null && length.length() > 0) {
380 			try {
381 				comp.setLength(Long.parseLong(length));
382 			} catch (NumberFormatException e) {
383 				throw new ProfileException("Length must be a long integer: " + length, e);
384 			}
385 		}
386 		comp.setConstantValue(elem.getAttribute("ConstantValue"));
387 		String table = elem.getAttribute("Table");
388 		if (table != null && table.length() > 0) {
389 			try {
390 				comp.setTable(table);
391 			} catch (NumberFormatException e) {
392 				throw new ProfileException("Table must be a short integer: " + table, e);
393 			}
394 		}
395 
396 		comp.setImpNote(getValueOfFirstElement("ImpNote", elem));
397 		comp.setDescription(getValueOfFirstElement("Description", elem));
398 		comp.setReference(getValueOfFirstElement("Reference", elem));
399 		comp.setPredicate(getValueOfFirstElement("Predicate", elem));
400 
401 		int dataValIndex = 0;
402 		NodeList children = elem.getChildNodes();
403 		for (int i = 0; i < children.getLength(); i++) {
404 			Node n = children.item(i);
405 			if (n.getNodeType() == Node.ELEMENT_NODE) {
406 				Element child = (Element) n;
407 				if (child.getNodeName().equalsIgnoreCase("DataValues")) {
408 					DataValue val = new DataValue();
409 					val.setExValue(child.getAttribute("ExValue"));
410 					comp.setDataValues(dataValIndex++, val);
411 				}
412 			}
413 		}
414 
415 	}
416 
417 	
418 	private Document parseIntoDOM(String profileString) throws ProfileException {
419 		try {
420 			Document doc = XMLUtils.parse(profileString, true);
421 			if (alwaysValidate)
422 				XMLUtils.validate(doc, PROFILE_XSD, errorHandler);
423 			return doc;
424 		} catch (Exception e) {
425 			throw new ProfileException("Exception parsing message profile: " + e.getMessage(), e);
426 		}
427 	}
428 
429 	
430 
431 
432 
433 	private Element getFirstElementByTagName(String name, Element parent) {
434 		NodeList nl = parent.getElementsByTagName(name);
435 		Element ret = null;
436 		if (nl.getLength() > 0) {
437 			ret = (Element) nl.item(0);
438 		}
439 		return ret;
440 	}
441 
442 	
443 
444 
445 	private String getValueOfFirstElement(String name, Element parent) throws ProfileException {
446 		Element el = getFirstElementByTagName(name, parent);
447 		String val = null;
448 		if (el != null) {
449 			try {
450 				Node n = el.getFirstChild();
451 				if (n.getNodeType() == Node.TEXT_NODE) {
452 					val = n.getNodeValue();
453 				}
454 			} catch (Exception e) {
455 				throw new ProfileException("Unable to get value of node " + name, e);
456 			}
457 		}
458 		return val;
459 	}
460 
461 	public static void main(String[] args) {
462 
463 		if (args.length != 1) {
464 			System.out.println("Usage: ProfileParser profile_file");
465 			System.exit(1);
466 		}
467 
468 		try {
469 			
470 			
471 			File f = new File(args[0]);
472 			@SuppressWarnings("resource")
473 			BufferedReader in = new BufferedReader(new FileReader(f));
474 			char[] cbuf = new char[(int) f.length()];
475 			in.read(cbuf, 0, (int) f.length());
476 			String xml = String.valueOf(cbuf);
477 			
478 
479 			ProfileParser pp = new ProfileParser(true);
480 			pp.parse(xml);
481 		} catch (Exception e) {
482 			e.printStackTrace();
483 		}
484 	}
485 
486 }