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<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. 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}