001/*
002 * #%L
003 * HAPI FHIR - Core Library
004 * %%
005 * Copyright (C) 2014 - 2024 Smile CDR, Inc.
006 * %%
007 * Licensed under the Apache License, Version 2.0 (the "License");
008 * you may not use this file except in compliance with the License.
009 * You may obtain a copy of the License at
010 *
011 *      http://www.apache.org/licenses/LICENSE-2.0
012 *
013 * Unless required by applicable law or agreed to in writing, software
014 * distributed under the License is distributed on an "AS IS" BASIS,
015 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
016 * See the License for the specific language governing permissions and
017 * limitations under the License.
018 * #L%
019 */
020package ca.uhn.fhir.model.api;
021
022import ca.uhn.fhir.i18n.Msg;
023import ca.uhn.fhir.model.base.composite.BaseCodingDt;
024import ca.uhn.fhir.model.primitive.IdDt;
025import ca.uhn.fhir.model.primitive.InstantDt;
026import ca.uhn.fhir.model.valueset.BundleEntrySearchModeEnum;
027import ca.uhn.fhir.model.valueset.BundleEntryTransactionMethodEnum;
028import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
029import org.hl7.fhir.instance.model.api.IAnyResource;
030import org.hl7.fhir.instance.model.api.IBaseResource;
031import org.hl7.fhir.instance.model.api.IPrimitiveType;
032
033import java.io.Serializable;
034import java.math.BigDecimal;
035import java.util.Date;
036import java.util.List;
037
038/**
039 * 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
040 * data elements which are sent/receieved in HTTP Headers along with read/create resource requests, or properties which can be found in bundle entries.
041 * <p>
042 * 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
043 * {@link #UPDATED} value, which is the "last updated" time for that resource, use the following code:
044 * </p>
045 * <p>
046 * <code>InstantDt updated = ResourceMetadataKeyEnum.UPDATED.get(resource);</code>
047 * <p>
048 * <p>
049 * To set this value, use the following:
050 * </p>
051 * <p>
052 * <code>InstantDt update = new InstantDt("2011-01-02T11:22:33.0000Z"); // populate with the actual time<br>
053 * ResourceMetadataKeyEnum.UPDATED.put(resource, update);</code>
054 * </p>
055 * <p>
056 * 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
057 * define their own keys for storage in resource metadata if needed.
058 * </p>
059 */
060public abstract class ResourceMetadataKeyEnum<T> implements Serializable {
061
062        /**
063         * 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
064         * of scenarios, such as POSTing transaction bundles to a server, or returning resource history.
065         * <p>
066         * Values for this key are of type <b>{@link InstantDt}</b>
067         * </p>
068         */
069        public static final ResourceMetadataKeyEnum<IPrimitiveType<Date>> DELETED_AT =
070                        new ResourceMetadataKeyEnum<>("DELETED_AT", IPrimitiveType.class) {};
071        /**
072         * 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.
073         * 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
074         * server, or to "match" to indicate that the resource was returned because it matched the given search criteria.
075         * <p>
076         * Note that status is only used in FHIR DSTU2 and later.
077         * </p>
078         * <p>
079         * Values for this key are of type <b>{@link BundleEntrySearchModeEnum}</b>
080         * </p>
081         */
082        public static final ResourceMetadataKeyEnum<BundleEntrySearchModeEnum> ENTRY_SEARCH_MODE =
083                        new ResourceMetadataKeyEnum<>("ENTRY_SEARCH_MODE", BundleEntrySearchModeEnum.class) {};
084        /**
085         * If present and populated with a decimal value, contains the "bundle entry search score", which is the value of the status field in the Bundle entry containing this resource.
086         * The value for this key corresponds to field <code>Bundle.entry.search.score</code>. This value represents the search ranking score, where 1.0 is relevant and 0.0 is irrelevant.
087         * <p>
088         * Note that status is only used in FHIR DSTU2 and later.
089         * </p>
090         */
091        public static final ResourceMetadataKeyEnum<BigDecimal> ENTRY_SEARCH_SCORE =
092                        new ResourceMetadataKeyEnum<>("ENTRY_SEARCH_SCORE", BigDecimal.class) {};
093        /**
094         * 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
095         * 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
096         * 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
097         * result of a transaction to indicate to the client what operation was actually performed.
098         * <p>
099         * Note that status is only used in FHIR DSTU2 and later.
100         * </p>
101         * <p>
102         * Values for this key are of type <b>{@link BundleEntryTransactionMethodEnum}</b>
103         * </p>
104         */
105        public static final ResourceMetadataKeyEnum<BundleEntryTransactionMethodEnum> ENTRY_TRANSACTION_METHOD =
106                        new ResourceMetadataKeyEnum<>("ENTRY_TRANSACTION_OPERATION", BundleEntryTransactionMethodEnum.class) {};
107        /**
108         * The value for this key represents a {@link List} of profile IDs that this resource claims to conform to.
109         * <p>
110         * <p>
111         * 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.
112         * </p>
113         */
114        public static final ResourceMetadataKeyEnum<List<IdDt>> PROFILES =
115                        new ResourceMetadataKeyEnum<>("PROFILES", List.class) {};
116        /**
117         * 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.
118         * <p>
119         * Values for this key are of type <b>{@link InstantDt}</b>
120         * </p>
121         * <p>
122         * <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.
123         * </p>
124         *
125         * @see InstantDt
126         */
127        public static final ResourceMetadataKeyEnum<InstantDt> PUBLISHED =
128                        new ResourceMetadataKeyEnum<>("PUBLISHED", InstantDt.class) {};
129
130        public static final ResourceMetadataKeyEnum<List<BaseCodingDt>> SECURITY_LABELS =
131                        new ResourceMetadataKeyEnum<>("SECURITY_LABELS", List.class) {};
132        /**
133         * The value for this key is the list of tags associated with this resource
134         * <p>
135         * Values for this key are of type <b>{@link TagList}</b>
136         * </p>
137         *
138         * @see TagList
139         */
140        public static final ResourceMetadataKeyEnum<TagList> TAG_LIST =
141                        new ResourceMetadataKeyEnum<>("TAG_LIST", TagList.class) {};
142        /**
143         * 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
144         * case of methods that return a single resource (read, vread, etc.)
145         * <p>
146         * Values for this key are of type <b>{@link InstantDt}</b>
147         * </p>
148         *
149         * @see InstantDt
150         */
151        public static final ResourceMetadataKeyEnum<InstantDt> UPDATED =
152                        new ResourceMetadataKeyEnum<>("UPDATED", InstantDt.class) {};
153        /**
154         * The value for this key is the version ID of the resource object.
155         * <p>
156         * Values for this key are of type <b>{@link String}</b>
157         * </p>
158         *
159         * @deprecated The {@link IResource#getId()} resource ID will now be populated with the version ID via the {@link IdDt#getVersionIdPart()} method
160         */
161        @Deprecated
162        public static final ResourceMetadataKeyEnum<String> VERSION =
163                        new ResourceMetadataKeyEnum<>("VERSION", String.class) {};
164        /**
165         * The value for this key is the version ID of the resource object.
166         * <p>
167         * Values for this key are of type <b>{@link IdDt}</b>
168         * </p>
169         *
170         * @deprecated The {@link IResource#getId()} resource ID will now be populated with the version ID via the {@link IdDt#getVersionIdPart()} method
171         */
172        @Deprecated
173        public static final ResourceMetadataKeyEnum<IdDt> VERSION_ID =
174                        new ResourceMetadataKeyEnum<>("VERSION_ID", IdDt.class) {};
175
176        private static final long serialVersionUID = 1L;
177        private final String myValue;
178        private final Class<?> myType;
179
180        public ResourceMetadataKeyEnum(String theValue, Class<?> theType) {
181                myValue = theValue;
182                myType = theType;
183        }
184
185        // TODO: JA - Replace all of the various other get/put methods in subclasses with just using the two that are here
186        public T get(IBaseResource theResource) {
187                Object retVal;
188                if (theResource instanceof IAnyResource) {
189                        retVal = theResource.getUserData(name());
190                } else {
191                        retVal = ((IResource) theResource).getResourceMetadata().get(this);
192                }
193
194                if (retVal != null && !myType.isAssignableFrom(retVal.getClass())) {
195                        throw new InternalErrorException(Msg.code(1890) + "Found an object of type '"
196                                        + retVal.getClass().getCanonicalName()
197                                        + "' in resource metadata for key " + this.name() + " - Expected "
198                                        + myType.getCanonicalName());
199                }
200
201                //noinspection unchecked
202                return (T) retVal;
203        }
204
205        public void put(IBaseResource theResource, T theValue) {
206                if (theValue != null && !myType.isAssignableFrom(theValue.getClass())) {
207                        throw new InternalErrorException(Msg.code(1891) + "Can not put object of type '"
208                                        + theValue.getClass().getCanonicalName()
209                                        + "' in resource metadata for key " + this.name() + " - Expected "
210                                        + myType.getCanonicalName());
211                }
212
213                if (theResource instanceof IAnyResource) {
214                        theResource.setUserData(name(), theValue);
215                } else {
216                        ((IResource) theResource).getResourceMetadata().put(this, theValue);
217                }
218        }
219
220        @Override
221        public boolean equals(Object obj) {
222                if (this == obj) return true;
223                if (obj == null) return false;
224                if (getClass() != obj.getClass()) return false;
225                ResourceMetadataKeyEnum<?> other = (ResourceMetadataKeyEnum<?>) obj;
226                if (myValue == null) {
227                        return other.myValue == null;
228                } else return myValue.equals(other.myValue);
229        }
230
231        @Override
232        public int hashCode() {
233                final int prime = 31;
234                int result = 1;
235                result = prime * result + ((myValue == null) ? 0 : myValue.hashCode());
236                return result;
237        }
238
239        public String name() {
240                return myValue;
241        }
242
243        public static final class ExtensionResourceMetadataKey extends ResourceMetadataKeyEnum<ExtensionDt> {
244                public ExtensionResourceMetadataKey(String theUrl) {
245                        super(theUrl, ExtensionDt.class);
246                }
247        }
248}