1 /* 2 * Copyright (C) 2024 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.health.connect.datatypes; 18 19 import static android.health.connect.datatypes.validation.ValidationUtils.validateIntDefValue; 20 21 import static com.android.healthfitness.flags.Flags.FLAG_PERSONAL_HEALTH_RECORD; 22 23 import static java.util.Objects.hash; 24 import static java.util.Objects.requireNonNull; 25 26 import android.annotation.FlaggedApi; 27 import android.annotation.IntDef; 28 import android.annotation.NonNull; 29 import android.os.Parcel; 30 import android.os.Parcelable; 31 32 import java.lang.annotation.Retention; 33 import java.lang.annotation.RetentionPolicy; 34 import java.util.Set; 35 36 /** 37 * A class to capture the FHIR resource data. This is the class used for all supported FHIR resource 38 * types, which is a subset of the resource list on <a 39 * href="https://build.fhir.org/resourcelist.html">the official FHIR website</a>. The list of 40 * supported types will likely expand in future releases. 41 * 42 * <p>FHIR stands for the <a href="https://hl7.org/fhir/">Fast Healthcare Interoperability Resources 43 * </a> standard. 44 */ 45 @FlaggedApi(FLAG_PERSONAL_HEALTH_RECORD) 46 public final class FhirResource implements Parcelable { 47 // LINT.IfChange 48 /** 49 * FHIR resource type for <a href="https://www.hl7.org/fhir/immunization.html">Immunization</a>. 50 */ 51 public static final int FHIR_RESOURCE_TYPE_IMMUNIZATION = 1; 52 53 /** 54 * FHIR resource type for <a 55 * href="https://www.hl7.org/fhir/allergyintolerance.html">AllergyIntolerance</a>. 56 */ 57 public static final int FHIR_RESOURCE_TYPE_ALLERGY_INTOLERANCE = 2; 58 59 /** 60 * FHIR resource type for a <a href="https://www.hl7.org/fhir/observation.html">FHIR 61 * Observation</a>. 62 */ 63 public static final int FHIR_RESOURCE_TYPE_OBSERVATION = 3; 64 65 /** 66 * FHIR resource type for a <a href="https://www.hl7.org/fhir/condition.html">FHIR 67 * Condition</a>. 68 */ 69 public static final int FHIR_RESOURCE_TYPE_CONDITION = 4; 70 71 /** 72 * FHIR resource type for a <a href="https://www.hl7.org/fhir/procedure.html">FHIR 73 * Procedure</a>. 74 */ 75 public static final int FHIR_RESOURCE_TYPE_PROCEDURE = 5; 76 77 /** 78 * FHIR resource type for a <a href="https://www.hl7.org/fhir/medication.html">FHIR 79 * Medication</a>. 80 */ 81 public static final int FHIR_RESOURCE_TYPE_MEDICATION = 6; 82 83 /** 84 * FHIR resource type for a <a href="https://www.hl7.org/fhir/medicationrequest.html">FHIR 85 * MedicationRequest</a>. 86 */ 87 public static final int FHIR_RESOURCE_TYPE_MEDICATION_REQUEST = 7; 88 89 /** 90 * FHIR resource type for a <a href="https://www.hl7.org/fhir/medicationstatement.html">FHIR 91 * MedicationStatement</a>. 92 */ 93 public static final int FHIR_RESOURCE_TYPE_MEDICATION_STATEMENT = 8; 94 95 /** 96 * FHIR resource type for a <a href="https://www.hl7.org/fhir/patient.html">FHIR Patient</a>. 97 */ 98 public static final int FHIR_RESOURCE_TYPE_PATIENT = 9; 99 100 /** 101 * FHIR resource type for a <a href="https://www.hl7.org/fhir/practitioner.html">FHIR 102 * Practitioner</a>. 103 */ 104 public static final int FHIR_RESOURCE_TYPE_PRACTITIONER = 10; 105 106 /** 107 * FHIR resource type for a <a href="https://www.hl7.org/fhir/practitionerrole.html">FHIR 108 * PractitionerRole</a>. 109 */ 110 public static final int FHIR_RESOURCE_TYPE_PRACTITIONER_ROLE = 11; 111 112 /** 113 * FHIR resource type for a <a href="https://www.hl7.org/fhir/encounter.html">FHIR 114 * Encounter</a>. 115 */ 116 public static final int FHIR_RESOURCE_TYPE_ENCOUNTER = 12; 117 118 /** 119 * FHIR resource type for a <a href="https://www.hl7.org/fhir/location.html">FHIR Location</a>. 120 */ 121 public static final int FHIR_RESOURCE_TYPE_LOCATION = 13; 122 123 /** 124 * FHIR resource type for a <a href="https://www.hl7.org/fhir/organization.html">FHIR 125 * Organization</a>. 126 */ 127 public static final int FHIR_RESOURCE_TYPE_ORGANIZATION = 14; 128 129 // LINT.ThenChange(/service/proto/phr/fhir_spec_utils.py:fhir_resource_type_mapping) 130 131 /** @hide */ 132 @IntDef({ 133 FHIR_RESOURCE_TYPE_IMMUNIZATION, 134 FHIR_RESOURCE_TYPE_ALLERGY_INTOLERANCE, 135 FHIR_RESOURCE_TYPE_OBSERVATION, 136 FHIR_RESOURCE_TYPE_CONDITION, 137 FHIR_RESOURCE_TYPE_PROCEDURE, 138 FHIR_RESOURCE_TYPE_MEDICATION, 139 FHIR_RESOURCE_TYPE_MEDICATION_REQUEST, 140 FHIR_RESOURCE_TYPE_MEDICATION_STATEMENT, 141 FHIR_RESOURCE_TYPE_PATIENT, 142 FHIR_RESOURCE_TYPE_PRACTITIONER, 143 FHIR_RESOURCE_TYPE_PRACTITIONER_ROLE, 144 FHIR_RESOURCE_TYPE_ENCOUNTER, 145 FHIR_RESOURCE_TYPE_LOCATION, 146 FHIR_RESOURCE_TYPE_ORGANIZATION, 147 }) 148 @Retention(RetentionPolicy.SOURCE) 149 public @interface FhirResourceType {} 150 151 @FhirResourceType private final int mType; 152 @NonNull private final String mId; 153 @NonNull private final String mData; 154 155 /** 156 * Creates a new instance of {@link FhirResource}. Please see {@link FhirResource.Builder} for 157 * more detailed parameters information. 158 */ FhirResource(@hirResourceType int type, @NonNull String id, @NonNull String data)159 private FhirResource(@FhirResourceType int type, @NonNull String id, @NonNull String data) { 160 validateFhirResourceType(type); 161 requireNonNull(id); 162 requireNonNull(data); 163 164 mType = type; 165 mId = id; 166 mData = data; 167 } 168 169 /** 170 * Constructs this object with the data present in {@code parcel}. It should be in the same 171 * order as {@link FhirResource#writeToParcel}. 172 */ FhirResource(@onNull Parcel in)173 private FhirResource(@NonNull Parcel in) { 174 requireNonNull(in); 175 mType = in.readInt(); 176 validateFhirResourceType(mType); 177 mId = requireNonNull(in.readString()); 178 mData = requireNonNull(in.readString()); 179 } 180 181 @NonNull 182 public static final Creator<FhirResource> CREATOR = 183 new Creator<>() { 184 @Override 185 public FhirResource createFromParcel(Parcel in) { 186 return new FhirResource(in); 187 } 188 189 @Override 190 public FhirResource[] newArray(int size) { 191 return new FhirResource[size]; 192 } 193 }; 194 195 /** 196 * Returns the FHIR resource type. This is extracted from the "resourceType" field in {@link 197 * #getData}. 198 * 199 * <p>The list of supported types is a subset of the resource list on <a 200 * href="https://build.fhir.org/resourcelist.html">the official FHIR website</a>. For a list of 201 * supported types, see the {@link FhirResource} constants, such as {@link 202 * #FHIR_RESOURCE_TYPE_IMMUNIZATION}. Clients should be aware that this list is non exhaustive 203 * and may increase in future releases when additional types will need to be handled. 204 */ 205 @FhirResourceType getType()206 public int getType() { 207 return mType; 208 } 209 210 /** 211 * Returns the FHIR resource ID. This is extracted from the "id" field in {@code data}. This is 212 * NOT a unique identifier among all {@link FhirResource}s. 213 */ 214 @NonNull getId()215 public String getId() { 216 return mId; 217 } 218 219 /** Returns the FHIR resource data in JSON representation. */ 220 @NonNull getData()221 public String getData() { 222 return mData; 223 } 224 225 @Override describeContents()226 public int describeContents() { 227 return 0; 228 } 229 230 @Override writeToParcel(@onNull Parcel dest, int flags)231 public void writeToParcel(@NonNull Parcel dest, int flags) { 232 requireNonNull(dest); 233 dest.writeInt(getType()); 234 dest.writeString(getId()); 235 dest.writeString(getData()); 236 } 237 238 /** 239 * Valid set of values for this IntDef. Update this set when add new type or deprecate existing 240 * type. 241 */ 242 private static final Set<Integer> VALID_TYPES = 243 Set.of( 244 FHIR_RESOURCE_TYPE_IMMUNIZATION, 245 FHIR_RESOURCE_TYPE_ALLERGY_INTOLERANCE, 246 FHIR_RESOURCE_TYPE_OBSERVATION, 247 FHIR_RESOURCE_TYPE_CONDITION, 248 FHIR_RESOURCE_TYPE_PROCEDURE, 249 FHIR_RESOURCE_TYPE_MEDICATION, 250 FHIR_RESOURCE_TYPE_MEDICATION_REQUEST, 251 FHIR_RESOURCE_TYPE_MEDICATION_STATEMENT, 252 FHIR_RESOURCE_TYPE_PATIENT, 253 FHIR_RESOURCE_TYPE_PRACTITIONER, 254 FHIR_RESOURCE_TYPE_PRACTITIONER_ROLE, 255 FHIR_RESOURCE_TYPE_ENCOUNTER, 256 FHIR_RESOURCE_TYPE_LOCATION, 257 FHIR_RESOURCE_TYPE_ORGANIZATION); 258 259 /** 260 * Validates the provided {@code fhirResourceType} is in the {@link FhirResource#VALID_TYPES} 261 * set. 262 * 263 * <p>Throws {@link IllegalArgumentException} if not. 264 * 265 * @hide 266 */ validateFhirResourceType(@hirResourceType int fhirResourceType)267 public static void validateFhirResourceType(@FhirResourceType int fhirResourceType) { 268 validateIntDefValue(fhirResourceType, VALID_TYPES, FhirResourceType.class.getSimpleName()); 269 } 270 271 @Override equals(Object o)272 public boolean equals(Object o) { 273 if (this == o) return true; 274 if (!(o instanceof FhirResource that)) return false; 275 return getType() == that.getType() 276 && getId().equals(that.getId()) 277 && getData().equals(that.getData()); 278 } 279 280 @Override hashCode()281 public int hashCode() { 282 return hash(getType(), getId(), getData()); 283 } 284 285 @Override toString()286 public String toString() { 287 StringBuilder sb = new StringBuilder(); 288 sb.append(this.getClass().getSimpleName()).append("{"); 289 sb.append("type=").append(getType()); 290 sb.append(",id=").append(getId()); 291 sb.append(",data=").append(getData()); 292 sb.append("}"); 293 return sb.toString(); 294 } 295 296 /** Builder class for {@link FhirResource}. */ 297 public static final class Builder { 298 @FhirResourceType private int mType; 299 @NonNull private String mId; 300 @NonNull private String mData; 301 302 /** 303 * Constructs a new {@link FhirResource.Builder} instance. 304 * 305 * @param type The FHIR resource type extracted from the "resourceType" field in {@code 306 * data}. 307 * @param id The FHIR resource ID extracted from the "id" field in {@code data}. 308 * @param data The FHIR resource data in JSON representation. 309 * @throws IllegalArgumentException if the provided FHIR resource {@code type} is not a 310 * valid supported type. 311 */ Builder(@hirResourceType int type, @NonNull String id, @NonNull String data)312 public Builder(@FhirResourceType int type, @NonNull String id, @NonNull String data) { 313 validateFhirResourceType(type); 314 requireNonNull(id); 315 requireNonNull(data); 316 317 mType = type; 318 mId = id; 319 mData = data; 320 } 321 322 /** Constructs a clone of the other {@link FhirResource.Builder}. */ Builder(@onNull Builder other)323 public Builder(@NonNull Builder other) { 324 requireNonNull(other); 325 mType = other.mType; 326 mId = other.mId; 327 mData = other.mData; 328 } 329 330 /** Constructs a clone of the other {@link FhirResource} instance. */ Builder(@onNull FhirResource other)331 public Builder(@NonNull FhirResource other) { 332 requireNonNull(other); 333 mType = other.getType(); 334 mId = other.getId(); 335 mData = other.getData(); 336 } 337 338 /** 339 * Sets the FHIR resource type. This is extracted from the "resourceType" field in {@code 340 * data}. 341 * 342 * @throws IllegalArgumentException if the provided FHIR resource {@code type} is not a 343 * valid supported type. 344 */ 345 @NonNull setType(@hirResourceType int type)346 public Builder setType(@FhirResourceType int type) { 347 validateFhirResourceType(type); 348 mType = type; 349 return this; 350 } 351 352 /** 353 * Sets the FHIR resource ID. This is extracted from the "id" field in {@code data}. This is 354 * NOT a unique identifier among all {@link FhirResource}s. 355 */ 356 @NonNull setId(@onNull String id)357 public Builder setId(@NonNull String id) { 358 requireNonNull(id); 359 mId = id; 360 return this; 361 } 362 363 /** Sets the FHIR resource data in JSON representation. */ 364 @NonNull setData(@onNull String data)365 public Builder setData(@NonNull String data) { 366 requireNonNull(data); 367 mData = data; 368 return this; 369 } 370 371 /** Returns a new instance of {@link FhirResource} with the specified parameters. */ 372 @NonNull build()373 public FhirResource build() { 374 return new FhirResource(mType, mId, mData); 375 } 376 } 377 } 378