001/** 002The contents of this file are subject to the Mozilla Public License Version 1.1 003(the "License"); you may not use this file except in compliance with the License. 004You may obtain a copy of the License at http://www.mozilla.org/MPL/ 005Software distributed under the License is distributed on an "AS IS" basis, 006WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the 007specific language governing rights and limitations under the License. 008 009The Original Code is "ConformanceProfileRule.java". Description: 010"A MessageRule that checks conformance to message profiles." 011 012The Initial Developer of the Original Code is University Health Network. Copyright (C) 0132005. All Rights Reserved. 014 015Contributor(s): ______________________________________. 016 017Alternatively, the contents of this file may be used under the terms of the 018GNU General Public License (the "GPL"), in which case the provisions of the GPL are 019applicable instead of those above. If you wish to allow use of your version of this 020file only under the terms of the GPL and not to allow others to use your version 021of this file under the MPL, indicate your decision by deleting the provisions above 022and replace them with the notice and other provisions required by the GPL License. 023If you do not delete the provisions above, a recipient may use your version of 024this file under either the MPL or the GPL. 025*/ 026package ca.uhn.hl7v2.validation.impl; 027 028import java.io.IOException; 029import java.util.ArrayList; 030import java.util.Arrays; 031import java.util.List; 032 033import org.slf4j.Logger; 034import org.slf4j.LoggerFactory; 035 036import ca.uhn.hl7v2.HL7Exception; 037import ca.uhn.hl7v2.HapiContext; 038import ca.uhn.hl7v2.conf.ProfileException; 039import ca.uhn.hl7v2.conf.check.Validator; 040import ca.uhn.hl7v2.conf.parser.ProfileParser; 041import ca.uhn.hl7v2.conf.spec.RuntimeProfile; 042import ca.uhn.hl7v2.conf.store.ProfileStore; 043import ca.uhn.hl7v2.model.Message; 044import ca.uhn.hl7v2.util.Terser; 045import ca.uhn.hl7v2.validation.ValidationException; 046 047/** 048 * A MessageRule that checks conformance to message profiles. Messages can either be tested 049 * against the profiles they declare, or against a pre-defined profile. If you want both, 050 * use two <code>ConformanceProfileRule</code>s. 051 * 052 * @author Bryan Tripp 053 * @version $Revision: 1.1 $ updated on $Date: 2007-02-19 02:24:40 $ by $Author: jamesagnew $ 054 */ 055@SuppressWarnings("serial") 056public class ConformanceProfileRule extends AbstractMessageRule { 057 058 private static final Logger log = LoggerFactory.getLogger(ConformanceProfileRule.class); 059 private static final ProfileParser PARSER = new ProfileParser(true); 060 private String myProfileID; 061 062 /** 063 * Creates an instance that tests messages against whatever profiles they declare in 064 * MSH-21. The ID declared in MSH-21 is evaluated in a way that the corresponding 065 * profile file is expected to be BASEDIR/profiles/ID.xml. 066 */ 067 public ConformanceProfileRule() { 068 super(); 069 setDescription("Unknown segments found in message"); 070 setSectionReference("HL7 2.5 section 2.12"); 071 } 072 073 /** 074 * @param theProfileID the ID of a constant profile against which to test all messages 075 * (instead of the profiles they declare in MSH-21). The ID is evaluated in a way 076 * that the corresponding profile file is expected to be BASEDIR/profiles/ID.xml. 077 */ 078 public ConformanceProfileRule(String theProfileID) { 079 this(); 080 myProfileID = theProfileID; 081 } 082 083 084 /** 085 * @see ca.uhn.hl7v2.validation.MessageRule#test(ca.uhn.hl7v2.model.Message) 086 */ 087 public ValidationException[] apply(Message msg) { 088 List<ValidationException> problems = new ArrayList<ValidationException>(); 089 String[] ids = {myProfileID}; 090 091 try { 092 if (myProfileID == null) { 093 ids = getDeclaredProfileIDs(msg); 094 } 095 096 for (String id : ids) { 097 log.debug("Testing message against profile: {}", id); 098 try { 099 ValidationException[] shortList = testAgainstProfile(msg, id); 100 log.debug("{} non-conformances", shortList.length); 101 problems.addAll(Arrays.asList(shortList)); 102 } catch (ProfileException e) { 103 problems.add(new ValidationException("Can't validate against profile: " + e.getMessage(), e)); 104 } 105 } 106 } catch (HL7Exception e) { 107 problems.add(new ValidationException("Can't validate against profile: " + e.getMessage(), e)); 108 } 109 110 return problems.toArray(new ValidationException[problems.size()]); 111 } 112 113 private String[] getDeclaredProfileIDs(Message theMessage) throws HL7Exception { 114 Terser t = new Terser(theMessage); 115 boolean noMore = false; 116 int c = 0; 117 List<String> declaredProfiles = new ArrayList<String>(8); 118 while (!noMore) { 119 String path = "MSH-21(" + c++ + ")"; 120 String idRep = t.get(path); 121 //FIXME fails if empty rep precedes full rep ... should add getAll() to Terser and use that 122 if (idRep == null || idRep.equals("")) { 123 noMore = true; 124 } else { 125 declaredProfiles.add(idRep); 126 } 127 } 128 return declaredProfiles.toArray(new String[declaredProfiles.size()]); 129 } 130 131 private ValidationException[] testAgainstProfile(Message message, String id) throws ProfileException, HL7Exception { 132 HL7Exception[] exceptions; 133 HapiContext context = message.getParser().getHapiContext(); 134 Validator validator = context.getConformanceValidator(); 135 try { 136 ProfileStore profileStore = context.getProfileStore(); 137 String profileString = profileStore.getProfile(id); 138 if (profileString != null) { 139 RuntimeProfile profile = PARSER.parse(profileString); 140 exceptions = validator.validate(message, profile.getMessage()); 141 } else { 142 throw new ProfileException("Unable to find the profile " + id); 143 } 144 } catch (IOException e) { 145 throw new ProfileException("Error retreiving profile " + id, e); 146 } 147 148 ValidationException[] result = new ValidationException[exceptions.length]; 149 for (int i = 0; i < exceptions.length; i++) { 150 result[i] = ValidationException.fromHL7Exception(exceptions[i]); 151 } 152 return result; 153 } 154 155 156 /** 157 * @see ca.uhn.hl7v2.validation.Rule#getDescription() 158 */ 159 public String getDescription() { 160 return "expected conformance to declared or predefined message profiles"; 161 } 162 163 /** 164 * @see ca.uhn.hl7v2.validation.Rule#getSectionReference() 165 */ 166 public String getSectionReference() { 167 return "HL7 2.5 section 2.12"; 168 } 169 170 public String getProfileID() { 171 return myProfileID; 172 } 173 174 175 176}