001/* 002 * #%L 003 * HAPI FHIR - Core Library 004 * %% 005 * Copyright (C) 2014 - 2023 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(String theVersionClass, FhirVersionEnum theEquivalent, boolean theIsRi, IVersionProvider theVersionExtractor) { 061 myVersionClass = theVersionClass; 062 myEquivalent = theEquivalent; 063 myFhirVersionString = theVersionExtractor.provideVersion(); 064 myIsRi = theIsRi; 065 } 066 067 public String getFhirVersionString() { 068 return myFhirVersionString; 069 } 070 071 public IFhirVersion getVersionImplementation() { 072 if (!isPresentOnClasspath()) { 073 throw new IllegalStateException(Msg.code(1709) + "Version " + name() + " is not present on classpath"); 074 } 075 if (myVersionImplementation == null) { 076 try { 077 myVersionImplementation = (IFhirVersion) Class.forName(myVersionClass).newInstance(); 078 } catch (Exception e) { 079 throw new InternalErrorException(Msg.code(1710) + "Failed to instantiate FHIR version " + name(), e); 080 } 081 } 082 return myVersionImplementation; 083 } 084 085 public boolean isEqualOrNewerThan(FhirVersionEnum theVersion) { 086 return ordinal() >= theVersion.ordinal(); 087 } 088 089 public boolean isEquivalentTo(FhirVersionEnum theVersion) { 090 if (this.equals(theVersion)) { 091 return true; 092 } 093 if (myEquivalent != null) { 094 return myEquivalent.equals(theVersion); 095 } 096 return false; 097 } 098 099 public boolean isNewerThan(FhirVersionEnum theVersion) { 100 return !isEquivalentTo(theVersion) && ordinal() > theVersion.ordinal(); 101 } 102 103 public boolean isOlderThan(FhirVersionEnum theVersion) { 104 return !isEquivalentTo(theVersion) && ordinal() < theVersion.ordinal(); 105 } 106 107 /** 108 * Returns true if the given version is present on the classpath 109 */ 110 public boolean isPresentOnClasspath() { 111 Boolean retVal = myPresentOnClasspath; 112 if (retVal == null) { 113 try { 114 Class.forName(myVersionClass); 115 retVal = true; 116 } catch (Exception e) { 117 retVal = false; 118 } 119 myPresentOnClasspath = retVal; 120 } 121 return retVal; 122 } 123 124 /** 125 * Is this version using the HL7.org RI structures? 126 */ 127 public boolean isRi() { 128 return myIsRi; 129 } 130 131 /** 132 * Creates a new FhirContext for this FHIR version 133 */ 134 public FhirContext newContext() { 135 return new FhirContext(this); 136 } 137 138 /** 139 * Creates a new FhirContext for this FHIR version, or returns a previously created one if one exists. This 140 * method uses {@link FhirContext#forCached(FhirVersionEnum)} to return a cached instance. 141 */ 142 public FhirContext newContextCached() { 143 return FhirContext.forCached(this); 144 } 145 146 147 private interface IVersionProvider { 148 String provideVersion(); 149 } 150 151 /** 152 * Given a FHIR model object type, determine which version of FHIR it is for 153 */ 154 public static FhirVersionEnum determineVersionForType(Class<?> theFhirType) { 155 switch (theFhirType.getName()) { 156 case "ca.uhn.fhir.model.api.BaseElement": 157 return DSTU2; 158 case "org.hl7.fhir.dstu2.model.Base": 159 return DSTU2_HL7ORG; 160 case "org.hl7.fhir.dstu3.model.Base": 161 return DSTU3; 162 case "org.hl7.fhir.r4.model.Base": 163 return R4; 164 case "org.hl7.fhir.r5.model.Base": 165 return R5; 166 case "java.lang.Object": 167 return null; 168 default: 169 return determineVersionForType(theFhirType.getSuperclass()); 170 } 171 172 } 173 174 private static class Version implements IVersionProvider { 175 176 private String myVersion; 177 178 public Version(String theVersion) { 179 super(); 180 myVersion = theVersion; 181 } 182 183 @Override 184 public String provideVersion() { 185 return myVersion; 186 } 187 188 } 189 190 /** 191 * This class attempts to read the FHIR version from the actual model 192 * classes in order to supply an accurate version string even over time 193 */ 194 private static class Dstu3Version implements IVersionProvider { 195 196 private String myVersion; 197 198 Dstu3Version() { 199 try { 200 Class<?> c = Class.forName("org.hl7.fhir.dstu3.model.Constants"); 201 myVersion = (String) c.getDeclaredField("VERSION").get(null); 202 } catch (Exception e) { 203 myVersion = "3.0.2"; 204 } 205 } 206 207 @Override 208 public String provideVersion() { 209 return myVersion; 210 } 211 212 } 213 214 private static class R4Version implements IVersionProvider { 215 216 private String myVersion; 217 218 R4Version() { 219 try { 220 Class<?> c = Class.forName("org.hl7.fhir.r4.model.Constants"); 221 myVersion = (String) c.getDeclaredField("VERSION").get(null); 222 } catch (Exception e) { 223 myVersion = "4.0.2"; 224 } 225 } 226 227 @Override 228 public String provideVersion() { 229 return myVersion; 230 } 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 254 private static class R5Version implements IVersionProvider { 255 256 private String myVersion; 257 258 R5Version() { 259 try { 260 Class<?> c = Class.forName("org.hl7.fhir.r5.model.Constants"); 261 myVersion = (String) c.getDeclaredField("VERSION").get(null); 262 } catch (Exception e) { 263 myVersion = "5.0.0"; 264 } 265 } 266 267 @Override 268 public String provideVersion() { 269 return myVersion; 270 } 271 272 } 273 274 /** 275 * Returns the {@link FhirVersionEnum} which corresponds to a specific version of 276 * FHIR. Partial version strings (e.g. "3.0") are acceptable. This method will 277 * also accept version names such as "DSTU2", "STU3", "R5", etc. 278 * 279 * @return Returns null if no version exists matching the given string 280 */ 281 public static FhirVersionEnum forVersionString(String theVersionString) { 282 283 // Trim the point release 284 String versionString = theVersionString; 285 int firstDot = versionString.indexOf('.'); 286 if (firstDot > 0) { 287 int secondDot = versionString.indexOf('.', firstDot + 1); 288 if (secondDot > 0) { 289 versionString = versionString.substring(0, secondDot); 290 } 291 } 292 293 for (FhirVersionEnum next : values()) { 294 if (next.getFhirVersionString().startsWith(versionString)) { 295 return next; 296 } 297 } 298 299 switch (theVersionString) { 300 case "DSTU2": 301 return FhirVersionEnum.DSTU2; 302 case "DSTU3": 303 case "STU3": 304 return FhirVersionEnum.DSTU3; 305 case "R4": 306 return FhirVersionEnum.R4; 307 case "R4B": 308 return FhirVersionEnum.R4B; 309 case "R5": 310 return FhirVersionEnum.R5; 311 } 312 313 return null; 314 } 315 316}