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 - 2018 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;
027
028import org.apache.commons.lang3.tuple.Pair;
029import org.hl7.fhir.instance.model.api.*;
030
031import ca.uhn.fhir.context.*;
032import ca.uhn.fhir.rest.api.RequestTypeEnum;
033
034/**
035 * Fetch resources from a bundle
036 */
037public class BundleUtil {
038
039        /**
040         * @return Returns <code>null</code> if the link isn't found or has no value
041         */
042        public static String getLinkUrlOfType(FhirContext theContext, IBaseBundle theBundle, String theLinkRelation) {
043                RuntimeResourceDefinition def = theContext.getResourceDefinition(theBundle);
044                BaseRuntimeChildDefinition entryChild = def.getChildByName("link");
045                List<IBase> links = entryChild.getAccessor().getValues(theBundle);
046                for (IBase nextLink : links) {
047
048                        boolean isRightRel = false;
049                        BaseRuntimeElementCompositeDefinition relDef = (BaseRuntimeElementCompositeDefinition) theContext.getElementDefinition(nextLink.getClass());
050                        BaseRuntimeChildDefinition relChild = relDef.getChildByName("relation");
051                        List<IBase> relValues = relChild.getAccessor().getValues(nextLink);
052                        for (IBase next : relValues) {
053                                IPrimitiveType<?> nextValue = (IPrimitiveType<?>)next;
054                                if (theLinkRelation.equals(nextValue.getValueAsString())) {
055                                        isRightRel = true;
056                                }
057                        }
058
059                        if (!isRightRel) {
060                                continue;
061                        }
062
063                        BaseRuntimeElementCompositeDefinition linkDef = (BaseRuntimeElementCompositeDefinition) theContext.getElementDefinition(nextLink.getClass());
064                        BaseRuntimeChildDefinition urlChild = linkDef.getChildByName("url");
065                        List<IBase> values = urlChild.getAccessor().getValues(nextLink);
066                        for (IBase nextUrl : values) {
067                                IPrimitiveType<?> nextValue = (IPrimitiveType<?>)nextUrl;
068                                if (isNotBlank(nextValue.getValueAsString())) {
069                                        return nextValue.getValueAsString();
070                                }
071                        }
072
073                }
074
075                return null;
076        }
077
078        @SuppressWarnings("unchecked")
079        public static List<Pair<String, IBaseResource>> getBundleEntryUrlsAndResources(FhirContext theContext, IBaseBundle theBundle) {
080                RuntimeResourceDefinition def = theContext.getResourceDefinition(theBundle);
081                BaseRuntimeChildDefinition entryChild = def.getChildByName("entry");
082                List<IBase> entries = entryChild.getAccessor().getValues(theBundle);
083
084                BaseRuntimeElementCompositeDefinition<?> entryChildElem = (BaseRuntimeElementCompositeDefinition<?>) entryChild.getChildByName("entry");
085                BaseRuntimeChildDefinition resourceChild = entryChildElem.getChildByName("resource");
086                
087                BaseRuntimeChildDefinition requestChild = entryChildElem.getChildByName("request");
088                BaseRuntimeElementCompositeDefinition<?> requestDef = (BaseRuntimeElementCompositeDefinition<?>) requestChild.getChildByName("request");
089                
090                BaseRuntimeChildDefinition urlChild = requestDef.getChildByName("url");
091
092                List<Pair<String, IBaseResource>> retVal = new ArrayList<>(entries.size());
093                for (IBase nextEntry : entries) {
094                        
095                        String url = null;
096                        IBaseResource resource = null;
097                        
098                        for (IBase nextEntryValue : requestChild.getAccessor().getValues(nextEntry)) {
099                                for (IBase nextUrlValue : urlChild.getAccessor().getValues(nextEntryValue)) {
100                                        url = ((IPrimitiveType<String>)nextUrlValue).getValue();
101                                }
102                        }
103                        
104                        // Should return 0..1 only
105                        for (IBase nextValue : resourceChild.getAccessor().getValues(nextEntry)) {
106                                resource = (IBaseResource) nextValue;
107                        }
108                        
109                        retVal.add(Pair.of(url, resource));
110                }
111                
112                return retVal;          
113        }
114        
115        public static String getBundleType(FhirContext theContext, IBaseBundle theBundle) {
116                RuntimeResourceDefinition def = theContext.getResourceDefinition(theBundle);
117                BaseRuntimeChildDefinition entryChild = def.getChildByName("type");
118                List<IBase> entries = entryChild.getAccessor().getValues(theBundle);
119                if (entries.size() > 0) {
120                        IPrimitiveType<?> typeElement = (IPrimitiveType<?>) entries.get(0);
121                        return typeElement.getValueAsString();
122                }
123                return null;
124        }
125
126        public static Integer getTotal(FhirContext theContext, IBaseBundle theBundle) {
127                RuntimeResourceDefinition def = theContext.getResourceDefinition(theBundle);
128                BaseRuntimeChildDefinition entryChild = def.getChildByName("total");
129                List<IBase> entries = entryChild.getAccessor().getValues(theBundle);
130                if (entries.size() > 0) {
131                        IPrimitiveType<Number> typeElement = (IPrimitiveType<Number>) entries.get(0);
132                        if (typeElement != null && typeElement.getValue() != null) {
133                                return typeElement.getValue().intValue();
134                        }
135                }
136                return null;
137        }
138
139        /**
140         * Extract all of the resources from a given bundle
141         */
142        public static List<BundleEntryParts> toListOfEntries(FhirContext theContext, IBaseBundle theBundle) {
143                List<BundleEntryParts> retVal = new ArrayList<>();
144
145                RuntimeResourceDefinition def = theContext.getResourceDefinition(theBundle);
146                BaseRuntimeChildDefinition entryChild = def.getChildByName("entry");
147                List<IBase> entries = entryChild.getAccessor().getValues(theBundle);
148
149                BaseRuntimeElementCompositeDefinition<?> entryChildElem = (BaseRuntimeElementCompositeDefinition<?>) entryChild.getChildByName("entry");
150                
151                BaseRuntimeChildDefinition resourceChild = entryChildElem.getChildByName("resource");
152                BaseRuntimeChildDefinition requestChild = entryChildElem.getChildByName("request");
153                BaseRuntimeElementCompositeDefinition<?>  requestElem = (BaseRuntimeElementCompositeDefinition<?>) requestChild.getChildByName("request");
154                BaseRuntimeChildDefinition urlChild = requestElem.getChildByName("url");
155                BaseRuntimeChildDefinition methodChild = requestElem.getChildByName("method");
156                
157                for (IBase nextEntry : entries) {
158                        IBaseResource resource = null;
159                        String url = null;
160                        RequestTypeEnum requestType = null;
161
162                        for (IBase next : resourceChild.getAccessor().getValues(nextEntry)) {
163                                resource = (IBaseResource) next;
164                        }
165                        for (IBase nextRequest : requestChild.getAccessor().getValues(nextEntry)) {
166                                for (IBase nextUrl : urlChild.getAccessor().getValues(nextRequest)) {
167                                        url = ((IPrimitiveType<?>)nextUrl).getValueAsString();
168                                }
169                                for (IBase nextUrl : methodChild.getAccessor().getValues(nextRequest)) {
170                                        String methodString = ((IPrimitiveType<?>)nextUrl).getValueAsString();
171                                        if (isNotBlank(methodString)) {
172                                                requestType = RequestTypeEnum.valueOf(methodString);
173                                        }
174                                }
175                        }
176
177                        /* 
178                         * All 3 might be null - That's ok because we still want to know the
179                         * order in the original bundle.
180                         */
181                        retVal.add(new BundleEntryParts(requestType, url, resource));
182                }
183
184                
185                return retVal;
186        }
187        
188        /**
189         * Extract all of the resources from a given bundle
190         */
191        public static List<IBaseResource> toListOfResources(FhirContext theContext, IBaseBundle theBundle) {
192                return toListOfResourcesOfType(theContext, theBundle, null);
193        }
194
195        /**
196         * Extract all of the resources of a given type from a given bundle 
197         */
198        @SuppressWarnings("unchecked")
199        public static <T extends IBaseResource> List<T> toListOfResourcesOfType(FhirContext theContext, IBaseBundle theBundle, Class<T> theTypeToInclude) {
200                List<T> retVal = new ArrayList<>();
201
202                RuntimeResourceDefinition def = theContext.getResourceDefinition(theBundle);
203                BaseRuntimeChildDefinition entryChild = def.getChildByName("entry");
204                List<IBase> entries = entryChild.getAccessor().getValues(theBundle);
205
206                BaseRuntimeElementCompositeDefinition<?> entryChildElem = (BaseRuntimeElementCompositeDefinition<?>) entryChild.getChildByName("entry");
207                BaseRuntimeChildDefinition resourceChild = entryChildElem.getChildByName("resource");
208                for (IBase nextEntry : entries) {
209                        for (IBase next : resourceChild.getAccessor().getValues(nextEntry)) {
210                                if (theTypeToInclude != null && !theTypeToInclude.isAssignableFrom(next.getClass())) {
211                                        continue;
212                                }
213                                retVal.add((T) next);
214                        }
215                }
216
217                return retVal;
218        }
219
220        public static class BundleEntryParts
221        {
222                private final RequestTypeEnum myRequestType;
223                private final IBaseResource myResource;
224                private final String myUrl;
225                BundleEntryParts(RequestTypeEnum theRequestType, String theUrl, IBaseResource theResource) {
226                        super();
227                        myRequestType = theRequestType;
228                        myUrl = theUrl;
229                        myResource = theResource;
230                }
231                public RequestTypeEnum getRequestType() {
232                        return myRequestType;
233                }
234                public IBaseResource getResource() {
235                        return myResource;
236                }
237                public String getUrl() {
238                        return myUrl;
239                }
240        }
241
242}