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}