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