001package org.hl7.fhir.common.hapi.validation.support; 002 003import ca.uhn.fhir.context.FhirContext; 004import ca.uhn.fhir.context.support.IValidationSupport; 005import ca.uhn.fhir.context.support.ValidationSupportContext; 006import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException; 007import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; 008import ca.uhn.fhir.rest.server.exceptions.PreconditionFailedException; 009import org.apache.commons.lang3.Validate; 010import org.hl7.fhir.common.hapi.validation.validator.ProfileKnowledgeWorkerR5; 011import org.hl7.fhir.common.hapi.validation.validator.VersionSpecificWorkerContextWrapper; 012import org.hl7.fhir.common.hapi.validation.validator.VersionTypeConverterDstu3; 013import org.hl7.fhir.common.hapi.validation.validator.VersionTypeConverterR4; 014import org.hl7.fhir.instance.model.api.IBaseResource; 015import org.hl7.fhir.r5.conformance.ProfileUtilities; 016import org.hl7.fhir.r5.context.IWorkerContext; 017import org.hl7.fhir.utilities.validation.ValidationMessage; 018import org.slf4j.Logger; 019import org.slf4j.LoggerFactory; 020 021import java.util.ArrayList; 022 023import static org.apache.commons.lang3.StringUtils.isBlank; 024 025/** 026 * Simple validation support module that handles profile snapshot generation. 027 * <p> 028 * This module currently supports the following FHIR versions: 029 * <ul> 030 * <li>DSTU3</li> 031 * <li>R4</li> 032 * <li>R5</li> 033 * </ul> 034 */ 035public class SnapshotGeneratingValidationSupport implements IValidationSupport { 036 private static final Logger ourLog = LoggerFactory.getLogger(SnapshotGeneratingValidationSupport.class); 037 private final FhirContext myCtx; 038 039 /** 040 * Constructor 041 */ 042 public SnapshotGeneratingValidationSupport(FhirContext theCtx) { 043 Validate.notNull(theCtx); 044 myCtx = theCtx; 045 } 046 047 @Override 048 public IBaseResource generateSnapshot(ValidationSupportContext theValidationSupportContext, IBaseResource theInput, String theUrl, String theWebUrl, String theProfileName) { 049 050 String inputUrl = null; 051 try { 052 assert theInput.getStructureFhirVersionEnum() == myCtx.getVersion().getVersion(); 053 054 VersionSpecificWorkerContextWrapper.IVersionTypeConverter converter; 055 switch (theInput.getStructureFhirVersionEnum()) { 056 case DSTU3: 057 converter = new VersionTypeConverterDstu3(); 058 break; 059 case R4: 060 converter = new VersionTypeConverterR4(); 061 break; 062 case R5: 063 converter = VersionSpecificWorkerContextWrapper.IDENTITY_VERSION_TYPE_CONVERTER; 064 break; 065 case DSTU2: 066 case DSTU2_HL7ORG: 067 case DSTU2_1: 068 default: 069 throw new IllegalStateException("Can not generate snapshot for version: " + theInput.getStructureFhirVersionEnum()); 070 } 071 072 073 org.hl7.fhir.r5.model.StructureDefinition inputCanonical = (org.hl7.fhir.r5.model.StructureDefinition) converter.toCanonical(theInput); 074 075 inputUrl = inputCanonical.getUrl(); 076 if (theValidationSupportContext.getCurrentlyGeneratingSnapshots().contains(inputUrl)) { 077 ourLog.warn("Detected circular dependency, already generating snapshot for: {}", inputUrl); 078 return theInput; 079 } 080 theValidationSupportContext.getCurrentlyGeneratingSnapshots().add(inputUrl); 081 082 String baseDefinition = inputCanonical.getBaseDefinition(); 083 if (isBlank(baseDefinition)) { 084 throw new PreconditionFailedException("StructureDefinition[id=" + inputCanonical.getIdElement().getId() + ", url=" + inputCanonical.getUrl() + "] has no base"); 085 } 086 087 IBaseResource base = theValidationSupportContext.getRootValidationSupport().fetchStructureDefinition(baseDefinition); 088 if (base == null) { 089 throw new PreconditionFailedException("Unknown base definition: " + baseDefinition); 090 } 091 092 org.hl7.fhir.r5.model.StructureDefinition baseCanonical = (org.hl7.fhir.r5.model.StructureDefinition) converter.toCanonical(base); 093 094 if (baseCanonical.getSnapshot().getElement().isEmpty()) { 095 // If the base definition also doesn't have a snapshot, generate that first 096 theValidationSupportContext.getRootValidationSupport().generateSnapshot(theValidationSupportContext, base, null, null, null); 097 baseCanonical = (org.hl7.fhir.r5.model.StructureDefinition) converter.toCanonical(base); 098 } 099 100 ArrayList<ValidationMessage> messages = new ArrayList<>(); 101 org.hl7.fhir.r5.conformance.ProfileUtilities.ProfileKnowledgeProvider profileKnowledgeProvider = new ProfileKnowledgeWorkerR5(myCtx); 102 IWorkerContext context = new VersionSpecificWorkerContextWrapper(theValidationSupportContext, converter); 103 ProfileUtilities profileUtilities = new ProfileUtilities(context, messages, profileKnowledgeProvider); 104 profileUtilities.generateSnapshot(baseCanonical, inputCanonical, theUrl, theWebUrl, theProfileName); 105 106 switch (theInput.getStructureFhirVersionEnum()) { 107 case DSTU3: 108 org.hl7.fhir.dstu3.model.StructureDefinition generatedDstu3 = (org.hl7.fhir.dstu3.model.StructureDefinition) converter.fromCanonical(inputCanonical); 109 ((org.hl7.fhir.dstu3.model.StructureDefinition) theInput).getSnapshot().getElement().clear(); 110 ((org.hl7.fhir.dstu3.model.StructureDefinition) theInput).getSnapshot().getElement().addAll(generatedDstu3.getSnapshot().getElement()); 111 break; 112 case R4: 113 org.hl7.fhir.r4.model.StructureDefinition generatedR4 = (org.hl7.fhir.r4.model.StructureDefinition) converter.fromCanonical(inputCanonical); 114 ((org.hl7.fhir.r4.model.StructureDefinition) theInput).getSnapshot().getElement().clear(); 115 ((org.hl7.fhir.r4.model.StructureDefinition) theInput).getSnapshot().getElement().addAll(generatedR4.getSnapshot().getElement()); 116 break; 117 case R5: 118 org.hl7.fhir.r5.model.StructureDefinition generatedR5 = (org.hl7.fhir.r5.model.StructureDefinition) converter.fromCanonical(inputCanonical); 119 ((org.hl7.fhir.r5.model.StructureDefinition) theInput).getSnapshot().getElement().clear(); 120 ((org.hl7.fhir.r5.model.StructureDefinition) theInput).getSnapshot().getElement().addAll(generatedR5.getSnapshot().getElement()); 121 break; 122 case DSTU2: 123 case DSTU2_HL7ORG: 124 case DSTU2_1: 125 default: 126 throw new IllegalStateException("Can not generate snapshot for version: " + theInput.getStructureFhirVersionEnum()); 127 } 128 129 return theInput; 130 131 } catch (BaseServerResponseException e) { 132 throw e; 133 } catch (Exception e) { 134 throw new InternalErrorException("Failed to generate snapshot", e); 135 } finally { 136 if (inputUrl != null) { 137 theValidationSupportContext.getCurrentlyGeneratingSnapshots().remove(inputUrl); 138 } 139 } 140 } 141 142 @Override 143 public FhirContext getFhirContext() { 144 return myCtx; 145 } 146 147 148}