ca.uhn.hl7v2.parser.XMLParser Maven / Gradle / Ivy
/**
* The contents of this file are subject to the Mozilla Public License Version 1.1
* (the "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.mozilla.org/MPL/
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the
* specific language governing rights and limitations under the License.
*
* The Original Code is "XMLParser.java". Description:
* "Parses and encodes HL7 messages in XML form, according to HL7's normative XML encoding
* specification."
*
* The Initial Developer of the Original Code is University Health Network. Copyright (C)
* 2002. All Rights Reserved.
*
* Contributor(s): ______________________________________.
*
* Alternatively, the contents of this file may be used under the terms of the
* GNU General Public License (the �GPL�), in which case the provisions of the GPL are
* applicable instead of those above. If you wish to allow use of your version of this
* file only under the terms of the GPL and not to allow others to use your version
* of this file under the MPL, indicate your decision by deleting the provisions above
* and replace them with the notice and other provisions required by the GPL License.
* If you do not delete the provisions above, a recipient may use your version of
* this file under either the MPL or the GPL.
*/
package ca.uhn.hl7v2.parser;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.StringReader;
import java.io.StringWriter;
import java.util.HashSet;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import org.apache.xerces.parsers.DOMParser;
import org.apache.xerces.parsers.StandardParserConfiguration;
import org.apache.xml.serialize.OutputFormat;
import org.apache.xml.serialize.XMLSerializer;
import org.w3c.dom.DOMException;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.w3c.dom.Text;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import ca.uhn.hl7v2.HL7Exception;
import ca.uhn.hl7v2.model.Composite;
import ca.uhn.hl7v2.model.DataTypeException;
import ca.uhn.hl7v2.model.GenericComposite;
import ca.uhn.hl7v2.model.GenericMessage;
import ca.uhn.hl7v2.model.GenericPrimitive;
import ca.uhn.hl7v2.model.Message;
import ca.uhn.hl7v2.model.Primitive;
import ca.uhn.hl7v2.model.Segment;
import ca.uhn.hl7v2.model.Structure;
import ca.uhn.hl7v2.model.Type;
import ca.uhn.hl7v2.model.Varies;
import ca.uhn.hl7v2.util.Terser;
import ca.uhn.log.HapiLog;
import ca.uhn.log.HapiLogFactory;
/**
* Parses and encodes HL7 messages in XML form, according to HL7's normative XML encoding
* specification. This is an abstract class that handles datatype and segment parsing/encoding,
* but not the parsing/encoding of entire messages. To use the XML parser, you should create a
* subclass for a certain message structure. This subclass must be able to identify the Segment
* objects that correspond to various Segment nodes in an XML document, and call the methods
* parse(Segment segment, ElementNode segmentNode)
and encode(Segment segment, ElementNode segmentNode)
*
as appropriate. XMLParser uses the Xerces parser, which must be installed in your classpath.
* @author Bryan Tripp, Shawn Bellina
*/
public abstract class XMLParser extends Parser {
private static final HapiLog log = HapiLogFactory.getHapiLog(XMLParser.class);
private DOMParser parser;
private String textEncoding;
/**
* The nodes whose names match these strings will be kept as original,
* meaning that no white space treaming will occur on them
*/
private String[] keepAsOriginalNodes;
/**
* All keepAsOriginalNodes names, concatenated by a pipe (|)
*/
private String concatKeepAsOriginalNodes = "";
/** Constructor */
public XMLParser() {
this(null);
}
/**
* Constructor
*
* @param theFactory custom factory to use for model class lookup
*/
public XMLParser(ModelClassFactory theFactory) {
parser = new DOMParser(new StandardParserConfiguration());
try {
parser.setFeature("http://apache.org/xml/features/dom/include-ignorable-whitespace", false);
}
catch (Exception e) {
log.error("Can't exclude whitespace from XML DOM", e);
}
}
/**
* Returns a String representing the encoding of the given message, if
* the encoding is recognized. For example if the given message appears
* to be encoded using HL7 2.x XML rules then "XML" would be returned.
* If the encoding is not recognized then null is returned. That this
* method returns a specific encoding does not guarantee that the
* message is correctly encoded (e.g. well formed XML) - just that
* it is not encoded using any other encoding than the one returned.
* Returns null if the encoding is not recognized.
*/
public String getEncoding(String message) {
String encoding = null;
//check for a number of expected strings
String[] expected = { "" };
boolean isXML = true;
for (int i = 0; i < expected.length; i++) {
if (message.indexOf(expected[i]) < 0)
isXML = false;
}
if (isXML)
encoding = "XML";
return encoding;
}
/**
* Returns true if and only if the given encoding is supported
* by this Parser.
*/
public boolean supportsEncoding(String encoding) {
if (encoding.equals("XML")) {
return true;
}
else {
return false;
}
}
/**
* @return the preferred encoding of this Parser
*/
public String getDefaultEncoding() {
return "XML";
}
/**
* Sets the keepAsOriginalNodes
*
* The nodes whose names match the keepAsOriginalNodes will be kept as original,
* meaning that no white space treaming will occur on them
*/
public void setKeepAsOriginalNodes(String[] keepAsOriginalNodes) {
this.keepAsOriginalNodes = keepAsOriginalNodes;
if (keepAsOriginalNodes.length != 0) {
//initializes the
StringBuffer strBuf = new StringBuffer(keepAsOriginalNodes[0]);
for (int i = 1; i < keepAsOriginalNodes.length; i++) {
strBuf.append("|");
strBuf.append(keepAsOriginalNodes[i]);
}
concatKeepAsOriginalNodes = strBuf.toString();
}
else {
concatKeepAsOriginalNodes = "";
}
}
/**
* Sets the keepAsOriginalNodes
*/
public String[] getKeepAsOriginalNodes() {
return keepAsOriginalNodes;
}
/**
* Creates and populates a Message object from an XML Document that contains an XML-encoded HL7 message.
* The easiest way to implement this method for a particular message structure is as follows:
*
- Create an instance of the Message type you are going to handle with your subclass
* of XMLParser
* - Go through the given Document and find the Elements that represent the top level of
* each message segment.
* - For each of these segments, call
parse(Segment segmentObject, Element segmentElement)
,
* providing the appropriate Segment from your Message object, and the corresponding Element.
* At the end of this process, your Message object should be populated with data from the XML
* Document.
* @throws HL7Exception if the message is not correctly formatted.
* @throws EncodingNotSupportedException if the message encoded
* is not supported by this parser.
*/
public abstract Message parseDocument(Document XMLMessage, String version) throws HL7Exception;
/**
* Parses a message string and returns the corresponding Message
* object. This method checks that the given message string is XML encoded, creates an
* XML Document object (using Xerces) from the given String, and calls the abstract
* method parse(Document XMLMessage)
*/
protected Message doParse(String message, String version) throws HL7Exception, EncodingNotSupportedException {
Message m = null;
//parse message string into a DOM document
try {
Document doc = null;
synchronized (this) {
parser.parse(new InputSource(new StringReader(message)));
doc = parser.getDocument();
}
m = parseDocument(doc, version);
}
catch (SAXException e) {
throw new HL7Exception("SAXException parsing XML", HL7Exception.APPLICATION_INTERNAL_ERROR, e);
}
catch (IOException e) {
throw new HL7Exception("IOException parsing XML", HL7Exception.APPLICATION_INTERNAL_ERROR, e);
}
return m;
}
/**
* Formats a Message object into an HL7 message string using the given
* encoding.
* @throws HL7Exception if the data fields in the message do not permit encoding
* (e.g. required fields are null)
* @throws EncodingNotSupportedException if the requested encoding is not
* supported by this parser.
*/
protected String doEncode(Message source, String encoding) throws HL7Exception, EncodingNotSupportedException {
if (!encoding.equals("XML"))
throw new EncodingNotSupportedException("XMLParser supports only XML encoding");
return encode(source);
}
/**
* Formats a Message object into an HL7 message string using this parser's
* default encoding (XML encoding). This method calls the abstract method
* encodeDocument(...)
in order to obtain XML Document object
* representation of the Message, then serializes it to a String.
* @throws HL7Exception if the data fields in the message do not permit encoding
* (e.g. required fields are null)
*/
protected String doEncode(Message source) throws HL7Exception {
if (source instanceof GenericMessage) {
throw new HL7Exception("Can't XML-encode a GenericMessage. Message must have a recognized structure.");
}
Document doc = encodeDocument(source);
Element documentElement = doc.getDocumentElement();
documentElement.setAttribute("xmlns", "urn:hl7-org:v2xml");
StringWriter out = new StringWriter();
OutputFormat outputFormat = new OutputFormat("", null, true);
outputFormat.setLineWidth(0);
if (textEncoding != null) {
outputFormat.setEncoding(textEncoding);
}
XMLSerializer ser = new XMLSerializer(out, outputFormat); //default output format
try {
ser.serialize(doc);
}
catch (IOException e) {
throw new HL7Exception(
"IOException serializing XML document to string",
HL7Exception.APPLICATION_INTERNAL_ERROR,
e);
}
return out.toString();
}
/**
* Creates an XML Document that corresponds to the given Message object.
* If you are implementing this method, you should create an XML Document, and insert XML Elements
* into it that correspond to the groups and segments that belong to the message type that your subclass
* of XMLParser supports. Then, for each segment in the message, call the method
* encode(Segment segmentObject, Element segmentElement)
using the Element for
* that segment and the corresponding Segment object from the given Message.
*/
public abstract Document encodeDocument(Message source) throws HL7Exception;
/**
* Populates the given Segment object with data from the given XML Element.
* @throws HL7Exception if the XML Element does not have the correct name and structure
* for the given Segment, or if there is an error while setting individual field values.
*/
public void parse(Segment segmentObject, Element segmentElement) throws HL7Exception {
HashSet done = new HashSet();
// for (int i = 1; i <= segmentObject.numFields(); i++) {
// String elementName = makeElementName(segmentObject, i);
// done.add(elementName);
// parseReps(segmentObject, segmentElement, elementName, i);
// }
NodeList all = segmentElement.getChildNodes();
for (int i = 0; i < all.getLength(); i++) {
String elementName = all.item(i).getNodeName();
if (all.item(i).getNodeType() == Node.ELEMENT_NODE && !done.contains(elementName)) {
done.add(elementName);
int index = elementName.indexOf('.');
if (index >= 0 && elementName.length() > index) { //properly formatted element
String fieldNumString = elementName.substring(index + 1);
int fieldNum = Integer.parseInt(fieldNumString);
parseReps(segmentObject, segmentElement, elementName, fieldNum);
} else {
log.debug("Child of segment " + segmentObject.getName()
+ " doesn't look like a field: " + elementName);
}
}
}
//set data type of OBX-5
if (segmentObject.getClass().getName().indexOf("OBX") >= 0) {
Varies.fixOBX5(segmentObject, getFactory());
}
}
private void parseReps(Segment segmentObject, Element segmentElement, String fieldName, int fieldNum)
throws DataTypeException, HL7Exception {
NodeList reps = segmentElement.getElementsByTagName(fieldName);
for (int i = 0; i < reps.getLength(); i++) {
parse(segmentObject.getField(fieldNum, i), (Element) reps.item(i));
}
}
/**
* Populates the given Element with data from the given Segment, by inserting
* Elements corresponding to the Segment's fields, their components, etc. Returns
* true if there is at least one data value in the segment.
*/
public boolean encode(Segment segmentObject, Element segmentElement) throws HL7Exception {
boolean hasValue = false;
int n = segmentObject.numFields();
for (int i = 1; i <= n; i++) {
String name = makeElementName(segmentObject, i);
Type[] reps = segmentObject.getField(i);
for (int j = 0; j < reps.length; j++) {
Element newNode = segmentElement.getOwnerDocument().createElement(name);
boolean componentHasValue = encode(reps[j], newNode);
if (componentHasValue) {
try {
segmentElement.appendChild(newNode);
}
catch (DOMException e) {
throw new HL7Exception(
"DOMException encoding Segment: ",
HL7Exception.APPLICATION_INTERNAL_ERROR,
e);
}
hasValue = true;
}
}
}
return hasValue;
}
/**
* Populates the given Type object with data from the given XML Element.
*/
public void parse(Type datatypeObject, Element datatypeElement) throws DataTypeException {
if (datatypeObject instanceof Varies) {
parseVaries((Varies) datatypeObject, datatypeElement);
}
else if (datatypeObject instanceof Primitive) {
parsePrimitive((Primitive) datatypeObject, datatypeElement);
}
else if (datatypeObject instanceof Composite) {
parseComposite((Composite) datatypeObject, datatypeElement);
}
}
/**
* Parses an XML element into a Varies by determining whether the element is primitive or
* composite, calling setData() on the Varies with a new generic primitive or composite as appropriate,
* and then calling parse again with the new Type object.
*/
private void parseVaries(Varies datatypeObject, Element datatypeElement) throws DataTypeException {
//figure out what data type it holds
//short nodeType = datatypeElement.getFirstChild().getNodeType();
if (!hasChildElement(datatypeElement)) {
//it's a primitive
datatypeObject.setData(new GenericPrimitive(datatypeObject.getMessage()));
}
else {
//it's a composite ... almost know what type, except that we don't have the version here
datatypeObject.setData(new GenericComposite(datatypeObject.getMessage()));
}
parse(datatypeObject.getData(), datatypeElement);
}
/** Returns true if any of the given element's children are elements */
private boolean hasChildElement(Element e) {
NodeList children = e.getChildNodes();
boolean hasElement = false;
int c = 0;
while (c < children.getLength() && !hasElement) {
if (children.item(c).getNodeType() == Node.ELEMENT_NODE) {
hasElement = true;
}
c++;
}
return hasElement;
}
/** Parses a primitive type by filling it with text child, if any */
private void parsePrimitive(Primitive datatypeObject, Element datatypeElement) throws DataTypeException {
NodeList children = datatypeElement.getChildNodes();
int c = 0;
boolean full = false;
while (c < children.getLength() && !full) {
Node child = children.item(c++);
if (child.getNodeType() == Node.TEXT_NODE) {
try {
if (child.getNodeValue() != null && !child.getNodeValue().equals("")) {
if (keepAsOriginal(child.getParentNode())) {
datatypeObject.setValue(child.getNodeValue());
}
else {
datatypeObject.setValue(removeWhitespace(child.getNodeValue()));
}
}
}
catch (DOMException e) {
log.error("Error parsing primitive value from TEXT_NODE", e);
}
full = true;
}
}
}
/**
* Checks if Node
content should be kept as original (ie.: whitespaces won't be removed)
*
* @param node The target Node
* @return boolean true
if whitespaces should not be removed from node content,
* false
otherwise
*/
protected boolean keepAsOriginal(Node node) {
if (node.getNodeName() == null)
return false;
return concatKeepAsOriginalNodes.indexOf(node.getNodeName()) != -1;
}
/**
* Removes all unecessary whitespace from the given String (intended to be used with Primitive values).
* This includes leading and trailing whitespace, and repeated space characters. Carriage returns,
* line feeds, and tabs are replaced with spaces.
*/
protected String removeWhitespace(String s) {
s = s.replace('\r', ' ');
s = s.replace('\n', ' ');
s = s.replace('\t', ' ');
boolean repeatedSpacesExist = true;
while (repeatedSpacesExist) {
int loc = s.indexOf(" ");
if (loc < 0) {
repeatedSpacesExist = false;
}
else {
StringBuffer buf = new StringBuffer();
buf.append(s.substring(0, loc));
buf.append(" ");
buf.append(s.substring(loc + 2));
s = buf.toString();
}
}
return s.trim();
}
/**
* Populates a Composite type by looping through it's children, finding corresponding
* Elements among the children of the given Element, and calling parse(Type, Element) for
* each.
*/
private void parseComposite(Composite datatypeObject, Element datatypeElement) throws DataTypeException {
if (datatypeObject instanceof GenericComposite) { //elements won't be named GenericComposite.x
NodeList children = datatypeElement.getChildNodes();
int compNum = 0;
for (int i = 0; i < children.getLength(); i++) {
if (children.item(i).getNodeType() == Node.ELEMENT_NODE) {
Element nextElement = (Element) children.item(i);
String localName = nextElement.getLocalName();
int dotIndex = localName.indexOf(".");
if (dotIndex > -1) {
compNum = Integer.parseInt(localName.substring(dotIndex + 1)) - 1;
} else {
log.debug("Datatype element " + datatypeElement.getLocalName()
+ " doesn't have a valid numbered name, usgin default index of " + compNum);
}
Type nextComponent = datatypeObject.getComponent(compNum);
parse(nextComponent, nextElement);
compNum++;
}
}
}
else {
Type[] children = datatypeObject.getComponents();
for (int i = 0; i < children.length; i++) {
NodeList matchingElements =
datatypeElement.getElementsByTagName(makeElementName(datatypeObject, i + 1));
if (matchingElements.getLength() > 0) {
parse(children[i], (Element) matchingElements.item(0)); //components don't repeat - use 1st
}
}
}
}
/**
* Returns the expected XML element name for the given child of a message constituent
* of the given class (the class should be a Composite or Segment class).
*/
/*private String makeElementName(Class c, int child) {
String longClassName = c.getName();
String shortClassName = longClassName.substring(longClassName.lastIndexOf('.') + 1, longClassName.length());
if (shortClassName.startsWith("Valid")) {
shortClassName = shortClassName.substring(5, shortClassName.length());
}
return shortClassName + "." + child;
}*/
/** Returns the expected XML element name for the given child of the given Segment */
private String makeElementName(Segment s, int child) {
return s.getName() + "." + child;
}
/** Returns the expected XML element name for the given child of the given Composite */
private String makeElementName(Composite composite, int child) {
return composite.getName() + "." + child;
}
/**
* Populates the given Element with data from the given Type, by inserting
* Elements corresponding to the Type's components and values. Returns true if
* the given type contains a value (i.e. for Primitives, if getValue() doesn't
* return null, and for Composites, if at least one underlying Primitive doesn't
* return null).
*/
private boolean encode(Type datatypeObject, Element datatypeElement) throws DataTypeException {
boolean hasData = false;
if (datatypeObject instanceof Varies) {
hasData = encodeVaries((Varies) datatypeObject, datatypeElement);
}
else if (datatypeObject instanceof Primitive) {
hasData = encodePrimitive((Primitive) datatypeObject, datatypeElement);
}
else if (datatypeObject instanceof Composite) {
hasData = encodeComposite((Composite) datatypeObject, datatypeElement);
}
return hasData;
}
/**
* Encodes a Varies type by extracting it's data field and encoding that. Returns true
* if the data field (or one of its components) contains a value.
*/
private boolean encodeVaries(Varies datatypeObject, Element datatypeElement) throws DataTypeException {
boolean hasData = false;
if (datatypeObject.getData() != null) {
hasData = encode(datatypeObject.getData(), datatypeElement);
}
return hasData;
}
/**
* Encodes a Primitive in XML by adding it's value as a child of the given Element.
* Returns true if the given Primitive contains a value.
*/
private boolean encodePrimitive(Primitive datatypeObject, Element datatypeElement) throws DataTypeException {
boolean hasValue = false;
if (datatypeObject.getValue() != null && !datatypeObject.getValue().equals(""))
hasValue = true;
Text t = datatypeElement.getOwnerDocument().createTextNode(datatypeObject.getValue());
if (hasValue) {
try {
datatypeElement.appendChild(t);
}
catch (DOMException e) {
throw new DataTypeException("DOMException encoding Primitive: ", e);
}
}
return hasValue;
}
/**
* Encodes a Composite in XML by looping through it's components, creating new
* children for each of them (with the appropriate names) and populating them by
* calling encode(Type, Element) using these children. Returns true if at least
* one component contains a value.
*/
private boolean encodeComposite(Composite datatypeObject, Element datatypeElement) throws DataTypeException {
Type[] components = datatypeObject.getComponents();
boolean hasValue = false;
for (int i = 0; i < components.length; i++) {
String name = makeElementName(datatypeObject, i + 1);
Element newNode = datatypeElement.getOwnerDocument().createElement(name);
boolean componentHasValue = encode(components[i], newNode);
if (componentHasValue) {
try {
datatypeElement.appendChild(newNode);
}
catch (DOMException e) {
throw new DataTypeException("DOMException encoding Composite: ", e);
}
hasValue = true;
}
}
return hasValue;
}
/**
* Returns a minimal amount of data from a message string, including only the
* data needed to send a response to the remote system. This includes the
* following fields:
*
- field separator
* - encoding characters
* - processing ID
* - message control ID
* This method is intended for use when there is an error parsing a message,
* (so the Message object is unavailable) but an error message must be sent
* back to the remote system including some of the information in the inbound
* message. This method parses only that required information, hopefully
* avoiding the condition that caused the original error.
*/
public Segment getCriticalResponseData(String message) throws HL7Exception {
String version = getVersion(message);
Segment criticalData = Parser.makeControlMSH(version, getFactory());
Terser.set(criticalData, 1, 0, 1, 1, parseLeaf(message, "MSH.1", 0));
Terser.set(criticalData, 2, 0, 1, 1, parseLeaf(message, "MSH.2", 0));
Terser.set(criticalData, 10, 0, 1, 1, parseLeaf(message, "MSH.10", 0));
String procID = parseLeaf(message, "MSH.11", 0);
if (procID == null || procID.length() == 0) {
procID = parseLeaf(message, "PT.1", message.indexOf("MSH.11"));
//this field is a composite in later versions
}
Terser.set(criticalData, 11, 0, 1, 1, procID);
return criticalData;
}
/**
* For response messages, returns the value of MSA-2 (the message ID of the message
* sent by the sending system). This value may be needed prior to main message parsing,
* so that (particularly in a multi-threaded scenario) the message can be routed to
* the thread that sent the request. We need this information first so that any
* parse exceptions are thrown to the correct thread. Implementers of Parsers should
* take care to make the implementation of this method very fast and robust.
* Returns null if MSA-2 can not be found (e.g. if the message is not a
* response message). Trims whitespace from around the MSA-2 field.
*/
public String getAckID(String message) {
String ackID = null;
try {
ackID = parseLeaf(message, "msa.2", 0).trim();
}
catch (HL7Exception e) { /* OK ... assume it isn't a response message */
}
return ackID;
}
public String getVersion(String message) throws HL7Exception {
String version = parseLeaf(message, "MSH.12", 0);
if (version == null || version.trim().length() == 0) {
version = parseLeaf(message, "VID.1", message.indexOf("MSH.12"));
}
return version;
}
/**
* Attempts to retrieve the value of a leaf tag without using DOM or SAX.
* This method searches the given message string for the given tag name, and returns
* everything after the given tag and before the start of the next tag. Whitespace
* is stripped. This is intended only for lead nodes, as the value is considered to
* end at the start of the next tag, regardless of whether it is the matching end
* tag or some other nested tag.
* @param message a string message in XML form
* @param tagName the name of the XML tag, e.g. "MSA.2"
* @param startAt the character location at which to start searching
* @throws HL7Exception if the tag can not be found
*/
protected String parseLeaf(String message, String tagName, int startAt) throws HL7Exception {
String value = null;
int tagStart = message.indexOf("<" + tagName, startAt);
if (tagStart < 0)
tagStart = message.indexOf("<" + tagName.toUpperCase(), startAt);
int valStart = message.indexOf(">", tagStart) + 1;
int valEnd = message.indexOf("<", valStart);
if (tagStart >= 0 && valEnd >= valStart) {
value = message.substring(valStart, valEnd);
}
else {
throw new HL7Exception(
"Couldn't find "
+ tagName
+ " in message beginning: "
+ message.substring(0, Math.min(150, message.length())),
HL7Exception.REQUIRED_FIELD_MISSING);
}
// Escape codes, as defined at http://hdf.ncsa.uiuc.edu/HDF5/XML/xml_escape_chars.htm
value = value.replaceAll(""", "\"");
value = value.replaceAll("'", "'");
value = value.replaceAll("&", "&");
value = value.replaceAll("<", "<");
value = value.replaceAll(">", ">");
return value;
}
/**
* Throws unsupported operation exception
*
* @throws Unsupported operation exception
*/
@Override
public String doEncode(Segment structure, EncodingCharacters encodingCharacters) throws HL7Exception {
throw new UnsupportedOperationException("Not supported yet.");
}
/**
* Throws unsupported operation exception
*
* @throws Unsupported operation exception
*/
@Override
public void parse(Message message, String string) throws HL7Exception {
throw new UnsupportedOperationException("Not supported yet.");
}
/**
* Throws unsupported operation exception
*
* @throws Unsupported operation exception
*/
@Override
public String doEncode(Type type, EncodingCharacters encodingCharacters) throws HL7Exception {
throw new UnsupportedOperationException("Not supported yet.");
}
/**
* Throws unsupported operation exception
*
* @throws Unsupported operation exception
*/
@Override
public void parse(Type type, String string, EncodingCharacters encodingCharacters) throws HL7Exception {
throw new UnsupportedOperationException("Not supported yet.");
}
/**
* Throws unsupported operation exception
*
* @throws Unsupported operation exception
*/
@Override
public void parse(Segment segment, String string, EncodingCharacters encodingCharacters) throws HL7Exception {
throw new UnsupportedOperationException("Not supported yet.");
}
/** Test harness */
public static void main(String args[]) {
if (args.length != 1) {
System.out.println("Usage: XMLParser pipe_encoded_file");
System.exit(1);
}
//read and parse message from file
try {
PipeParser parser = new PipeParser();
File messageFile = new File(args[0]);
long fileLength = messageFile.length();
FileReader r = new FileReader(messageFile);
char[] cbuf = new char[(int) fileLength];
System.out.println("Reading message file ... " + r.read(cbuf) + " of " + fileLength + " chars");
r.close();
String messString = String.valueOf(cbuf);
Message mess = parser.parse(messString);
System.out.println("Got message of type " + mess.getClass().getName());
ca.uhn.hl7v2.parser.XMLParser xp = new XMLParser() {
public Message parseDocument(Document XMLMessage, String version) throws HL7Exception {
return null;
}
public Document encodeDocument(Message source) throws HL7Exception {
return null;
}
public String getVersion(String message) throws HL7Exception {
return null;
}
@Override
public String doEncode(Segment structure, EncodingCharacters encodingCharacters) throws HL7Exception {
throw new UnsupportedOperationException("Not supported yet.");
}
@Override
public String doEncode(Type type, EncodingCharacters encodingCharacters) throws HL7Exception {
throw new UnsupportedOperationException("Not supported yet.");
}
@Override
public void parse(Type type, String string, EncodingCharacters encodingCharacters) throws HL7Exception {
throw new UnsupportedOperationException("Not supported yet.");
}
@Override
public void parse(Segment segment, String string, EncodingCharacters encodingCharacters) throws HL7Exception {
throw new UnsupportedOperationException("Not supported yet.");
}
};
//loop through segment children of message, encode, print to console
String[] structNames = mess.getNames();
for (int i = 0; i < structNames.length; i++) {
Structure[] reps = mess.getAll(structNames[i]);
for (int j = 0; j < reps.length; j++) {
if (Segment.class.isAssignableFrom(reps[j].getClass())) { //ignore groups
DocumentBuilder docBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
Document doc = docBuilder.newDocument(); //new doc for each segment
Element root = doc.createElement(reps[j].getClass().getName());
doc.appendChild(root);
xp.encode((Segment) reps[j], root);
StringWriter out = new StringWriter();
XMLSerializer ser = new XMLSerializer(out, null); //default output format
ser.serialize(doc);
System.out.println("Segment " + reps[j].getClass().getName() + ": \r\n" + out.toString());
Class[] segmentConstructTypes = { Message.class };
Object[] segmentConstructArgs = { null };
Segment s =
(Segment) reps[j].getClass().getConstructor(segmentConstructTypes).newInstance(
segmentConstructArgs);
xp.parse(s, root);
Document doc2 = docBuilder.newDocument();
Element root2 = doc2.createElement(s.getClass().getName());
doc2.appendChild(root2);
xp.encode(s, root2);
StringWriter out2 = new StringWriter();
ser = new XMLSerializer(out2, null); //default output format
ser.serialize(doc2);
if (out2.toString().equals(out.toString())) {
System.out.println("Re-encode OK");
}
else {
System.out.println(
"Warning: XML different after parse and re-encode: \r\n" + out2.toString());
}
}
}
}
}
catch (Exception e) {
e.printStackTrace();
}
}
/**
* Returns the text encoding to be used in generating new messages. Note that this affects encoding to string only, not parsing.
* @return
*/
public String getTextEncoding() {
return textEncoding;
}
/**
* Sets the text encoding to be used in generating new messages. Note that this affects encoding to string only, not parsing.
* @param textEncoding The encoding. Default is the platform default.
*/
public void setTextEncoding(String textEncoding) {
this.textEncoding = textEncoding;
}
}