001/*
002 * #%L
003 * HAPI FHIR - Core Library
004 * %%
005 * Copyright (C) 2014 - 2023 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 = new ResourceMetadataKeyEnum<>("DELETED_AT", IPrimitiveType.class) {
069        };
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 = new ResourceMetadataKeyEnum<>("ENTRY_SEARCH_MODE", BundleEntrySearchModeEnum.class) {
082        };
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 = new ResourceMetadataKeyEnum<>("ENTRY_TRANSACTION_OPERATION", BundleEntryTransactionMethodEnum.class) {
096        };
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 = new ResourceMetadataKeyEnum<>("PROFILES", List.class) {
105        };
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 = new ResourceMetadataKeyEnum<>("PUBLISHED", InstantDt.class) {
118        };
119        public static final ResourceMetadataKeyEnum<List<BaseCodingDt>> SECURITY_LABELS = new ResourceMetadataKeyEnum<>("SECURITY_LABELS", List.class) {
120        };
121        /**
122         * The value for this key is the list of tags associated with this resource
123         * <p>
124         * Values for this key are of type <b>{@link TagList}</b>
125         * </p>
126         *
127         * @see TagList
128         */
129        public static final ResourceMetadataKeyEnum<TagList> TAG_LIST = new ResourceMetadataKeyEnum<>("TAG_LIST", TagList.class) {
130        };
131        /**
132         * 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
133         * case of methods that return a single resource (read, vread, etc.)
134         * <p>
135         * Values for this key are of type <b>{@link InstantDt}</b>
136         * </p>
137         *
138         * @see InstantDt
139         */
140        public static final ResourceMetadataKeyEnum<InstantDt> UPDATED = new ResourceMetadataKeyEnum<>("UPDATED", InstantDt.class) {
141        };
142        /**
143         * The value for this key is the version ID of the resource object.
144         * <p>
145         * Values for this key are of type <b>{@link String}</b>
146         * </p>
147         *
148         * @deprecated The {@link IResource#getId()} resource ID will now be populated with the version ID via the {@link IdDt#getVersionIdPart()} method
149         */
150        @Deprecated
151        public static final ResourceMetadataKeyEnum<String> VERSION = new ResourceMetadataKeyEnum<>("VERSION", String.class) {
152        };
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 IdDt}</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<IdDt> VERSION_ID = new ResourceMetadataKeyEnum<>("VERSION_ID", IdDt.class) {
163        };
164        private static final long serialVersionUID = 1L;
165        private final String myValue;
166        private final Class<?> myType;
167
168        public ResourceMetadataKeyEnum(String theValue, Class<?> theType) {
169                myValue = theValue;
170                myType = theType;
171        }
172
173        // TODO: JA - Replace all of the various other get/put methods in subclasses with just using the two that are here
174        public T get(IBaseResource theResource) {
175                Object retVal;
176                if (theResource instanceof IAnyResource) {
177                        retVal = theResource.getUserData(name());
178                } else {
179                        retVal = ((IResource) theResource).getResourceMetadata().get(this);
180                }
181
182                if (retVal != null && !myType.isAssignableFrom(retVal.getClass())) {
183                        throw new InternalErrorException(Msg.code(1890) + "Found an object of type '" + retVal.getClass().getCanonicalName()
184                                + "' in resource metadata for key " + this.name() + " - Expected "
185                                + myType.getCanonicalName());
186                }
187
188                //noinspection unchecked
189                return (T) retVal;
190        }
191
192        public void put(IBaseResource theResource, T theValue) {
193                if (theValue != null && !myType.isAssignableFrom(theValue.getClass())) {
194                        throw new InternalErrorException(Msg.code(1891) + "Can not put object of type '" + theValue.getClass().getCanonicalName()
195                                + "' in resource metadata for key " + this.name() + " - Expected "
196                                + myType.getCanonicalName());
197                }
198
199                if (theResource instanceof IAnyResource) {
200                        theResource.setUserData(name(), theValue);
201                } else {
202                        ((IResource) theResource).getResourceMetadata().put(this, theValue);
203                }
204        }
205
206        @Override
207        public boolean equals(Object obj) {
208                if (this == obj)
209                        return true;
210                if (obj == null)
211                        return false;
212                if (getClass() != obj.getClass())
213                        return false;
214                ResourceMetadataKeyEnum<?> other = (ResourceMetadataKeyEnum<?>) obj;
215                if (myValue == null) {
216                        return other.myValue == null;
217                } else return myValue.equals(other.myValue);
218        }
219
220        @Override
221        public int hashCode() {
222                final int prime = 31;
223                int result = 1;
224                result = prime * result + ((myValue == null) ? 0 : myValue.hashCode());
225                return result;
226        }
227
228        public String name() {
229                return myValue;
230        }
231
232        public static final class ExtensionResourceMetadataKey extends ResourceMetadataKeyEnum<ExtensionDt> {
233                public ExtensionResourceMetadataKey(String theUrl) {
234                        super(theUrl, ExtensionDt.class);
235                }
236        }
237}