View Javadoc
1   package ca.uhn.fhir.validation;
2   
3   /*
4    * #%L
5    * HAPI FHIR - Core Library
6    * %%
7    * Copyright (C) 2014 University Health Network
8    * %%
9    * Licensed under the Apache License, Version 2.0 (the "License");
10   * you may not use this file except in compliance with the License.
11   * You may obtain a copy of the License at
12   * 
13   *      http://www.apache.org/licenses/LICENSE-2.0
14   * 
15   * Unless required by applicable law or agreed to in writing, software
16   * distributed under the License is distributed on an "AS IS" BASIS,
17   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18   * See the License for the specific language governing permissions and
19   * limitations under the License.
20   * #L%
21   */
22  
23  import ca.uhn.fhir.context.FhirContext;
24  import ca.uhn.fhir.model.api.Bundle;
25  import ca.uhn.fhir.model.api.IResource;
26  import ca.uhn.fhir.model.dstu.resource.OperationOutcome;
27  import org.apache.commons.lang3.Validate;
28  
29  import java.util.ArrayList;
30  import java.util.Iterator;
31  import java.util.List;
32  
33  /**
34   * Resource validator, which checks resources for compliance against various validation schemes (schemas, schematrons, etc.)
35   * 
36   * <p>
37   * To obtain a resource validator, call {@link FhirContext#newValidator()}
38   * </p>
39   */
40  public class FhirValidator {
41  
42  	private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirValidator.class);
43  
44  	private static final String I18N_KEY_NO_PHLOC_WARNING = FhirValidator.class.getName()+".noPhlocWarningOnStartup";
45  	private static final String I18N_KEY_NO_PHLOC_ERROR = FhirValidator.class.getName()+".noPhlocError";
46  
47  	private FhirContext myContext;
48  	private List<IValidator> myValidators = new ArrayList<IValidator>();
49  	private static volatile Boolean ourPhlocPresentOnClasspath;
50  
51  	/**
52  	 * Constructor (this should not be called directly, but rather {@link FhirContext#newValidator()} should be called to obtain an instance of {@link FhirValidator})
53  	 */
54  	public FhirValidator(FhirContext theFhirContext) {
55  		myContext = theFhirContext;
56  		setValidateAgainstStandardSchema(true);
57  
58  		if (ourPhlocPresentOnClasspath == null) {
59  			try {
60  				Class.forName("com.phloc.schematron.ISchematronResource");
61  				ourPhlocPresentOnClasspath = true;
62  			} catch (ClassNotFoundException e) {
63  				ourLog.info(theFhirContext.getLocalizer().getMessage(I18N_KEY_NO_PHLOC_WARNING));
64  				ourPhlocPresentOnClasspath = false;
65  			}
66  		}
67  		if (ourPhlocPresentOnClasspath) {
68  			setValidateAgainstStandardSchematron(true);
69  		}
70  
71  	}
72  
73  	private void addOrRemoveValidator(boolean theValidateAgainstStandardSchema, Class<? extends IValidator> type, IValidator instance) {
74  		if (theValidateAgainstStandardSchema) {
75  			boolean found = haveValidatorOfType(type);
76  			if (!found) {
77  				myValidators.add(instance);
78  			}
79  		} else {
80  			for (Iterator<IValidator> iter = myValidators.iterator(); iter.hasNext();) {
81  				IValidator next = iter.next();
82  				if (next.getClass().equals(type)) {
83  					iter.remove();
84  				}
85  			}
86  		}
87  	}
88  
89  	private boolean haveValidatorOfType(Class<? extends IValidator> type) {
90  		boolean found = false;
91  		for (IValidator next : myValidators) {
92  			if (next.getClass().equals(type)) {
93  				found = true;
94  			}
95  		}
96  		return found;
97  	}
98  
99  	/**
100 	 * Should the validator validate the resource against the base schema (the schema provided with the FHIR distribution itself)
101 	 */
102 	public boolean isValidateAgainstStandardSchema() {
103 		return haveValidatorOfType(SchemaBaseValidator.class);
104 	}
105 
106 	/**
107 	 * Should the validator validate the resource against the base schema (the schema provided with the FHIR distribution itself)
108 	 */
109 	public boolean isValidateAgainstStandardSchematron() {
110 		return haveValidatorOfType(SchematronBaseValidator.class);
111 	}
112 
113 	/**
114 	 * Should the validator validate the resource against the base schema (the schema provided with the FHIR distribution itself)
115 	 */
116 	public void setValidateAgainstStandardSchema(boolean theValidateAgainstStandardSchema) {
117 		addOrRemoveValidator(theValidateAgainstStandardSchema, SchemaBaseValidator.class, new SchemaBaseValidator());
118 	}
119 
120 	/**
121 	 * Should the validator validate the resource against the base schematron (the schematron provided with the FHIR distribution itself)
122 	 */
123 	public void setValidateAgainstStandardSchematron(boolean theValidateAgainstStandardSchematron) {
124 		if (theValidateAgainstStandardSchematron && !ourPhlocPresentOnClasspath) {
125 			throw new IllegalArgumentException(myContext.getLocalizer().getMessage(I18N_KEY_NO_PHLOC_ERROR));
126 		}
127 		addOrRemoveValidator(theValidateAgainstStandardSchematron, SchematronBaseValidator.class, new SchematronBaseValidator());
128 	}
129 
130 	/**
131 	 * Validates a bundle instance, throwing a {@link ValidationFailureException} if the validation fails. This validation includes validation of all resources in the bundle.
132 	 * 
133 	 * @param theBundle
134 	 *            The resource to validate
135 	 * @throws ValidationFailureException
136 	 *             If the validation fails
137      * @deprecated use {@link #validateWithResult(ca.uhn.fhir.model.api.Bundle)} instead
138 	 */
139     @Deprecated
140 	public void validate(Bundle theBundle) {
141         ValidationResult validationResult = validateWithResult(theBundle);
142         if (!validationResult.isSuccessful()) {
143             throw new ValidationFailureException(validationResult.getOperationOutcome());
144         }
145 	}
146 
147 	/**
148 	 * Validates a resource instance, throwing a {@link ValidationFailureException} if the validation fails
149 	 * 
150 	 * @param theResource
151 	 *            The resource to validate
152 	 * @throws ValidationFailureException
153 	 *             If the validation fails
154      * @deprecated use {@link #validateWithResult(ca.uhn.fhir.model.api.IResource)} instead
155 	 */
156     @Deprecated
157 	public void validate(IResource theResource) throws ValidationFailureException {
158         ValidationResult validationResult = validateWithResult(theResource);
159         if (!validationResult.isSuccessful()) {
160             throw new ValidationFailureException(validationResult.getOperationOutcome());
161         }
162     }
163 
164     /**
165      * Validates a bundle instance returning a {@link ca.uhn.fhir.validation.ValidationResult} which contains the results.
166      * This validation includes validation of all resources in the bundle.
167      *
168      * @param theBundle the bundle to validate
169      * @return the results of validation
170      * @since 0.7
171      */
172     public ValidationResult validateWithResult(Bundle theBundle) {
173         Validate.notNull(theBundle, "theBundle must not be null");
174 
175         ValidationContext<Bundle> ctx = ValidationContext.forBundle(myContext, theBundle);
176 
177         for (IValidator next : myValidators) {
178             next.validateBundle(ctx);
179         }
180 
181         OperationOutcome oo = ctx.getOperationOutcome();
182         return ValidationResult.valueOf(oo);
183     }
184 
185     /**
186      * Validates a resource instance returning a {@link ca.uhn.fhir.validation.ValidationResult} which contains the results.
187      *
188      * @param theResource the resource to validate
189      * @return the results of validation
190      * @since 0.7
191      */
192     public ValidationResult validateWithResult(IResource theResource) {
193         Validate.notNull(theResource, "theResource must not be null");
194 
195         ValidationContext<IResource> ctx = ValidationContext.forResource(myContext, theResource);
196 
197         for (IValidator next : myValidators) {
198             next.validateResource(ctx);
199         }
200 
201         OperationOutcome oo = ctx.getOperationOutcome();
202         return ValidationResult.valueOf(oo);
203     }
204 }