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 * @deprecated since 7.7. Use {@link FhirContext#forVersion(FhirVersionEnum)} instead 139 */ 140 @Deprecated(forRemoval = true, since = "7.7") 141 public FhirContext newContext() { 142 return FhirContext.forVersion(this); 143 } 144 145 /** 146 * Creates a new FhirContext for this FHIR version, or returns a previously created one if one exists. This 147 * method uses {@link FhirContext#forCached(FhirVersionEnum)} to return a cached instance. 148 * @deprecated since 7.7. Use {@link FhirContext#forCached(FhirVersionEnum)} instead 149 */ 150 @Deprecated(forRemoval = true, since = "7.7") 151 public FhirContext newContextCached() { 152 return FhirContext.forCached(this); 153 } 154 155 private interface IVersionProvider { 156 String provideVersion(); 157 } 158 159 /** 160 * Given a FHIR model object type, determine which version of FHIR it is for 161 */ 162 public static FhirVersionEnum determineVersionForType(Class<?> theFhirType) { 163 switch (theFhirType.getName()) { 164 case "ca.uhn.fhir.model.api.BaseElement": 165 return DSTU2; 166 case "org.hl7.fhir.dstu2.model.Base": 167 return DSTU2_HL7ORG; 168 case "org.hl7.fhir.dstu3.model.Base": 169 return DSTU3; 170 case "org.hl7.fhir.r4.model.Base": 171 return R4; 172 case "org.hl7.fhir.r5.model.Base": 173 return R5; 174 case "java.lang.Object": 175 return null; 176 default: 177 return determineVersionForType(theFhirType.getSuperclass()); 178 } 179 } 180 181 private static class Version implements IVersionProvider { 182 183 private String myVersion; 184 185 public Version(String theVersion) { 186 super(); 187 myVersion = theVersion; 188 } 189 190 @Override 191 public String provideVersion() { 192 return myVersion; 193 } 194 } 195 196 /** 197 * This class attempts to read the FHIR version from the actual model 198 * classes in order to supply an accurate version string even over time 199 */ 200 private static class Dstu3Version implements IVersionProvider { 201 202 private String myVersion; 203 204 Dstu3Version() { 205 try { 206 Class<?> c = Class.forName("org.hl7.fhir.dstu3.model.Constants"); 207 myVersion = (String) c.getDeclaredField("VERSION").get(null); 208 } catch (Exception e) { 209 myVersion = "3.0.2"; 210 } 211 } 212 213 @Override 214 public String provideVersion() { 215 return myVersion; 216 } 217 } 218 219 private static class R4Version implements IVersionProvider { 220 221 private String myVersion; 222 223 R4Version() { 224 try { 225 Class<?> c = Class.forName("org.hl7.fhir.r4.model.Constants"); 226 myVersion = (String) c.getDeclaredField("VERSION").get(null); 227 } catch (Exception e) { 228 myVersion = "4.0.2"; 229 } 230 } 231 232 @Override 233 public String provideVersion() { 234 return myVersion; 235 } 236 } 237 238 private static class R4BVersion implements IVersionProvider { 239 240 private String myVersion; 241 242 R4BVersion() { 243 try { 244 Class<?> c = Class.forName("org.hl7.fhir.r4b.model.Constants"); 245 myVersion = (String) c.getDeclaredField("VERSION").get(null); 246 } catch (Exception e) { 247 myVersion = "4.3.0"; 248 } 249 } 250 251 @Override 252 public String provideVersion() { 253 return myVersion; 254 } 255 } 256 257 private static class R5Version implements IVersionProvider { 258 259 private String myVersion; 260 261 R5Version() { 262 try { 263 Class<?> c = Class.forName("org.hl7.fhir.r5.model.Constants"); 264 myVersion = (String) c.getDeclaredField("VERSION").get(null); 265 } catch (Exception e) { 266 myVersion = "5.0.0"; 267 } 268 } 269 270 @Override 271 public String provideVersion() { 272 return myVersion; 273 } 274 } 275 276 /** 277 * Returns the {@link FhirVersionEnum} which corresponds to a specific version of 278 * FHIR. Partial version strings (e.g. "3.0") are acceptable. This method will 279 * also accept version names such as "DSTU2", "STU3", "R5", etc. 280 * 281 * @return Returns null if no version exists matching the given string 282 */ 283 public static FhirVersionEnum forVersionString(String theVersionString) { 284 285 // Trim the point release 286 String versionString = theVersionString; 287 int firstDot = versionString.indexOf('.'); 288 if (firstDot > 0) { 289 int secondDot = versionString.indexOf('.', firstDot + 1); 290 if (secondDot > 0) { 291 versionString = versionString.substring(0, secondDot); 292 } 293 } 294 295 for (FhirVersionEnum next : values()) { 296 if (next.getFhirVersionString().startsWith(versionString)) { 297 return next; 298 } 299 } 300 301 switch (theVersionString) { 302 case "DSTU2": 303 return FhirVersionEnum.DSTU2; 304 case "DSTU3": 305 case "STU3": 306 return FhirVersionEnum.DSTU3; 307 case "R4": 308 return FhirVersionEnum.R4; 309 case "R4B": 310 return FhirVersionEnum.R4B; 311 case "R5": 312 return FhirVersionEnum.R5; 313 } 314 315 return null; 316 } 317}