001package ca.uhn.fhir.model.api;
002
003/*
004 * #%L
005 * HAPI FHIR - Core Library
006 * %%
007 * Copyright (C) 2014 - 2019 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 */
022
023import ca.uhn.fhir.model.base.composite.BaseCodingDt;
024import ca.uhn.fhir.model.primitive.DecimalDt;
025import ca.uhn.fhir.model.primitive.IdDt;
026import ca.uhn.fhir.model.primitive.InstantDt;
027import ca.uhn.fhir.model.valueset.BundleEntrySearchModeEnum;
028import ca.uhn.fhir.model.valueset.BundleEntryTransactionMethodEnum;
029import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
030import org.apache.commons.lang3.StringUtils;
031import org.hl7.fhir.instance.model.api.IAnyResource;
032import org.hl7.fhir.instance.model.api.IPrimitiveType;
033
034import java.io.Serializable;
035import java.util.*;
036
037import static org.apache.commons.lang3.StringUtils.isNotBlank;
038
039/**
040 * 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
041 * data elements which are sent/receieved in HTTP Headers along with read/create resource requests, or properties which can be found in bundle entries.
042 * <p>
043 * 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
044 * {@link #UPDATED} value, which is the "last updated" time for that resource, use the following code:
045 * </p>
046 * <p>
047 * <code>InstantDt updated = ResourceMetadataKeyEnum.UPDATED.get(resource);</code>
048 * <p>
049 * <p>
050 * To set this value, use the following:
051 * </p>
052 * <p>
053 * <code>InstantDt update = new InstantDt("2011-01-02T11:22:33.0000Z"); // populate with the actual time<br>
054 * ResourceMetadataKeyEnum.UPDATED.put(resource, update);</code>
055 * </p>
056 * <p>
057 * 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
058 * define their own keys for storage in resource metadata if needed.
059 * </p>
060 */
061public abstract class ResourceMetadataKeyEnum<T> implements Serializable {
062
063        /**
064         * 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
065         * of scenarios, such as POSTing transaction bundles to a server, or returning resource history.
066         * <p>
067         * Values for this key are of type <b>{@link InstantDt}</b>
068         * </p>
069         */
070        public static final ResourceMetadataKeySupportingAnyResource<InstantDt, IPrimitiveType<Date>> DELETED_AT = new ResourceMetadataKeySupportingAnyResource<InstantDt, IPrimitiveType<Date>>("DELETED_AT") {
071                private static final long serialVersionUID = 1L;
072
073                @Override
074                public InstantDt get(IResource theResource) {
075                        return getInstantFromMetadataOrNullIfNone(theResource.getResourceMetadata(), DELETED_AT);
076                }
077
078                @SuppressWarnings("unchecked")
079                @Override
080                public IPrimitiveType<Date> get(IAnyResource theResource) {
081                        return (IPrimitiveType<Date>) theResource.getUserData(DELETED_AT.name());
082                }
083
084                @Override
085                public void put(IResource theResource, InstantDt theObject) {
086                        theResource.getResourceMetadata().put(DELETED_AT, theObject);
087                }
088
089                @Override
090                public void put(IAnyResource theResource, IPrimitiveType<Date> theObject) {
091                        theResource.setUserData(DELETED_AT.name(), theObject);
092                }
093        };
094        /**
095         * 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
096         * <code>Bundle.entry.score</code> in a Bundle resource.
097         * <p>
098         * Note that search URL is only used in FHIR DSTU2 and later.
099         * </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}