001package ca.uhn.fhir.model.api;
002
003/*
004 * #%L
005 * HAPI FHIR - Core Library
006 * %%
007 * Copyright (C) 2014 - 2016 University Health Network
008 * %%
009 * Licensed under the Apache License, Version 2.0 (the "License");
010 * you may not use this file except in compliance with the License.
011 * You may obtain a copy of the License at
012 * 
013 *      http://www.apache.org/licenses/LICENSE-2.0
014 * 
015 * Unless required by applicable law or agreed to in writing, software
016 * distributed under the License is distributed on an "AS IS" BASIS,
017 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
018 * See the License for the specific language governing permissions and
019 * limitations under the License.
020 * #L%
021 */
022import static org.apache.commons.lang3.StringUtils.isNotBlank;
023
024import java.io.Serializable;
025import java.util.ArrayList;
026import java.util.Collections;
027import java.util.Date;
028import java.util.List;
029import java.util.Map;
030
031import org.apache.commons.lang3.StringUtils;
032import org.hl7.fhir.instance.model.api.IAnyResource;
033import org.hl7.fhir.instance.model.api.IPrimitiveType;
034
035import ca.uhn.fhir.model.base.composite.BaseCodingDt;
036import ca.uhn.fhir.model.primitive.DecimalDt;
037import ca.uhn.fhir.model.primitive.IdDt;
038import ca.uhn.fhir.model.primitive.InstantDt;
039import ca.uhn.fhir.model.valueset.BundleEntrySearchModeEnum;
040import ca.uhn.fhir.model.valueset.BundleEntryTransactionMethodEnum;
041import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
042
043/**
044 * 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
045 * data elements which are sent/receieved in HTTP Headers along with read/create resource requests, or properties which can be found in bundle entries.
046 * <p>
047 * 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
048 * {@link #UPDATED} value, which is the "last updated" time for that resource, use the following code:
049 * </p>
050 * <p>
051 * <code>InstantDt updated = ResourceMetadataKeyEnum.UPDATED.get(resource);</code>
052 * <p>
053 * <p>
054 * To set this value, use the following:
055 * </p>
056 * <p>
057 * <code>InstantDt update = new InstantDt("2011-01-02T11:22:33.0000Z"); // populate with the actual time<br>
058 * ResourceMetadataKeyEnum.UPDATED.put(resource, update);</code>
059 * </p>
060 * <p>
061 * 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
062 * define their own keys for storage in resource metadata if needed.
063 * </p>
064 */
065public abstract class ResourceMetadataKeyEnum<T> implements Serializable {
066
067        private static final long serialVersionUID = 1L;
068
069        /**
070         * 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
071         * of scenarios, such as POSTing transaction bundles to a server, or returning resource history.
072         * <p>
073         * Values for this key are of type <b>{@link InstantDt}</b>
074         * </p>
075         */
076        public static final ResourceMetadataKeySupportingAnyResource<InstantDt, IPrimitiveType<Date>> DELETED_AT = new ResourceMetadataKeySupportingAnyResource<InstantDt, IPrimitiveType<Date>>("DELETED_AT") {
077                private static final long serialVersionUID = 1L;
078
079                @Override
080                public InstantDt get(IResource theResource) {
081                        return getInstantFromMetadataOrNullIfNone(theResource.getResourceMetadata(), DELETED_AT);
082                }
083
084                @Override
085                public void put(IResource theResource, InstantDt theObject) {
086                        theResource.getResourceMetadata().put(DELETED_AT, theObject);
087                }
088
089                @SuppressWarnings("unchecked")
090                @Override
091                public IPrimitiveType<Date> get(IAnyResource theResource) {
092                        return (IPrimitiveType<Date>) theResource.getUserData(DELETED_AT.name());
093                }
094
095                @Override
096                public void put(IAnyResource theResource, IPrimitiveType<Date> theObject) {
097                        theResource.setUserData(DELETED_AT.name(), theObject);
098                }
099        };
100
101        /**
102         * 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
103         * <code>Bundle.entry.score</code> in a Bundle resource.
104         * <p>
105         * Note that search URL is only used in FHIR DSTU2 and later.
106         * </p>
107         * <p>
108         * Values for this key are of type <b>{@link DecimalDt}</b>
109         * </p>
110         */
111        public static final ResourceMetadataKeyEnum<DecimalDt> ENTRY_SCORE = new ResourceMetadataKeyEnum<DecimalDt>("ENTRY_SCORE") {
112                private static final long serialVersionUID = 1L;
113
114                @Override
115                public DecimalDt get(IResource theResource) {
116                        return getDecimalFromMetadataOrNullIfNone(theResource.getResourceMetadata(), ENTRY_SCORE);
117                }
118
119                @Override
120                public void put(IResource theResource, DecimalDt theObject) {
121                        theResource.getResourceMetadata().put(ENTRY_SCORE, theObject);
122                }
123        };
124
125        /**
126         * 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.
127         * 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
128         * server, or to "match" to indicate that the resource was returned because it matched the given search criteria.
129         * <p>
130         * Note that status is only used in FHIR DSTU2 and later.
131         * </p>
132         * <p>
133         * Values for this key are of type <b>{@link BundleEntrySearchModeEnum}</b>
134         * </p>
135         */
136        public static final ResourceMetadataKeySupportingAnyResource<BundleEntrySearchModeEnum, String> ENTRY_SEARCH_MODE = new ResourceMetadataKeySupportingAnyResource<BundleEntrySearchModeEnum, String>("ENTRY_SEARCH_MODE") {
137                private static final long serialVersionUID = 1L;
138                @Override
139                public BundleEntrySearchModeEnum get(IResource theResource) {
140                        return getEnumFromMetadataOrNullIfNone(theResource.getResourceMetadata(), ENTRY_SEARCH_MODE, BundleEntrySearchModeEnum.class, BundleEntrySearchModeEnum.VALUESET_BINDER);
141                }
142
143                @Override
144                public void put(IResource theResource, BundleEntrySearchModeEnum theObject) {
145                        theResource.getResourceMetadata().put(ENTRY_SEARCH_MODE, theObject);
146                }
147
148                @Override
149                public String get(IAnyResource theResource) {
150                        return (String) theResource.getUserData(ENTRY_SEARCH_MODE.name());
151                }
152
153                @Override
154                public void put(IAnyResource theResource, String theObject) {
155                        theResource.setUserData(ENTRY_SEARCH_MODE.name(), theObject);
156                }
157        };
158
159        /**
160         * 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
161         * 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
162         * 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
163         * result of a transaction to indicate to the client what operation was actually performed.
164         * <p>
165         * Note that status is only used in FHIR DSTU2 and later.
166         * </p>
167         * <p>
168         * Values for this key are of type <b>{@link BundleEntryTransactionMethodEnum}</b>
169         * </p>
170         */
171        public static final ResourceMetadataKeySupportingAnyResource<BundleEntryTransactionMethodEnum, String> ENTRY_TRANSACTION_METHOD = new ResourceMetadataKeySupportingAnyResource<BundleEntryTransactionMethodEnum, String>(
172                        "ENTRY_TRANSACTION_OPERATION") {
173                                private static final long serialVersionUID = 1L;
174
175                @Override
176                public BundleEntryTransactionMethodEnum get(IResource theResource) {
177                        return getEnumFromMetadataOrNullIfNone(theResource.getResourceMetadata(), ENTRY_TRANSACTION_METHOD, BundleEntryTransactionMethodEnum.class,
178                                        BundleEntryTransactionMethodEnum.VALUESET_BINDER);
179                }
180
181                @Override
182                public void put(IResource theResource, BundleEntryTransactionMethodEnum theObject) {
183                        theResource.getResourceMetadata().put(ENTRY_TRANSACTION_METHOD, theObject);
184                }
185
186                @Override
187                public String get(IAnyResource theResource) {
188                        return (String) theResource.getUserData(ENTRY_TRANSACTION_METHOD.name());
189                }
190
191                @Override
192                public void put(IAnyResource theResource, String theObject) {
193                        theResource.setUserData(ENTRY_TRANSACTION_METHOD.name(), theObject);
194                }
195                
196        };
197
198        /**
199         * 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
200         * 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
201         * an absolute URL at runtime.
202         * <p>
203         * Values for this key are of type <b>{@link String}</b>
204         * </p>
205         */
206        public static final ResourceMetadataKeyEnum<String> LINK_ALTERNATE = new ResourceMetadataKeyEnum<String>("LINK_ALTERNATE") {
207                private static final long serialVersionUID = 1L;
208                @Override
209                public String get(IResource theResource) {
210                        return getStringFromMetadataOrNullIfNone(theResource.getResourceMetadata(), LINK_ALTERNATE);
211                }
212
213                @Override
214                public void put(IResource theResource, String theObject) {
215                        theResource.getResourceMetadata().put(LINK_ALTERNATE, theObject);
216                }
217        };
218
219        /**
220         * 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
221         * 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
222         * this to an absolute URL at runtime.
223         * <p>
224         * Values for this key are of type <b>{@link String}</b>
225         * </p>
226         */
227        public static final ResourceMetadataKeyEnum<String> LINK_SEARCH = new ResourceMetadataKeyEnum<String>("LINK_SEARCH") {
228                private static final long serialVersionUID = 1L;
229                @Override
230                public String get(IResource theResource) {
231                        return getStringFromMetadataOrNullIfNone(theResource.getResourceMetadata(), LINK_SEARCH);
232                }
233
234                @Override
235                public void put(IResource theResource, String theObject) {
236                        theResource.getResourceMetadata().put(LINK_SEARCH, theObject);
237                }
238        };
239
240        /**
241         * 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.
242         * <p>
243         * Values for this key are of type <b>{@link IdDt}</b>
244         * </p>
245         */
246        public static final ResourceMetadataKeyEnum<IdDt> PREVIOUS_ID = new ResourceMetadataKeyEnum<IdDt>("PREVIOUS_ID") {
247                private static final long serialVersionUID = 1L;
248                @Override
249                public IdDt get(IResource theResource) {
250                        return getIdFromMetadataOrNullIfNone(theResource.getResourceMetadata(), PREVIOUS_ID);
251                }
252
253                @Override
254                public void put(IResource theResource, IdDt theObject) {
255                        theResource.getResourceMetadata().put(PREVIOUS_ID, theObject);
256                }
257        };
258
259        /**
260         * The value for this key represents a {@link List} of profile IDs that this resource claims to conform to.
261         * 
262         * <p>
263         * 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.
264         * </p>
265         */
266        public static final ResourceMetadataKeyEnum<List<IdDt>> PROFILES = new ResourceMetadataKeyEnum<List<IdDt>>("PROFILES") {
267                private static final long serialVersionUID = 1L;
268                @Override
269                public List<IdDt> get(IResource theResource) {
270                        return getIdListFromMetadataOrNullIfNone(theResource.getResourceMetadata(), PROFILES);
271                }
272
273                @Override
274                public void put(IResource theResource, List<IdDt> theObject) {
275                        theResource.getResourceMetadata().put(PROFILES, theObject);
276                }
277        };
278
279        /**
280         * 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.
281         * <p>
282         * Values for this key are of type <b>{@link InstantDt}</b>
283         * </p>
284         * <p>
285         * <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.
286         * </p>
287         * 
288         * @see InstantDt
289         */
290        public static final ResourceMetadataKeyEnum<InstantDt> PUBLISHED = new ResourceMetadataKeyEnum<InstantDt>("PUBLISHED") {
291                private static final long serialVersionUID = 1L;
292                @Override
293                public InstantDt get(IResource theResource) {
294                        return getInstantFromMetadataOrNullIfNone(theResource.getResourceMetadata(), PUBLISHED);
295                }
296
297                @Override
298                public void put(IResource theResource, InstantDt theObject) {
299                        theResource.getResourceMetadata().put(PUBLISHED, theObject);
300                }
301        };
302
303        public static final ResourceMetadataKeyEnum<List<BaseCodingDt>> SECURITY_LABELS = new ResourceMetadataKeyEnum<List<BaseCodingDt>>("SECURITY_LABELS") {
304                private static final long serialVersionUID = 1L;
305                @Override
306                public List<BaseCodingDt> get(IResource resource) {
307                        Object obj = resource.getResourceMetadata().get(SECURITY_LABELS);
308                        if (obj == null) {
309                                return null;
310                        } else {
311                                try {
312                                        @SuppressWarnings("unchecked")
313                                        List<BaseCodingDt> securityLabels = (List<BaseCodingDt>) obj;
314                                        if (securityLabels.isEmpty())
315                                                return null;
316                                        else
317                                                return securityLabels;
318                                } catch (ClassCastException e) {
319                                        throw new InternalErrorException("Found an object of type '" + obj.getClass().getCanonicalName() + "' in resource metadata for key SECURITY_LABELS - Expected "
320                                                        + BaseCodingDt.class.getCanonicalName());
321
322                                }
323
324                        }
325                }
326
327                @Override
328                public void put(IResource iResource, List<BaseCodingDt> labels) {
329                        iResource.getResourceMetadata().put(SECURITY_LABELS, labels);
330                }
331
332        };
333
334        /**
335         * The value for this key is the list of tags associated with this resource
336         * <p>
337         * Values for this key are of type <b>{@link TagList}</b>
338         * </p>
339         * 
340         * @see TagList
341         */
342        public static final ResourceMetadataKeyEnum<TagList> TAG_LIST = new ResourceMetadataKeyEnum<TagList>("TAG_LIST") {
343                private static final long serialVersionUID = 1L;
344                @Override
345                public TagList get(IResource theResource) {
346                        Object retValObj = theResource.getResourceMetadata().get(TAG_LIST);
347                        if (retValObj == null) {
348                                return null;
349                        } else if (retValObj instanceof TagList) {
350                                if (((TagList) retValObj).isEmpty()) {
351                                        return null;
352                                } else {
353                                        return (TagList) retValObj;
354                                }
355                        }
356                        throw new InternalErrorException("Found an object of type '" + retValObj.getClass().getCanonicalName() + "' in resource metadata for key " + TAG_LIST.name() + " - Expected "
357                                        + TagList.class.getCanonicalName());
358                }
359
360                @Override
361                public void put(IResource theResource, TagList theObject) {
362                        theResource.getResourceMetadata().put(TAG_LIST, theObject);
363                }
364        };
365
366        /**
367         * 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.
368         * <p>
369         * Values for this key are of type <b>{@link String}</b>
370         * </p>
371         */
372        public static final ResourceMetadataKeyEnum<String> TITLE = new ResourceMetadataKeyEnum<String>("TITLE") {
373                private static final long serialVersionUID = 1L;
374                @Override
375                public String get(IResource theResource) {
376                        return getStringFromMetadataOrNullIfNone(theResource.getResourceMetadata(), TITLE);
377                }
378
379                @Override
380                public void put(IResource theResource, String theObject) {
381                        theResource.getResourceMetadata().put(TITLE, theObject);
382                }
383        };
384
385        /**
386         * 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
387         * case of methods that return a single resource (read, vread, etc.)
388         * <p>
389         * Values for this key are of type <b>{@link InstantDt}</b>
390         * </p>
391         * 
392         * @see InstantDt
393         */
394        public static final ResourceMetadataKeyEnum<InstantDt> UPDATED = new ResourceMetadataKeyEnum<InstantDt>("UPDATED") {
395                private static final long serialVersionUID = 1L;
396                @Override
397                public InstantDt get(IResource theResource) {
398                        return getInstantFromMetadataOrNullIfNone(theResource.getResourceMetadata(), UPDATED);
399                }
400
401                @Override
402                public void put(IResource theResource, InstantDt theObject) {
403                        theResource.getResourceMetadata().put(UPDATED, theObject);
404                }
405        };
406
407        /**
408         * The value for this key is the version ID of the resource object.
409         * <p>
410         * Values for this key are of type <b>{@link String}</b>
411         * </p>
412         */
413        public static final ResourceMetadataKeyEnum<String> VERSION = new ResourceMetadataKeyEnum<String>("VERSION") {
414                private static final long serialVersionUID = 1L;
415                @Override
416                public String get(IResource theResource) {
417                        return getStringFromMetadataOrNullIfNone(theResource.getResourceMetadata(), VERSION);
418                }
419
420                @Override
421                public void put(IResource theResource, String theObject) {
422                        theResource.getResourceMetadata().put(VERSION, theObject);
423                }
424        };
425
426        /**
427         * The value for this key is the version ID of the resource object.
428         * <p>
429         * Values for this key are of type <b>{@link IdDt}</b>
430         * </p>
431         * 
432         * @deprecated The {@link IResource#getId()} resource ID will now be populated with the version ID via the {@link IdDt#getVersionIdPart()} method
433         */
434        @Deprecated
435        public static final ResourceMetadataKeyEnum<IdDt> VERSION_ID = new ResourceMetadataKeyEnum<IdDt>("VERSION_ID") {
436                private static final long serialVersionUID = 1L;
437                @Override
438                public IdDt get(IResource theResource) {
439                        return getIdFromMetadataOrNullIfNone(theResource.getResourceMetadata(), VERSION_ID);
440                }
441
442                @Override
443                public void put(IResource theResource, IdDt theObject) {
444                        theResource.getResourceMetadata().put(VERSION_ID, theObject);
445                }
446        };
447
448        private final String myValue;
449
450        public ResourceMetadataKeyEnum(String theValue) {
451                myValue = theValue;
452        }
453
454        @Override
455        public boolean equals(Object obj) {
456                if (this == obj)
457                        return true;
458                if (obj == null)
459                        return false;
460                if (getClass() != obj.getClass())
461                        return false;
462                ResourceMetadataKeyEnum<?> other = (ResourceMetadataKeyEnum<?>) obj;
463                if (myValue == null) {
464                        if (other.myValue != null)
465                                return false;
466                } else if (!myValue.equals(other.myValue))
467                        return false;
468                return true;
469        }
470
471        public abstract T get(IResource theResource);
472
473        @Override
474        public int hashCode() {
475                final int prime = 31;
476                int result = 1;
477                result = prime * result + ((myValue == null) ? 0 : myValue.hashCode());
478                return result;
479        }
480
481        public String name() {
482                return myValue;
483        }
484
485        public abstract void put(IResource theResource, T theObject);
486
487        @Override
488        public String toString() {
489                return myValue;
490        }
491
492        private static DecimalDt getDecimalFromMetadataOrNullIfNone(Map<ResourceMetadataKeyEnum<?>, Object> theResourceMetadata, ResourceMetadataKeyEnum<DecimalDt> theKey) {
493                Object retValObj = theResourceMetadata.get(theKey);
494                if (retValObj == null) {
495                        return null;
496                } else if (retValObj instanceof DecimalDt) {
497                        if (((DecimalDt) retValObj).isEmpty()) {
498                                return null;
499                        } else {
500                                return (DecimalDt) retValObj;
501                        }
502                } else if (retValObj instanceof String) {
503                        if (StringUtils.isBlank((String) retValObj)) {
504                                return null;
505                        }
506                        return new DecimalDt((String) retValObj);
507                } else if (retValObj instanceof Double) {
508                        return new DecimalDt((Double) retValObj);
509                }
510                throw new InternalErrorException("Found an object of type '" + retValObj.getClass().getCanonicalName() + "' in resource metadata for key " + theKey.name() + " - Expected "
511                                + InstantDt.class.getCanonicalName());
512        }
513
514        @SuppressWarnings("unchecked")
515        private static <T extends Enum<?>> T getEnumFromMetadataOrNullIfNone(Map<ResourceMetadataKeyEnum<?>, Object> theResourceMetadata, ResourceMetadataKeyEnum<T> theKey, Class<T> theEnumType,
516                        IValueSetEnumBinder<T> theBinder) {
517                Object retValObj = theResourceMetadata.get(theKey);
518                if (retValObj == null) {
519                        return null;
520                } else if (theEnumType.equals(retValObj.getClass())) {
521                        return (T) retValObj;
522                } else if (retValObj instanceof String) {
523                        return theBinder.fromCodeString((String) retValObj);
524                }
525                throw new InternalErrorException("Found an object of type '" + retValObj.getClass().getCanonicalName() + "' in resource metadata for key " + theKey.name() + " - Expected "
526                                + InstantDt.class.getCanonicalName());
527        }
528
529        private static IdDt getIdFromMetadataOrNullIfNone(Map<ResourceMetadataKeyEnum<?>, Object> theResourceMetadata, ResourceMetadataKeyEnum<?> theKey) {
530                return toId(theKey, theResourceMetadata.get(theKey));
531        }
532
533        private static List<IdDt> getIdListFromMetadataOrNullIfNone(Map<ResourceMetadataKeyEnum<?>, Object> theResourceMetadata, ResourceMetadataKeyEnum<?> theKey) {
534                Object retValObj = theResourceMetadata.get(theKey);
535                if (retValObj == null) {
536                        return null;
537                } else if (retValObj instanceof List) {
538                        List<?> retValList = (List<?>) retValObj;
539                        for (Object next : retValList) {
540                                if (!(next instanceof IdDt)) {
541                                        List<IdDt> retVal = new ArrayList<IdDt>();
542                                        for (Object nextVal : retValList) {
543                                                retVal.add(toId(theKey, nextVal));
544                                        }
545                                        return Collections.unmodifiableList(retVal);
546                                }
547                        }
548                        @SuppressWarnings("unchecked")
549                        List<IdDt> retVal = (List<IdDt>) retValList;
550                        return Collections.unmodifiableList(retVal);
551                } else {
552                        return Collections.singletonList(toId(theKey, retValObj));
553                }
554        }
555
556        private static InstantDt getInstantFromMetadataOrNullIfNone(Map<ResourceMetadataKeyEnum<?>, Object> theResourceMetadata, ResourceMetadataKeyEnum<InstantDt> theKey) {
557                Object retValObj = theResourceMetadata.get(theKey);
558                if (retValObj == null) {
559                        return null;
560                } else if (retValObj instanceof Date) {
561                        return new InstantDt((Date) retValObj);
562                } else if (retValObj instanceof InstantDt) {
563                        if (((InstantDt) retValObj).isEmpty()) {
564                                return null;
565                        } else {
566                                return (InstantDt) retValObj;
567                        }
568                }
569                throw new InternalErrorException("Found an object of type '" + retValObj.getClass().getCanonicalName() + "' in resource metadata for key " + theKey.name() + " - Expected "
570                                + InstantDt.class.getCanonicalName());
571        }
572
573        private static String getStringFromMetadataOrNullIfNone(Map<ResourceMetadataKeyEnum<?>, Object> theResourceMetadata, ResourceMetadataKeyEnum<String> theKey) {
574                Object retValObj = theResourceMetadata.get(theKey);
575                if (retValObj == null) {
576                        return null;
577                } else if (retValObj instanceof String) {
578                        if (StringUtils.isBlank(((String) retValObj))) {
579                                return null;
580                        } else {
581                                return (String) retValObj;
582                        }
583                }
584                throw new InternalErrorException("Found an object of type '" + retValObj.getClass().getCanonicalName() + "' in resource metadata for key " + theKey.name() + " - Expected "
585                                + String.class.getCanonicalName());
586        }
587
588        private static IdDt toId(ResourceMetadataKeyEnum<?> theKey, Object retValObj) {
589                if (retValObj == null) {
590                        return null;
591                } else if (retValObj instanceof String) {
592                        if (isNotBlank((String) retValObj)) {
593                                return new IdDt((String) retValObj);
594                        } else {
595                                return null;
596                        }
597                } else if (retValObj instanceof IdDt) {
598                        if (((IdDt) retValObj).isEmpty()) {
599                                return null;
600                        } else {
601                                return (IdDt) retValObj;
602                        }
603                } else if (retValObj instanceof Number) {
604                        return new IdDt(((Number) retValObj).toString());
605                }
606                throw new InternalErrorException("Found an object of type '" + retValObj.getClass().getCanonicalName() + "' in resource metadata for key " + theKey.name() + " - Expected "
607                                + IdDt.class.getCanonicalName());
608        }
609
610        public static abstract class ResourceMetadataKeySupportingAnyResource<T, T2> extends ResourceMetadataKeyEnum<T> {
611
612                public ResourceMetadataKeySupportingAnyResource(String theValue) {
613                        super(theValue);
614                }
615
616                private static final long serialVersionUID = 1L;
617
618                
619                public abstract T2 get(IAnyResource theResource);
620
621                public abstract void put(IAnyResource theResource, T2 theObject);
622
623        }
624        
625}