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