1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.openehealth.ipf.commons.ihe.fhir;
18
19 import com.google.common.collect.DiscreteDomain;
20 import com.google.common.collect.Range;
21 import com.google.common.collect.RangeSet;
22 import com.google.common.collect.TreeRangeSet;
23 import org.hl7.fhir.instance.model.api.IBaseResource;
24 import org.slf4j.Logger;
25 import org.slf4j.LoggerFactory;
26
27 import java.util.ArrayList;
28 import java.util.List;
29 import java.util.Map;
30
31 import static org.openehealth.ipf.commons.ihe.fhir.Constants.FHIR_FROM_INDEX;
32 import static org.openehealth.ipf.commons.ihe.fhir.Constants.FHIR_REQUEST_SIZE_ONLY;
33 import static org.openehealth.ipf.commons.ihe.fhir.Constants.FHIR_TO_INDEX;
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51 public class LazyBundleProvider extends AbstractBundleProvider {
52
53 private static final Logger LOG = LoggerFactory.getLogger(LazyBundleProvider.class);
54
55 private final boolean cacheResults;
56 private int size = -1;
57 private transient List<IBaseResource> cachedResults = new ArrayList<>();
58 private transient ResultRanges resultRanges = new ResultRanges();
59
60
61
62
63
64
65
66
67
68 public LazyBundleProvider(RequestConsumer consumer, boolean cacheResults, Object payload, Map<String, Object> headers) {
69 super(consumer, payload, headers);
70 this.cacheResults = cacheResults;
71 }
72
73 @Override
74 public List<IBaseResource> getResources(int fromIndex, int toIndex) {
75 if (!cacheResults) {
76 return getPartialResult(fromIndex, toIndex);
77 }
78 LOG.debug("Cached results contain the following ranges: {}. Requesting resources from index {} to {}", resultRanges, fromIndex, toIndex);
79 Range<Integer> wanted = Range.closedOpen(fromIndex, toIndex);
80 RangeSet<Integer> needed = resultRanges.required(wanted);
81 LOG.debug("Requiring the following ranges {}", needed);
82 for (Range<Integer> requiredRange : needed.asDescendingSetOfRanges()) {
83 LOG.debug("Now requesting the following range {}", requiredRange);
84 List<IBaseResource> results = getPartialResult(requiredRange.lowerEndpoint(), requiredRange.upperEndpoint());
85 LOG.debug("Got back a list of size {}", results.size());
86 if (!results.isEmpty()) {
87 cacheAll(requiredRange.lowerEndpoint(), results);
88
89 resultRanges.add(Range.closedOpen(requiredRange.lowerEndpoint(), requiredRange.lowerEndpoint() + results.size()));
90 }
91 }
92 LOG.debug("Cached results now contain the following ranges: {}", resultRanges);
93
94
95 return cachedResults.subList(fromIndex, Math.min(cachedResults.size(), Math.min(cachedResults.size(), toIndex)));
96 }
97
98 private List<IBaseResource> getPartialResult(int fromIndex, int toIndex) {
99 Map<String, Object> headers = getHeaders();
100 headers.put(FHIR_FROM_INDEX, fromIndex);
101 headers.put(FHIR_TO_INDEX, toIndex);
102 return obtainResources(getPayload(), headers);
103 }
104
105 @Override
106 public Integer size() {
107 if (!cacheResults || size < 0) {
108 Map<String, Object> headers = getHeaders();
109 headers.put(FHIR_REQUEST_SIZE_ONLY, null);
110 size = getConsumer().handleSizeRequest(getPayload(), headers);
111 }
112 return size;
113 }
114
115 private void cacheAll(int fromIndex, List<IBaseResource> resources) {
116 if (cachedResults.size() <= fromIndex) {
117 for (int i = cachedResults.size(); i < fromIndex; i++) {
118 LOG.debug("Adding null for index {}", i);
119 cachedResults.add(null);
120 }
121 cachedResults.addAll(resources);
122 } else {
123 for (int i = 0; i < resources.size(); i++) {
124 cachedResults.set(fromIndex + i, resources.get(i));
125 }
126 }
127 }
128
129 private static class ResultRanges {
130
131 private RangeSet<Integer> rangeSet = TreeRangeSet.create();
132
133
134
135
136
137 public RangeSet<Integer> required(Range<Integer> wantedRange) {
138 RangeSet<Integer> intersection = rangeSet.subRangeSet(wantedRange);
139 return TreeRangeSet.create(intersection.complement().subRangeSet(wantedRange));
140 }
141
142 public void add(Range<Integer> wantedRange) {
143 rangeSet.add(wantedRange.canonical(DiscreteDomain.integers()));
144 }
145
146 @Override
147 public String toString() {
148 return rangeSet.toString();
149 }
150 }
151
152
153
154
155 }