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.setSegmentName(s.getName()); 116 location.setField(field); 117 location.setFieldRepetition(rep); 118 testType(t[rep], handler, location); 119 } 120 } 121 } 122 } 123 124 private void testType(Type type, ValidationExceptionHandler<R> handler, Location l) { 125 if (type instanceof Composite) { 126 Type[] components = ((Composite) type).getComponents(); 127 for (int comp = 0; comp < components.length; comp++) { 128 Location location = new Location(l); 129 location.setComponent(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); 144 location.setSubcomponent(sub + 1); 145 testSubComponent(component[sub], handler, location); 146 } 147 } else if (type instanceof Varies) { 148 testComponent(((Varies) type).getData(), handler, l); 149 } else { 150 testPrimitive((Primitive) type, handler, l); 151 } 152 } 153 154 private void testSubComponent(Type type, ValidationExceptionHandler<R> handler, Location l) { 155 if (type instanceof Primitive) { 156 testPrimitive((Primitive) type, handler, l); 157 } else if (type instanceof Varies) { 158 testSubComponent(((Varies) type).getData(), handler, l); 159 } 160 } 161 162 private void testPrimitive(Primitive p, ValidationExceptionHandler<R> handler, Location l) { 163 List<PrimitiveTypeRule> rules = new ArrayList<PrimitiveTypeRule>(); 164 Message m = p.getMessage(); 165 if (getValidationContext() != null) { 166 rules.addAll(getValidationContext().getPrimitiveRules(m.getVersion(), p.getName(), p)); 167 } 168 for (PrimitiveTypeRule rule : rules) { 169 ValidationException[] exceptions = rule.apply(p.getValue()); 170 for (ValidationException ve : exceptions) { 171 ve.setLocation(l); 172 } 173 if (exceptions.length > 0) { 174 handler.onExceptions(exceptions); 175 } 176 } 177 } 178 179 /** 180 * Calls {@link #initializeHandler()} to obtain a default instance of a 181 * {@link ValidationExceptionHandler} before starting the validation. 182 * 183 * @see ca.uhn.hl7v2.validation.Validator#validate(Message, 184 * ValidationExceptionHandler) 185 */ 186 public R validate(String message, boolean isXML, String version) throws HL7Exception { 187 return validate(message, isXML, version, initializeHandler()); 188 } 189 190 /** 191 * @see ca.uhn.hl7v2.validation.Validator#validate(java.lang.String, 192 * boolean, java.lang.String, 193 * ca.uhn.hl7v2.validation.ValidationExceptionHandler) 194 */ 195 public R validate(String message, boolean isXML, String version, 196 ValidationExceptionHandler<R> handler) throws HL7Exception { 197 if (message == null) { 198 throw new NullPointerException("Message may not be null"); 199 } 200 if (handler == null) { 201 throw new NullPointerException("ValidationExceptionHandler may not be null"); 202 } 203 handler.setValidationSubject(message); 204 List<EncodingRule> rules = new ArrayList<EncodingRule>(); 205 if (getValidationContext() != null) { 206 rules.addAll(getValidationContext().getEncodingRules(version, isXML ? "XML" : "ER7")); 207 } 208 LOG.debug("Validating message against {} encoding rules", rules.size()); 209 for (EncodingRule rule : rules) { 210 ValidationException[] ex = rule.apply(message); 211 if (ex != null && ex.length > 0) { 212 handler.onExceptions(ex); 213 } 214 } 215 return handler.result(); 216 } 217 218 protected abstract ValidationContext getValidationContext(); 219 220 protected abstract ValidationExceptionHandler<R> initializeHandler(); 221 222}