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.util.Date;
035import java.util.List;
036
037/**
038 * 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
039 * data elements which are sent/receieved in HTTP Headers along with read/create resource requests, or properties which can be found in bundle entries.
040 * <p>
041 * 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
042 * {@link #UPDATED} value, which is the "last updated" time for that resource, use the following code:
043 * </p>
044 * <p>
045 * <code>InstantDt updated = ResourceMetadataKeyEnum.UPDATED.get(resource);</code>
046 * <p>
047 * <p>
048 * To set this value, use the following:
049 * </p>
050 * <p>
051 * <code>InstantDt update = new InstantDt("2011-01-02T11:22:33.0000Z"); // populate with the actual time<br>
052 * ResourceMetadataKeyEnum.UPDATED.put(resource, update);</code>
053 * </p>
054 * <p>
055 * 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
056 * define their own keys for storage in resource metadata if needed.
057 * </p>
058 */
059public abstract class ResourceMetadataKeyEnum<T> implements Serializable {
060
061        /**
062         * 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
063         * of scenarios, such as POSTing transaction bundles to a server, or returning resource history.
064         * <p>
065         * Values for this key are of type <b>{@link InstantDt}</b>
066         * </p>
067         */
068        public static final ResourceMetadataKeyEnum<IPrimitiveType<Date>> DELETED_AT =
069                        new ResourceMetadataKeyEnum<>("DELETED_AT", IPrimitiveType.class) {};
070        /**
071         * 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.
072         * 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
073         * server, or to "match" to indicate that the resource was returned because it matched the given search criteria.
074         * <p>
075         * Note that status is only used in FHIR DSTU2 and later.
076         * </p>
077         * <p>
078         * Values for this key are of type <b>{@link BundleEntrySearchModeEnum}</b>
079         * </p>
080         */
081        public static final ResourceMetadataKeyEnum<BundleEntrySearchModeEnum> ENTRY_SEARCH_MODE =
082                        new ResourceMetadataKeyEnum<>("ENTRY_SEARCH_MODE", BundleEntrySearchModeEnum.class) {};
083        /**
084         * 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
085         * 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
086         * 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
087         * result of a transaction to indicate to the client what operation was actually performed.
088         * <p>
089         * Note that status is only used in FHIR DSTU2 and later.
090         * </p>
091         * <p>
092         * Values for this key are of type <b>{@link BundleEntryTransactionMethodEnum}</b>
093         * </p>
094         */
095        public static final ResourceMetadataKeyEnum<BundleEntryTransactionMethodEnum> ENTRY_TRANSACTION_METHOD =
096                        new ResourceMetadataKeyEnum<>("ENTRY_TRANSACTION_OPERATION", BundleEntryTransactionMethodEnum.class) {};
097        /**
098         * The value for this key represents a {@link List} of profile IDs that this resource claims to conform to.
099         * <p>
100         * <p>
101         * 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.
102         * </p>
103         */
104        public static final ResourceMetadataKeyEnum<List<IdDt>> PROFILES =
105                        new ResourceMetadataKeyEnum<>("PROFILES", List.class) {};
106        /**
107         * 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.
108         * <p>
109         * Values for this key are of type <b>{@link InstantDt}</b>
110         * </p>
111         * <p>
112         * <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.
113         * </p>
114         *
115         * @see InstantDt
116         */
117        public static final ResourceMetadataKeyEnum<InstantDt> PUBLISHED =
118                        new ResourceMetadataKeyEnum<>("PUBLISHED", InstantDt.class) {};
119
120        public static final ResourceMetadataKeyEnum<List<BaseCodingDt>> SECURITY_LABELS =
121                        new ResourceMetadataKeyEnum<>("SECURITY_LABELS", List.class) {};
122        /**
123         * The value for this key is the list of tags associated with this resource
124         * <p>
125         * Values for this key are of type <b>{@link TagList}</b>
126         * </p>
127         *
128         * @see TagList
129         */
130        public static final ResourceMetadataKeyEnum<TagList> TAG_LIST =
131                        new ResourceMetadataKeyEnum<>("TAG_LIST", TagList.class) {};
132        /**
133         * 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
134         * case of methods that return a single resource (read, vread, etc.)
135         * <p>
136         * Values for this key are of type <b>{@link InstantDt}</b>
137         * </p>
138         *
139         * @see InstantDt
140         */
141        public static final ResourceMetadataKeyEnum<InstantDt> UPDATED =
142                        new ResourceMetadataKeyEnum<>("UPDATED", InstantDt.class) {};
143        /**
144         * The value for this key is the version ID of the resource object.
145         * <p>
146         * Values for this key are of type <b>{@link String}</b>
147         * </p>
148         *
149         * @deprecated The {@link IResource#getId()} resource ID will now be populated with the version ID via the {@link IdDt#getVersionIdPart()} method
150         */
151        @Deprecated
152        public static final ResourceMetadataKeyEnum<String> VERSION =
153                        new ResourceMetadataKeyEnum<>("VERSION", String.class) {};
154        /**
155         * The value for this key is the version ID of the resource object.
156         * <p>
157         * Values for this key are of type <b>{@link IdDt}</b>
158         * </p>
159         *
160         * @deprecated The {@link IResource#getId()} resource ID will now be populated with the version ID via the {@link IdDt#getVersionIdPart()} method
161         */
162        @Deprecated
163        public static final ResourceMetadataKeyEnum<IdDt> VERSION_ID =
164                        new ResourceMetadataKeyEnum<>("VERSION_ID", IdDt.class) {};
165
166        private static final long serialVersionUID = 1L;
167        private final String myValue;
168        private final Class<?> myType;
169
170        public ResourceMetadataKeyEnum(String theValue, Class<?> theType) {
171                myValue = theValue;
172                myType = theType;
173        }
174
175        // TODO: JA - Replace all of the various other get/put methods in subclasses with just using the two that are here
176        public T get(IBaseResource theResource) {
177                Object retVal;
178                if (theResource instanceof IAnyResource) {
179                        retVal = theResource.getUserData(name());
180                } else {
181                        retVal = ((IResource) theResource).getResourceMetadata().get(this);
182                }
183
184                if (retVal != null && !myType.isAssignableFrom(retVal.getClass())) {
185                        throw new InternalErrorException(Msg.code(1890) + "Found an object of type '"
186                                        + retVal.getClass().getCanonicalName()
187                                        + "' in resource metadata for key " + this.name() + " - Expected "
188                                        + myType.getCanonicalName());
189                }
190
191                //noinspection unchecked
192                return (T) retVal;
193        }
194
195        public void put(IBaseResource theResource, T theValue) {
196                if (theValue != null && !myType.isAssignableFrom(theValue.getClass())) {
197                        throw new InternalErrorException(Msg.code(1891) + "Can not put object of type '"
198                                        + theValue.getClass().getCanonicalName()
199                                        + "' in resource metadata for key " + this.name() + " - Expected "
200                                        + myType.getCanonicalName());
201                }
202
203                if (theResource instanceof IAnyResource) {
204                        theResource.setUserData(name(), theValue);
205                } else {
206                        ((IResource) theResource).getResourceMetadata().put(this, theValue);
207                }
208        }
209
210        @Override
211        public boolean equals(Object obj) {
212                if (this == obj) return true;
213                if (obj == null) return false;
214                if (getClass() != obj.getClass()) return false;
215                ResourceMetadataKeyEnum<?> other = (ResourceMetadataKeyEnum<?>) obj;
216                if (myValue == null) {
217                        return other.myValue == null;
218                } else return myValue.equals(other.myValue);
219        }
220
221        @Override
222        public int hashCode() {
223                final int prime = 31;
224                int result = 1;
225                result = prime * result + ((myValue == null) ? 0 : myValue.hashCode());
226                return result;
227        }
228
229        public String name() {
230                return myValue;
231        }
232
233        public static final class ExtensionResourceMetadataKey extends ResourceMetadataKeyEnum<ExtensionDt> {
234                public ExtensionResourceMetadataKey(String theUrl) {
235                        super(theUrl, ExtensionDt.class);
236                }
237        }
238}