View Javadoc
1   package ca.uhn.fhir.model.api;
2   
3   /*
4    * #%L
5    * HAPI FHIR - Core Library
6    * %%
7    * Copyright (C) 2014 - 2018 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 ca.uhn.fhir.model.base.composite.BaseCodingDt;
24  import ca.uhn.fhir.model.primitive.DecimalDt;
25  import ca.uhn.fhir.model.primitive.IdDt;
26  import ca.uhn.fhir.model.primitive.InstantDt;
27  import ca.uhn.fhir.model.valueset.BundleEntrySearchModeEnum;
28  import ca.uhn.fhir.model.valueset.BundleEntryTransactionMethodEnum;
29  import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
30  import org.apache.commons.lang3.StringUtils;
31  import org.hl7.fhir.instance.model.api.IAnyResource;
32  import org.hl7.fhir.instance.model.api.IPrimitiveType;
33  
34  import java.io.Serializable;
35  import java.util.*;
36  
37  import static org.apache.commons.lang3.StringUtils.isNotBlank;
38  
39  /**
40   * Keys in this map refer to <b>resource metadata keys</b>, which are keys used to access information about specific resource instances that live outside of the resource body. Typically, these are
41   * data elements which are sent/receieved in HTTP Headers along with read/create resource requests, or properties which can be found in bundle entries.
42   * <p>
43   * To access or set resource metadata values, every resource has a metadata map, and this class provides convenient getters/setters for interacting with that map. For example, to get a resource's
44   * {@link #UPDATED} value, which is the "last updated" time for that resource, use the following code:
45   * </p>
46   * <p>
47   * <code>InstantDt updated = ResourceMetadataKeyEnum.UPDATED.get(resource);</code>
48   * <p>
49   * <p>
50   * To set this value, use the following:
51   * </p>
52   * <p>
53   * <code>InstantDt update = new InstantDt("2011-01-02T11:22:33.0000Z"); // populate with the actual time<br>
54   * ResourceMetadataKeyEnum.UPDATED.put(resource, update);</code>
55   * </p>
56   * <p>
57   * Note that this class is not a Java Enum, and can therefore be extended (this is why it is not actually an Enum). Users of HAPI-FHIR are able to create their own classes extending this class to
58   * define their own keys for storage in resource metadata if needed.
59   * </p>
60   */
61  public abstract class ResourceMetadataKeyEnum<T> implements Serializable {
62  
63  	/**
64  	 * If present and populated with a date/time (as an instance of {@link InstantDt}), this value is an indication that the resource is in the deleted state. This key is only used in a limited number
65  	 * of scenarios, such as POSTing transaction bundles to a server, or returning resource history.
66  	 * <p>
67  	 * Values for this key are of type <b>{@link InstantDt}</b>
68  	 * </p>
69  	 */
70  	public static final ResourceMetadataKeySupportingAnyResource<InstantDt, IPrimitiveType<Date>> DELETED_AT = new ResourceMetadataKeySupportingAnyResource<InstantDt, IPrimitiveType<Date>>("DELETED_AT") {
71  		private static final long serialVersionUID = 1L;
72  
73  		@Override
74  		public InstantDt get(IResource theResource) {
75  			return getInstantFromMetadataOrNullIfNone(theResource.getResourceMetadata(), DELETED_AT);
76  		}
77  
78  		@SuppressWarnings("unchecked")
79  		@Override
80  		public IPrimitiveType<Date> get(IAnyResource theResource) {
81  			return (IPrimitiveType<Date>) theResource.getUserData(DELETED_AT.name());
82  		}
83  
84  		@Override
85  		public void put(IResource theResource, InstantDt theObject) {
86  			theResource.getResourceMetadata().put(DELETED_AT, theObject);
87  		}
88  
89  		@Override
90  		public void put(IAnyResource theResource, IPrimitiveType<Date> theObject) {
91  			theResource.setUserData(DELETED_AT.name(), theObject);
92  		}
93  	};
94  	/**
95  	 * Denotes the search score which a given resource should match in a transaction. See the FHIR transaction definition for information about this. Corresponds to the value in
96  	 * <code>Bundle.entry.score</code> in a Bundle resource.
97  	 * <p>
98  	 * Note that search URL is only used in FHIR DSTU2 and later.
99  	 * </p>
100 	 * <p>
101 	 * Values for this key are of type <b>{@link DecimalDt}</b>
102 	 * </p>
103 	 */
104 	public static final ResourceMetadataKeyEnum<DecimalDt> ENTRY_SCORE = new ResourceMetadataKeyEnum<DecimalDt>("ENTRY_SCORE") {
105 		private static final long serialVersionUID = 1L;
106 
107 		@Override
108 		public DecimalDt get(IResource theResource) {
109 			return getDecimalFromMetadataOrNullIfNone(theResource.getResourceMetadata(), ENTRY_SCORE);
110 		}
111 
112 		@Override
113 		public void put(IResource theResource, DecimalDt theObject) {
114 			theResource.getResourceMetadata().put(ENTRY_SCORE, theObject);
115 		}
116 	};
117 	/**
118 	 * If present and populated with a {@link BundleEntrySearchModeEnum}, contains the "bundle entry search mode", which is the value of the status field in the Bundle entry containing this resource.
119 	 * The value for this key corresponds to field <code>Bundle.entry.search.mode</code>. This value can be set to provide a status value of "include" for included resources being returned by a
120 	 * server, or to "match" to indicate that the resource was returned because it matched the given search criteria.
121 	 * <p>
122 	 * Note that status is only used in FHIR DSTU2 and later.
123 	 * </p>
124 	 * <p>
125 	 * Values for this key are of type <b>{@link BundleEntrySearchModeEnum}</b>
126 	 * </p>
127 	 */
128 	public static final ResourceMetadataKeySupportingAnyResource<BundleEntrySearchModeEnum, String> ENTRY_SEARCH_MODE = new ResourceMetadataKeySupportingAnyResource<BundleEntrySearchModeEnum, String>("ENTRY_SEARCH_MODE") {
129 		private static final long serialVersionUID = 1L;
130 
131 		@Override
132 		public BundleEntrySearchModeEnum get(IResource theResource) {
133 			return getEnumFromMetadataOrNullIfNone(theResource.getResourceMetadata(), ENTRY_SEARCH_MODE, BundleEntrySearchModeEnum.class, BundleEntrySearchModeEnum.VALUESET_BINDER);
134 		}
135 
136 		@Override
137 		public String get(IAnyResource theResource) {
138 			return (String) theResource.getUserData(ENTRY_SEARCH_MODE.name());
139 		}
140 
141 		@Override
142 		public void put(IResource theResource, BundleEntrySearchModeEnum theObject) {
143 			theResource.getResourceMetadata().put(ENTRY_SEARCH_MODE, theObject);
144 		}
145 
146 		@Override
147 		public void put(IAnyResource theResource, String theObject) {
148 			theResource.setUserData(ENTRY_SEARCH_MODE.name(), theObject);
149 		}
150 	};
151 	/**
152 	 * If present and populated with a {@link BundleEntryTransactionMethodEnum}, contains the "bundle entry transaction operation", which is the value of the status field in the Bundle entry
153 	 * containing this resource. The value for this key corresponds to field <code>Bundle.entry.transaction.operation</code>. This value can be set in resources being transmitted to a server to
154 	 * provide a status value of "create" or "update" to indicate behaviour the server should observe. It may also be set to similar values (or to "noop") in resources being returned by a server as a
155 	 * result of a transaction to indicate to the client what operation was actually performed.
156 	 * <p>
157 	 * Note that status is only used in FHIR DSTU2 and later.
158 	 * </p>
159 	 * <p>
160 	 * Values for this key are of type <b>{@link BundleEntryTransactionMethodEnum}</b>
161 	 * </p>
162 	 */
163 	public static final ResourceMetadataKeySupportingAnyResource<BundleEntryTransactionMethodEnum, String> ENTRY_TRANSACTION_METHOD = new ResourceMetadataKeySupportingAnyResource<BundleEntryTransactionMethodEnum, String>(
164 		"ENTRY_TRANSACTION_OPERATION") {
165 		private static final long serialVersionUID = 1L;
166 
167 		@Override
168 		public BundleEntryTransactionMethodEnum get(IResource theResource) {
169 			return getEnumFromMetadataOrNullIfNone(theResource.getResourceMetadata(), ENTRY_TRANSACTION_METHOD, BundleEntryTransactionMethodEnum.class,
170 				BundleEntryTransactionMethodEnum.VALUESET_BINDER);
171 		}
172 
173 		@Override
174 		public String get(IAnyResource theResource) {
175 			return (String) theResource.getUserData(ENTRY_TRANSACTION_METHOD.name());
176 		}
177 
178 		@Override
179 		public void put(IResource theResource, BundleEntryTransactionMethodEnum theObject) {
180 			theResource.getResourceMetadata().put(ENTRY_TRANSACTION_METHOD, theObject);
181 		}
182 
183 		@Override
184 		public void put(IAnyResource theResource, String theObject) {
185 			theResource.setUserData(ENTRY_TRANSACTION_METHOD.name(), theObject);
186 		}
187 
188 	};
189 	/**
190 	 * If present and populated with a string, provides the "alternate link" (the link element in the bundle entry with <code>rel="alternate"</code>). Server implementations may populate this with a
191 	 * complete URL, in which case the URL will be placed as-is in the bundle. They may alternately specify a resource relative URL (e.g. "Patient/1243") in which case the server will convert this to
192 	 * an absolute URL at runtime.
193 	 * <p>
194 	 * Values for this key are of type <b>{@link String}</b>
195 	 * </p>
196 	 */
197 	public static final ResourceMetadataKeyEnum<String> LINK_ALTERNATE = new ResourceMetadataKeyEnum<String>("LINK_ALTERNATE") {
198 		private static final long serialVersionUID = 1L;
199 
200 		@Override
201 		public String get(IResource theResource) {
202 			return getStringFromMetadataOrNullIfNone(theResource.getResourceMetadata(), LINK_ALTERNATE);
203 		}
204 
205 		@Override
206 		public void put(IResource theResource, String theObject) {
207 			theResource.getResourceMetadata().put(LINK_ALTERNATE, theObject);
208 		}
209 	};
210 	/**
211 	 * If present and populated with a string, provides the "search link" (the link element in the bundle entry with <code>rel="search"</code>). Server implementations may populate this with a
212 	 * complete URL, in which case the URL will be placed as-is in the bundle. They may alternately specify a resource relative URL (e.g. "Patient?name=tester") in which case the server will convert
213 	 * this to an absolute URL at runtime.
214 	 * <p>
215 	 * Values for this key are of type <b>{@link String}</b>
216 	 * </p>
217 	 */
218 	public static final ResourceMetadataKeyEnum<String> LINK_SEARCH = new ResourceMetadataKeyEnum<String>("LINK_SEARCH") {
219 		private static final long serialVersionUID = 1L;
220 
221 		@Override
222 		public String get(IResource theResource) {
223 			return getStringFromMetadataOrNullIfNone(theResource.getResourceMetadata(), LINK_SEARCH);
224 		}
225 
226 		@Override
227 		public void put(IResource theResource, String theObject) {
228 			theResource.getResourceMetadata().put(LINK_SEARCH, theObject);
229 		}
230 	};
231 	/**
232 	 * The value for this key represents a previous ID used to identify this resource. This key is currently only used internally during transaction method processing.
233 	 * <p>
234 	 * Values for this key are of type <b>{@link IdDt}</b>
235 	 * </p>
236 	 */
237 	public static final ResourceMetadataKeyEnum<IdDt> PREVIOUS_ID = new ResourceMetadataKeyEnum<IdDt>("PREVIOUS_ID") {
238 		private static final long serialVersionUID = 1L;
239 
240 		@Override
241 		public IdDt get(IResource theResource) {
242 			return getIdFromMetadataOrNullIfNone(theResource.getResourceMetadata(), PREVIOUS_ID);
243 		}
244 
245 		@Override
246 		public void put(IResource theResource, IdDt theObject) {
247 			theResource.getResourceMetadata().put(PREVIOUS_ID, theObject);
248 		}
249 	};
250 	/**
251 	 * The value for this key represents a {@link List} of profile IDs that this resource claims to conform to.
252 	 * <p>
253 	 * <p>
254 	 * Values for this key are of type <b>List&lt;IdDt&gt;</b>. Note that the returned list is <i>unmodifiable</i>, so you need to create a new list and call <code>put</code> to change its value.
255 	 * </p>
256 	 */
257 	public static final ResourceMetadataKeyEnum<List<IdDt>> PROFILES = new ResourceMetadataKeyEnum<List<IdDt>>("PROFILES") {
258 		private static final long serialVersionUID = 1L;
259 
260 		@Override
261 		public List<IdDt> get(IResource theResource) {
262 			return getIdListFromMetadataOrNullIfNone(theResource.getResourceMetadata(), PROFILES);
263 		}
264 
265 		@Override
266 		public void put(IResource theResource, List<IdDt> theObject) {
267 			theResource.getResourceMetadata().put(PROFILES, theObject);
268 		}
269 	};
270 	/**
271 	 * The value for this key is the bundle entry <b>Published</b> time. This is defined by FHIR as "Time resource copied into the feed", which is generally best left to the current time.
272 	 * <p>
273 	 * Values for this key are of type <b>{@link InstantDt}</b>
274 	 * </p>
275 	 * <p>
276 	 * <b>Server Note</b>: In servers, it is generally advisable to leave this value <code>null</code>, in which case the server will substitute the current time automatically.
277 	 * </p>
278 	 *
279 	 * @see InstantDt
280 	 */
281 	public static final ResourceMetadataKeyEnum<InstantDt> PUBLISHED = new ResourceMetadataKeyEnum<InstantDt>("PUBLISHED") {
282 		private static final long serialVersionUID = 1L;
283 
284 		@Override
285 		public InstantDt get(IResource theResource) {
286 			return getInstantFromMetadataOrNullIfNone(theResource.getResourceMetadata(), PUBLISHED);
287 		}
288 
289 		@Override
290 		public void put(IResource theResource, InstantDt theObject) {
291 			theResource.getResourceMetadata().put(PUBLISHED, theObject);
292 		}
293 	};
294 	public static final ResourceMetadataKeyEnum<List<BaseCodingDt>> SECURITY_LABELS = new ResourceMetadataKeyEnum<List<BaseCodingDt>>("SECURITY_LABELS") {
295 		private static final long serialVersionUID = 1L;
296 
297 		@Override
298 		public List<BaseCodingDt> get(IResource resource) {
299 			Object obj = resource.getResourceMetadata().get(SECURITY_LABELS);
300 			if (obj == null) {
301 				return null;
302 			}
303 			try {
304 				@SuppressWarnings("unchecked")
305 				List<BaseCodingDt> securityLabels = (List<BaseCodingDt>) obj;
306 				if (securityLabels.isEmpty()) {
307 					return null;
308 				}
309 				return securityLabels;
310 			} catch (ClassCastException e) {
311 				throw new InternalErrorException("Found an object of type '" + obj.getClass().getCanonicalName() + "' in resource metadata for key SECURITY_LABELS - Expected "
312 					+ BaseCodingDt.class.getCanonicalName());
313 			}
314 
315 		}
316 
317 		@Override
318 		public void put(IResource iResource, List<BaseCodingDt> labels) {
319 			iResource.getResourceMetadata().put(SECURITY_LABELS, labels);
320 		}
321 
322 	};
323 	/**
324 	 * The value for this key is the list of tags associated with this resource
325 	 * <p>
326 	 * Values for this key are of type <b>{@link TagList}</b>
327 	 * </p>
328 	 *
329 	 * @see TagList
330 	 */
331 	public static final ResourceMetadataKeyEnum<TagList> TAG_LIST = new ResourceMetadataKeyEnum<TagList>("TAG_LIST") {
332 		private static final long serialVersionUID = 1L;
333 
334 		@Override
335 		public TagList get(IResource theResource) {
336 			Object retValObj = theResource.getResourceMetadata().get(TAG_LIST);
337 			if (retValObj == null) {
338 				return null;
339 			} else if (retValObj instanceof TagList) {
340 				if (((TagList) retValObj).isEmpty()) {
341 					return null;
342 				}
343 				return (TagList) retValObj;
344 			}
345 			throw new InternalErrorException("Found an object of type '" + retValObj.getClass().getCanonicalName() + "' in resource metadata for key " + TAG_LIST.name() + " - Expected "
346 				+ TagList.class.getCanonicalName());
347 		}
348 
349 		@Override
350 		public void put(IResource theResource, TagList theObject) {
351 			theResource.getResourceMetadata().put(TAG_LIST, theObject);
352 		}
353 	};
354 	/**
355 	 * If present and populated with a string (as an instance of {@link String}), this value contains the title for this resource, as supplied in any bundles containing the resource.
356 	 * <p>
357 	 * Values for this key are of type <b>{@link String}</b>
358 	 * </p>
359 	 */
360 	public static final ResourceMetadataKeyEnum<String> TITLE = new ResourceMetadataKeyEnum<String>("TITLE") {
361 		private static final long serialVersionUID = 1L;
362 
363 		@Override
364 		public String get(IResource theResource) {
365 			return getStringFromMetadataOrNullIfNone(theResource.getResourceMetadata(), TITLE);
366 		}
367 
368 		@Override
369 		public void put(IResource theResource, String theObject) {
370 			theResource.getResourceMetadata().put(TITLE, theObject);
371 		}
372 	};
373 	/**
374 	 * The value for this key is the bundle entry <b>Updated</b> time. This is defined by FHIR as "Last Updated for resource". This value is also used for populating the "Last-Modified" header in the
375 	 * case of methods that return a single resource (read, vread, etc.)
376 	 * <p>
377 	 * Values for this key are of type <b>{@link InstantDt}</b>
378 	 * </p>
379 	 *
380 	 * @see InstantDt
381 	 */
382 	public static final ResourceMetadataKeyEnum<InstantDt> UPDATED = new ResourceMetadataKeyEnum<InstantDt>("UPDATED") {
383 		private static final long serialVersionUID = 1L;
384 
385 		@Override
386 		public InstantDt get(IResource theResource) {
387 			return getInstantFromMetadataOrNullIfNone(theResource.getResourceMetadata(), UPDATED);
388 		}
389 
390 		@Override
391 		public void put(IResource theResource, InstantDt theObject) {
392 			theResource.getResourceMetadata().put(UPDATED, theObject);
393 		}
394 	};
395 	/**
396 	 * The value for this key is the version ID of the resource object.
397 	 * <p>
398 	 * Values for this key are of type <b>{@link String}</b>
399 	 * </p>
400 	 */
401 	public static final ResourceMetadataKeyEnum<String> VERSION = new ResourceMetadataKeyEnum<String>("VERSION") {
402 		private static final long serialVersionUID = 1L;
403 
404 		@Override
405 		public String get(IResource theResource) {
406 			return getStringFromMetadataOrNullIfNone(theResource.getResourceMetadata(), VERSION);
407 		}
408 
409 		@Override
410 		public void put(IResource theResource, String theObject) {
411 			theResource.getResourceMetadata().put(VERSION, theObject);
412 		}
413 	};
414 	/**
415 	 * The value for this key is the version ID of the resource object.
416 	 * <p>
417 	 * Values for this key are of type <b>{@link IdDt}</b>
418 	 * </p>
419 	 *
420 	 * @deprecated The {@link IResource#getId()} resource ID will now be populated with the version ID via the {@link IdDt#getVersionIdPart()} method
421 	 */
422 	@Deprecated
423 	public static final ResourceMetadataKeyEnum<IdDt> VERSION_ID = new ResourceMetadataKeyEnum<IdDt>("VERSION_ID") {
424 		private static final long serialVersionUID = 1L;
425 
426 		@Override
427 		public IdDt get(IResource theResource) {
428 			return getIdFromMetadataOrNullIfNone(theResource.getResourceMetadata(), VERSION_ID);
429 		}
430 
431 		@Override
432 		public void put(IResource theResource, IdDt theObject) {
433 			theResource.getResourceMetadata().put(VERSION_ID, theObject);
434 		}
435 	};
436 	private static final long serialVersionUID = 1L;
437 	private final String myValue;
438 
439 	public ResourceMetadataKeyEnum(String theValue) {
440 		myValue = theValue;
441 	}
442 
443 	@Override
444 	public boolean equals(Object obj) {
445 		if (this == obj)
446 			return true;
447 		if (obj == null)
448 			return false;
449 		if (getClass() != obj.getClass())
450 			return false;
451 		ResourceMetadataKeyEnum<?> other = (ResourceMetadataKeyEnum<?>) obj;
452 		if (myValue == null) {
453 			if (other.myValue != null)
454 				return false;
455 		} else if (!myValue.equals(other.myValue))
456 			return false;
457 		return true;
458 	}
459 
460 	public abstract T get(IResource theResource);
461 
462 	@Override
463 	public int hashCode() {
464 		final int prime = 31;
465 		int result = 1;
466 		result = prime * result + ((myValue == null) ? 0 : myValue.hashCode());
467 		return result;
468 	}
469 
470 	public String name() {
471 		return myValue;
472 	}
473 
474 	public abstract void put(IResource theResource, T theObject);
475 
476 	@Override
477 	public String toString() {
478 		return myValue;
479 	}
480 
481 	private static DecimalDt getDecimalFromMetadataOrNullIfNone(Map<ResourceMetadataKeyEnum<?>, Object> theResourceMetadata, ResourceMetadataKeyEnum<DecimalDt> theKey) {
482 		Object retValObj = theResourceMetadata.get(theKey);
483 		if (retValObj == null) {
484 			return null;
485 		} else if (retValObj instanceof DecimalDt) {
486 			if (((DecimalDt) retValObj).isEmpty()) {
487 				return null;
488 			}
489 			return (DecimalDt) retValObj;
490 		} else if (retValObj instanceof String) {
491 			if (StringUtils.isBlank((String) retValObj)) {
492 				return null;
493 			}
494 			return new DecimalDt((String) retValObj);
495 		} else if (retValObj instanceof Double) {
496 			return new DecimalDt((Double) retValObj);
497 		}
498 		throw new InternalErrorException("Found an object of type '" + retValObj.getClass().getCanonicalName() + "' in resource metadata for key " + theKey.name() + " - Expected "
499 			+ InstantDt.class.getCanonicalName());
500 	}
501 
502 	@SuppressWarnings("unchecked")
503 	private static <T extends Enum<?>> T getEnumFromMetadataOrNullIfNone(Map<ResourceMetadataKeyEnum<?>, Object> theResourceMetadata, ResourceMetadataKeyEnum<T> theKey, Class<T> theEnumType,
504 																								IValueSetEnumBinder<T> theBinder) {
505 		Object retValObj = theResourceMetadata.get(theKey);
506 		if (retValObj == null) {
507 			return null;
508 		} else if (theEnumType.equals(retValObj.getClass())) {
509 			return (T) retValObj;
510 		} else if (retValObj instanceof String) {
511 			return theBinder.fromCodeString((String) retValObj);
512 		}
513 		throw new InternalErrorException("Found an object of type '" + retValObj.getClass().getCanonicalName() + "' in resource metadata for key " + theKey.name() + " - Expected "
514 			+ InstantDt.class.getCanonicalName());
515 	}
516 
517 	private static IdDt getIdFromMetadataOrNullIfNone(Map<ResourceMetadataKeyEnum<?>, Object> theResourceMetadata, ResourceMetadataKeyEnum<?> theKey) {
518 		return toId(theKey, theResourceMetadata.get(theKey));
519 	}
520 
521 	private static List<IdDt> getIdListFromMetadataOrNullIfNone(Map<ResourceMetadataKeyEnum<?>, Object> theResourceMetadata, ResourceMetadataKeyEnum<?> theKey) {
522 		Object retValObj = theResourceMetadata.get(theKey);
523 		if (retValObj == null) {
524 			return null;
525 		} else if (retValObj instanceof List) {
526 			List<?> retValList = (List<?>) retValObj;
527 			for (Object next : retValList) {
528 				if (!(next instanceof IdDt)) {
529 					List<IdDt> retVal = new ArrayList<IdDt>();
530 					for (Object nextVal : retValList) {
531 						retVal.add(toId(theKey, nextVal));
532 					}
533 					return Collections.unmodifiableList(retVal);
534 				}
535 			}
536 			@SuppressWarnings("unchecked")
537 			List<IdDt> retVal = (List<IdDt>) retValList;
538 			return Collections.unmodifiableList(retVal);
539 		} else {
540 			return Collections.singletonList(toId(theKey, retValObj));
541 		}
542 	}
543 
544 	private static InstantDt getInstantFromMetadataOrNullIfNone(Map<ResourceMetadataKeyEnum<?>, Object> theResourceMetadata, ResourceMetadataKeyEnum<InstantDt> theKey) {
545 		Object retValObj = theResourceMetadata.get(theKey);
546 		if (retValObj == null) {
547 			return null;
548 		} else if (retValObj instanceof Date) {
549 			return new InstantDt((Date) retValObj);
550 		} else if (retValObj instanceof InstantDt) {
551 			if (((InstantDt) retValObj).isEmpty()) {
552 				return null;
553 			}
554 			return (InstantDt) retValObj;
555 		}
556 		throw new InternalErrorException("Found an object of type '" + retValObj.getClass().getCanonicalName() + "' in resource metadata for key " + theKey.name() + " - Expected "
557 			+ InstantDt.class.getCanonicalName());
558 	}
559 
560 	private static String getStringFromMetadataOrNullIfNone(Map<ResourceMetadataKeyEnum<?>, Object> theResourceMetadata, ResourceMetadataKeyEnum<String> theKey) {
561 		Object retValObj = theResourceMetadata.get(theKey);
562 		if (retValObj == null) {
563 			return null;
564 		} else if (retValObj instanceof String) {
565 			if (StringUtils.isBlank(((String) retValObj))) {
566 				return null;
567 			}
568 			return (String) retValObj;
569 		}
570 		throw new InternalErrorException("Found an object of type '" + retValObj.getClass().getCanonicalName() + "' in resource metadata for key " + theKey.name() + " - Expected "
571 			+ String.class.getCanonicalName());
572 	}
573 
574 	private static IdDt toId(ResourceMetadataKeyEnum<?> theKey, Object retValObj) {
575 		if (retValObj == null) {
576 			return null;
577 		} else if (retValObj instanceof String) {
578 			if (isNotBlank((String) retValObj)) {
579 				return new IdDt((String) retValObj);
580 			}
581 			return null;
582 		} else if (retValObj instanceof IdDt) {
583 			if (((IdDt) retValObj).isEmpty()) {
584 				return null;
585 			}
586 			return (IdDt) retValObj;
587 		} else if (retValObj instanceof Number) {
588 			return new IdDt(((Number) retValObj).toString());
589 		}
590 		throw new InternalErrorException("Found an object of type '" + retValObj.getClass().getCanonicalName() + "' in resource metadata for key " + theKey.name() + " - Expected "
591 			+ IdDt.class.getCanonicalName());
592 	}
593 
594 	public static abstract class ResourceMetadataKeySupportingAnyResource<T, T2> extends ResourceMetadataKeyEnum<T> {
595 
596 		private static final long serialVersionUID = 1L;
597 
598 		public ResourceMetadataKeySupportingAnyResource(String theValue) {
599 			super(theValue);
600 		}
601 
602 		public abstract T2 get(IAnyResource theResource);
603 
604 		public abstract void put(IAnyResource theResource, T2 theObject);
605 
606 	}
607 
608 	public static final class ExtensionResourceMetadataKey extends ResourceMetadataKeyEnum<ExtensionDt> {
609 		public ExtensionResourceMetadataKey(String url) {
610 			super(url);
611 		}
612 
613 		@Override
614 		public ExtensionDt get(IResource theResource) {
615 			Object retValObj = theResource.getResourceMetadata().get(this);
616 			if (retValObj == null) {
617 				return null;
618 			} else if (retValObj instanceof ExtensionDt) {
619 				return (ExtensionDt) retValObj;
620 			}
621 			throw new InternalErrorException("Found an object of type '" + retValObj.getClass().getCanonicalName()
622 				+ "' in resource metadata for key " + this.name() + " - Expected "
623 				+ ExtensionDt.class.getCanonicalName());
624 		}
625 
626 		@Override
627 		public void put(IResource theResource, ExtensionDt theObject) {
628 			theResource.getResourceMetadata().put(this, theObject);
629 		}
630 	}
631 
632 	
633 }