001/*
002 * #%L
003 * HAPI FHIR - Server Framework
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.rest.server;
021
022import ca.uhn.fhir.model.primitive.InstantDt;
023import ca.uhn.fhir.rest.api.server.IBundleProvider;
024import ca.uhn.fhir.rest.server.method.ResponsePage;
025import ca.uhn.fhir.util.BundleUtil;
026import jakarta.annotation.Nonnull;
027import org.apache.commons.lang3.builder.ToStringBuilder;
028import org.hl7.fhir.instance.model.api.IBaseResource;
029import org.hl7.fhir.instance.model.api.IPrimitiveType;
030
031import java.util.Arrays;
032import java.util.Collections;
033import java.util.Date;
034import java.util.List;
035import java.util.stream.Collectors;
036
037public class SimpleBundleProvider implements IBundleProvider {
038
039        private final List<? extends IBaseResource> myList;
040        private final String myUuid;
041        private Integer myPreferredPageSize;
042        private Integer mySize;
043        private IPrimitiveType<Date> myPublished = InstantDt.withCurrentTime();
044        private Integer myCurrentPageOffset;
045        private Integer myCurrentPageSize;
046        private boolean myHasAllResources;
047        private ResponsePage.ResponsePageBuilder myPageBuilder;
048
049        /**
050         * The actual number of resources we have tried to fetch.
051         * This value will only be populated if there is a
052         * _count query parameter provided.
053         * In which case, it will be the total number of resources
054         * we tried to fetch (should be _count + 1 for accurate paging)
055         */
056        private int myTotalResourcesRequestedReturned = -1;
057
058        /**
059         * Constructor
060         */
061        public SimpleBundleProvider(IBaseResource theResource) {
062                this(Collections.singletonList(theResource));
063        }
064
065        /**
066         * Create an empty bundle
067         */
068        public SimpleBundleProvider() {
069                this(Collections.emptyList());
070        }
071
072        /**
073         * Constructor
074         */
075        public SimpleBundleProvider(List<? extends IBaseResource> theList) {
076                this(theList, null);
077        }
078
079        /**
080         * Constructor
081         *
082         * @since 6.8.0
083         */
084        public SimpleBundleProvider(IBaseResource... theList) {
085                this(Arrays.asList(theList), null);
086        }
087
088        public SimpleBundleProvider(List<? extends IBaseResource> theList, String theUuid) {
089                myList = theList;
090                myUuid = theUuid;
091                int size = 0;
092                for (IBaseResource r : theList) {
093                        if (r != null && BundleUtil.isMatchResource(r)) {
094                                size++;
095                        }
096                }
097                myHasAllResources = true;
098                setSize(size);
099        }
100
101        /**
102         * Constructor that provides only a size but no actual data (useful for _count = 0)
103         */
104        public SimpleBundleProvider(int theSize) {
105                myList = Collections.emptyList();
106                myUuid = null;
107                setSize(theSize);
108        }
109
110        /**
111         * @since 5.5.0
112         */
113        @Override
114        public Integer getCurrentPageOffset() {
115                return myCurrentPageOffset;
116        }
117
118        /**
119         * @since 5.5.0
120         */
121        public void setCurrentPageOffset(Integer theCurrentPageOffset) {
122                myCurrentPageOffset = theCurrentPageOffset;
123        }
124
125        /**
126         * @since 5.5.0
127         */
128        @Override
129        public Integer getCurrentPageSize() {
130                return myCurrentPageSize;
131        }
132
133        /**
134         * @since 5.5.0
135         */
136        public void setCurrentPageSize(Integer theCurrentPageSize) {
137                myCurrentPageSize = theCurrentPageSize;
138        }
139
140        /**
141         * Returns the results stored in this provider
142         */
143        protected List<? extends IBaseResource> getList() {
144                return myList;
145        }
146
147        @Override
148        public IPrimitiveType<Date> getPublished() {
149                return myPublished;
150        }
151
152        /**
153         * By default this class uses the object creation date/time (for this object)
154         * to determine {@link #getPublished() the published date} but this
155         * method may be used to specify an alternate date/time
156         */
157        public void setPublished(IPrimitiveType<Date> thePublished) {
158                myPublished = thePublished;
159        }
160
161        @SuppressWarnings("unchecked")
162        @Nonnull
163        @Override
164        public List<IBaseResource> getResources(
165                        int theFromIndex, int theToIndex, @Nonnull ResponsePage.ResponsePageBuilder theResponsePageBuilder) {
166                theResponsePageBuilder.setTotalRequestedResourcesFetched(myTotalResourcesRequestedReturned);
167                return (List<IBaseResource>)
168                                myList.subList(Math.min(theFromIndex, myList.size()), Math.min(theToIndex, myList.size()));
169        }
170
171        @Override
172        public String getUuid() {
173                return myUuid;
174        }
175
176        public void setTotalResourcesRequestedReturned(int theAmount) {
177                myTotalResourcesRequestedReturned = theAmount;
178        }
179
180        /**
181         * Defaults to null
182         */
183        @Override
184        public Integer preferredPageSize() {
185                return myPreferredPageSize;
186        }
187
188        /**
189         * Sets the preferred page size to be returned by {@link #preferredPageSize()}.
190         * Default is <code>null</code>.
191         */
192        public void setPreferredPageSize(Integer thePreferredPageSize) {
193                myPreferredPageSize = thePreferredPageSize;
194        }
195
196        /**
197         * Sets the total number of results, if this provider
198         * corresponds to a single page within a larger search result
199         */
200        public SimpleBundleProvider setSize(Integer theSize) {
201                mySize = theSize;
202                return this;
203        }
204
205        @Override
206        public Integer size() {
207                return mySize;
208        }
209
210        @Override
211        public boolean containsAllResources() {
212                return myHasAllResources;
213        }
214
215        @Override
216        public List<IBaseResource> getResourceListComplete() {
217                return myList.stream().map(r -> (IBaseResource) r).collect(Collectors.toList());
218        }
219
220        @Override
221        public String toString() {
222                return new ToStringBuilder(this).append("mySize", mySize).toString();
223        }
224}