001package org.hl7.fhir.common.hapi.validation.support; 002 003import ca.uhn.fhir.context.FhirContext; 004import ca.uhn.fhir.context.support.ConceptValidationOptions; 005import ca.uhn.fhir.context.support.ValidationSupportContext; 006import org.apache.commons.lang3.Validate; 007import org.hl7.fhir.instance.model.api.IBaseResource; 008import org.slf4j.Logger; 009import org.slf4j.LoggerFactory; 010 011import javax.annotation.Nonnull; 012import javax.annotation.Nullable; 013 014/** 015 * This validation support module may be placed at the end of a {@link ValidationSupportChain} 016 * in order to configure the validator to generate a warning if a resource being validated 017 * contains an unknown code system. 018 * 019 * Note that this module must also be activated by calling {@link #setAllowNonExistentCodeSystem(boolean)} 020 * in order to specify that unknown code systems should be allowed. 021 */ 022public class UnknownCodeSystemWarningValidationSupport extends BaseValidationSupport { 023 private static final Logger ourLog = LoggerFactory.getLogger(UnknownCodeSystemWarningValidationSupport.class); 024 025 public static final IssueSeverity DEFAULT_SEVERITY = IssueSeverity.ERROR; 026 027 private IssueSeverity myNonExistentCodeSystemSeverity = DEFAULT_SEVERITY; 028 029 /** 030 * Constructor 031 */ 032 public UnknownCodeSystemWarningValidationSupport(FhirContext theFhirContext) { 033 super(theFhirContext); 034 } 035 036 @Override 037 public boolean isValueSetSupported(ValidationSupportContext theValidationSupportContext, String theValueSetUrl) { 038 return true; 039 } 040 041 @Override 042 public boolean isCodeSystemSupported(ValidationSupportContext theValidationSupportContext, String theSystem) { 043 return canValidateCodeSystem(theValidationSupportContext, theSystem); 044 } 045 046 @Nullable 047 @Override 048 public LookupCodeResult lookupCode(ValidationSupportContext theValidationSupportContext, String theSystem, String theCode, String theDisplayLanguage) { 049 // filters out error/fatal 050 if (canValidateCodeSystem(theValidationSupportContext, theSystem)) { 051 return new LookupCodeResult() 052 .setFound(true); 053 } 054 055 return null; 056 } 057 058 @Override 059 public CodeValidationResult validateCode(@Nonnull ValidationSupportContext theValidationSupportContext, @Nonnull ConceptValidationOptions theOptions, String theCodeSystem, String theCode, String theDisplay, String theValueSetUrl) { 060 // filters out error/fatal 061 if (!canValidateCodeSystem(theValidationSupportContext, theCodeSystem)) { 062 return null; 063 } 064 065 CodeValidationResult result = new CodeValidationResult(); 066 // will be warning or info (error/fatal filtered out above) 067 result.setSeverity(myNonExistentCodeSystemSeverity); 068 result.setMessage("CodeSystem is unknown and can't be validated: " + theCodeSystem); 069 070 if (myNonExistentCodeSystemSeverity == IssueSeverity.INFORMATION) { 071 // for warnings, we don't set the code 072 // cause if we do, the severity is stripped out 073 // (see VersionSpecificWorkerContextWrapper.convertValidationResult) 074 result.setCode(theCode); 075 } 076 077 return result; 078 } 079 080 @Nullable 081 @Override 082 public CodeValidationResult validateCodeInValueSet(ValidationSupportContext theValidationSupportContext, ConceptValidationOptions theOptions, String theCodeSystem, String theCode, String theDisplay, @Nonnull IBaseResource theValueSet) { 083 if (!canValidateCodeSystem(theValidationSupportContext, theCodeSystem)) { 084 return null; 085 } 086 087 return new CodeValidationResult() 088 .setCode(theCode) 089 .setSeverity(IssueSeverity.INFORMATION) 090 .setMessage("Code " + theCodeSystem + "#" + theCode + " was not checked because the CodeSystem is not available"); 091 } 092 093 /** 094 * Returns true if non existent code systems will still validate. 095 * False if they will throw errors. 096 * @return 097 */ 098 private boolean allowNonExistentCodeSystems() { 099 switch (myNonExistentCodeSystemSeverity) { 100 case ERROR: 101 case FATAL: 102 return false; 103 case WARNING: 104 case INFORMATION: 105 return true; 106 default: 107 ourLog.info("Unknown issue severity " + myNonExistentCodeSystemSeverity.name() 108 + ". Treating as INFO/WARNING"); 109 return true; 110 } 111 } 112 113 /** 114 * Determines if the code system can (and should) be validated. 115 * @param theValidationSupportContext 116 * @param theCodeSystem 117 * @return 118 */ 119 private boolean canValidateCodeSystem(ValidationSupportContext theValidationSupportContext, 120 String theCodeSystem) { 121 if (!allowNonExistentCodeSystems()) { 122 return false; 123 } 124 if (theCodeSystem == null) { 125 return false; 126 } 127 IBaseResource codeSystem = theValidationSupportContext.getRootValidationSupport().fetchCodeSystem(theCodeSystem); 128 if (codeSystem != null) { 129 return false; 130 } 131 return true; 132 } 133 134 /** 135 * If set to allow, code system violations will be flagged with Warning by default. 136 * Use setNonExistentCodeSystemSeverity instead. 137 */ 138 @Deprecated 139 public void setAllowNonExistentCodeSystem(boolean theAllowNonExistentCodeSystem) { 140 if (theAllowNonExistentCodeSystem) { 141 setNonExistentCodeSystemSeverity(IssueSeverity.WARNING); 142 } else { 143 setNonExistentCodeSystemSeverity(IssueSeverity.ERROR); 144 } 145 } 146 147 /** 148 * Sets the non-existent code system severity. 149 */ 150 public void setNonExistentCodeSystemSeverity(@Nonnull IssueSeverity theSeverity) { 151 Validate.notNull(theSeverity, "theSeverity must not be null"); 152 myNonExistentCodeSystemSeverity = theSeverity; 153 } 154}