View Javadoc
1   package ca.uhn.fhir.model.api;
2   
3   /*
4    * #%L
5    * HAPI FHIR - Core Library
6    * %%
7    * Copyright (C) 2014 University Health Network
8    * %%
9    * Licensed under the Apache License, Version 2.0 (the "License");
10   * you may not use this file except in compliance with the License.
11   * You may obtain a copy of the License at
12   * 
13   *      http://www.apache.org/licenses/LICENSE-2.0
14   * 
15   * Unless required by applicable law or agreed to in writing, software
16   * distributed under the License is distributed on an "AS IS" BASIS,
17   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18   * See the License for the specific language governing permissions and
19   * limitations under the License.
20   * #L%
21   */
22  
23  import static org.apache.commons.lang3.StringUtils.*;
24  
25  import java.util.ArrayList;
26  import java.util.HashMap;
27  import java.util.List;
28  import java.util.Map;
29  
30  import org.apache.commons.lang3.StringUtils;
31  import org.apache.commons.lang3.Validate;
32  import org.apache.commons.lang3.builder.ToStringBuilder;
33  import org.apache.commons.lang3.builder.ToStringStyle;
34  
35  import ca.uhn.fhir.context.FhirContext;
36  import ca.uhn.fhir.context.RuntimeResourceDefinition;
37  import ca.uhn.fhir.model.primitive.IdDt;
38  import ca.uhn.fhir.model.primitive.InstantDt;
39  import ca.uhn.fhir.model.primitive.IntegerDt;
40  import ca.uhn.fhir.model.primitive.StringDt;
41  import ca.uhn.fhir.rest.server.Constants;
42  import ca.uhn.fhir.util.UrlUtil;
43  
44  public class Bundle extends BaseBundle /* implements IElement */{
45  
46  	private StringDt myBundleId;
47  
48  	private TagList myCategories;
49  	private List<BundleEntry> myEntries;
50  	private volatile transient Map<IdDt, IResource> myIdToEntries;
51  	private StringDt myLinkBase;
52  	private StringDt myLinkFirst;
53  	private StringDt myLinkLast;
54  	private StringDt myLinkNext;
55  	private StringDt myLinkPrevious;
56  	private StringDt myLinkSelf;
57  	private InstantDt myPublished;
58  	private StringDt myTitle;
59  	private IntegerDt myTotalResults;
60  	private InstantDt myUpdated;
61  
62  	/**
63  	 * @deprecated Tags wil become immutable in a future release of HAPI, so {@link #addCategory(String, String, String)} should be used instead
64  	 */
65  	public Tag addCategory() {
66  		Tag retVal = new Tag();
67  		getCategories().add(retVal);
68  		return retVal;
69  	}
70  
71  	public void addCategory(String theScheme, String theTerm, String theLabel) {
72  		getCategories().add(new Tag(theScheme, theTerm, theLabel));
73  	}
74  
75  	
76  	public void addCategory(Tag theTag) {
77  		getCategories().add(theTag);
78  	}
79  
80  	/**
81  	 * Adds and returns a new bundle entry
82  	 */
83  	public BundleEntry addEntry() {
84  		BundleEntry retVal = new BundleEntry();
85  		getEntries().add(retVal);
86  		return retVal;
87  	}
88  
89  	/**
90  	 * Adds a new entry
91  	 * 
92  	 * @param theBundleEntry
93  	 *            The entry to add
94  	 */
95  	public void addEntry(BundleEntry theBundleEntry) {
96  		Validate.notNull(theBundleEntry, "theBundleEntry can not be null");
97  		getEntries().add(theBundleEntry);
98  	}
99  
100 	/**
101 	 * Creates a new entry using the given resource and populates it accordingly
102 	 * 
103 	 * @param theResource
104 	 *            The resource to add
105 	 * @return Returns the newly created bundle entry that was added to the bundle
106 	 */
107 	public BundleEntry addResource(IResource theResource, FhirContext theContext, String theServerBase) {
108 		BundleEntry entry = addEntry();
109 		entry.setResource(theResource);
110 
111 		RuntimeResourceDefinition def = theContext.getResourceDefinition(theResource);
112 
113 		String title = ResourceMetadataKeyEnum.TITLE.get(theResource);
114 		if (title != null) {
115 			entry.getTitle().setValue(title);
116 		} else {
117 			entry.getTitle().setValue(def.getName() + " " + StringUtils.defaultString(theResource.getId().getValue(), "(no ID)"));
118 		}
119 
120 		if (theResource.getId() != null && StringUtils.isNotBlank(theResource.getId().getValue())) {
121 
122 			StringBuilder b = new StringBuilder();
123 			b.append(theServerBase);
124 			if (b.length() > 0 && b.charAt(b.length() - 1) != '/') {
125 				b.append('/');
126 			}
127 			b.append(def.getName());
128 			b.append('/');
129 			String resId = theResource.getId().getIdPart();
130 			b.append(resId);
131 
132 			entry.getId().setValue(b.toString());
133 
134 			if (isNotBlank(theResource.getId().getVersionIdPart())) {
135 				b.append('/');
136 				b.append(Constants.PARAM_HISTORY);
137 				b.append('/');
138 				b.append(theResource.getId().getVersionIdPart());
139 			} else {
140 				IdDt versionId = (IdDt) ResourceMetadataKeyEnum.VERSION_ID.get(theResource);
141 				if (versionId != null) {
142 					b.append('/');
143 					b.append(Constants.PARAM_HISTORY);
144 					b.append('/');
145 					b.append(versionId.getValue());
146 				}
147 			}
148 
149 			String qualifiedId = b.toString();
150 			entry.getLinkSelf().setValue(qualifiedId);
151 
152 			// String resourceType = theContext.getResourceDefinition(theResource).getName();
153 
154 			String linkSearch = ResourceMetadataKeyEnum.LINK_SEARCH.get(theResource);
155 			if (isNotBlank(linkSearch)) {
156 				if (!UrlUtil.isAbsolute(linkSearch)) {
157 					linkSearch = (theServerBase + "/" + linkSearch);
158 				}
159 				entry.getLinkSearch().setValue(linkSearch);
160 			}
161 
162 			String linkAlternate = ResourceMetadataKeyEnum.LINK_ALTERNATE.get(theResource);
163 			if (isNotBlank(linkAlternate)) {
164 				if (!UrlUtil.isAbsolute(linkAlternate)) {
165 					linkSearch = (theServerBase + "/" + linkAlternate);
166 				}
167 				entry.getLinkAlternate().setValue(linkSearch);
168 			}
169 
170 		}
171 
172 		InstantDt published = ResourceMetadataKeyEnum.PUBLISHED.get(theResource);
173 		if (published == null) {
174 			entry.getPublished().setToCurrentTimeInLocalTimeZone();
175 		} else {
176 			entry.setPublished(published);
177 		}
178 
179 		InstantDt updated = ResourceMetadataKeyEnum.UPDATED.get(theResource);
180 		if (updated != null) {
181 			entry.setUpdated(updated);
182 		}
183 
184 		InstantDt deleted = ResourceMetadataKeyEnum.DELETED_AT.get(theResource);
185 		if (deleted != null) {
186 			entry.setDeleted(deleted);
187 		}
188 
189 		IdDt previous = ResourceMetadataKeyEnum.PREVIOUS_ID.get(theResource);
190 		if (previous != null) {
191 			entry.getLinkAlternate().setValue(previous.withServerBase(theServerBase, def.getName()));
192 		}
193 
194 		TagList tagList = ResourceMetadataKeyEnum.TAG_LIST.get(theResource);
195 		if (tagList != null) {
196 			for (Tag nextTag : tagList) {
197 				entry.addCategory(nextTag);
198 			}
199 		}
200 
201 		return entry;
202 	}
203 
204 	// public static void main(String[] args) {
205 	//
206 	// FhirContext ctx = new FhirContext();
207 	// String txt = "<Organization xmlns=\"http://hl7.org/fhir\">\n" +
208 	// "   <extension url=\"http://fhir.connectinggta.ca/Profile/organization#providerIdPool\">\n" +
209 	// "      <valueUri value=\"urn:oid:2.16.840.1.113883.3.239.23.21.1\"/>\n" +
210 	// "   </extension>\n" +
211 	// "   <text>\n" +
212 	// "      <status value=\"generated\"/>\n" +
213 	// "      <div xmlns=\"http://www.w3.org/1999/xhtml\"/>\n" +
214 	// "   </text>\n" +
215 	// "   <identifier>\n" +
216 	// "      <use value=\"official\"/>\n" +
217 	// "      <label value=\"HSP 2.16.840.1.113883.3.239.23.21\"/>\n" +
218 	// "      <system value=\"urn:cgta:hsp_ids\"/>\n" +
219 	// "      <value value=\"urn:oid:2.16.840.1.113883.3.239.23.21\"/>\n" +
220 	// "   </identifier>\n" +
221 	// "   <name value=\"火星第五人民医院\"/>\n" +
222 	// "</Organization>";
223 	//
224 	// IGenericClient c = ctx.newRestfulGenericClient("http://fhirtest.uhn.ca/base");
225 	// c.registerInterceptor(new LoggingInterceptor(true));
226 	// c.update().resource(txt).withId("1665").execute();
227 	// }
228 	//
229 
230 	public StringDt getBundleId() {
231 		if (myBundleId == null) {
232 			myBundleId = new StringDt();
233 		}
234 		return myBundleId;
235 	}
236 
237 	public TagList getCategories() {
238 		if (myCategories == null) {
239 			myCategories = new TagList();
240 		}
241 		return myCategories;
242 	}
243 
244 	public List<BundleEntry> getEntries() {
245 		if (myEntries == null) {
246 			myEntries = new ArrayList<BundleEntry>();
247 		}
248 		return myEntries;
249 	}
250 
251 	public StringDt getLinkBase() {
252 		if (myLinkBase == null) {
253 			myLinkBase = new StringDt();
254 		}
255 		return myLinkBase;
256 	}
257 
258 	public StringDt getLinkFirst() {
259 		if (myLinkFirst == null) {
260 			myLinkFirst = new StringDt();
261 		}
262 		return myLinkFirst;
263 	}
264 
265 	public StringDt getLinkLast() {
266 		if (myLinkLast == null) {
267 			myLinkLast = new StringDt();
268 		}
269 		return myLinkLast;
270 	}
271 
272 	public StringDt getLinkNext() {
273 		if (myLinkNext == null) {
274 			myLinkNext = new StringDt();
275 		}
276 		return myLinkNext;
277 	}
278 
279 	public StringDt getLinkPrevious() {
280 		if (myLinkPrevious == null) {
281 			myLinkPrevious = new StringDt();
282 		}
283 		return myLinkPrevious;
284 	}
285 
286 	public StringDt getLinkSelf() {
287 		if (myLinkSelf == null) {
288 			myLinkSelf = new StringDt();
289 		}
290 		return myLinkSelf;
291 	}
292 
293 	public InstantDt getPublished() {
294 		if (myPublished == null) {
295 			myPublished = new InstantDt();
296 		}
297 		return myPublished;
298 	}
299 
300 	/**
301 	 * Retrieves a resource from a bundle given its logical ID.
302 	 * <p>
303 	 * <b>Important usage notes</b>: This method ignores base URLs (so passing in an ID of <code>http://foo/Patient/123</code> will return a resource if it has the logical ID of
304 	 * <code>http://bar/Patient/123</code>. Also, this method is intended to be used for bundles which have already been populated. It will cache its results for fast performance, but that means that
305 	 * modifications to the bundle after this method is called may not be accurately reflected.
306 	 * </p>
307 	 * 
308 	 * @param theId
309 	 *            The resource ID
310 	 * @return Returns the resource with the given ID, or <code>null</code> if none is found
311 	 */
312 	public IResource getResourceById(IdDt theId) {
313 		Map<IdDt, IResource> map = myIdToEntries;
314 		if (map == null) {
315 			map = new HashMap<IdDt, IResource>();
316 			for (BundleEntry next : this.getEntries()) {
317 				if (next.getId().isEmpty() == false) {
318 					map.put(next.getId().toUnqualified(), next.getResource());
319 				}
320 			}
321 			myIdToEntries = map;
322 		}
323 		return map.get(theId.toUnqualified());
324 	}
325 
326 	/**
327 	 * Returns a list containing all resources of the given type from this bundle
328 	 */
329 	public <T extends IResource> List<T> getResources(Class<T> theClass) {
330 		ArrayList<T> retVal = new ArrayList<T>();
331 		for (BundleEntry next : getEntries()) {
332 			if (next.getResource() != null && theClass.isAssignableFrom(next.getResource().getClass())) {
333 				@SuppressWarnings("unchecked")
334 				T resource = (T) next.getResource();
335 				retVal.add(resource);
336 			}
337 		}
338 		return retVal;
339 	}
340 
341 	public StringDt getTitle() {
342 		if (myTitle == null) {
343 			myTitle = new StringDt();
344 		}
345 		return myTitle;
346 	}
347 
348 	public IntegerDt getTotalResults() {
349 		if (myTotalResults == null) {
350 			myTotalResults = new IntegerDt();
351 		}
352 		return myTotalResults;
353 	}
354 
355 	public InstantDt getUpdated() {
356 		if (myUpdated == null) {
357 			myUpdated = new InstantDt();
358 		}
359 		return myUpdated;
360 	}
361 
362 	/**
363 	 * Returns true if this bundle contains zero entries
364 	 */
365 	@Override
366 	public boolean isEmpty() {
367 		return getEntries().isEmpty();
368 	}
369 
370 	public void setCategories(TagList theCategories) {
371 		myCategories = theCategories;
372 	}
373 
374 	public void setPublished(InstantDt thePublished) {
375 		myPublished = thePublished;
376 	}
377 
378 	/**
379 	 * Returns the number of entries in this bundle
380 	 */
381 	public int size() {
382 		return getEntries().size();
383 	}
384 
385 	public List<IResource> toListOfResources() {
386 		ArrayList<IResource> retVal = new ArrayList<IResource>();
387 		for (BundleEntry next : getEntries()) {
388 			if (next.getResource() != null) {
389 				retVal.add(next.getResource());
390 			}
391 		}
392 		return retVal;
393 	}
394 
395 	@Override
396 	public String toString() {
397 		ToStringBuilder b = new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE);
398 		b.append(getEntries().size() + " entries");
399 		b.append("id", getId());
400 		return b.toString();
401 	}
402 
403 	public static Bundle withResources(List<IResource> theResources, FhirContext theContext, String theServerBase) {
404 		Bundle retVal = new Bundle();
405 		for (IResource next : theResources) {
406 			retVal.addResource(next, theContext, theServerBase);
407 		}
408 		return retVal;
409 	}
410 
411 	public static Bundle withSingleResource(IResource theResource) {
412 		Bundle retVal = new Bundle();
413 		retVal.addEntry().setResource(theResource);
414 		return retVal;
415 	}
416 
417 }