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        private final FhirVersionEnum myEquivalent;
051        private final boolean myIsRi;
052        private final String myVersionClass;
053        private volatile Boolean myPresentOnClasspath;
054        private volatile IFhirVersion myVersionImplementation;
055        private String myFhirVersionString;
056
057        FhirVersionEnum(String theVersionClass, FhirVersionEnum theEquivalent, boolean theIsRi, IVersionProvider theVersionExtractor) {
058                myVersionClass = theVersionClass;
059                myEquivalent = theEquivalent;
060                myFhirVersionString = theVersionExtractor.provideVersion();
061                myIsRi = theIsRi;
062        }
063
064        public String getFhirVersionString() {
065                return myFhirVersionString;
066        }
067
068        public IFhirVersion getVersionImplementation() {
069                if (!isPresentOnClasspath()) {
070                        throw new IllegalStateException(Msg.code(1709) + "Version " + name() + " is not present on classpath");
071                }
072                if (myVersionImplementation == null) {
073                        try {
074                                myVersionImplementation = (IFhirVersion) Class.forName(myVersionClass).newInstance();
075                        } catch (Exception e) {
076                                throw new InternalErrorException(Msg.code(1710) + "Failed to instantiate FHIR version " + name(), e);
077                        }
078                }
079                return myVersionImplementation;
080        }
081
082        public boolean isEqualOrNewerThan(FhirVersionEnum theVersion) {
083                return ordinal() >= theVersion.ordinal();
084        }
085
086        public boolean isEquivalentTo(FhirVersionEnum theVersion) {
087                if (this.equals(theVersion)) {
088                        return true;
089                }
090                if (myEquivalent != null) {
091                        return myEquivalent.equals(theVersion);
092                }
093                return false;
094        }
095
096        public boolean isNewerThan(FhirVersionEnum theVersion) {
097                return !isEquivalentTo(theVersion) && ordinal() > theVersion.ordinal();
098        }
099
100        public boolean isOlderThan(FhirVersionEnum theVersion) {
101                return !isEquivalentTo(theVersion) && ordinal() < theVersion.ordinal();
102        }
103
104        /**
105         * Returns true if the given version is present on the classpath
106         */
107        public boolean isPresentOnClasspath() {
108                Boolean retVal = myPresentOnClasspath;
109                if (retVal == null) {
110                        try {
111                                Class.forName(myVersionClass);
112                                retVal = true;
113                        } catch (Exception e) {
114                                retVal = false;
115                        }
116                        myPresentOnClasspath = retVal;
117                }
118                return retVal;
119        }
120
121        /**
122         * Is this version using the HL7.org RI structures?
123         */
124        public boolean isRi() {
125                return myIsRi;
126        }
127
128        public FhirContext newContext() {
129                switch (this) {
130                        case DSTU2:
131                                return FhirContext.forDstu2();
132                        case DSTU2_HL7ORG:
133                                return FhirContext.forDstu2Hl7Org();
134                        case DSTU2_1:
135                                return FhirContext.forDstu2_1();
136                        case DSTU3:
137                                return FhirContext.forDstu3();
138                        case R4:
139                                return FhirContext.forR4();
140                        case R5:
141                                return FhirContext.forR5();
142                }
143                throw new IllegalStateException(Msg.code(1711) + "Unknown version: " + this); // should not happen
144        }
145
146        private interface IVersionProvider {
147                String provideVersion();
148        }
149
150        private static class Version implements IVersionProvider {
151
152                private String myVersion;
153
154                public Version(String theVersion) {
155                        super();
156                        myVersion = theVersion;
157                }
158
159                @Override
160                public String provideVersion() {
161                        return myVersion;
162                }
163
164        }
165
166        /**
167         * This class attempts to read the FHIR version from the actual model
168         * classes in order to supply an accurate version string even over time
169         */
170        private static class Dstu3Version implements IVersionProvider {
171
172                private String myVersion;
173
174                Dstu3Version() {
175                        try {
176                                Class<?> c = Class.forName("org.hl7.fhir.dstu3.model.Constants");
177                                myVersion = (String) c.getDeclaredField("VERSION").get(null);
178                        } catch (Exception e) {
179                                myVersion = "3.0.2";
180                        }
181                }
182
183                @Override
184                public String provideVersion() {
185                        return myVersion;
186                }
187
188        }
189
190        private static class R4Version implements IVersionProvider {
191
192                private String myVersion;
193
194                R4Version() {
195                        try {
196                                Class<?> c = Class.forName("org.hl7.fhir.r4.model.Constants");
197                                myVersion = (String) c.getDeclaredField("VERSION").get(null);
198                        } catch (Exception e) {
199                                myVersion = "4.0.2";
200                        }
201                }
202
203                @Override
204                public String provideVersion() {
205                        return myVersion;
206                }
207
208        }
209
210        private static class R5Version implements IVersionProvider {
211
212                private String myVersion;
213
214                R5Version() {
215                        try {
216                                Class<?> c = Class.forName("org.hl7.fhir.r5.model.Constants");
217                                myVersion = (String) c.getDeclaredField("VERSION").get(null);
218                        } catch (Exception e) {
219                                myVersion = "5.0.0";
220                        }
221                }
222
223                @Override
224                public String provideVersion() {
225                        return myVersion;
226                }
227
228        }
229
230        /**
231         * Returns the {@link FhirVersionEnum} which corresponds to a specific version of
232         * FHIR. Partial version strings (e.g. "3.0") are acceptable. This method will
233         * also accept version names such as "DSTU2", "STU3", "R5", etc.
234         *
235         * @return Returns null if no version exists matching the given string
236         */
237        public static FhirVersionEnum forVersionString(String theVersionString) {
238
239                // Trim the point release
240                String versionString = theVersionString;
241                int firstDot = versionString.indexOf('.');
242                if (firstDot > 0) {
243                        int secondDot = versionString.indexOf('.', firstDot + 1);
244                        if (secondDot > 0) {
245                                versionString = versionString.substring(0, secondDot);
246                        }
247                }
248
249                for (FhirVersionEnum next : values()) {
250                        if (next.getFhirVersionString().startsWith(versionString)) {
251                                return next;
252                        }
253                }
254
255                switch (theVersionString) {
256                        case "DSTU2":
257                                return FhirVersionEnum.DSTU2;
258                        case "DSTU3":
259                        case "STU3":
260                                return FhirVersionEnum.DSTU3;
261                        case "R4":
262                                return FhirVersionEnum.R4;
263                        case "R5":
264                                return FhirVersionEnum.R5;
265                }
266
267                return null;
268        }
269
270}