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