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 "AbstractValidator.java".  Description: 
010"Abstract implementation of a message validator." 
011
012The Initial Developer of the Original Code is University Health Network. Copyright (C) 
0132012.  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;
027
028import java.util.ArrayList;
029import java.util.Iterator;
030import java.util.List;
031
032import org.slf4j.Logger;
033import org.slf4j.LoggerFactory;
034
035import ca.uhn.hl7v2.HL7Exception;
036import ca.uhn.hl7v2.Location;
037import ca.uhn.hl7v2.model.Composite;
038import ca.uhn.hl7v2.model.Message;
039import ca.uhn.hl7v2.model.Primitive;
040import ca.uhn.hl7v2.model.Segment;
041import ca.uhn.hl7v2.model.Structure;
042import ca.uhn.hl7v2.model.Type;
043import ca.uhn.hl7v2.model.Varies;
044import ca.uhn.hl7v2.util.ReadOnlyMessageIterator;
045import ca.uhn.hl7v2.util.Terser;
046
047/**
048 * Abstract implementation of a message validator.
049 * 
050 * @param <R> The type parameter R denotes the result type of the validation
051 *            process.
052 * 
053 * @author Christian Ohr
054 */
055public abstract class AbstractValidator<R> implements Validator<R> {
056
057        private static final Logger LOG = LoggerFactory.getLogger(AbstractValidator.class);
058
059        /**
060         * Calls {@link #initializeHandler()} to obtain a default instance of a
061         * {@link ValidationExceptionHandler} before starting the validation.
062         * 
063         * @see ca.uhn.hl7v2.validation.Validator#validate(ca.uhn.hl7v2.model.Message)
064         */
065        public R validate(Message message) throws HL7Exception {
066                return validate(message, initializeHandler());
067        }
068
069        /**
070         * @see ca.uhn.hl7v2.validation.Validator#validate(ca.uhn.hl7v2.model.Message,
071         *      ca.uhn.hl7v2.validation.ValidationExceptionHandler)
072         */
073        public R validate(Message message, ValidationExceptionHandler<R> handler) throws HL7Exception {
074                if (message == null) {
075                        throw new NullPointerException("Message may not be null");
076                }
077                if (handler == null) {
078                        throw new NullPointerException("ValidationExceptionHandler may not be null");
079                }
080                handler.setValidationSubject(message);
081                // testPrimitiveRules(message, handler); TODO this slows down parser 3x
082                testMessageRules(message, handler);
083                return handler.result();
084        }
085
086        private void testMessageRules(Message message, ValidationExceptionHandler<R> handler)
087                        throws HL7Exception {
088                Terser t = new Terser(message);
089                String messageType = t.get("MSH-9-1");
090                String triggerEvent = t.get("MSH-9-2");
091                List<MessageRule> rules = new ArrayList<MessageRule>();
092                if (getValidationContext() != null) {
093                        rules.addAll(getValidationContext().getMessageRules(message.getVersion(), messageType,
094                                        triggerEvent));
095                }
096                LOG.debug("Validating message against {} message rules", rules.size());
097                for (MessageRule rule : rules) {
098                        ValidationException[] ex = rule.apply(message);
099                        if (ex != null && ex.length > 0) {
100                                handler.onExceptions(ex);
101                        }
102                }
103        }
104
105        private void testPrimitiveRules(Message message, ValidationExceptionHandler<R> handler)
106                        throws HL7Exception {
107                LOG.debug("Validating message against primitive type rules");
108                for (Iterator<Structure> iter = ReadOnlyMessageIterator
109                                .createPopulatedSegmentIterator(message); iter.hasNext();) {
110                        Segment s = (Segment) iter.next();
111                        for (int field = 1; field <= s.numFields(); field++) {
112                                Type[] t = s.getField(field);
113                                for (int rep = 0; rep < t.length; rep++) {
114                                        Location location = new Location();
115                                        location
116                                            .withSegmentName(s.getName())
117                                            .withField(field)
118                                            .withFieldRepetition(rep);
119                                        testType(t[rep], handler, location);
120                                }
121                        }
122                }
123        }
124
125        private void testType(Type type, ValidationExceptionHandler<R> handler, Location l) {
126                if (type instanceof Composite) {
127                        Type[] components = ((Composite) type).getComponents();
128                        for (int comp = 0; comp < components.length; comp++) {
129                                Location location = new Location(l).withComponent(comp + 1);
130                                testComponent(components[comp], handler, location);
131                        }
132                } else if (type instanceof Varies) {
133                        testType(((Varies) type).getData(), handler, l);
134                } else {
135                        testPrimitive((Primitive) type, handler, l);
136                }
137        }
138
139        private void testComponent(Type type, ValidationExceptionHandler<R> handler, Location l) {
140                if (type instanceof Composite) {
141                        Type[] component = ((Composite) type).getComponents();
142                        for (int sub = 0; sub < component.length; sub++) {
143                                Location location = new Location(l).withSubcomponent(sub + 1);
144                                testSubComponent(component[sub], handler, location);
145                        }
146                } else if (type instanceof Varies) {
147                        testComponent(((Varies) type).getData(), handler, l);
148                } else {
149                        testPrimitive((Primitive) type, handler, l);
150                }
151        }
152
153        private void testSubComponent(Type type, ValidationExceptionHandler<R> handler, Location l) {
154                if (type instanceof Primitive) {
155                        testPrimitive((Primitive) type, handler, l);
156                } else if (type instanceof Varies) {
157                        testSubComponent(((Varies) type).getData(), handler, l);
158                }
159        }
160
161        private void testPrimitive(Primitive p, ValidationExceptionHandler<R> handler, Location l) {
162                List<PrimitiveTypeRule> rules = new ArrayList<PrimitiveTypeRule>();
163                Message m = p.getMessage();
164                if (getValidationContext() != null) {
165                        rules.addAll(getValidationContext().getPrimitiveRules(m.getVersion(), p.getName(), p));
166                }
167                for (PrimitiveTypeRule rule : rules) {
168                        ValidationException[] exceptions = rule.apply(p.getValue());
169                        for (ValidationException ve : exceptions) {
170                ve.setLocation(l);
171                        }
172                        if (exceptions.length > 0) {
173                                handler.onExceptions(exceptions);
174                        }
175                }
176        }
177
178        /**
179         * Calls {@link #initializeHandler()} to obtain a default instance of a
180         * {@link ValidationExceptionHandler} before starting the validation.
181         * 
182         * @see ca.uhn.hl7v2.validation.Validator#validate(Message,
183         *      ValidationExceptionHandler)
184         */
185        public R validate(String message, boolean isXML, String version) throws HL7Exception {
186                return validate(message, isXML, version, initializeHandler());
187        }
188
189        /**
190         * @see ca.uhn.hl7v2.validation.Validator#validate(java.lang.String,
191         *      boolean, java.lang.String,
192         *      ca.uhn.hl7v2.validation.ValidationExceptionHandler)
193         */
194        public R validate(String message, boolean isXML, String version,
195                        ValidationExceptionHandler<R> handler) throws HL7Exception {
196                if (message == null) {
197                        throw new NullPointerException("Message may not be null");
198                }
199                if (handler == null) {
200                        throw new NullPointerException("ValidationExceptionHandler may not be null");
201                }
202                handler.setValidationSubject(message);
203                List<EncodingRule> rules = new ArrayList<EncodingRule>();
204                if (getValidationContext() != null) {
205                        rules.addAll(getValidationContext().getEncodingRules(version, isXML ? "XML" : "ER7"));
206                }
207                LOG.debug("Validating message against {} encoding rules", rules.size());
208                for (EncodingRule rule : rules) {
209                        ValidationException[] ex = rule.apply(message);
210                        if (ex != null && ex.length > 0) {
211                                handler.onExceptions(ex);
212                        }
213                }
214                return handler.result();
215        }
216
217        protected abstract ValidationContext getValidationContext();
218
219        protected abstract ValidationExceptionHandler<R> initializeHandler();
220
221}