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}