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<IdDt></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}