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        private boolean validatePrimitives;
060
061        /**
062         * Turns validating primtives on and off (default). Note that primitive validation
063         * will significantly slow down the validation process.
064         *
065         * @param validatePrimitives true if primitive fields shall be validated, false if not
066         */
067        public void setValidatePrimitives(boolean validatePrimitives) {
068                this.validatePrimitives = validatePrimitives;
069        }
070
071        /**
072         * @return true if primitive fields shall be validated, false if not
073         */
074        public boolean isValidatePrimitives() {
075                return validatePrimitives;
076        }
077
078        /**
079         * Calls {@link #initializeHandler()} to obtain a default instance of a
080         * {@link ValidationExceptionHandler} before starting the validation.
081         * 
082         * @see ca.uhn.hl7v2.validation.Validator#validate(ca.uhn.hl7v2.model.Message)
083         */
084        public R validate(Message message) throws HL7Exception {
085                return validate(message, initializeHandler());
086        }
087
088        /**
089         * @see ca.uhn.hl7v2.validation.Validator#validate(ca.uhn.hl7v2.model.Message,
090         *      ca.uhn.hl7v2.validation.ValidationExceptionHandler)
091         */
092        public R validate(Message message, ValidationExceptionHandler<R> handler) throws HL7Exception {
093                if (message == null) {
094                        throw new NullPointerException("Message may not be null");
095                }
096                if (handler == null) {
097                        throw new NullPointerException("ValidationExceptionHandler may not be null");
098                }
099                handler.setValidationSubject(message);
100                if (isValidatePrimitives()) testPrimitiveRules(message, handler);
101                testMessageRules(message, handler);
102                return handler.result();
103        }
104
105        private void testMessageRules(Message message, ValidationExceptionHandler<R> handler)
106                        throws HL7Exception {
107                Terser t = new Terser(message);
108                String messageType = t.get("MSH-9-1");
109                String triggerEvent = t.get("MSH-9-2");
110                List<MessageRule> rules = new ArrayList<MessageRule>();
111                if (getValidationContext() != null) {
112                        rules.addAll(getValidationContext().getMessageRules(message.getVersion(), messageType,
113                                        triggerEvent));
114                }
115                LOG.debug("Validating message against {} message rules", rules.size());
116                for (MessageRule rule : rules) {
117                        ValidationException[] ex = rule.apply(message);
118                        if (ex != null && ex.length > 0) {
119                                handler.onExceptions(ex);
120                        }
121                }
122        }
123
124        private void testPrimitiveRules(Message message, ValidationExceptionHandler<R> handler)
125                        throws HL7Exception {
126                LOG.debug("Validating message against primitive type rules");
127                for (Iterator<Structure> iter = ReadOnlyMessageIterator
128                                .createPopulatedSegmentIterator(message); iter.hasNext();) {
129                        Segment s = (Segment) iter.next();
130                        for (int field = 1; field <= s.numFields(); field++) {
131                                Type[] t = s.getField(field);
132                                for (int rep = 0; rep < t.length; rep++) {
133                                        Location location = new Location();
134                                        location
135                                            .withSegmentName(s.getName())
136                                            .withField(field)
137                                            .withFieldRepetition(rep);
138                                        testType(t[rep], handler, location);
139                                }
140                        }
141                }
142        }
143
144        private void testType(Type type, ValidationExceptionHandler<R> handler, Location l) {
145                if (type instanceof Composite) {
146                        Type[] components = ((Composite) type).getComponents();
147                        for (int comp = 0; comp < components.length; comp++) {
148                                Location location = new Location(l).withComponent(comp + 1);
149                                testComponent(components[comp], handler, location);
150                        }
151                } else if (type instanceof Varies) {
152                        testType(((Varies) type).getData(), handler, l);
153                } else {
154                        testPrimitive((Primitive) type, handler, l);
155                }
156        }
157
158        private void testComponent(Type type, ValidationExceptionHandler<R> handler, Location l) {
159                if (type instanceof Composite) {
160                        Type[] component = ((Composite) type).getComponents();
161                        for (int sub = 0; sub < component.length; sub++) {
162                                Location location = new Location(l).withSubcomponent(sub + 1);
163                                testSubComponent(component[sub], handler, location);
164                        }
165                } else if (type instanceof Varies) {
166                        testComponent(((Varies) type).getData(), handler, l);
167                } else {
168                        testPrimitive((Primitive) type, handler, l);
169                }
170        }
171
172        private void testSubComponent(Type type, ValidationExceptionHandler<R> handler, Location l) {
173                if (type instanceof Primitive) {
174                        testPrimitive((Primitive) type, handler, l);
175                } else if (type instanceof Varies) {
176                        testSubComponent(((Varies) type).getData(), handler, l);
177                }
178        }
179
180        private void testPrimitive(Primitive p, ValidationExceptionHandler<R> handler, Location l) {
181                List<PrimitiveTypeRule> rules = new ArrayList<PrimitiveTypeRule>();
182                Message m = p.getMessage();
183                if (getValidationContext() != null) {
184                        rules.addAll(getValidationContext().getPrimitiveRules(m.getVersion(), p.getName(), p));
185                }
186                for (PrimitiveTypeRule rule : rules) {
187                        ValidationException[] exceptions = rule.apply(p.getValue());
188                        for (ValidationException ve : exceptions) {
189                ve.setLocation(l);
190                        }
191                        if (exceptions.length > 0) {
192                                handler.onExceptions(exceptions);
193                        }
194                }
195        }
196
197        /**
198         * Calls {@link #initializeHandler()} to obtain a default instance of a
199         * {@link ValidationExceptionHandler} before starting the validation.
200         * 
201         * @see ca.uhn.hl7v2.validation.Validator#validate(Message,
202         *      ValidationExceptionHandler)
203         */
204        public R validate(String message, boolean isXML, String version) throws HL7Exception {
205                return validate(message, isXML, version, initializeHandler());
206        }
207
208        /**
209         * @see ca.uhn.hl7v2.validation.Validator#validate(java.lang.String,
210         *      boolean, java.lang.String,
211         *      ca.uhn.hl7v2.validation.ValidationExceptionHandler)
212         */
213        public R validate(String message, boolean isXML, String version,
214                        ValidationExceptionHandler<R> handler) throws HL7Exception {
215                if (message == null) {
216                        throw new NullPointerException("Message may not be null");
217                }
218                if (handler == null) {
219                        throw new NullPointerException("ValidationExceptionHandler may not be null");
220                }
221                handler.setValidationSubject(message);
222                List<EncodingRule> rules = new ArrayList<EncodingRule>();
223                if (getValidationContext() != null) {
224                        rules.addAll(getValidationContext().getEncodingRules(version, isXML ? "XML" : "ER7"));
225                }
226                LOG.debug("Validating message against {} encoding rules", rules.size());
227                for (EncodingRule rule : rules) {
228                        ValidationException[] ex = rule.apply(message);
229                        if (ex != null && ex.length > 0) {
230                                handler.onExceptions(ex);
231                        }
232                }
233                return handler.result();
234        }
235
236        protected abstract ValidationContext getValidationContext();
237
238        protected abstract ValidationExceptionHandler<R> initializeHandler();
239
240}