View Javadoc
1   /**
2   The contents of this file are subject to the Mozilla Public License Version 1.1 
3   (the "License"); you may not use this file except in compliance with the License. 
4   You may obtain a copy of the License at http://www.mozilla.org/MPL/ 
5   Software distributed under the License is distributed on an "AS IS" basis, 
6   WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the 
7   specific language governing rights and limitations under the License. 
8   
9   The Original Code is "AbstractValidator.java".  Description: 
10  "Abstract implementation of a message validator." 
11  
12  The Initial Developer of the Original Code is University Health Network. Copyright (C) 
13  2012.  All Rights Reserved. 
14  
15  Contributor(s): ______________________________________. 
16  
17  Alternatively, the contents of this file may be used under the terms of the 
18  GNU General Public License (the "GPL"), in which case the provisions of the GPL are 
19  applicable instead of those above.  If you wish to allow use of your version of this 
20  file only under the terms of the GPL and not to allow others to use your version 
21  of this file under the MPL, indicate your decision by deleting  the provisions above 
22  and replace  them with the notice and other provisions required by the GPL License.  
23  If you do not delete the provisions above, a recipient may use your version of 
24  this file under either the MPL or the GPL. 
25   */
26  package ca.uhn.hl7v2.validation;
27  
28  import java.util.ArrayList;
29  import java.util.Iterator;
30  import java.util.List;
31  
32  import org.slf4j.Logger;
33  import org.slf4j.LoggerFactory;
34  
35  import ca.uhn.hl7v2.HL7Exception;
36  import ca.uhn.hl7v2.Location;
37  import ca.uhn.hl7v2.model.Composite;
38  import ca.uhn.hl7v2.model.Message;
39  import ca.uhn.hl7v2.model.Primitive;
40  import ca.uhn.hl7v2.model.Segment;
41  import ca.uhn.hl7v2.model.Structure;
42  import ca.uhn.hl7v2.model.Type;
43  import ca.uhn.hl7v2.model.Varies;
44  import ca.uhn.hl7v2.util.ReadOnlyMessageIterator;
45  import ca.uhn.hl7v2.util.Terser;
46  
47  /**
48   * Abstract implementation of a message validator.
49   * 
50   * @param <R> The type parameter R denotes the result type of the validation
51   *            process.
52   * 
53   * @author Christian Ohr
54   */
55  public abstract class AbstractValidator<R> implements Validator<R> {
56  
57  	private static final Logger LOG = LoggerFactory.getLogger(AbstractValidator.class);
58  
59  	private boolean validatePrimitives;
60  
61  	/**
62  	 * Turns validating primtives on and off (default). Note that primitive validation
63  	 * will significantly slow down the validation process.
64  	 *
65  	 * @param validatePrimitives true if primitive fields shall be validated, false if not
66  	 */
67  	public void setValidatePrimitives(boolean validatePrimitives) {
68  		this.validatePrimitives = validatePrimitives;
69  	}
70  
71  	/**
72  	 * @return true if primitive fields shall be validated, false if not
73  	 */
74  	public boolean isValidatePrimitives() {
75  		return validatePrimitives;
76  	}
77  
78  	/**
79  	 * Calls {@link #initializeHandler()} to obtain a default instance of a
80  	 * {@link ValidationExceptionHandler} before starting the validation.
81  	 * 
82  	 * @see ca.uhn.hl7v2.validation.Validator#validate(ca.uhn.hl7v2.model.Message)
83  	 */
84  	public R validate(Message message) throws HL7Exception {
85  		return validate(message, initializeHandler());
86  	}
87  
88  	/**
89  	 * @see ca.uhn.hl7v2.validation.Validator#validate(ca.uhn.hl7v2.model.Message,
90  	 *      ca.uhn.hl7v2.validation.ValidationExceptionHandler)
91  	 */
92  	public R validate(Message message, ValidationExceptionHandler<R> handler) throws HL7Exception {
93  		if (message == null) {
94  			throw new NullPointerException("Message may not be null");
95  		}
96  		if (handler == null) {
97  			throw new NullPointerException("ValidationExceptionHandler may not be null");
98  		}
99  		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<>();
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<>();
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<>();
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 }