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    }