001package ca.uhn.fhir.context; 002 003/* 004 * #%L 005 * HAPI FHIR - Core Library 006 * %% 007 * Copyright (C) 2014 - 2023 Smile CDR, Inc. 008 * %% 009 * Licensed under the Apache License, Version 2.0 (the "License"); 010 * you may not use this file except in compliance with the License. 011 * You may obtain a copy of the License at 012 * 013 * http://www.apache.org/licenses/LICENSE-2.0 014 * 015 * Unless required by applicable law or agreed to in writing, software 016 * distributed under the License is distributed on an "AS IS" BASIS, 017 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 018 * See the License for the specific language governing permissions and 019 * limitations under the License. 020 * #L% 021 */ 022 023import ca.uhn.fhir.i18n.Msg; 024import ca.uhn.fhir.model.api.IFhirVersion; 025import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; 026 027public enum FhirVersionEnum { 028 029 /* 030 * *********************** 031 * Don't auto-sort this type!!! 032 * 033 * Or more accurately, entries should be sorted from OLDEST FHIR release 034 * to NEWEST FHIR release instead of alphabetically 035 * *********************** 036 */ 037 038 DSTU2("ca.uhn.fhir.model.dstu2.FhirDstu2", null, false, new Version("1.0.2")), 039 040 DSTU2_HL7ORG("org.hl7.fhir.dstu2.hapi.ctx.FhirDstu2Hl7Org", DSTU2, true, new Version("1.0.2")), 041 042 DSTU2_1("org.hl7.fhir.dstu2016may.hapi.ctx.FhirDstu2_1", null, true, new Version("1.4.0")), 043 044 DSTU3("org.hl7.fhir.dstu3.hapi.ctx.FhirDstu3", null, true, new Dstu3Version()), 045 046 R4("org.hl7.fhir.r4.hapi.ctx.FhirR4", null, true, new R4Version()), 047 048 R4B("org.hl7.fhir.r4b.hapi.ctx.FhirR4B", null, true, new R4BVersion()), 049 050 R5("org.hl7.fhir.r5.hapi.ctx.FhirR5", null, true, new R5Version()); 051 052 // If you add new constants, add to the various methods below too! 053 054 private final FhirVersionEnum myEquivalent; 055 private final boolean myIsRi; 056 private final String myVersionClass; 057 private volatile Boolean myPresentOnClasspath; 058 private volatile IFhirVersion myVersionImplementation; 059 private String myFhirVersionString; 060 061 FhirVersionEnum(String theVersionClass, FhirVersionEnum theEquivalent, boolean theIsRi, IVersionProvider theVersionExtractor) { 062 myVersionClass = theVersionClass; 063 myEquivalent = theEquivalent; 064 myFhirVersionString = theVersionExtractor.provideVersion(); 065 myIsRi = theIsRi; 066 } 067 068 public String getFhirVersionString() { 069 return myFhirVersionString; 070 } 071 072 public IFhirVersion getVersionImplementation() { 073 if (!isPresentOnClasspath()) { 074 throw new IllegalStateException(Msg.code(1709) + "Version " + name() + " is not present on classpath"); 075 } 076 if (myVersionImplementation == null) { 077 try { 078 myVersionImplementation = (IFhirVersion) Class.forName(myVersionClass).newInstance(); 079 } catch (Exception e) { 080 throw new InternalErrorException(Msg.code(1710) + "Failed to instantiate FHIR version " + name(), e); 081 } 082 } 083 return myVersionImplementation; 084 } 085 086 public boolean isEqualOrNewerThan(FhirVersionEnum theVersion) { 087 return ordinal() >= theVersion.ordinal(); 088 } 089 090 public boolean isEquivalentTo(FhirVersionEnum theVersion) { 091 if (this.equals(theVersion)) { 092 return true; 093 } 094 if (myEquivalent != null) { 095 return myEquivalent.equals(theVersion); 096 } 097 return false; 098 } 099 100 public boolean isNewerThan(FhirVersionEnum theVersion) { 101 return !isEquivalentTo(theVersion) && ordinal() > theVersion.ordinal(); 102 } 103 104 public boolean isOlderThan(FhirVersionEnum theVersion) { 105 return !isEquivalentTo(theVersion) && ordinal() < theVersion.ordinal(); 106 } 107 108 /** 109 * Returns true if the given version is present on the classpath 110 */ 111 public boolean isPresentOnClasspath() { 112 Boolean retVal = myPresentOnClasspath; 113 if (retVal == null) { 114 try { 115 Class.forName(myVersionClass); 116 retVal = true; 117 } catch (Exception e) { 118 retVal = false; 119 } 120 myPresentOnClasspath = retVal; 121 } 122 return retVal; 123 } 124 125 /** 126 * Is this version using the HL7.org RI structures? 127 */ 128 public boolean isRi() { 129 return myIsRi; 130 } 131 132 public FhirContext newContext() { 133 switch (this) { 134 case DSTU2: 135 return FhirContext.forDstu2(); 136 case DSTU2_HL7ORG: 137 return FhirContext.forDstu2Hl7Org(); 138 case DSTU2_1: 139 return FhirContext.forDstu2_1(); 140 case DSTU3: 141 return FhirContext.forDstu3(); 142 case R4: 143 return FhirContext.forR4(); 144 case R4B: 145 return FhirContext.forR4B(); 146 case R5: 147 return FhirContext.forR5(); 148 } 149 throw new IllegalStateException(Msg.code(1711) + "Unknown version: " + this); // should not happen 150 } 151 152 private interface IVersionProvider { 153 String provideVersion(); 154 } 155 156 /** 157 * Given a FHIR model object type, determine which version of FHIR it is for 158 */ 159 public static FhirVersionEnum determineVersionForType(Class<?> theFhirType) { 160 switch (theFhirType.getName()) { 161 case "ca.uhn.fhir.model.api.BaseElement": 162 return DSTU2; 163 case "org.hl7.fhir.dstu2.model.Base": 164 return DSTU2_HL7ORG; 165 case "org.hl7.fhir.dstu3.model.Base": 166 return DSTU3; 167 case "org.hl7.fhir.r4.model.Base": 168 return R4; 169 case "org.hl7.fhir.r5.model.Base": 170 return R5; 171 case "java.lang.Object": 172 return null; 173 default: 174 return determineVersionForType(theFhirType.getSuperclass()); 175 } 176 177 } 178 179 private static class Version implements IVersionProvider { 180 181 private String myVersion; 182 183 public Version(String theVersion) { 184 super(); 185 myVersion = theVersion; 186 } 187 188 @Override 189 public String provideVersion() { 190 return myVersion; 191 } 192 193 } 194 195 /** 196 * This class attempts to read the FHIR version from the actual model 197 * classes in order to supply an accurate version string even over time 198 */ 199 private static class Dstu3Version implements IVersionProvider { 200 201 private String myVersion; 202 203 Dstu3Version() { 204 try { 205 Class<?> c = Class.forName("org.hl7.fhir.dstu3.model.Constants"); 206 myVersion = (String) c.getDeclaredField("VERSION").get(null); 207 } catch (Exception e) { 208 myVersion = "3.0.2"; 209 } 210 } 211 212 @Override 213 public String provideVersion() { 214 return myVersion; 215 } 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 239 private static class R4BVersion implements IVersionProvider { 240 241 private String myVersion; 242 243 R4BVersion() { 244 try { 245 Class<?> c = Class.forName("org.hl7.fhir.r4b.model.Constants"); 246 myVersion = (String) c.getDeclaredField("VERSION").get(null); 247 } catch (Exception e) { 248 myVersion = "4.3.0"; 249 } 250 } 251 252 @Override 253 public String provideVersion() { 254 return myVersion; 255 } 256 257 } 258 259 private static class R5Version implements IVersionProvider { 260 261 private String myVersion; 262 263 R5Version() { 264 try { 265 Class<?> c = Class.forName("org.hl7.fhir.r5.model.Constants"); 266 myVersion = (String) c.getDeclaredField("VERSION").get(null); 267 } catch (Exception e) { 268 myVersion = "5.0.0"; 269 } 270 } 271 272 @Override 273 public String provideVersion() { 274 return myVersion; 275 } 276 277 } 278 279 /** 280 * Returns the {@link FhirVersionEnum} which corresponds to a specific version of 281 * FHIR. Partial version strings (e.g. "3.0") are acceptable. This method will 282 * also accept version names such as "DSTU2", "STU3", "R5", etc. 283 * 284 * @return Returns null if no version exists matching the given string 285 */ 286 public static FhirVersionEnum forVersionString(String theVersionString) { 287 288 // Trim the point release 289 String versionString = theVersionString; 290 int firstDot = versionString.indexOf('.'); 291 if (firstDot > 0) { 292 int secondDot = versionString.indexOf('.', firstDot + 1); 293 if (secondDot > 0) { 294 versionString = versionString.substring(0, secondDot); 295 } 296 } 297 298 for (FhirVersionEnum next : values()) { 299 if (next.getFhirVersionString().startsWith(versionString)) { 300 return next; 301 } 302 } 303 304 switch (theVersionString) { 305 case "DSTU2": 306 return FhirVersionEnum.DSTU2; 307 case "DSTU3": 308 case "STU3": 309 return FhirVersionEnum.DSTU3; 310 case "R4": 311 return FhirVersionEnum.R4; 312 case "R4B": 313 return FhirVersionEnum.R4B; 314 case "R5": 315 return FhirVersionEnum.R5; 316 } 317 318 return null; 319 } 320 321}