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}