001package ca.uhn.fhir.model.api;
002
003/*
004 * #%L
005 * HAPI FHIR - Core Library
006 * %%
007 * Copyright (C) 2014 - 2017 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                        }
311                        try {
312                                @SuppressWarnings("unchecked")
313                                List<BaseCodingDt> securityLabels = (List<BaseCodingDt>) obj;
314                                if (securityLabels.isEmpty()) {
315                                        return null;
316                                }
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                @Override
326                public void put(IResource iResource, List<BaseCodingDt> labels) {
327                        iResource.getResourceMetadata().put(SECURITY_LABELS, labels);
328                }
329
330        };
331
332        /**
333         * The value for this key is the list of tags associated with this resource
334         * <p>
335         * Values for this key are of type <b>{@link TagList}</b>
336         * </p>
337         * 
338         * @see TagList
339         */
340        public static final ResourceMetadataKeyEnum<TagList> TAG_LIST = new ResourceMetadataKeyEnum<TagList>("TAG_LIST") {
341                private static final long serialVersionUID = 1L;
342                @Override
343                public TagList get(IResource theResource) {
344                        Object retValObj = theResource.getResourceMetadata().get(TAG_LIST);
345                        if (retValObj == null) {
346                                return null;
347                        } else if (retValObj instanceof TagList) {
348                                if (((TagList) retValObj).isEmpty()) {
349                                        return null;
350                                }
351                                return (TagList) retValObj;
352                        }
353                        throw new InternalErrorException("Found an object of type '" + retValObj.getClass().getCanonicalName() + "' in resource metadata for key " + TAG_LIST.name() + " - Expected "
354                                        + TagList.class.getCanonicalName());
355                }
356
357                @Override
358                public void put(IResource theResource, TagList theObject) {
359                        theResource.getResourceMetadata().put(TAG_LIST, theObject);
360                }
361        };
362
363        /**
364         * 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.
365         * <p>
366         * Values for this key are of type <b>{@link String}</b>
367         * </p>
368         */
369        public static final ResourceMetadataKeyEnum<String> TITLE = new ResourceMetadataKeyEnum<String>("TITLE") {
370                private static final long serialVersionUID = 1L;
371                @Override
372                public String get(IResource theResource) {
373                        return getStringFromMetadataOrNullIfNone(theResource.getResourceMetadata(), TITLE);
374                }
375
376                @Override
377                public void put(IResource theResource, String theObject) {
378                        theResource.getResourceMetadata().put(TITLE, theObject);
379                }
380        };
381
382        /**
383         * 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
384         * case of methods that return a single resource (read, vread, etc.)
385         * <p>
386         * Values for this key are of type <b>{@link InstantDt}</b>
387         * </p>
388         * 
389         * @see InstantDt
390         */
391        public static final ResourceMetadataKeyEnum<InstantDt> UPDATED = new ResourceMetadataKeyEnum<InstantDt>("UPDATED") {
392                private static final long serialVersionUID = 1L;
393                @Override
394                public InstantDt get(IResource theResource) {
395                        return getInstantFromMetadataOrNullIfNone(theResource.getResourceMetadata(), UPDATED);
396                }
397
398                @Override
399                public void put(IResource theResource, InstantDt theObject) {
400                        theResource.getResourceMetadata().put(UPDATED, theObject);
401                }
402        };
403
404        /**
405         * The value for this key is the version ID of the resource object.
406         * <p>
407         * Values for this key are of type <b>{@link String}</b>
408         * </p>
409         */
410        public static final ResourceMetadataKeyEnum<String> VERSION = new ResourceMetadataKeyEnum<String>("VERSION") {
411                private static final long serialVersionUID = 1L;
412                @Override
413                public String get(IResource theResource) {
414                        return getStringFromMetadataOrNullIfNone(theResource.getResourceMetadata(), VERSION);
415                }
416
417                @Override
418                public void put(IResource theResource, String theObject) {
419                        theResource.getResourceMetadata().put(VERSION, theObject);
420                }
421        };
422
423        /**
424         * The value for this key is the version ID of the resource object.
425         * <p>
426         * Values for this key are of type <b>{@link IdDt}</b>
427         * </p>
428         * 
429         * @deprecated The {@link IResource#getId()} resource ID will now be populated with the version ID via the {@link IdDt#getVersionIdPart()} method
430         */
431        @Deprecated
432        public static final ResourceMetadataKeyEnum<IdDt> VERSION_ID = new ResourceMetadataKeyEnum<IdDt>("VERSION_ID") {
433                private static final long serialVersionUID = 1L;
434                @Override
435                public IdDt get(IResource theResource) {
436                        return getIdFromMetadataOrNullIfNone(theResource.getResourceMetadata(), VERSION_ID);
437                }
438
439                @Override
440                public void put(IResource theResource, IdDt theObject) {
441                        theResource.getResourceMetadata().put(VERSION_ID, theObject);
442                }
443        };
444
445        private final String myValue;
446
447        public ResourceMetadataKeyEnum(String theValue) {
448                myValue = theValue;
449        }
450
451        @Override
452        public boolean equals(Object obj) {
453                if (this == obj)
454                        return true;
455                if (obj == null)
456                        return false;
457                if (getClass() != obj.getClass())
458                        return false;
459                ResourceMetadataKeyEnum<?> other = (ResourceMetadataKeyEnum<?>) obj;
460                if (myValue == null) {
461                        if (other.myValue != null)
462                                return false;
463                } else if (!myValue.equals(other.myValue))
464                        return false;
465                return true;
466        }
467
468        public abstract T get(IResource theResource);
469
470        @Override
471        public int hashCode() {
472                final int prime = 31;
473                int result = 1;
474                result = prime * result + ((myValue == null) ? 0 : myValue.hashCode());
475                return result;
476        }
477
478        public String name() {
479                return myValue;
480        }
481
482        public abstract void put(IResource theResource, T theObject);
483
484        @Override
485        public String toString() {
486                return myValue;
487        }
488
489        private static DecimalDt getDecimalFromMetadataOrNullIfNone(Map<ResourceMetadataKeyEnum<?>, Object> theResourceMetadata, ResourceMetadataKeyEnum<DecimalDt> theKey) {
490                Object retValObj = theResourceMetadata.get(theKey);
491                if (retValObj == null) {
492                        return null;
493                } else if (retValObj instanceof DecimalDt) {
494                        if (((DecimalDt) retValObj).isEmpty()) {
495                                return null;
496                        }
497                        return (DecimalDt) retValObj;
498                } else if (retValObj instanceof String) {
499                        if (StringUtils.isBlank((String) retValObj)) {
500                                return null;
501                        }
502                        return new DecimalDt((String) retValObj);
503                } else if (retValObj instanceof Double) {
504                        return new DecimalDt((Double) retValObj);
505                }
506                throw new InternalErrorException("Found an object of type '" + retValObj.getClass().getCanonicalName() + "' in resource metadata for key " + theKey.name() + " - Expected "
507                                + InstantDt.class.getCanonicalName());
508        }
509
510        @SuppressWarnings("unchecked")
511        private static <T extends Enum<?>> T getEnumFromMetadataOrNullIfNone(Map<ResourceMetadataKeyEnum<?>, Object> theResourceMetadata, ResourceMetadataKeyEnum<T> theKey, Class<T> theEnumType,
512                        IValueSetEnumBinder<T> theBinder) {
513                Object retValObj = theResourceMetadata.get(theKey);
514                if (retValObj == null) {
515                        return null;
516                } else if (theEnumType.equals(retValObj.getClass())) {
517                        return (T) retValObj;
518                } else if (retValObj instanceof String) {
519                        return theBinder.fromCodeString((String) retValObj);
520                }
521                throw new InternalErrorException("Found an object of type '" + retValObj.getClass().getCanonicalName() + "' in resource metadata for key " + theKey.name() + " - Expected "
522                                + InstantDt.class.getCanonicalName());
523        }
524
525        private static IdDt getIdFromMetadataOrNullIfNone(Map<ResourceMetadataKeyEnum<?>, Object> theResourceMetadata, ResourceMetadataKeyEnum<?> theKey) {
526                return toId(theKey, theResourceMetadata.get(theKey));
527        }
528
529        private static List<IdDt> getIdListFromMetadataOrNullIfNone(Map<ResourceMetadataKeyEnum<?>, Object> theResourceMetadata, ResourceMetadataKeyEnum<?> theKey) {
530                Object retValObj = theResourceMetadata.get(theKey);
531                if (retValObj == null) {
532                        return null;
533                } else if (retValObj instanceof List) {
534                        List<?> retValList = (List<?>) retValObj;
535                        for (Object next : retValList) {
536                                if (!(next instanceof IdDt)) {
537                                        List<IdDt> retVal = new ArrayList<IdDt>();
538                                        for (Object nextVal : retValList) {
539                                                retVal.add(toId(theKey, nextVal));
540                                        }
541                                        return Collections.unmodifiableList(retVal);
542                                }
543                        }
544                        @SuppressWarnings("unchecked")
545                        List<IdDt> retVal = (List<IdDt>) retValList;
546                        return Collections.unmodifiableList(retVal);
547                } else {
548                        return Collections.singletonList(toId(theKey, retValObj));
549                }
550        }
551
552        private static InstantDt getInstantFromMetadataOrNullIfNone(Map<ResourceMetadataKeyEnum<?>, Object> theResourceMetadata, ResourceMetadataKeyEnum<InstantDt> theKey) {
553                Object retValObj = theResourceMetadata.get(theKey);
554                if (retValObj == null) {
555                        return null;
556                } else if (retValObj instanceof Date) {
557                        return new InstantDt((Date) retValObj);
558                } else if (retValObj instanceof InstantDt) {
559                        if (((InstantDt) retValObj).isEmpty()) {
560                                return null;
561                        } 
562                        return (InstantDt) retValObj;
563                }
564                throw new InternalErrorException("Found an object of type '" + retValObj.getClass().getCanonicalName() + "' in resource metadata for key " + theKey.name() + " - Expected "
565                                + InstantDt.class.getCanonicalName());
566        }
567
568        private static String getStringFromMetadataOrNullIfNone(Map<ResourceMetadataKeyEnum<?>, Object> theResourceMetadata, ResourceMetadataKeyEnum<String> theKey) {
569                Object retValObj = theResourceMetadata.get(theKey);
570                if (retValObj == null) {
571                        return null;
572                } else if (retValObj instanceof String) {
573                        if (StringUtils.isBlank(((String) retValObj))) {
574                                return null;
575                        } 
576                        return (String) retValObj;
577                }
578                throw new InternalErrorException("Found an object of type '" + retValObj.getClass().getCanonicalName() + "' in resource metadata for key " + theKey.name() + " - Expected "
579                                + String.class.getCanonicalName());
580        }
581
582        private static IdDt toId(ResourceMetadataKeyEnum<?> theKey, Object retValObj) {
583                if (retValObj == null) {
584                        return null;
585                } else if (retValObj instanceof String) {
586                        if (isNotBlank((String) retValObj)) {
587                                return new IdDt((String) retValObj);
588                        }
589                        return null;
590                } else if (retValObj instanceof IdDt) {
591                        if (((IdDt) retValObj).isEmpty()) {
592                                return null;
593                        }
594                        return (IdDt) retValObj;
595                } else if (retValObj instanceof Number) {
596                        return new IdDt(((Number) retValObj).toString());
597                }
598                throw new InternalErrorException("Found an object of type '" + retValObj.getClass().getCanonicalName() + "' in resource metadata for key " + theKey.name() + " - Expected "
599                                + IdDt.class.getCanonicalName());
600        }
601
602        public static abstract class ResourceMetadataKeySupportingAnyResource<T, T2> extends ResourceMetadataKeyEnum<T> {
603
604                public ResourceMetadataKeySupportingAnyResource(String theValue) {
605                        super(theValue);
606                }
607
608                private static final long serialVersionUID = 1L;
609
610                
611                public abstract T2 get(IAnyResource theResource);
612
613                public abstract void put(IAnyResource theResource, T2 theObject);
614
615        }
616        
617}