001package ca.uhn.fhir.context; 002 003/* 004 * #%L 005 * HAPI FHIR - Core Library 006 * %% 007 * Copyright (C) 2014 - 2022 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 R5("org.hl7.fhir.r5.hapi.ctx.FhirR5", null, true, new R5Version()); 049 050 // If you add new constants, add to the various methods below too! 051 052 private final FhirVersionEnum myEquivalent; 053 private final boolean myIsRi; 054 private final String myVersionClass; 055 private volatile Boolean myPresentOnClasspath; 056 private volatile IFhirVersion myVersionImplementation; 057 private String myFhirVersionString; 058 059 FhirVersionEnum(String theVersionClass, FhirVersionEnum theEquivalent, boolean theIsRi, IVersionProvider theVersionExtractor) { 060 myVersionClass = theVersionClass; 061 myEquivalent = theEquivalent; 062 myFhirVersionString = theVersionExtractor.provideVersion(); 063 myIsRi = theIsRi; 064 } 065 066 public String getFhirVersionString() { 067 return myFhirVersionString; 068 } 069 070 public IFhirVersion getVersionImplementation() { 071 if (!isPresentOnClasspath()) { 072 throw new IllegalStateException(Msg.code(1709) + "Version " + name() + " is not present on classpath"); 073 } 074 if (myVersionImplementation == null) { 075 try { 076 myVersionImplementation = (IFhirVersion) Class.forName(myVersionClass).newInstance(); 077 } catch (Exception e) { 078 throw new InternalErrorException(Msg.code(1710) + "Failed to instantiate FHIR version " + name(), e); 079 } 080 } 081 return myVersionImplementation; 082 } 083 084 public boolean isEqualOrNewerThan(FhirVersionEnum theVersion) { 085 return ordinal() >= theVersion.ordinal(); 086 } 087 088 public boolean isEquivalentTo(FhirVersionEnum theVersion) { 089 if (this.equals(theVersion)) { 090 return true; 091 } 092 if (myEquivalent != null) { 093 return myEquivalent.equals(theVersion); 094 } 095 return false; 096 } 097 098 public boolean isNewerThan(FhirVersionEnum theVersion) { 099 return !isEquivalentTo(theVersion) && ordinal() > theVersion.ordinal(); 100 } 101 102 public boolean isOlderThan(FhirVersionEnum theVersion) { 103 return !isEquivalentTo(theVersion) && ordinal() < theVersion.ordinal(); 104 } 105 106 /** 107 * Returns true if the given version is present on the classpath 108 */ 109 public boolean isPresentOnClasspath() { 110 Boolean retVal = myPresentOnClasspath; 111 if (retVal == null) { 112 try { 113 Class.forName(myVersionClass); 114 retVal = true; 115 } catch (Exception e) { 116 retVal = false; 117 } 118 myPresentOnClasspath = retVal; 119 } 120 return retVal; 121 } 122 123 /** 124 * Is this version using the HL7.org RI structures? 125 */ 126 public boolean isRi() { 127 return myIsRi; 128 } 129 130 public FhirContext newContext() { 131 switch (this) { 132 case DSTU2: 133 return FhirContext.forDstu2(); 134 case DSTU2_HL7ORG: 135 return FhirContext.forDstu2Hl7Org(); 136 case DSTU2_1: 137 return FhirContext.forDstu2_1(); 138 case DSTU3: 139 return FhirContext.forDstu3(); 140 case R4: 141 return FhirContext.forR4(); 142 case R5: 143 return FhirContext.forR5(); 144 } 145 throw new IllegalStateException(Msg.code(1711) + "Unknown version: " + this); // should not happen 146 } 147 148 private interface IVersionProvider { 149 String provideVersion(); 150 } 151 152 /** 153 * Given a FHIR model object type, determine which version of FHIR it is for 154 */ 155 public static FhirVersionEnum determineVersionForType(Class<?> theFhirType) { 156 switch (theFhirType.getName()) { 157 case "ca.uhn.fhir.model.api.BaseElement": 158 return DSTU2; 159 case "org.hl7.fhir.dstu2.model.Base": 160 return DSTU2_HL7ORG; 161 case "org.hl7.fhir.dstu3.model.Base": 162 return DSTU3; 163 case "org.hl7.fhir.r4.model.Base": 164 return R4; 165 case "org.hl7.fhir.r5.model.Base": 166 return R5; 167 case "java.lang.Object": 168 return null; 169 default: 170 return determineVersionForType(theFhirType.getSuperclass()); 171 } 172 173 } 174 175 private static class Version implements IVersionProvider { 176 177 private String myVersion; 178 179 public Version(String theVersion) { 180 super(); 181 myVersion = theVersion; 182 } 183 184 @Override 185 public String provideVersion() { 186 return myVersion; 187 } 188 189 } 190 191 /** 192 * This class attempts to read the FHIR version from the actual model 193 * classes in order to supply an accurate version string even over time 194 */ 195 private static class Dstu3Version implements IVersionProvider { 196 197 private String myVersion; 198 199 Dstu3Version() { 200 try { 201 Class<?> c = Class.forName("org.hl7.fhir.dstu3.model.Constants"); 202 myVersion = (String) c.getDeclaredField("VERSION").get(null); 203 } catch (Exception e) { 204 myVersion = "3.0.2"; 205 } 206 } 207 208 @Override 209 public String provideVersion() { 210 return myVersion; 211 } 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 235 private static class R5Version implements IVersionProvider { 236 237 private String myVersion; 238 239 R5Version() { 240 try { 241 Class<?> c = Class.forName("org.hl7.fhir.r5.model.Constants"); 242 myVersion = (String) c.getDeclaredField("VERSION").get(null); 243 } catch (Exception e) { 244 myVersion = "5.0.0"; 245 } 246 } 247 248 @Override 249 public String provideVersion() { 250 return myVersion; 251 } 252 253 } 254 255 /** 256 * Returns the {@link FhirVersionEnum} which corresponds to a specific version of 257 * FHIR. Partial version strings (e.g. "3.0") are acceptable. This method will 258 * also accept version names such as "DSTU2", "STU3", "R5", etc. 259 * 260 * @return Returns null if no version exists matching the given string 261 */ 262 public static FhirVersionEnum forVersionString(String theVersionString) { 263 264 // Trim the point release 265 String versionString = theVersionString; 266 int firstDot = versionString.indexOf('.'); 267 if (firstDot > 0) { 268 int secondDot = versionString.indexOf('.', firstDot + 1); 269 if (secondDot > 0) { 270 versionString = versionString.substring(0, secondDot); 271 } 272 } 273 274 for (FhirVersionEnum next : values()) { 275 if (next.getFhirVersionString().startsWith(versionString)) { 276 return next; 277 } 278 } 279 280 switch (theVersionString) { 281 case "DSTU2": 282 return FhirVersionEnum.DSTU2; 283 case "DSTU3": 284 case "STU3": 285 return FhirVersionEnum.DSTU3; 286 case "R4": 287 return FhirVersionEnum.R4; 288 case "R5": 289 return FhirVersionEnum.R5; 290 } 291 292 return null; 293 } 294 295}