|
From: Davide A. <dav...@if...> - 2006-07-25 09:42:09
|
Hi, I have test with the attached file and all work fine!!! all my problem=20 are solved thanks Brian. Bryan Tripp ha scritto: > Hi, > > Regarding the change in the last OBX field: I think you found a bug. > Could you please try it again with the attached XMLParser.java? I > think I've fixed it properly but let me know if you have any problems > with it. > > Thanks, > Bryan > > On 7/24/06, Davide Antoni <dav...@if...> wrote: >> Hi >> I have made changes and now it works!!! many many thanks... and i have >> changed the limiti of 200 charatcter for obx5., because in my >> application i have a large base64 object. >> >> I have a question: >> When i put inside my application MDM^T02 in xmlv2(in.xml) and i parse >> this xml file with this: >> //encode message in XML >> String ackMessageInXML =3D xmlParser.encode(inBound); >> >> The xml that i have put inside change the last component of OBX. >> (out.xml) after the parsing >> WHY? >> Thanks. >> >> >> Life is hard, and then you die ha scritto: >> > Hi Davide, >> > >> > >> >> Another question: when i send an'mdm xml to server 0.5 i have thi=20 >> error: >> >> >> >> ca.uhn.hl7v2.HL7Exception: Encoding characters '^~\&' invalid -= - >> >> must be 4 characters >> >> at ca.uhn.hl7v2.parser.PipeParser.doEncode(PipeParser.java:495) >> >> at ca.uhn.hl7v2.parser.GenericParser.doEncode(GenericParser.java:19= 8) >> >> at ca.uhn.hl7v2.parser.Parser.encode(Parser.java:221) >> >> at=20 >> ca.uhn.hl7v2.app.Responder.logAndMakeErrorMessage(Responder.java:412) >> >> at ca.uhn.hl7v2.app.Responder.processMessage(Responder.java:216) >> >> at ca.uhn.hl7v2.app.Receiver$Grunt.run(Receiver.java:134) >> >> >> >> If i send an'adt message i don't have this error and all work fine.= .. >> >> >> > >> > I ran into this a couple weeks ago too and fixed. Forgot to report >> > this bug... I've just done so now and included my patch for it - you >> > can find it at >> >=20 >> https://sourceforge.net/tracker/index.php?func=3Ddetail&aid=3D1525029&= group_id=3D38899&atid=3D423835=20 >> >> > >> > >> > Cheers, >> > >> > Ronald >> > >> > >> > >> >> >> >> ----------------------------------------------------------------------= ---=20 >> >> Take Surveys. Earn Cash. Influence the Future of IT >> Join SourceForge.net's Techsay panel and you'll get the chance to=20 >> share your >> opinions on IT & business topics through brief surveys -- and earn cas= h >> http://www.techsay.com/default.php?page=3Djoin.php&p=3Dsourceforge&CID= =3DDEVDEV=20 >> >> >> _______________________________________________ >> Hl7api-devel mailing list >> Hl7...@li... >> https://lists.sourceforge.net/lists/listinfo/hl7api-devel >> >> >> >> > -----------------------------------------------------------------------= - > > /** > * 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 Lic= ense 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 nor= mative XML encoding > * specification." > * > * The Initial Developer of the Original Code is University Health Netw= ork. 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 =E2=80=9CGPL=E2=80=9D), in which ca= se 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 t= he 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 norm= ative 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 par= ser, you should create a > * subclass for a certain message structure. This subclass must be abl= e to identify the Segment > * objects that correspond to various Segment nodes in an XML document,= and call the methods <code> > * parse(Segment segment, ElementNode segmentNode)</code> and <code>enc= ode(Segment segment, ElementNode segmentNode) > * </code> as appropriate. XMLParser uses the Xerces parser, which mus= t be installed in your classpath. > * @author Bryan Tripp, Shawn Bellina > */ > public abstract class XMLParser extends Parser { > > private static final HapiLog log =3D HapiLogFactory.getHapiLog(XMLP= arser.class); > > private DOMParser parser; > > /** > * The nodes whose names match these strings will be kept as origin= al,=20 > * meaning that no white space treaming will occur on them > */ > private String[] keepAsOriginalNodes; > > /** > * All keepAsOriginalNodes names, concatenated by a pipe (|) > */ > private String concatKeepAsOriginalNodes =3D ""; > > public XMLParser() { > parser =3D new DOMParser(new StandardParserConfiguration()); > try { > parser.setFeature("http://apache.org/xml/features/dom/inclu= de-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 ap= pears > * to be encoded using HL7 2.x XML rules then "XML" would be return= ed. > * If the encoding is not recognized then null is returned. That t= his > * 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 =3D null; > > //check for a number of expected strings=20 > String[] expected =3D { "<MSH.1", "<MSH.2", "</MSH>" }; > boolean isXML =3D true; > for (int i =3D 0; i < expected.length; i++) { > if (message.indexOf(expected[i]) < 0) > isXML =3D false; > } > if (isXML) > encoding =3D "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"; > } > =20 > /** > * Sets the <i>keepAsOriginalNodes<i> > *=20 > * The nodes whose names match the <i>keepAsOriginalNodes<i> will b= e kept as original,=20 > * meaning that no white space treaming will occur on them > */ > public void setKeepAsOriginalNodes(String[] keepAsOriginalNodes) { > this.keepAsOriginalNodes =3D keepAsOriginalNodes; > > if (keepAsOriginalNodes.length !=3D 0) { > //initializes the =20 > StringBuffer strBuf =3D new StringBuffer(keepAsOriginalNode= s[0]); > for (int i =3D 1; i < keepAsOriginalNodes.length; i++) { > strBuf.append("|"); > strBuf.append(keepAsOriginalNodes[i]); > } > concatKeepAsOriginalNodes =3D strBuf.toString(); > } > else { > concatKeepAsOriginalNodes =3D ""; > } > } > > /** > * Sets the <i>keepAsOriginalNodes<i> > */ > public String[] getKeepAsOriginalNodes() { > return keepAsOriginalNodes; > } > > /** > * <p>Creates and populates a Message object from an XML Document t= hat contains an XML-encoded HL7 message.</p> > * <p>The easiest way to implement this method for a particular mes= sage structure is as follows: > * <ol><li>Create an instance of the Message type you are going to = handle with your subclass > * of XMLParser</li> > * <li>Go through the given Document and find the Elements that rep= resent the top level of > * each message segment. </li> > * <li>For each of these segments, call <code>parse(Segment segment= Object, Element segmentElement)</code>, > * providing the appropriate Segment from your Message object, and = the corresponding Element.</li></ol> > * At the end of this process, your Message object should be popula= ted with data from the XML > * Document.</p> > * @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 v= ersion) throws HL7Exception; > > /** > * <p>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 ca= lls the abstract > * method <code>parse(Document XMLMessage)</code></p> > */ > protected Message doParse(String message, String version) throws HL= 7Exception, EncodingNotSupportedException { > Message m =3D null; > > //parse message string into a DOM document=20 > try { > Document doc =3D null; > synchronized (this) { > parser.parse(new InputSource(new StringReader(message))= ); > doc =3D parser.getDocument(); > } > m =3D parseDocument(doc, version); > } > catch (SAXException e) { > throw new HL7Exception("SAXException parsing XML", HL7Excep= tion.APPLICATION_INTERNAL_ERROR, e); > } > catch (IOException e) { > throw new HL7Exception("IOException parsing XML", HL7Except= ion.APPLICATION_INTERNAL_ERROR, e); > } > > return m; > } > > /** > * Formats a Message object into an HL7 message string using the gi= ven > * encoding. > * @throws HL7Exception if the data fields in the message do not pe= rmit 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 H= L7Exception, 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 p= arser's > * default encoding (XML encoding). This method calls the abstract = method > * <code>encodeDocument(...)</code> 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 pe= rmit 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."); > } > =20 > Document doc =3D encodeDocument(source); > doc.getDocumentElement().setAttribute("xmlns", "urn:hl7-org:v2x= ml"); > =20 > StringWriter out =3D new StringWriter(); > > OutputFormat outputFormat =3D new OutputFormat("", null, true); > > XMLSerializer ser =3D new XMLSerializer(out, outputFormat); //d= efault 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(); > } > > /** > * <p>Creates an XML Document that corresponds to the given Message= object. </p> > * <p>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 t= o the message type that your subclass > * of XMLParser supports. Then, for each segment in the message, c= all the method > * <code>encode(Segment segmentObject, Element segmentElement)</cod= e> using the Element for > * that segment and the corresponding Segment object from the given= Message.</p> > */ > public abstract Document encodeDocument(Message source) throws HL7E= xception; > > /**=20 > * Populates the given Segment object with data from the given XML = Element. > * @throws HL7Exception if the XML Element does not have the correc= t name and structure > * for the given Segment, or if there is an error while settin= g individual field values. > */ > public void parse(Segment segmentObject, Element segmentElement) th= rows HL7Exception { > HashSet done =3D new HashSet(); > =20 > // for (int i =3D 1; i <=3D segmentObject.numFields(); i++) { > // String elementName =3D makeElementName(segmentObject, i); > // done.add(elementName); > // parseReps(segmentObject, segmentElement, elementName, i); > // } > =20 > NodeList all =3D segmentElement.getChildNodes(); > for (int i =3D 0; i < all.getLength(); i++) { > String elementName =3D all.item(i).getNodeName(); > if (all.item(i).getNodeType() =3D=3D Node.ELEMENT_NODE && != done.contains(elementName)) { > done.add(elementName); > =20 > int index =3D elementName.indexOf('.'); > if (index >=3D 0 && elementName.length() > index) { //p= roperly formatted element > String fieldNumString =3D elementName.substring(ind= ex + 1); > int fieldNum =3D Integer.parseInt(fieldNumString); > parseReps(segmentObject, segmentElement, elementNam= e, fieldNum); =20 > } else { =20 > log.debug("Child of segment " + segmentObject.getNa= me()=20 > + " doesn't look like a field: " + elementN= ame); > } > } > } > > //set data type of OBX-5 =20 > if (segmentObject.getClass().getName().indexOf("OBX") >=3D 0) { > Varies.fixOBX5(segmentObject, getFactory()); > } > } > =20 > private void parseReps(Segment segmentObject, Element segmentElemen= t, String fieldName, int fieldNum)=20 > throws DataTypeException, HL7Exception { > =20 > NodeList reps =3D segmentElement.getElementsByTagName(fieldName= ); > for (int i =3D 0; i < reps.getLength(); i++) { > parse(segmentObject.getField(fieldNum, i), (Element) reps.i= tem(i)); > } =20 > } > > /** > * Populates the given Element with data from the given Segment, by= inserting > * Elements corresponding to the Segment's fields, their components= , etc. Returns=20 > * true if there is at least one data value in the segment. =20 > */ > public boolean encode(Segment segmentObject, Element segmentElement= ) throws HL7Exception { > boolean hasValue =3D false; > int n =3D segmentObject.numFields(); > for (int i =3D 1; i <=3D n; i++) { > String name =3D makeElementName(segmentObject, i); > Type[] reps =3D segmentObject.getField(i); > for (int j =3D 0; j < reps.length; j++) { > Element newNode =3D segmentElement.getOwnerDocument().c= reateElement(name); > boolean componentHasValue =3D 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 =3D true; > } > } > } > return hasValue; > } > > /** > * Populates the given Type object with data from the given XML Ele= ment. > */ > public void parse(Type datatypeObject, Element datatypeElement) thr= ows 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 e= lement is primitive or=20 > * composite, calling setData() on the Varies with a new generic pr= imitive or composite as appropriate,=20 > * and then calling parse again with the new Type object. =20 > */ > private void parseVaries(Varies datatypeObject, Element datatypeEle= ment) throws DataTypeException { > //figure out what data type it holds=20 > //short nodeType =3D datatypeElement.getFirstChild().getNodeTyp= e(); =20 > if (!hasChildElement(datatypeElement)) { > //it's a primitive=20 > datatypeObject.setData(new GenericPrimitive(datatypeObject.= getMessage())); > } > else { > //it's a composite ... almost know what type, except that w= e don't have the version here=20 > datatypeObject.setData(new GenericComposite(datatypeObject.= getMessage())); > } > parse(datatypeObject.getData(), datatypeElement); > } > > /** Returns true if any of the given element's children are element= s */ > private boolean hasChildElement(Element e) { > NodeList children =3D e.getChildNodes(); > boolean hasElement =3D false; > int c =3D 0; > while (c < children.getLength() && !hasElement) { > if (children.item(c).getNodeType() =3D=3D Node.ELEMENT_NODE= ) { > hasElement =3D true; > } > c++; > } > return hasElement; > } > > /** Parses a primitive type by filling it with text child, if any *= / > private void parsePrimitive(Primitive datatypeObject, Element datat= ypeElement) throws DataTypeException { > NodeList children =3D datatypeElement.getChildNodes(); > int c =3D 0; > boolean full =3D false; > while (c < children.getLength() && !full) { > Node child =3D children.item(c++); > if (child.getNodeType() =3D=3D Node.TEXT_NODE) { > try { > if (child.getNodeValue() !=3D null && !child.getNod= eValue().equals("")) { > if (keepAsOriginal(child.getParentNode())) { > datatypeObject.setValue(child.getNodeValue(= )); > } > else { > datatypeObject.setValue(removeWhitespace(ch= ild.getNodeValue())); > } > } > } > catch (DOMException e) { > log.error("Error parsing primitive value from TEXT_= NODE", e); > } > full =3D true; > } > } > } > > /** > * Checks if <code>Node</code> content should be kept as original (= ie.: whitespaces won't be removed) > *=20 > * @param node The target <code>Node</code>=20 > * @return boolean <code>true</code> if whitespaces should not be r= emoved from node content,=20 > * <code>false</code> otherwise > */ > protected boolean keepAsOriginal(Node node) { > if (node.getNodeName() =3D=3D null) > return false; > return concatKeepAsOriginalNodes.indexOf(node.getNodeName()) !=3D= -1; > } > > /**=20 > * Removes all unecessary whitespace from the given String (intende= d to be used with Primitive values). =20 > * This includes leading and trailing whitespace, and repeated spac= e characters. Carriage returns,=20 > * line feeds, and tabs are replaced with spaces.=20 > */ > protected String removeWhitespace(String s) { > s =3D s.replace('\r', ' '); > s =3D s.replace('\n', ' '); > s =3D s.replace('\t', ' '); > > boolean repeatedSpacesExist =3D true; > while (repeatedSpacesExist) { > int loc =3D s.indexOf(" "); > if (loc < 0) { > repeatedSpacesExist =3D false; > } > else { > StringBuffer buf =3D new StringBuffer(); > buf.append(s.substring(0, loc)); > buf.append(" "); > buf.append(s.substring(loc + 2)); > s =3D buf.toString(); > } > } > return s.trim(); > } > > /** > * Populates a Composite type by looping through it's children, fin= ding corresponding=20 > * Elements among the children of the given Element, and calling pa= rse(Type, Element) for > * each. > */ > private void parseComposite(Composite datatypeObject, Element datat= ypeElement) throws DataTypeException { > if (datatypeObject instanceof GenericComposite) { //elements wo= n't be named GenericComposite.x > NodeList children =3D datatypeElement.getChildNodes(); > int compNum =3D -1; > for (int i =3D 0; i < children.getLength(); i++) { > if (children.item(i).getNodeType() =3D=3D Node.ELEMENT_= NODE) { > String nodeName =3D children.item(i).getNodeName(); > if (nodeName.matches(".*\\.\\d+")) { > String numString =3D nodeName.substring(nodeName.last= IndexOf(".")+1); > compNum =3D Integer.parseInt(numString) - 1; > } else { > compNum++; =09 > } > parse(datatypeObject.getComponent(compNum), (Elemen= t) children.item(i)); > } > } > } > else { > Type[] children =3D datatypeObject.getComponents(); > for (int i =3D 0; i < children.length; i++) { > NodeList matchingElements =3D > datatypeElement.getElementsByTagName(makeElementNam= e(datatypeObject, i + 1)); > if (matchingElements.getLength() > 0) { > parse(children[i], (Element) matchingElements.item(= 0)); //components don't repeat - use 1st > } > } > } > } > > /**=20 > * Returns the expected XML element name for the given child of a m= essage constituent=20 > * of the given class (the class should be a Composite or Segment c= lass).=20 > */ > /*private String makeElementName(Class c, int child) { > String longClassName =3D c.getName(); > String shortClassName =3D longClassName.substring(longClassName= .lastIndexOf('.') + 1, longClassName.length()); > if (shortClassName.startsWith("Valid")) { > shortClassName =3D shortClassName.substring(5, shortClassNa= me.length()); > } > return shortClassName + "." + child; > }*/ > > /** Returns the expected XML element name for the given child of th= e given Segment */ > private String makeElementName(Segment s, int child) { > return s.getName() + "." + child; > } > > /** Returns the expected XML element name for the given child of th= e given Composite */ > private String makeElementName(Composite composite, int child) { > return composite.getName() + "." + child; > } > > /** > * Populates the given Element with data from the given Type, by in= serting > * Elements corresponding to the Type's components and values. Ret= urns true if=20 > * the given type contains a value (i.e. for Primitives, if getValu= e() doesn't=20 > * return null, and for Composites, if at least one underlying Prim= itive doesn't=20 > * return null). > */ > private boolean encode(Type datatypeObject, Element datatypeElement= ) throws DataTypeException { > boolean hasData =3D false; > if (datatypeObject instanceof Varies) { > hasData =3D encodeVaries((Varies) datatypeObject, datatypeE= lement); > } > else if (datatypeObject instanceof Primitive) { > hasData =3D encodePrimitive((Primitive) datatypeObject, dat= atypeElement); > } > else if (datatypeObject instanceof Composite) { > hasData =3D encodeComposite((Composite) datatypeObject, dat= atypeElement); > } > return hasData; > } > > /** > * Encodes a Varies type by extracting it's data field and encoding= that. Returns true=20 > * if the data field (or one of its components) contains a value. =20 > */ > private boolean encodeVaries(Varies datatypeObject, Element datatyp= eElement) throws DataTypeException { > boolean hasData =3D false; > if (datatypeObject.getData() !=3D null) { > hasData =3D encode(datatypeObject.getData(), datatypeElemen= t); > } > return hasData; > } > > /**=20 > * Encodes a Primitive in XML by adding it's value as a child of th= e given Element. =20 > * Returns true if the given Primitive contains a value. =20 > */ > private boolean encodePrimitive(Primitive datatypeObject, Element d= atatypeElement) throws DataTypeException { > boolean hasValue =3D false; > if (datatypeObject.getValue() !=3D null && !datatypeObject.getV= alue().equals("")) > hasValue =3D true; > > Text t =3D datatypeElement.getOwnerDocument().createTextNode(da= tatypeObject.getValue()); > if (hasValue) { > try { > datatypeElement.appendChild(t); > } > catch (DOMException e) { > throw new DataTypeException("DOMException encoding Prim= itive: ", e); > } > } > return hasValue; > } > > /** > * Encodes a Composite in XML by looping through it's components, c= reating new=20 > * children for each of them (with the appropriate names) and popul= ating them by=20 > * calling encode(Type, Element) using these children. Returns tru= e if at least=20 > * one component contains a value. =20 > */ > private boolean encodeComposite(Composite datatypeObject, Element d= atatypeElement) throws DataTypeException { > Type[] components =3D datatypeObject.getComponents(); > boolean hasValue =3D false; > for (int i =3D 0; i < components.length; i++) { > String name =3D makeElementName(datatypeObject, i + 1); > Element newNode =3D datatypeElement.getOwnerDocument().crea= teElement(name); > boolean componentHasValue =3D encode(components[i], newNode= ); > if (componentHasValue) { > try { > datatypeElement.appendChild(newNode); > } > catch (DOMException e) { > throw new DataTypeException("DOMException encoding = Composite: ", e); > } > hasValue =3D true; > } > } > return hasValue; > } > > /** > * <p>Returns a minimal amount of data from a message string, inclu= ding only the > * data needed to send a response to the remote system. This inclu= des the > * following fields: > * <ul><li>field separator</li> > * <li>encoding characters</li> > * <li>processing ID</li> > * <li>message control ID</li></ul> > * 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 t= he inbound > * message. This method parses only that required information, hop= efully > * avoiding the condition that caused the original error.</p> > */ > public Segment getCriticalResponseData(String message) throws HL7Ex= ception { > String version =3D getVersion(message); > Segment criticalData =3D Parser.makeControlMSH(version, getFact= ory()); > > 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.1= 0", 0)); > String procID =3D parseLeaf(message, "MSH.11", 0); > if (procID =3D=3D null || procID.length() =3D=3D 0) { > procID =3D 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 I= D 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 firs= t 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 an= d robust. > * Returns null if MSA-2 can not be found (e.g. if the message is n= ot a > * response message). Trims whitespace from around the MSA-2 field= . =20 > */ > public String getAckID(String message) { > String ackID =3D null; > try { > ackID =3D parseLeaf(message, "msa.2", 0).trim(); > } > catch (HL7Exception e) { /* OK ... assume it isn't a response m= essage */ > } > return ackID; > } > > public String getVersion(String message) throws HL7Exception { > String version =3D parseLeaf(message, "MSH.12", 0); > if (version =3D=3D null || version.trim().length() =3D=3D 0) { > version =3D parseLeaf(message, "VID.1", message.indexOf("MS= H.12")); > } > return version; > } > > /** > * Attempts to retrieve the value of a leaf tag without using DOM o= r SAX. =20 > * This method searches the given message string for the given tag = name, and returns=20 > * 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=20 > * end at the start of the next tag, regardless of whether it is th= e matching end=20 > * tag or some other nested tag. =20 > * @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 searchin= g > * @throws HL7Exception if the tag can not be found > */ > protected String parseLeaf(String message, String tagName, int star= tAt) throws HL7Exception { > String value =3D null; > > int tagStart =3D message.indexOf("<" + tagName, startAt); > if (tagStart < 0) > tagStart =3D message.indexOf("<" + tagName.toUpperCase(), s= tartAt); > int valStart =3D message.indexOf(">", tagStart) + 1; > int valEnd =3D message.indexOf("<", valStart); > > if (tagStart >=3D 0 && valEnd >=3D valStart) { > value =3D 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); > } > > return value; > } > > /** Test harness */ > public static void main(String args[]) { > if (args.length !=3D 1) { > System.out.println("Usage: XMLParser pipe_encoded_file"); > System.exit(1); > } > > //read and parse message from file=20 > try { > PipeParser parser =3D new PipeParser(); > File messageFile =3D new File(args[0]); > long fileLength =3D messageFile.length(); > FileReader r =3D new FileReader(messageFile); > char[] cbuf =3D new char[(int) fileLength]; > System.out.println("Reading message file ... " + r.read(cbu= f) + " of " + fileLength + " chars"); > r.close(); > String messString =3D String.valueOf(cbuf); > Message mess =3D parser.parse(messString); > System.out.println("Got message of type " + mess.getClass()= .getName()); > > ca.uhn.hl7v2.parser.XMLParser xp =3D new XMLParser() { > public Message parseDocument(Document XMLMessage, Strin= g version) throws HL7Exception { > return null; > } > public Document encodeDocument(Message source) throws H= L7Exception { > return null; > } > public String getVersion(String message) throws HL7Exce= ption { > return null; > } > }; > > //loop through segment children of message, encode, print t= o console > String[] structNames =3D mess.getNames(); > for (int i =3D 0; i < structNames.length; i++) { > Structure[] reps =3D mess.getAll(structNames[i]); > for (int j =3D 0; j < reps.length; j++) { > if (Segment.class.isAssignableFrom(reps[j].getClass= ())) { //ignore groups > DocumentBuilder docBuilder =3D DocumentBuilderF= actory.newInstance().newDocumentBuilder(); > Document doc =3D docBuilder.newDocument(); //ne= w doc for each segment > Element root =3D doc.createElement(reps[j].getC= lass().getName()); > doc.appendChild(root); > xp.encode((Segment) reps[j], root); > StringWriter out =3D new StringWriter(); > XMLSerializer ser =3D new XMLSerializer(out, nu= ll); //default output format > ser.serialize(doc); > System.out.println("Segment " + reps[j].getClas= s().getName() + ": \r\n" + out.toString()); > > Class[] segmentConstructTypes =3D { Message.cla= ss }; > Object[] segmentConstructArgs =3D { null }; > Segment s =3D > (Segment) reps[j].getClass().getConstructor= (segmentConstructTypes).newInstance( > segmentConstructArgs); > xp.parse(s, root); > Document doc2 =3D docBuilder.newDocument(); > Element root2 =3D doc2.createElement(s.getClass= ().getName()); > doc2.appendChild(root2); > xp.encode(s, root2); > StringWriter out2 =3D new StringWriter(); > ser =3D new XMLSerializer(out2, null); //defaul= t 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(); > } > } > > } > =20 |