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