001package ca.uhn.fhir.util; 002 003import static org.apache.commons.lang3.StringUtils.isNotBlank; 004 005/* 006 * #%L 007 * HAPI FHIR - Core Library 008 * %% 009 * Copyright (C) 2014 - 2019 University Health Network 010 * %% 011 * Licensed under the Apache License, Version 2.0 (the "License"); 012 * you may not use this file except in compliance with the License. 013 * You may obtain a copy of the License at 014 * 015 * http://www.apache.org/licenses/LICENSE-2.0 016 * 017 * Unless required by applicable law or agreed to in writing, software 018 * distributed under the License is distributed on an "AS IS" BASIS, 019 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 020 * See the License for the specific language governing permissions and 021 * limitations under the License. 022 * #L% 023 */ 024 025import java.util.ArrayList; 026import java.util.List; 027import java.util.Objects; 028 029import org.apache.commons.lang3.tuple.Pair; 030import org.hl7.fhir.instance.model.api.*; 031 032import ca.uhn.fhir.context.*; 033import ca.uhn.fhir.rest.api.RequestTypeEnum; 034 035/** 036 * Fetch resources from a bundle 037 */ 038public class BundleUtil { 039 040 /** 041 * @return Returns <code>null</code> if the link isn't found or has no value 042 */ 043 public static String getLinkUrlOfType(FhirContext theContext, IBaseBundle theBundle, String theLinkRelation) { 044 RuntimeResourceDefinition def = theContext.getResourceDefinition(theBundle); 045 BaseRuntimeChildDefinition entryChild = def.getChildByName("link"); 046 List<IBase> links = entryChild.getAccessor().getValues(theBundle); 047 for (IBase nextLink : links) { 048 049 boolean isRightRel = false; 050 BaseRuntimeElementCompositeDefinition relDef = (BaseRuntimeElementCompositeDefinition) theContext.getElementDefinition(nextLink.getClass()); 051 BaseRuntimeChildDefinition relChild = relDef.getChildByName("relation"); 052 List<IBase> relValues = relChild.getAccessor().getValues(nextLink); 053 for (IBase next : relValues) { 054 IPrimitiveType<?> nextValue = (IPrimitiveType<?>) next; 055 if (theLinkRelation.equals(nextValue.getValueAsString())) { 056 isRightRel = true; 057 } 058 } 059 060 if (!isRightRel) { 061 continue; 062 } 063 064 BaseRuntimeElementCompositeDefinition linkDef = (BaseRuntimeElementCompositeDefinition) theContext.getElementDefinition(nextLink.getClass()); 065 BaseRuntimeChildDefinition urlChild = linkDef.getChildByName("url"); 066 List<IBase> values = urlChild.getAccessor().getValues(nextLink); 067 for (IBase nextUrl : values) { 068 IPrimitiveType<?> nextValue = (IPrimitiveType<?>) nextUrl; 069 if (isNotBlank(nextValue.getValueAsString())) { 070 return nextValue.getValueAsString(); 071 } 072 } 073 074 } 075 076 return null; 077 } 078 079 @SuppressWarnings("unchecked") 080 public static List<Pair<String, IBaseResource>> getBundleEntryUrlsAndResources(FhirContext theContext, IBaseBundle theBundle) { 081 RuntimeResourceDefinition def = theContext.getResourceDefinition(theBundle); 082 BaseRuntimeChildDefinition entryChild = def.getChildByName("entry"); 083 List<IBase> entries = entryChild.getAccessor().getValues(theBundle); 084 085 BaseRuntimeElementCompositeDefinition<?> entryChildElem = (BaseRuntimeElementCompositeDefinition<?>) entryChild.getChildByName("entry"); 086 BaseRuntimeChildDefinition resourceChild = entryChildElem.getChildByName("resource"); 087 088 BaseRuntimeChildDefinition requestChild = entryChildElem.getChildByName("request"); 089 BaseRuntimeElementCompositeDefinition<?> requestDef = (BaseRuntimeElementCompositeDefinition<?>) requestChild.getChildByName("request"); 090 091 BaseRuntimeChildDefinition urlChild = requestDef.getChildByName("url"); 092 093 List<Pair<String, IBaseResource>> retVal = new ArrayList<>(entries.size()); 094 for (IBase nextEntry : entries) { 095 096 String url = null; 097 IBaseResource resource = null; 098 099 for (IBase nextEntryValue : requestChild.getAccessor().getValues(nextEntry)) { 100 for (IBase nextUrlValue : urlChild.getAccessor().getValues(nextEntryValue)) { 101 url = ((IPrimitiveType<String>) nextUrlValue).getValue(); 102 } 103 } 104 105 // Should return 0..1 only 106 for (IBase nextValue : resourceChild.getAccessor().getValues(nextEntry)) { 107 resource = (IBaseResource) nextValue; 108 } 109 110 retVal.add(Pair.of(url, resource)); 111 } 112 113 return retVal; 114 } 115 116 public static String getBundleType(FhirContext theContext, IBaseBundle theBundle) { 117 RuntimeResourceDefinition def = theContext.getResourceDefinition(theBundle); 118 BaseRuntimeChildDefinition entryChild = def.getChildByName("type"); 119 List<IBase> entries = entryChild.getAccessor().getValues(theBundle); 120 if (entries.size() > 0) { 121 IPrimitiveType<?> typeElement = (IPrimitiveType<?>) entries.get(0); 122 return typeElement.getValueAsString(); 123 } 124 return null; 125 } 126 127 public static Integer getTotal(FhirContext theContext, IBaseBundle theBundle) { 128 RuntimeResourceDefinition def = theContext.getResourceDefinition(theBundle); 129 BaseRuntimeChildDefinition entryChild = def.getChildByName("total"); 130 List<IBase> entries = entryChild.getAccessor().getValues(theBundle); 131 if (entries.size() > 0) { 132 IPrimitiveType<Number> typeElement = (IPrimitiveType<Number>) entries.get(0); 133 if (typeElement != null && typeElement.getValue() != null) { 134 return typeElement.getValue().intValue(); 135 } 136 } 137 return null; 138 } 139 140 /** 141 * Extract all of the resources from a given bundle 142 */ 143 public static List<BundleEntryParts> toListOfEntries(FhirContext theContext, IBaseBundle theBundle) { 144 List<BundleEntryParts> retVal = new ArrayList<>(); 145 146 RuntimeResourceDefinition def = theContext.getResourceDefinition(theBundle); 147 BaseRuntimeChildDefinition entryChild = def.getChildByName("entry"); 148 List<IBase> entries = entryChild.getAccessor().getValues(theBundle); 149 150 BaseRuntimeElementCompositeDefinition<?> entryChildElem = (BaseRuntimeElementCompositeDefinition<?>) entryChild.getChildByName("entry"); 151 152 BaseRuntimeChildDefinition resourceChild = entryChildElem.getChildByName("resource"); 153 BaseRuntimeChildDefinition requestChild = entryChildElem.getChildByName("request"); 154 BaseRuntimeElementCompositeDefinition<?> requestElem = (BaseRuntimeElementCompositeDefinition<?>) requestChild.getChildByName("request"); 155 BaseRuntimeChildDefinition urlChild = requestElem.getChildByName("url"); 156 BaseRuntimeChildDefinition methodChild = requestElem.getChildByName("method"); 157 158 for (IBase nextEntry : entries) { 159 IBaseResource resource = null; 160 String url = null; 161 RequestTypeEnum requestType = null; 162 163 for (IBase next : resourceChild.getAccessor().getValues(nextEntry)) { 164 resource = (IBaseResource) next; 165 } 166 for (IBase nextRequest : requestChild.getAccessor().getValues(nextEntry)) { 167 for (IBase nextUrl : urlChild.getAccessor().getValues(nextRequest)) { 168 url = ((IPrimitiveType<?>) nextUrl).getValueAsString(); 169 } 170 for (IBase nextUrl : methodChild.getAccessor().getValues(nextRequest)) { 171 String methodString = ((IPrimitiveType<?>) nextUrl).getValueAsString(); 172 if (isNotBlank(methodString)) { 173 requestType = RequestTypeEnum.valueOf(methodString); 174 } 175 } 176 } 177 178 /* 179 * All 3 might be null - That's ok because we still want to know the 180 * order in the original bundle. 181 */ 182 retVal.add(new BundleEntryParts(requestType, url, resource)); 183 } 184 185 186 return retVal; 187 } 188 189 /** 190 * Extract all of the resources from a given bundle 191 */ 192 public static List<IBaseResource> toListOfResources(FhirContext theContext, IBaseBundle theBundle) { 193 return toListOfResourcesOfType(theContext, theBundle, IBaseResource.class); 194 } 195 196 /** 197 * Extract all of the resources of a given type from a given bundle 198 */ 199 @SuppressWarnings("unchecked") 200 public static <T extends IBaseResource> List<T> toListOfResourcesOfType(FhirContext theContext, IBaseBundle theBundle, Class<T> theTypeToInclude) { 201 Objects.requireNonNull(theTypeToInclude, "ResourceType must not be null"); 202 List<T> retVal = new ArrayList<>(); 203 204 RuntimeResourceDefinition def = theContext.getResourceDefinition(theBundle); 205 BaseRuntimeChildDefinition entryChild = def.getChildByName("entry"); 206 List<IBase> entries = entryChild.getAccessor().getValues(theBundle); 207 208 BaseRuntimeElementCompositeDefinition<?> entryChildElem = (BaseRuntimeElementCompositeDefinition<?>) entryChild.getChildByName("entry"); 209 BaseRuntimeChildDefinition resourceChild = entryChildElem.getChildByName("resource"); 210 for (IBase nextEntry : entries) { 211 for (IBase next : resourceChild.getAccessor().getValues(nextEntry)) { 212 if (theTypeToInclude.isAssignableFrom(next.getClass())) { 213 retVal.add((T) next); 214 } 215 } 216 } 217 return retVal; 218 } 219 220 public static class BundleEntryParts { 221 private final RequestTypeEnum myRequestType; 222 private final IBaseResource myResource; 223 private final String myUrl; 224 225 BundleEntryParts(RequestTypeEnum theRequestType, String theUrl, IBaseResource theResource) { 226 super(); 227 myRequestType = theRequestType; 228 myUrl = theUrl; 229 myResource = theResource; 230 } 231 232 public RequestTypeEnum getRequestType() { 233 return myRequestType; 234 } 235 236 public IBaseResource getResource() { 237 return myResource; 238 } 239 240 public String getUrl() { 241 return myUrl; 242 } 243 } 244}