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.context; 021 022import ca.uhn.fhir.i18n.Msg; 023import ca.uhn.fhir.model.api.IFhirVersion; 024import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; 025 026public enum FhirVersionEnum { 027 028 /* 029 * *********************** 030 * Don't auto-sort this type!!! 031 * 032 * Or more accurately, entries should be sorted from OLDEST FHIR release 033 * to NEWEST FHIR release instead of alphabetically 034 * *********************** 035 */ 036 037 DSTU2("ca.uhn.fhir.model.dstu2.FhirDstu2", null, false, new Version("1.0.2")), 038 039 DSTU2_HL7ORG("org.hl7.fhir.dstu2.hapi.ctx.FhirDstu2Hl7Org", DSTU2, true, new Version("1.0.2")), 040 041 DSTU2_1("org.hl7.fhir.dstu2016may.hapi.ctx.FhirDstu2_1", null, true, new Version("1.4.0")), 042 043 DSTU3("org.hl7.fhir.dstu3.hapi.ctx.FhirDstu3", null, true, new Dstu3Version()), 044 045 R4("org.hl7.fhir.r4.hapi.ctx.FhirR4", null, true, new R4Version()), 046 047 R4B("org.hl7.fhir.r4b.hapi.ctx.FhirR4B", null, true, new R4BVersion()), 048 049 R5("org.hl7.fhir.r5.hapi.ctx.FhirR5", null, true, new R5Version()); 050 051 // If you add new constants, add to the various methods below too! 052 053 private final FhirVersionEnum myEquivalent; 054 private final boolean myIsRi; 055 private final String myVersionClass; 056 private volatile Boolean myPresentOnClasspath; 057 private volatile IFhirVersion myVersionImplementation; 058 private String myFhirVersionString; 059 060 FhirVersionEnum( 061 String theVersionClass, 062 FhirVersionEnum theEquivalent, 063 boolean theIsRi, 064 IVersionProvider theVersionExtractor) { 065 myVersionClass = theVersionClass; 066 myEquivalent = theEquivalent; 067 myFhirVersionString = theVersionExtractor.provideVersion(); 068 myIsRi = theIsRi; 069 } 070 071 public String getFhirVersionString() { 072 return myFhirVersionString; 073 } 074 075 public IFhirVersion getVersionImplementation() { 076 if (!isPresentOnClasspath()) { 077 throw new IllegalStateException(Msg.code(1709) + "Version " + name() + " is not present on classpath"); 078 } 079 if (myVersionImplementation == null) { 080 try { 081 myVersionImplementation = 082 (IFhirVersion) Class.forName(myVersionClass).newInstance(); 083 } catch (Exception e) { 084 throw new InternalErrorException(Msg.code(1710) + "Failed to instantiate FHIR version " + name(), e); 085 } 086 } 087 return myVersionImplementation; 088 } 089 090 public boolean isEqualOrNewerThan(FhirVersionEnum theVersion) { 091 return ordinal() >= theVersion.ordinal(); 092 } 093 094 public boolean isEquivalentTo(FhirVersionEnum theVersion) { 095 if (this.equals(theVersion)) { 096 return true; 097 } 098 if (myEquivalent != null) { 099 return myEquivalent.equals(theVersion); 100 } 101 return false; 102 } 103 104 public boolean isNewerThan(FhirVersionEnum theVersion) { 105 return !isEquivalentTo(theVersion) && ordinal() > theVersion.ordinal(); 106 } 107 108 public boolean isOlderThan(FhirVersionEnum theVersion) { 109 return !isEquivalentTo(theVersion) && ordinal() < theVersion.ordinal(); 110 } 111 112 /** 113 * Returns true if the given version is present on the classpath 114 */ 115 public boolean isPresentOnClasspath() { 116 Boolean retVal = myPresentOnClasspath; 117 if (retVal == null) { 118 try { 119 Class.forName(myVersionClass); 120 retVal = true; 121 } catch (Exception e) { 122 retVal = false; 123 } 124 myPresentOnClasspath = retVal; 125 } 126 return retVal; 127 } 128 129 /** 130 * Is this version using the HL7.org RI structures? 131 */ 132 public boolean isRi() { 133 return myIsRi; 134 } 135 136 /** 137 * Creates a new FhirContext for this FHIR version 138 */ 139 public FhirContext newContext() { 140 return new FhirContext(this); 141 } 142 143 /** 144 * Creates a new FhirContext for this FHIR version, or returns a previously created one if one exists. This 145 * method uses {@link FhirContext#forCached(FhirVersionEnum)} to return a cached instance. 146 */ 147 public FhirContext newContextCached() { 148 return FhirContext.forCached(this); 149 } 150 151 private interface IVersionProvider { 152 String provideVersion(); 153 } 154 155 /** 156 * Given a FHIR model object type, determine which version of FHIR it is for 157 */ 158 public static FhirVersionEnum determineVersionForType(Class<?> theFhirType) { 159 switch (theFhirType.getName()) { 160 case "ca.uhn.fhir.model.api.BaseElement": 161 return DSTU2; 162 case "org.hl7.fhir.dstu2.model.Base": 163 return DSTU2_HL7ORG; 164 case "org.hl7.fhir.dstu3.model.Base": 165 return DSTU3; 166 case "org.hl7.fhir.r4.model.Base": 167 return R4; 168 case "org.hl7.fhir.r5.model.Base": 169 return R5; 170 case "java.lang.Object": 171 return null; 172 default: 173 return determineVersionForType(theFhirType.getSuperclass()); 174 } 175 } 176 177 private static class Version implements IVersionProvider { 178 179 private String myVersion; 180 181 public Version(String theVersion) { 182 super(); 183 myVersion = theVersion; 184 } 185 186 @Override 187 public String provideVersion() { 188 return myVersion; 189 } 190 } 191 192 /** 193 * This class attempts to read the FHIR version from the actual model 194 * classes in order to supply an accurate version string even over time 195 */ 196 private static class Dstu3Version implements IVersionProvider { 197 198 private String myVersion; 199 200 Dstu3Version() { 201 try { 202 Class<?> c = Class.forName("org.hl7.fhir.dstu3.model.Constants"); 203 myVersion = (String) c.getDeclaredField("VERSION").get(null); 204 } catch (Exception e) { 205 myVersion = "3.0.2"; 206 } 207 } 208 209 @Override 210 public String provideVersion() { 211 return myVersion; 212 } 213 } 214 215 private static class R4Version implements IVersionProvider { 216 217 private String myVersion; 218 219 R4Version() { 220 try { 221 Class<?> c = Class.forName("org.hl7.fhir.r4.model.Constants"); 222 myVersion = (String) c.getDeclaredField("VERSION").get(null); 223 } catch (Exception e) { 224 myVersion = "4.0.2"; 225 } 226 } 227 228 @Override 229 public String provideVersion() { 230 return myVersion; 231 } 232 } 233 234 private static class R4BVersion implements IVersionProvider { 235 236 private String myVersion; 237 238 R4BVersion() { 239 try { 240 Class<?> c = Class.forName("org.hl7.fhir.r4b.model.Constants"); 241 myVersion = (String) c.getDeclaredField("VERSION").get(null); 242 } catch (Exception e) { 243 myVersion = "4.3.0"; 244 } 245 } 246 247 @Override 248 public String provideVersion() { 249 return myVersion; 250 } 251 } 252 253 private static class R5Version implements IVersionProvider { 254 255 private String myVersion; 256 257 R5Version() { 258 try { 259 Class<?> c = Class.forName("org.hl7.fhir.r5.model.Constants"); 260 myVersion = (String) c.getDeclaredField("VERSION").get(null); 261 } catch (Exception e) { 262 myVersion = "5.0.0"; 263 } 264 } 265 266 @Override 267 public String provideVersion() { 268 return myVersion; 269 } 270 } 271 272 /** 273 * Returns the {@link FhirVersionEnum} which corresponds to a specific version of 274 * FHIR. Partial version strings (e.g. "3.0") are acceptable. This method will 275 * also accept version names such as "DSTU2", "STU3", "R5", etc. 276 * 277 * @return Returns null if no version exists matching the given string 278 */ 279 public static FhirVersionEnum forVersionString(String theVersionString) { 280 281 // Trim the point release 282 String versionString = theVersionString; 283 int firstDot = versionString.indexOf('.'); 284 if (firstDot > 0) { 285 int secondDot = versionString.indexOf('.', firstDot + 1); 286 if (secondDot > 0) { 287 versionString = versionString.substring(0, secondDot); 288 } 289 } 290 291 for (FhirVersionEnum next : values()) { 292 if (next.getFhirVersionString().startsWith(versionString)) { 293 return next; 294 } 295 } 296 297 switch (theVersionString) { 298 case "DSTU2": 299 return FhirVersionEnum.DSTU2; 300 case "DSTU3": 301 case "STU3": 302 return FhirVersionEnum.DSTU3; 303 case "R4": 304 return FhirVersionEnum.R4; 305 case "R4B": 306 return FhirVersionEnum.R4B; 307 case "R5": 308 return FhirVersionEnum.R5; 309 } 310 311 return null; 312 } 313}