001/*- 002 * #%L 003 * HAPI FHIR - Core Library 004 * %% 005 * Copyright (C) 2014 - 2025 Smile CDR, Inc. 006 * %% 007 * Licensed under the Apache License, Version 2.0 (the "License"); 008 * you may not use this file except in compliance with the License. 009 * You may obtain a copy of the License at 010 * 011 * http://www.apache.org/licenses/LICENSE-2.0 012 * 013 * Unless required by applicable law or agreed to in writing, software 014 * distributed under the License is distributed on an "AS IS" BASIS, 015 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 016 * See the License for the specific language governing permissions and 017 * limitations under the License. 018 * #L% 019 */ 020package ca.uhn.fhir.narrative2; 021 022import ca.uhn.fhir.context.FhirContext; 023import ca.uhn.fhir.context.FhirVersionEnum; 024import ca.uhn.fhir.util.BundleUtil; 025import ca.uhn.fhir.util.TerserUtil; 026import org.apache.commons.lang3.StringUtils; 027import org.apache.commons.lang3.tuple.Pair; 028import org.hl7.fhir.instance.model.api.IBase; 029import org.hl7.fhir.instance.model.api.IBaseBundle; 030import org.hl7.fhir.instance.model.api.IBaseCoding; 031import org.hl7.fhir.instance.model.api.IBaseResource; 032 033import java.util.List; 034import java.util.Objects; 035import java.util.stream.Stream; 036 037/** 038 * An instance of this class is added to the Thymeleaf context as a variable with 039 * name <code>"narrativeUtil"</code> and can be accessed from narrative templates. 040 * 041 * @since 7.0.0 042 */ 043public class NarrativeGeneratorTemplateUtils { 044 045 public static final NarrativeGeneratorTemplateUtils INSTANCE = new NarrativeGeneratorTemplateUtils(); 046 047 /** 048 * Given a Bundle as input, are any entries present with a given resource type 049 */ 050 public boolean bundleHasEntriesWithResourceType(IBaseBundle theBaseBundle, String theResourceType) { 051 FhirVersionEnum fhirVersionEnum = theBaseBundle.getStructureFhirVersionEnum(); 052 FhirContext ctx = FhirContext.forCached(fhirVersionEnum); 053 List<Pair<String, IBaseResource>> entryResources = 054 BundleUtil.getBundleEntryUrlsAndResources(ctx, theBaseBundle); 055 return entryResources.stream() 056 .map(Pair::getValue) 057 .filter(Objects::nonNull) 058 .anyMatch(t -> ctx.getResourceType(t).equals(theResourceType)); 059 } 060 061 /** 062 * Returns if the bundle contains an entry resource whose `code` property contains a matching code system and code. 063 * 064 * @param theBundle the bundle to inspect 065 * @param theResourceType the resource type to look for 066 * @param theCodeSystem the code system to find 067 * @param theCode the code to find 068 * @return returns true if bundle has a resource that with matching code/code system 069 */ 070 public boolean bundleHasEntriesWithCode( 071 IBaseBundle theBundle, String theResourceType, String theCodeSystem, String theCode) { 072 FhirVersionEnum fhirVersionEnum = theBundle.getStructureFhirVersionEnum(); 073 FhirContext ctx = FhirContext.forCached(fhirVersionEnum); 074 075 return getEntryResources(ctx, theBundle, theResourceType).anyMatch(t -> { 076 List<IBase> codeList = TerserUtil.getFieldByFhirPath(ctx, "code.coding", t); 077 return codeList.stream().anyMatch(m -> { 078 IBaseCoding coding = (IBaseCoding) m; 079 return StringUtils.equals(coding.getSystem(), theCodeSystem) 080 && StringUtils.equals(coding.getCode(), theCode); 081 }); 082 }); 083 } 084 085 /** 086 * Gets a boolean indicating if at least one bundle entry resource's `code` property does NOT contain the 087 * code system/code specified. 088 * 089 * @param theBundle the bundle to inspect 090 * @param theResourceType the resource type to find 091 * @param theCodeSystem the code system to find 092 * @param theCode the code to find 093 * @return Returns true if one entry of resource type requested does not contain the specified code/system 094 */ 095 public boolean bundleHasEntriesWithoutCode( 096 IBaseBundle theBundle, String theResourceType, String theCodeSystem, String theCode) { 097 098 FhirVersionEnum fhirVersionEnum = theBundle.getStructureFhirVersionEnum(); 099 FhirContext ctx = FhirContext.forCached(fhirVersionEnum); 100 101 return getEntryResources(ctx, theBundle, theResourceType).anyMatch(t -> { 102 List<IBase> codeList = TerserUtil.getFieldByFhirPath(ctx, "code.coding", t); 103 return codeList.stream().allMatch(m -> { 104 IBaseCoding coding = (IBaseCoding) m; 105 return !(StringUtils.equals(coding.getSystem(), theCodeSystem) 106 && StringUtils.equals(coding.getCode(), theCode)); 107 }); 108 }); 109 } 110 111 private Stream<IBaseResource> getEntryResources( 112 FhirContext theContext, IBaseBundle theBundle, String theResourceType) { 113 List<Pair<String, IBaseResource>> entryResources = 114 BundleUtil.getBundleEntryUrlsAndResources(theContext, theBundle); 115 return entryResources.stream() 116 .map(Pair::getValue) 117 .filter(Objects::nonNull) 118 .filter(t -> theContext.getResourceType(t).equals(theResourceType)); 119 } 120}