001package org.hl7.fhir.common.hapi.validation.support;
002
003import ca.uhn.fhir.context.FhirContext;
004import ca.uhn.fhir.context.FhirVersionEnum;
005import ca.uhn.fhir.context.support.ConceptValidationOptions;
006import ca.uhn.fhir.context.support.IValidationSupport;
007import ca.uhn.fhir.context.support.ValidationSupportContext;
008import ca.uhn.fhir.context.support.ValueSetExpansionOptions;
009import ca.uhn.fhir.i18n.Msg;
010import ca.uhn.fhir.parser.IParser;
011import ca.uhn.fhir.util.FhirVersionIndependentConcept;
012import ca.uhn.hapi.converters.canonical.VersionCanonicalizer;
013import org.apache.commons.lang3.Validate;
014import org.hl7.fhir.convertors.advisors.impl.BaseAdvisor_10_50;
015import org.hl7.fhir.convertors.advisors.impl.BaseAdvisor_30_50;
016import org.hl7.fhir.convertors.advisors.impl.BaseAdvisor_40_50;
017import org.hl7.fhir.convertors.advisors.impl.BaseAdvisor_43_50;
018import org.hl7.fhir.convertors.factory.VersionConvertorFactory_10_50;
019import org.hl7.fhir.convertors.factory.VersionConvertorFactory_30_50;
020import org.hl7.fhir.convertors.factory.VersionConvertorFactory_40_50;
021import org.hl7.fhir.convertors.factory.VersionConvertorFactory_43_50;
022import org.hl7.fhir.dstu2.model.ValueSet;
023import org.hl7.fhir.instance.model.api.IBaseResource;
024import org.hl7.fhir.instance.model.api.IPrimitiveType;
025import org.hl7.fhir.r5.model.CanonicalType;
026import org.hl7.fhir.r5.model.CodeSystem;
027import org.hl7.fhir.r5.model.Enumerations;
028import org.hl7.fhir.utilities.validation.ValidationMessage;
029
030import javax.annotation.Nonnull;
031import javax.annotation.Nullable;
032import java.util.ArrayList;
033import java.util.Collections;
034import java.util.HashSet;
035import java.util.List;
036import java.util.Objects;
037import java.util.Optional;
038import java.util.Set;
039import java.util.function.Consumer;
040import java.util.function.Function;
041import java.util.stream.Collectors;
042
043import static org.apache.commons.lang3.StringUtils.contains;
044import static org.apache.commons.lang3.StringUtils.defaultString;
045import static org.apache.commons.lang3.StringUtils.isBlank;
046import static org.apache.commons.lang3.StringUtils.isNotBlank;
047import static org.apache.commons.lang3.StringUtils.substringAfter;
048import static org.apache.commons.lang3.StringUtils.substringBefore;
049import static org.hl7.fhir.common.hapi.validation.support.CommonCodeSystemsTerminologyService.getFhirVersionEnum;
050
051/**
052 * This class is a basic in-memory terminology service, designed to expand ValueSets and validate codes
053 * completely in-memory. It is suitable for runtime validation purposes where no dedicated terminology
054 * service exists (either an internal one such as the HAPI FHIR JPA terminology service, or an
055 * external term service API)
056 */
057@SuppressWarnings("EnhancedSwitchMigration")
058public class InMemoryTerminologyServerValidationSupport implements IValidationSupport {
059        private static final String OUR_PIPE_CHARACTER = "|";
060
061        private final FhirContext myCtx;
062        private final VersionCanonicalizer myVersionCanonicalizer;
063
064        public InMemoryTerminologyServerValidationSupport(FhirContext theCtx) {
065                Validate.notNull(theCtx, "theCtx must not be null");
066                myCtx = theCtx;
067                myVersionCanonicalizer = new VersionCanonicalizer(theCtx);
068        }
069
070        @Override
071        public FhirContext getFhirContext() {
072                return myCtx;
073        }
074
075        @Override
076        public ValueSetExpansionOutcome expandValueSet(ValidationSupportContext theValidationSupportContext, ValueSetExpansionOptions theExpansionOptions, @Nonnull IBaseResource theValueSetToExpand) {
077                return expandValueSet(theValidationSupportContext, theValueSetToExpand, null, null);
078        }
079
080        private ValueSetExpansionOutcome expandValueSet(ValidationSupportContext theValidationSupportContext, IBaseResource theValueSetToExpand, String theWantSystemAndVersion, String theWantCode) {
081                org.hl7.fhir.r5.model.ValueSet expansionR5;
082                try {
083                        expansionR5 = expandValueSetToCanonical(theValidationSupportContext, theValueSetToExpand, theWantSystemAndVersion, theWantCode);
084                } catch (ExpansionCouldNotBeCompletedInternallyException e) {
085                        return new ValueSetExpansionOutcome(e.getMessage());
086                }
087                if (expansionR5 == null) {
088                        return null;
089                }
090
091                IBaseResource expansion;
092                switch (myCtx.getVersion().getVersion()) {
093                        case DSTU2: {
094                                org.hl7.fhir.r4.model.ValueSet expansionR4 = (org.hl7.fhir.r4.model.ValueSet) VersionConvertorFactory_40_50.convertResource(expansionR5, new BaseAdvisor_40_50(false));
095                                expansion = myVersionCanonicalizer.valueSetFromCanonical(expansionR4);
096                                break;
097                        }
098                        case DSTU2_HL7ORG: {
099                                expansion = VersionConvertorFactory_10_50.convertResource(expansionR5, new BaseAdvisor_10_50(false));
100                                break;
101                        }
102                        case DSTU3: {
103                                expansion = VersionConvertorFactory_30_50.convertResource(expansionR5, new BaseAdvisor_30_50(false));
104                                break;
105                        }
106                        case R4: {
107                                expansion = VersionConvertorFactory_40_50.convertResource(expansionR5, new BaseAdvisor_40_50(false));
108                                break;
109                        }
110                        case R4B: {
111                                expansion = VersionConvertorFactory_43_50.convertResource(expansionR5, new BaseAdvisor_43_50(false));
112                                break;
113                        }
114                        case R5: {
115                                expansion = expansionR5;
116                                break;
117                        }
118                        case DSTU2_1:
119                        default:
120                                throw new IllegalArgumentException(Msg.code(697) + "Can not handle version: " + myCtx.getVersion().getVersion());
121                }
122
123                return new ValueSetExpansionOutcome(expansion);
124        }
125
126        private org.hl7.fhir.r5.model.ValueSet expandValueSetToCanonical(ValidationSupportContext theValidationSupportContext, IBaseResource theValueSetToExpand, @Nullable String theWantSystemUrlAndVersion, @Nullable String theWantCode) throws ExpansionCouldNotBeCompletedInternallyException {
127                org.hl7.fhir.r5.model.ValueSet expansionR5;
128                switch (getFhirVersionEnum(theValidationSupportContext.getRootValidationSupport().getFhirContext(), theValueSetToExpand)) {
129                        case DSTU2: {
130                                expansionR5 = expandValueSetDstu2(theValidationSupportContext, (ca.uhn.fhir.model.dstu2.resource.ValueSet) theValueSetToExpand, theWantSystemUrlAndVersion, theWantCode);
131                                break;
132                        }
133                        case DSTU2_HL7ORG: {
134                                expansionR5 = expandValueSetDstu2Hl7Org(theValidationSupportContext, (ValueSet) theValueSetToExpand, theWantSystemUrlAndVersion, theWantCode);
135                                break;
136                        }
137                        case DSTU3: {
138                                expansionR5 = expandValueSetDstu3(theValidationSupportContext, (org.hl7.fhir.dstu3.model.ValueSet) theValueSetToExpand, theWantSystemUrlAndVersion, theWantCode);
139                                break;
140                        }
141                        case R4: {
142                                expansionR5 = expandValueSetR4(theValidationSupportContext, (org.hl7.fhir.r4.model.ValueSet) theValueSetToExpand, theWantSystemUrlAndVersion, theWantCode);
143                                break;
144                        }
145                        case R4B: {
146                                expansionR5 = expandValueSetR4B(theValidationSupportContext, (org.hl7.fhir.r4b.model.ValueSet) theValueSetToExpand, theWantSystemUrlAndVersion, theWantCode);
147                                break;
148                        }
149                        case R5: {
150                                expansionR5 = expandValueSetR5(theValidationSupportContext, (org.hl7.fhir.r5.model.ValueSet) theValueSetToExpand, theWantSystemUrlAndVersion, theWantCode);
151                                break;
152                        }
153                        case DSTU2_1:
154                        default:
155                                throw new IllegalArgumentException(Msg.code(698) + "Can not handle version: " + myCtx.getVersion().getVersion());
156                }
157
158                return expansionR5;
159        }
160
161        @Override
162        public CodeValidationResult validateCodeInValueSet(ValidationSupportContext theValidationSupportContext, ConceptValidationOptions theOptions, String theCodeSystemUrlAndVersion, String theCode, String theDisplay, @Nonnull IBaseResource theValueSet) {
163                org.hl7.fhir.r5.model.ValueSet expansion;
164                String vsUrl = CommonCodeSystemsTerminologyService.getValueSetUrl(getFhirContext(), theValueSet);
165                try {
166                        expansion = expandValueSetToCanonical(theValidationSupportContext, theValueSet, theCodeSystemUrlAndVersion, theCode);
167                } catch (ExpansionCouldNotBeCompletedInternallyException e) {
168                        CodeValidationResult codeValidationResult = new CodeValidationResult();
169                        codeValidationResult.setSeverityCode("error");
170
171                        String msg = "Failed to expand ValueSet '" + vsUrl + "' (in-memory). Could not validate code " + theCodeSystemUrlAndVersion + "#" + theCode;
172                        if (e.getMessage() != null) {
173                                msg += ". Error was: " + e.getMessage();
174                        }
175
176                        codeValidationResult.setMessage(msg);
177                        return codeValidationResult;
178                }
179
180                if (expansion == null) {
181                        return null;
182                }
183
184                return validateCodeInExpandedValueSet(theValidationSupportContext, theOptions, theCodeSystemUrlAndVersion, theCode, theDisplay, expansion, vsUrl);
185        }
186
187
188        @Override
189        @Nullable
190        public CodeValidationResult validateCode(@Nonnull ValidationSupportContext theValidationSupportContext, @Nonnull ConceptValidationOptions theOptions, String theCodeSystem, String theCode, String theDisplay, String theValueSetUrl) {
191                IBaseResource vs;
192                if (isNotBlank(theValueSetUrl)) {
193                        vs = theValidationSupportContext.getRootValidationSupport().fetchValueSet(theValueSetUrl);
194                        if (vs == null) {
195                                return null;
196                        }
197                } else {
198                        String codeSystemUrl;
199                        String codeSystemVersion = null;
200                        int codeSystemVersionIndex = theCodeSystem.indexOf("|");
201                        if (codeSystemVersionIndex > -1) {
202                                codeSystemUrl = theCodeSystem.substring(0, codeSystemVersionIndex);
203                                codeSystemVersion = theCodeSystem.substring(codeSystemVersionIndex + 1);
204                        } else {
205                                codeSystemUrl = theCodeSystem;
206                        }
207                        switch (myCtx.getVersion().getVersion()) {
208                                case DSTU2:
209                                case DSTU2_HL7ORG:
210                                        vs = new org.hl7.fhir.dstu2.model.ValueSet()
211                                                .setCompose(new org.hl7.fhir.dstu2.model.ValueSet.ValueSetComposeComponent()
212                                                        .addInclude(new org.hl7.fhir.dstu2.model.ValueSet.ConceptSetComponent().setSystem(theCodeSystem)));
213                                        break;
214                                case DSTU3:
215                                        if (codeSystemVersion != null) {
216                                                vs = new org.hl7.fhir.dstu3.model.ValueSet()
217                                                        .setCompose(new org.hl7.fhir.dstu3.model.ValueSet.ValueSetComposeComponent()
218                                                                .addInclude(new org.hl7.fhir.dstu3.model.ValueSet.ConceptSetComponent().setSystem(codeSystemUrl).setVersion(codeSystemVersion)));
219                                        } else {
220                                                vs = new org.hl7.fhir.dstu3.model.ValueSet()
221                                                        .setCompose(new org.hl7.fhir.dstu3.model.ValueSet.ValueSetComposeComponent()
222                                                                .addInclude(new org.hl7.fhir.dstu3.model.ValueSet.ConceptSetComponent().setSystem(theCodeSystem)));
223                                        }
224                                        break;
225                                case R4:
226                                        if (codeSystemVersion != null) {
227                                                vs = new org.hl7.fhir.r4.model.ValueSet()
228                                                        .setCompose(new org.hl7.fhir.r4.model.ValueSet.ValueSetComposeComponent()
229                                                                .addInclude(new org.hl7.fhir.r4.model.ValueSet.ConceptSetComponent().setSystem(codeSystemUrl).setVersion(codeSystemVersion)));
230                                        } else {
231                                                vs = new org.hl7.fhir.r4.model.ValueSet()
232                                                        .setCompose(new org.hl7.fhir.r4.model.ValueSet.ValueSetComposeComponent()
233                                                                .addInclude(new org.hl7.fhir.r4.model.ValueSet.ConceptSetComponent().setSystem(theCodeSystem)));
234                                        }
235                                        break;
236                                case R4B:
237                                        if (codeSystemVersion != null) {
238                                                vs = new org.hl7.fhir.r4b.model.ValueSet()
239                                                        .setCompose(new org.hl7.fhir.r4b.model.ValueSet.ValueSetComposeComponent()
240                                                                .addInclude(new org.hl7.fhir.r4b.model.ValueSet.ConceptSetComponent().setSystem(codeSystemUrl).setVersion(codeSystemVersion)));
241                                        } else {
242                                                vs = new org.hl7.fhir.r4b.model.ValueSet()
243                                                        .setCompose(new org.hl7.fhir.r4b.model.ValueSet.ValueSetComposeComponent()
244                                                                .addInclude(new org.hl7.fhir.r4b.model.ValueSet.ConceptSetComponent().setSystem(theCodeSystem)));
245                                        }
246                                        break;
247                                case R5:
248                                        if (codeSystemVersion != null) {
249                                                vs = new org.hl7.fhir.r5.model.ValueSet()
250                                                        .setCompose(new org.hl7.fhir.r5.model.ValueSet.ValueSetComposeComponent()
251                                                                .addInclude(new org.hl7.fhir.r5.model.ValueSet.ConceptSetComponent().setSystem(codeSystemUrl).setVersion(codeSystemVersion)));
252                                        } else {
253                                                vs = new org.hl7.fhir.r5.model.ValueSet()
254                                                        .setCompose(new org.hl7.fhir.r5.model.ValueSet.ValueSetComposeComponent()
255                                                                .addInclude(new org.hl7.fhir.r5.model.ValueSet.ConceptSetComponent().setSystem(theCodeSystem)));
256                                        }
257                                        break;
258                                case DSTU2_1:
259                                default:
260                                        throw new IllegalArgumentException(Msg.code(699) + "Can not handle version: " + myCtx.getVersion().getVersion());
261                        }
262                }
263
264                ValueSetExpansionOutcome valueSetExpansionOutcome = expandValueSet(theValidationSupportContext, vs, theCodeSystem, theCode);
265                if (valueSetExpansionOutcome == null) {
266                        return null;
267                }
268
269                if (valueSetExpansionOutcome.getError() != null) {
270                        return new CodeValidationResult()
271                                .setSeverity(IssueSeverity.ERROR)
272                                .setMessage(valueSetExpansionOutcome.getError());
273                }
274
275                IBaseResource expansion = valueSetExpansionOutcome.getValueSet();
276                return validateCodeInExpandedValueSet(theValidationSupportContext, theOptions, theCodeSystem, theCode, theDisplay, expansion, theValueSetUrl);
277        }
278
279        private CodeValidationResult validateCodeInExpandedValueSet(ValidationSupportContext theValidationSupportContext, ConceptValidationOptions theOptions, String theCodeSystemUrlAndVersionToValidate, String theCodeToValidate, String theDisplayToValidate, IBaseResource theExpansion, String theValueSetUrl) {
280                assert theExpansion != null;
281
282                boolean caseSensitive = true;
283                IBaseResource codeSystemToValidateResource = null;
284                if (!theOptions.isInferSystem() && isNotBlank(theCodeSystemUrlAndVersionToValidate)) {
285                        codeSystemToValidateResource = theValidationSupportContext.getRootValidationSupport().fetchCodeSystem(theCodeSystemUrlAndVersionToValidate);
286                }
287
288                List<FhirVersionIndependentConcept> codes = new ArrayList<>();
289                switch (getFhirVersionEnum(theValidationSupportContext.getRootValidationSupport().getFhirContext(), theExpansion)) {
290                        case DSTU2: {
291                                ca.uhn.fhir.model.dstu2.resource.ValueSet expansionVs = (ca.uhn.fhir.model.dstu2.resource.ValueSet) theExpansion;
292                                List<ca.uhn.fhir.model.dstu2.resource.ValueSet.ExpansionContains> contains = expansionVs.getExpansion().getContains();
293                                flattenAndConvertCodesDstu2(contains, codes);
294                                break;
295                        }
296                        case DSTU2_HL7ORG: {
297                                ValueSet expansionVs = (ValueSet) theExpansion;
298                                List<ValueSet.ValueSetExpansionContainsComponent> contains = expansionVs.getExpansion().getContains();
299                                flattenAndConvertCodesDstu2Hl7Org(contains, codes);
300                                break;
301                        }
302                        case DSTU3: {
303                                org.hl7.fhir.dstu3.model.ValueSet expansionVs = (org.hl7.fhir.dstu3.model.ValueSet) theExpansion;
304                                List<org.hl7.fhir.dstu3.model.ValueSet.ValueSetExpansionContainsComponent> contains = expansionVs.getExpansion().getContains();
305                                flattenAndConvertCodesDstu3(contains, codes);
306                                break;
307                        }
308                        case R4: {
309                                org.hl7.fhir.r4.model.ValueSet expansionVs = (org.hl7.fhir.r4.model.ValueSet) theExpansion;
310                                List<org.hl7.fhir.r4.model.ValueSet.ValueSetExpansionContainsComponent> contains = expansionVs.getExpansion().getContains();
311                                flattenAndConvertCodesR4(contains, codes);
312                                break;
313                        }
314                        case R4B: {
315                                org.hl7.fhir.r4b.model.ValueSet expansionVs = (org.hl7.fhir.r4b.model.ValueSet) theExpansion;
316                                List<org.hl7.fhir.r4b.model.ValueSet.ValueSetExpansionContainsComponent> contains = expansionVs.getExpansion().getContains();
317                                flattenAndConvertCodesR4B(contains, codes);
318                                break;
319                        }
320                        case R5: {
321                                org.hl7.fhir.r5.model.ValueSet expansionVs = (org.hl7.fhir.r5.model.ValueSet) theExpansion;
322                                List<org.hl7.fhir.r5.model.ValueSet.ValueSetExpansionContainsComponent> contains = expansionVs.getExpansion().getContains();
323                                flattenAndConvertCodesR5(contains, codes);
324                                break;
325                        }
326                        case DSTU2_1:
327                        default:
328                                throw new IllegalArgumentException(Msg.code(700) + "Can not handle version: " + myCtx.getVersion().getVersion());
329                }
330
331                String codeSystemResourceName = null;
332                String codeSystemResourceVersion = null;
333                String codeSystemResourceContentMode = null;
334                if (codeSystemToValidateResource != null) {
335                        switch (getFhirVersionEnum(theValidationSupportContext.getRootValidationSupport().getFhirContext(), codeSystemToValidateResource)) {
336                                case DSTU2:
337                                case DSTU2_HL7ORG: {
338                                        caseSensitive = true;
339                                        break;
340                                }
341                                case DSTU3: {
342                                        org.hl7.fhir.dstu3.model.CodeSystem systemDstu3 = (org.hl7.fhir.dstu3.model.CodeSystem) codeSystemToValidateResource;
343                                        caseSensitive = systemDstu3.getCaseSensitive();
344                                        codeSystemResourceName = systemDstu3.getName();
345                                        codeSystemResourceVersion = systemDstu3.getVersion();
346                                        codeSystemResourceContentMode = systemDstu3.getContentElement().getValueAsString();
347                                        break;
348                                }
349                                case R4: {
350                                        org.hl7.fhir.r4.model.CodeSystem systemR4 = (org.hl7.fhir.r4.model.CodeSystem) codeSystemToValidateResource;
351                                        caseSensitive = systemR4.getCaseSensitive();
352                                        codeSystemResourceName = systemR4.getName();
353                                        codeSystemResourceVersion = systemR4.getVersion();
354                                        codeSystemResourceContentMode = systemR4.getContentElement().getValueAsString();
355                                        break;
356                                }
357                                case R4B: {
358                                        org.hl7.fhir.r4b.model.CodeSystem systemR4B = (org.hl7.fhir.r4b.model.CodeSystem) codeSystemToValidateResource;
359                                        caseSensitive = systemR4B.getCaseSensitive();
360                                        codeSystemResourceName = systemR4B.getName();
361                                        codeSystemResourceVersion = systemR4B.getVersion();
362                                        codeSystemResourceContentMode = systemR4B.getContentElement().getValueAsString();
363                                        break;
364                                }
365                                case R5: {
366                                        CodeSystem systemR5 = (CodeSystem) codeSystemToValidateResource;
367                                        caseSensitive = systemR5.getCaseSensitive();
368                                        codeSystemResourceName = systemR5.getName();
369                                        codeSystemResourceVersion = systemR5.getVersion();
370                                        codeSystemResourceContentMode = systemR5.getContentElement().getValueAsString();
371                                        break;
372                                }
373                                case DSTU2_1:
374                                default:
375                                        throw new IllegalArgumentException(Msg.code(701) + "Can not handle version: " + myCtx.getVersion().getVersion());
376                        }
377                }
378
379                String codeSystemUrlToValidate = null;
380                String codeSystemVersionToValidate = null;
381                if (theCodeSystemUrlAndVersionToValidate != null) {
382                        int versionIndex = theCodeSystemUrlAndVersionToValidate.indexOf("|");
383                        if (versionIndex > -1) {
384                                codeSystemUrlToValidate = theCodeSystemUrlAndVersionToValidate.substring(0, versionIndex);
385                                codeSystemVersionToValidate = theCodeSystemUrlAndVersionToValidate.substring(versionIndex + 1);
386                        } else {
387                                codeSystemUrlToValidate = theCodeSystemUrlAndVersionToValidate;
388                        }
389                }
390                for (FhirVersionIndependentConcept nextExpansionCode : codes) {
391
392                        boolean codeMatches;
393                        if (caseSensitive) {
394                                codeMatches = defaultString(theCodeToValidate).equals(nextExpansionCode.getCode());
395                        } else {
396                                codeMatches = defaultString(theCodeToValidate).equalsIgnoreCase(nextExpansionCode.getCode());
397                        }
398                        if (codeMatches) {
399                                if (theOptions.isInferSystem() || (nextExpansionCode.getSystem().equals(codeSystemUrlToValidate) && (codeSystemVersionToValidate == null || codeSystemVersionToValidate.equals(nextExpansionCode.getSystemVersion())))) {
400                                        String csVersion = codeSystemResourceVersion;
401                                        if (isNotBlank(nextExpansionCode.getSystemVersion())) {
402                                                csVersion = nextExpansionCode.getSystemVersion();
403                                        }
404                                        if (!theOptions.isValidateDisplay() || (isBlank(nextExpansionCode.getDisplay()) || isBlank(theDisplayToValidate) || nextExpansionCode.getDisplay().equals(theDisplayToValidate))) {
405                                                CodeValidationResult codeValidationResult = new CodeValidationResult()
406                                                        .setCode(theCodeToValidate)
407                                                        .setDisplay(nextExpansionCode.getDisplay())
408                                                        .setCodeSystemName(codeSystemResourceName)
409                                                        .setCodeSystemVersion(csVersion);
410                                                if (isNotBlank(theValueSetUrl)) {
411                                                        codeValidationResult.setMessage("Code was validated against in-memory expansion of ValueSet: " + theValueSetUrl);
412                                                }
413                                                return codeValidationResult;
414                                        } else {
415                                                String message = "Concept Display \"" + theDisplayToValidate + "\" does not match expected \"" + nextExpansionCode.getDisplay() + "\"";
416                                                if (isNotBlank(theValueSetUrl)) {
417                                                        message += " for in-memory expansion of ValueSet: " + theValueSetUrl;
418                                                }
419                                                return new CodeValidationResult()
420                                                        .setSeverity(IssueSeverity.ERROR)
421                                                        .setDisplay(nextExpansionCode.getDisplay())
422                                                        .setMessage(message)
423                                                        .setCodeSystemName(codeSystemResourceName)
424                                                        .setCodeSystemVersion(csVersion);
425                                        }
426                                }
427                        }
428                }
429
430                ValidationMessage.IssueSeverity severity;
431                String message;
432                if ("fragment".equals(codeSystemResourceContentMode)) {
433                        severity = ValidationMessage.IssueSeverity.WARNING;
434                        message = "Unknown code in fragment CodeSystem '" + (isNotBlank(theCodeSystemUrlAndVersionToValidate) ? theCodeSystemUrlAndVersionToValidate + "#" : "") + theCodeToValidate + "'";
435                } else {
436                        severity = ValidationMessage.IssueSeverity.ERROR;
437                        message = "Unknown code '" + (isNotBlank(theCodeSystemUrlAndVersionToValidate) ? theCodeSystemUrlAndVersionToValidate + "#" : "") + theCodeToValidate + "'";
438                }
439                if (isNotBlank(theValueSetUrl)) {
440                        message += " for in-memory expansion of ValueSet '" + theValueSetUrl + "'";
441                }
442
443                return new CodeValidationResult()
444                        .setSeverityCode(severity.toCode())
445                        .setMessage(message);
446        }
447
448        @Override
449        public LookupCodeResult lookupCode(ValidationSupportContext theValidationSupportContext, String theSystem, String theCode, String theDisplayLanguage) {
450                CodeValidationResult codeValidationResult = validateCode(theValidationSupportContext, new ConceptValidationOptions(), theSystem, theCode, null, null);
451                if (codeValidationResult == null) {
452                        return null;
453                }
454                return codeValidationResult.asLookupCodeResult(theSystem, theCode);
455        }
456
457        @Nullable
458        private org.hl7.fhir.r5.model.ValueSet expandValueSetDstu2Hl7Org(ValidationSupportContext theValidationSupportContext, ValueSet theInput, @Nullable String theWantSystemUrlAndVersion, @Nullable String theWantCode) throws ExpansionCouldNotBeCompletedInternallyException {
459                org.hl7.fhir.r5.model.ValueSet input = (org.hl7.fhir.r5.model.ValueSet) VersionConvertorFactory_10_50.convertResource(theInput, new BaseAdvisor_10_50(false));
460                return (expandValueSetR5(theValidationSupportContext, input, theWantSystemUrlAndVersion, theWantCode));
461        }
462
463        @Nullable
464        private org.hl7.fhir.r5.model.ValueSet expandValueSetDstu2(ValidationSupportContext theValidationSupportContext, ca.uhn.fhir.model.dstu2.resource.ValueSet theInput, @Nullable String theWantSystemUrlAndVersion, @Nullable String theWantCode) throws ExpansionCouldNotBeCompletedInternallyException {
465                IParser parserRi = FhirContext.forCached(FhirVersionEnum.DSTU2_HL7ORG).newJsonParser();
466                IParser parserHapi = FhirContext.forDstu2Cached().newJsonParser();
467
468                org.hl7.fhir.dstu2.model.ValueSet valueSetRi = parserRi.parseResource(org.hl7.fhir.dstu2.model.ValueSet.class, parserHapi.encodeResourceToString(theInput));
469                org.hl7.fhir.r5.model.ValueSet input = (org.hl7.fhir.r5.model.ValueSet) VersionConvertorFactory_10_50.convertResource(valueSetRi, new BaseAdvisor_10_50(false));
470                return (expandValueSetR5(theValidationSupportContext, input, theWantSystemUrlAndVersion, theWantCode));
471        }
472
473        @Override
474        public boolean isCodeSystemSupported(ValidationSupportContext theValidationSupportContext, String theSystem) {
475                if (isBlank(theSystem)) {
476                        return false;
477                }
478
479                IBaseResource cs = theValidationSupportContext.getRootValidationSupport().fetchCodeSystem(theSystem);
480
481                if (!myCtx.getVersion().getVersion().isEqualOrNewerThan(FhirVersionEnum.DSTU2_1)) {
482                        return cs != null;
483                }
484
485                if (cs != null) {
486                        IPrimitiveType<?> content = getFhirContext().newTerser().getSingleValueOrNull(cs, "content", IPrimitiveType.class);
487                        return !"not-present".equals(content.getValueAsString());
488                }
489
490                return false;
491        }
492
493        @Override
494        public boolean isValueSetSupported(ValidationSupportContext theValidationSupportContext, String theValueSetUrl) {
495                return isNotBlank(theValueSetUrl) && theValidationSupportContext.getRootValidationSupport().fetchValueSet(theValueSetUrl) != null;
496        }
497
498
499        private void addCodesDstu2Hl7Org(List<ValueSet.ConceptDefinitionComponent> theSourceList, List<CodeSystem.ConceptDefinitionComponent> theTargetList) {
500                for (ValueSet.ConceptDefinitionComponent nextSource : theSourceList) {
501                        CodeSystem.ConceptDefinitionComponent targetConcept = new CodeSystem.ConceptDefinitionComponent().setCode(nextSource.getCode()).setDisplay(nextSource.getDisplay());
502                        theTargetList.add(targetConcept);
503                        addCodesDstu2Hl7Org(nextSource.getConcept(), targetConcept.getConcept());
504                }
505        }
506
507        private void addCodesDstu2(List<ca.uhn.fhir.model.dstu2.resource.ValueSet.CodeSystemConcept> theSourceList, List<CodeSystem.ConceptDefinitionComponent> theTargetList) {
508                for (ca.uhn.fhir.model.dstu2.resource.ValueSet.CodeSystemConcept nextSource : theSourceList) {
509                        CodeSystem.ConceptDefinitionComponent targetConcept = new CodeSystem.ConceptDefinitionComponent().setCode(nextSource.getCode()).setDisplay(nextSource.getDisplay());
510                        theTargetList.add(targetConcept);
511                        addCodesDstu2(nextSource.getConcept(), targetConcept.getConcept());
512                }
513        }
514
515        @Nullable
516        private org.hl7.fhir.r5.model.ValueSet expandValueSetDstu3(ValidationSupportContext theValidationSupportContext, org.hl7.fhir.dstu3.model.ValueSet theInput, @Nullable String theWantSystemUrlAndVersion, @Nullable String theWantCode) throws ExpansionCouldNotBeCompletedInternallyException {
517                org.hl7.fhir.r5.model.ValueSet input = (org.hl7.fhir.r5.model.ValueSet) VersionConvertorFactory_30_50.convertResource(theInput, new BaseAdvisor_30_50(false));
518                return (expandValueSetR5(theValidationSupportContext, input, theWantSystemUrlAndVersion, theWantCode));
519        }
520
521        @Nullable
522        private org.hl7.fhir.r5.model.ValueSet expandValueSetR4(ValidationSupportContext theValidationSupportContext, org.hl7.fhir.r4.model.ValueSet theInput, @Nullable String theWantSystemUrlAndVersion, @Nullable String theWantCode) throws ExpansionCouldNotBeCompletedInternallyException {
523                org.hl7.fhir.r5.model.ValueSet input = (org.hl7.fhir.r5.model.ValueSet) VersionConvertorFactory_40_50.convertResource(theInput, new BaseAdvisor_40_50(false));
524                return expandValueSetR5(theValidationSupportContext, input, theWantSystemUrlAndVersion, theWantCode);
525        }
526
527        @Nullable
528        private org.hl7.fhir.r5.model.ValueSet expandValueSetR4B(ValidationSupportContext theValidationSupportContext, org.hl7.fhir.r4b.model.ValueSet theInput, @Nullable String theWantSystemUrlAndVersion, @Nullable String theWantCode) throws ExpansionCouldNotBeCompletedInternallyException {
529                org.hl7.fhir.r5.model.ValueSet input = (org.hl7.fhir.r5.model.ValueSet) VersionConvertorFactory_43_50.convertResource(theInput, new BaseAdvisor_43_50(false));
530                return expandValueSetR5(theValidationSupportContext, input, theWantSystemUrlAndVersion, theWantCode);
531        }
532
533        @Nullable
534        private org.hl7.fhir.r5.model.ValueSet expandValueSetR5(ValidationSupportContext theValidationSupportContext, org.hl7.fhir.r5.model.ValueSet theInput) throws ExpansionCouldNotBeCompletedInternallyException {
535                return expandValueSetR5(theValidationSupportContext, theInput, null, null);
536        }
537
538        @Nullable
539        private org.hl7.fhir.r5.model.ValueSet expandValueSetR5(ValidationSupportContext theValidationSupportContext, org.hl7.fhir.r5.model.ValueSet theInput, @Nullable String theWantSystemUrlAndVersion, @Nullable String theWantCode) throws ExpansionCouldNotBeCompletedInternallyException {
540                Set<FhirVersionIndependentConcept> concepts = new HashSet<>();
541
542                expandValueSetR5IncludeOrExcludes(theValidationSupportContext, concepts, theInput.getCompose().getInclude(), true, theWantSystemUrlAndVersion, theWantCode);
543                expandValueSetR5IncludeOrExcludes(theValidationSupportContext, concepts, theInput.getCompose().getExclude(), false, theWantSystemUrlAndVersion, theWantCode);
544
545                org.hl7.fhir.r5.model.ValueSet retVal = new org.hl7.fhir.r5.model.ValueSet();
546                for (FhirVersionIndependentConcept next : concepts) {
547                        org.hl7.fhir.r5.model.ValueSet.ValueSetExpansionContainsComponent contains = retVal.getExpansion().addContains();
548                        contains.setSystem(next.getSystem());
549                        contains.setCode(next.getCode());
550                        contains.setDisplay(next.getDisplay());
551                        contains.setVersion(next.getSystemVersion());
552                }
553
554                return retVal;
555        }
556
557        /**
558         * Use with caution - this is not a stable API
559         *
560         * @since 5.6.0
561         */
562        public void expandValueSetIncludeOrExclude(ValidationSupportContext theValidationSupportContext, Consumer<FhirVersionIndependentConcept> theConsumer, org.hl7.fhir.r5.model.ValueSet.ConceptSetComponent theIncludeOrExclude) throws ExpansionCouldNotBeCompletedInternallyException {
563                expandValueSetR5IncludeOrExclude(theValidationSupportContext, theConsumer, null, null, theIncludeOrExclude);
564        }
565
566
567        private void expandValueSetR5IncludeOrExcludes(ValidationSupportContext theValidationSupportContext, Set<FhirVersionIndependentConcept> theConcepts, List<org.hl7.fhir.r5.model.ValueSet.ConceptSetComponent> theComposeList, boolean theComposeListIsInclude, @Nullable String theWantSystemUrlAndVersion, @Nullable String theWantCode) throws ExpansionCouldNotBeCompletedInternallyException {
568                Consumer<FhirVersionIndependentConcept> consumer = c -> {
569                        if (theComposeListIsInclude) {
570                                theConcepts.add(c);
571                        } else {
572                                theConcepts.remove(c);
573                        }
574                };
575                expandValueSetR5IncludeOrExcludes(theValidationSupportContext, consumer, theComposeList, theWantSystemUrlAndVersion, theWantCode);
576        }
577
578
579        private void expandValueSetR5IncludeOrExcludes(ValidationSupportContext theValidationSupportContext, Consumer<FhirVersionIndependentConcept> theConsumer, List<org.hl7.fhir.r5.model.ValueSet.ConceptSetComponent> theComposeList, @Nullable String theWantSystemUrlAndVersion, @Nullable String theWantCode) throws ExpansionCouldNotBeCompletedInternallyException {
580                ExpansionCouldNotBeCompletedInternallyException caughtException = null;
581                for (org.hl7.fhir.r5.model.ValueSet.ConceptSetComponent nextInclude : theComposeList) {
582                        try {
583                                boolean outcome = expandValueSetR5IncludeOrExclude(theValidationSupportContext, theConsumer, theWantSystemUrlAndVersion, theWantCode, nextInclude);
584                                if (isNotBlank(theWantCode)) {
585                                        if (outcome) {
586                                                return;
587                                        }
588                                }
589                        } catch (ExpansionCouldNotBeCompletedInternallyException e) {
590                                if (isBlank(theWantCode)) {
591                                        throw e;
592                                } else {
593                                        caughtException = e;
594                                }
595                        }
596                }
597                if (caughtException != null) {
598                        throw caughtException;
599                }
600        }
601
602        /**
603         * Returns <code>true</code> if at least one code was added
604         */
605        private boolean expandValueSetR5IncludeOrExclude(ValidationSupportContext theValidationSupportContext, Consumer<FhirVersionIndependentConcept> theConsumer, @Nullable String theWantSystemUrlAndVersion, @Nullable String theWantCode, org.hl7.fhir.r5.model.ValueSet.ConceptSetComponent theInclude) throws ExpansionCouldNotBeCompletedInternallyException {
606
607                String wantSystemUrl = null;
608                String wantSystemVersion = null;
609
610                if (theWantSystemUrlAndVersion != null) {
611                        int versionIndex = theWantSystemUrlAndVersion.indexOf(OUR_PIPE_CHARACTER);
612                        if (versionIndex > -1) {
613                                wantSystemUrl = theWantSystemUrlAndVersion.substring(0, versionIndex);
614                                wantSystemVersion = theWantSystemUrlAndVersion.substring(versionIndex + 1);
615                        } else {
616                                wantSystemUrl = theWantSystemUrlAndVersion;
617                        }
618                }
619
620                String includeOrExcludeConceptSystemUrl = theInclude.getSystem();
621                String includeOrExcludeConceptSystemVersion = theInclude.getVersion();
622
623                Function<String, CodeSystem> codeSystemLoader = newCodeSystemLoader(theValidationSupportContext);
624                Function<String, org.hl7.fhir.r5.model.ValueSet> valueSetLoader = newValueSetLoader(theValidationSupportContext);
625
626                List<FhirVersionIndependentConcept> nextCodeList = new ArrayList<>();
627                CodeSystem includeOrExcludeSystemResource = null;
628
629                if (isNotBlank(includeOrExcludeConceptSystemUrl)) {
630
631                        includeOrExcludeConceptSystemVersion = optionallyPopulateVersionFromUrl(includeOrExcludeConceptSystemUrl, includeOrExcludeConceptSystemVersion);
632                        includeOrExcludeConceptSystemUrl = substringBefore(includeOrExcludeConceptSystemUrl, OUR_PIPE_CHARACTER);
633
634                        if (wantSystemUrl != null && !wantSystemUrl.equals(includeOrExcludeConceptSystemUrl)) {
635                                return false;
636                        }
637
638                        if (wantSystemVersion != null && !wantSystemVersion.equals(includeOrExcludeConceptSystemVersion)) {
639                                return false;
640                        }
641
642                        String loadedCodeSystemUrl;
643                        if (includeOrExcludeConceptSystemVersion != null) {
644                                loadedCodeSystemUrl = includeOrExcludeConceptSystemUrl + OUR_PIPE_CHARACTER + includeOrExcludeConceptSystemVersion;
645                        } else {
646                                loadedCodeSystemUrl = includeOrExcludeConceptSystemUrl;
647                        }
648
649                        includeOrExcludeSystemResource = codeSystemLoader.apply(loadedCodeSystemUrl);
650
651                        Set<String> wantCodes;
652                        if (theInclude.getConcept().isEmpty()) {
653                                wantCodes = null;
654                        } else {
655                                wantCodes = theInclude
656                                        .getConcept()
657                                        .stream().map(t -> t.getCode()).collect(Collectors.toSet());
658                        }
659
660                        boolean ableToHandleCode = false;
661                        String failureMessage = null;
662                        FailureType failureType = FailureType.OTHER;
663
664                        if (includeOrExcludeSystemResource == null || includeOrExcludeSystemResource.getContent() == Enumerations.CodeSystemContentMode.NOTPRESENT) {
665
666                                if (theWantCode != null) {
667                                        if (theValidationSupportContext.getRootValidationSupport().isCodeSystemSupported(theValidationSupportContext, includeOrExcludeConceptSystemUrl)) {
668                                                LookupCodeResult lookup = theValidationSupportContext.getRootValidationSupport().lookupCode(theValidationSupportContext, includeOrExcludeConceptSystemUrl, theWantCode, null);
669                                                if (lookup != null) {
670                                                        ableToHandleCode = true;
671                                                        if (lookup.isFound()) {
672                                                                CodeSystem.ConceptDefinitionComponent conceptDefinition = new CodeSystem.ConceptDefinitionComponent()
673                                                                        .addConcept()
674                                                                        .setCode(theWantCode)
675                                                                        .setDisplay(lookup.getCodeDisplay());
676                                                                List<CodeSystem.ConceptDefinitionComponent> codesList = Collections.singletonList(conceptDefinition);
677                                                                addCodes(includeOrExcludeConceptSystemUrl, includeOrExcludeConceptSystemVersion, codesList, nextCodeList, wantCodes);
678                                                        }
679                                                }
680                                        } else {
681
682                                                /*
683                                                 * If we're doing an expansion specifically looking for a single code, that means we're validating that code.
684                                                 * In the case where we have a ValueSet that explicitly enumerates a collection of codes
685                                                 * (via ValueSet.compose.include.code) in a code system that is unknown we'll assume the code is valid
686                                                 * even if we can't find the CodeSystem. This is a compromise obviously, since it would be ideal for
687                                                 * CodeSystems to always be known, but realistically there are always going to be CodeSystems that
688                                                 * can't be supplied because of copyright issues, or because they are grammar based. Allowing a VS to
689                                                 * enumerate a set of good codes for them is a nice compromise there.
690                                                 */
691                                                if (Objects.equals(theInclude.getSystem(), theWantSystemUrlAndVersion)) {
692                                                        Optional<org.hl7.fhir.r5.model.ValueSet.ConceptReferenceComponent> matchingEnumeratedConcept = theInclude.getConcept().stream().filter(t -> Objects.equals(t.getCode(), theWantCode)).findFirst();
693
694                                                        // If the ValueSet.compose.include has no individual concepts in it, and
695                                                        // we can't find the actual referenced CodeSystem, we have no choice
696                                                        // but to fail
697                                                        if (!theInclude.getConcept().isEmpty()) {
698                                                                ableToHandleCode = true;
699                                                        } else {
700                                                                failureMessage = getFailureMessageForMissingOrUnusableCodeSystem(includeOrExcludeSystemResource, loadedCodeSystemUrl);
701                                                        }
702
703                                                        if (matchingEnumeratedConcept.isPresent()) {
704                                                                CodeSystem.ConceptDefinitionComponent conceptDefinition = new CodeSystem.ConceptDefinitionComponent()
705                                                                        .addConcept()
706                                                                        .setCode(theWantCode)
707                                                                        .setDisplay(matchingEnumeratedConcept.get().getDisplay());
708                                                                List<CodeSystem.ConceptDefinitionComponent> codesList = Collections.singletonList(conceptDefinition);
709                                                                addCodes(includeOrExcludeConceptSystemUrl, includeOrExcludeConceptSystemVersion, codesList, nextCodeList, wantCodes);
710                                                        }
711                                                }
712
713                                        }
714                                } else {
715                                        if (isNotBlank(theInclude.getSystem()) && !theInclude.getConcept().isEmpty() && theInclude.getFilter().isEmpty() && theInclude.getValueSet().isEmpty()) {
716                                                theInclude
717                                                        .getConcept()
718                                                        .stream()
719                                                        .map(t -> new FhirVersionIndependentConcept(theInclude.getSystem(), t.getCode(), t.getDisplay(), theInclude.getVersion()))
720                                                        .forEach(t -> nextCodeList.add(t));
721                                                ableToHandleCode = true;
722                                        }
723
724                                        if (!ableToHandleCode) {
725                                                failureMessage = getFailureMessageForMissingOrUnusableCodeSystem(includeOrExcludeSystemResource, loadedCodeSystemUrl);
726                                        }
727                                }
728
729                        } else {
730                                ableToHandleCode = true;
731                        }
732
733                        if (!ableToHandleCode) {
734                                if (includeOrExcludeSystemResource == null && failureMessage == null) {
735                                        failureMessage = getFailureMessageForMissingOrUnusableCodeSystem(includeOrExcludeSystemResource, loadedCodeSystemUrl);
736                                }
737
738                                if (includeOrExcludeSystemResource == null) {
739                                        failureType = FailureType.UNKNOWN_CODE_SYSTEM;
740                                }
741
742                                throw new ExpansionCouldNotBeCompletedInternallyException(Msg.code(702) + failureMessage, failureType);
743                        }
744
745                        if (includeOrExcludeSystemResource != null && includeOrExcludeSystemResource.getContent() != Enumerations.CodeSystemContentMode.NOTPRESENT) {
746                                addCodes(includeOrExcludeConceptSystemUrl, includeOrExcludeConceptSystemVersion, includeOrExcludeSystemResource.getConcept(), nextCodeList, wantCodes);
747                        }
748
749                }
750
751                for (CanonicalType nextValueSetInclude : theInclude.getValueSet()) {
752                        org.hl7.fhir.r5.model.ValueSet vs = valueSetLoader.apply(nextValueSetInclude.getValueAsString());
753                        if (vs != null) {
754                                org.hl7.fhir.r5.model.ValueSet subExpansion = expandValueSetR5(theValidationSupportContext, vs, theWantSystemUrlAndVersion, theWantCode);
755                                if (subExpansion == null) {
756                                        throw new ExpansionCouldNotBeCompletedInternallyException(Msg.code(703) + "Failed to expand ValueSet: " + nextValueSetInclude.getValueAsString(), FailureType.OTHER);
757                                }
758                                for (org.hl7.fhir.r5.model.ValueSet.ValueSetExpansionContainsComponent next : subExpansion.getExpansion().getContains()) {
759                                        nextCodeList.add(new FhirVersionIndependentConcept(next.getSystem(), next.getCode(), next.getDisplay(), next.getVersion()));
760                                }
761                        }
762                }
763
764                boolean retVal = false;
765
766                for (FhirVersionIndependentConcept next : nextCodeList) {
767                        if (includeOrExcludeSystemResource != null && theWantCode != null) {
768                                boolean matches;
769                                if (includeOrExcludeSystemResource.getCaseSensitive()) {
770                                        matches = theWantCode.equals(next.getCode());
771                                } else {
772                                        matches = theWantCode.equalsIgnoreCase(next.getCode());
773                                }
774                                if (!matches) {
775                                        continue;
776                                }
777                        }
778
779                        theConsumer.accept(next);
780                        retVal = true;
781                }
782
783                return retVal;
784        }
785
786        private Function<String, org.hl7.fhir.r5.model.ValueSet> newValueSetLoader(ValidationSupportContext theValidationSupportContext) {
787                switch (myCtx.getVersion().getVersion()) {
788                        case DSTU2:
789                        case DSTU2_HL7ORG:
790                                return t -> {
791                                        IBaseResource vs = theValidationSupportContext.getRootValidationSupport().fetchValueSet(t);
792                                        if (vs instanceof ca.uhn.fhir.model.dstu2.resource.ValueSet) {
793                                                IParser parserRi = FhirContext.forCached(FhirVersionEnum.DSTU2_HL7ORG).newJsonParser();
794                                                IParser parserHapi = FhirContext.forDstu2Cached().newJsonParser();
795                                                ca.uhn.fhir.model.dstu2.resource.ValueSet valueSet = (ca.uhn.fhir.model.dstu2.resource.ValueSet) vs;
796                                                org.hl7.fhir.dstu2.model.ValueSet valueSetRi = parserRi.parseResource(org.hl7.fhir.dstu2.model.ValueSet.class, parserHapi.encodeResourceToString(valueSet));
797                                                return (org.hl7.fhir.r5.model.ValueSet) VersionConvertorFactory_10_50.convertResource(valueSetRi, new BaseAdvisor_10_50(false));
798                                        } else {
799                                                org.hl7.fhir.dstu2.model.ValueSet valueSet = (org.hl7.fhir.dstu2.model.ValueSet) theValidationSupportContext.getRootValidationSupport().fetchValueSet(t);
800                                                return (org.hl7.fhir.r5.model.ValueSet) VersionConvertorFactory_10_50.convertResource(valueSet, new BaseAdvisor_10_50(false));
801                                        }
802                                };
803                        case DSTU3:
804                                return t -> {
805                                        org.hl7.fhir.dstu3.model.ValueSet valueSet = (org.hl7.fhir.dstu3.model.ValueSet) theValidationSupportContext.getRootValidationSupport().fetchValueSet(t);
806                                        return (org.hl7.fhir.r5.model.ValueSet) VersionConvertorFactory_30_50.convertResource(valueSet, new BaseAdvisor_30_50(false));
807                                };
808                        case R4:
809                                return t -> {
810                                        org.hl7.fhir.r4.model.ValueSet valueSet = (org.hl7.fhir.r4.model.ValueSet) theValidationSupportContext.getRootValidationSupport().fetchValueSet(t);
811                                        return (org.hl7.fhir.r5.model.ValueSet) VersionConvertorFactory_40_50.convertResource(valueSet, new BaseAdvisor_40_50(false));
812                                };
813                        case R4B:
814                                return t -> {
815                                        org.hl7.fhir.r4b.model.ValueSet valueSet = (org.hl7.fhir.r4b.model.ValueSet) theValidationSupportContext.getRootValidationSupport().fetchValueSet(t);
816                                        return (org.hl7.fhir.r5.model.ValueSet) VersionConvertorFactory_43_50.convertResource(valueSet, new BaseAdvisor_43_50(false));
817                                };
818                        default:
819                        case DSTU2_1:
820                        case R5:
821                                return t -> (org.hl7.fhir.r5.model.ValueSet) theValidationSupportContext.getRootValidationSupport().fetchValueSet(t);
822                }
823        }
824
825        private Function<String, CodeSystem> newCodeSystemLoader(ValidationSupportContext theValidationSupportContext) {
826                switch (myCtx.getVersion().getVersion()) {
827                        case DSTU2:
828                        case DSTU2_HL7ORG:
829                                return t -> {
830                                        IBaseResource codeSystem = theValidationSupportContext.getRootValidationSupport().fetchCodeSystem(t);
831                                        CodeSystem retVal = null;
832                                        if (codeSystem != null) {
833                                                retVal = new CodeSystem();
834                                                if (codeSystem instanceof ca.uhn.fhir.model.dstu2.resource.ValueSet) {
835                                                        ca.uhn.fhir.model.dstu2.resource.ValueSet codeSystemCasted = (ca.uhn.fhir.model.dstu2.resource.ValueSet) codeSystem;
836                                                        retVal.setUrl(codeSystemCasted.getUrl());
837                                                        addCodesDstu2(codeSystemCasted.getCodeSystem().getConcept(), retVal.getConcept());
838                                                } else {
839                                                        org.hl7.fhir.dstu2.model.ValueSet codeSystemCasted = (org.hl7.fhir.dstu2.model.ValueSet) codeSystem;
840                                                        retVal.setUrl(codeSystemCasted.getUrl());
841                                                        addCodesDstu2Hl7Org(codeSystemCasted.getCodeSystem().getConcept(), retVal.getConcept());
842                                                }
843                                        }
844                                        return retVal;
845                                };
846                        case DSTU3:
847                                return t -> {
848                                        org.hl7.fhir.dstu3.model.CodeSystem codeSystem = (org.hl7.fhir.dstu3.model.CodeSystem) theValidationSupportContext.getRootValidationSupport().fetchCodeSystem(t);
849                                        return (CodeSystem) VersionConvertorFactory_30_50.convertResource(codeSystem, new BaseAdvisor_30_50(false));
850                                };
851                        case R4:
852                                return t -> {
853                                        org.hl7.fhir.r4.model.CodeSystem codeSystem = (org.hl7.fhir.r4.model.CodeSystem) theValidationSupportContext.getRootValidationSupport().fetchCodeSystem(t);
854                                        return (CodeSystem) VersionConvertorFactory_40_50.convertResource(codeSystem, new BaseAdvisor_40_50(false));
855                                };
856                        case R4B:
857                                return t -> {
858                                        org.hl7.fhir.r4b.model.CodeSystem codeSystem = (org.hl7.fhir.r4b.model.CodeSystem) theValidationSupportContext.getRootValidationSupport().fetchCodeSystem(t);
859                                        return (CodeSystem) VersionConvertorFactory_43_50.convertResource(codeSystem, new BaseAdvisor_43_50(false));
860                                };
861                        case DSTU2_1:
862                        case R5:
863                        default:
864                                return t -> (org.hl7.fhir.r5.model.CodeSystem) theValidationSupportContext.getRootValidationSupport().fetchCodeSystem(t);
865
866                }
867        }
868
869        private String getFailureMessageForMissingOrUnusableCodeSystem(CodeSystem includeOrExcludeSystemResource, String loadedCodeSystemUrl) {
870                String failureMessage;
871                if (includeOrExcludeSystemResource == null) {
872                        failureMessage = "Unable to expand ValueSet because CodeSystem could not be found: " + loadedCodeSystemUrl;
873                } else {
874                        assert includeOrExcludeSystemResource.getContent() == Enumerations.CodeSystemContentMode.NOTPRESENT;
875                        failureMessage = "Unable to expand ValueSet because CodeSystem has CodeSystem.content=not-present but contents were not found: " + loadedCodeSystemUrl;
876                }
877                return failureMessage;
878        }
879
880        private void addCodes(String theCodeSystemUrl, String theCodeSystemVersion, List<CodeSystem.ConceptDefinitionComponent> theSource, List<FhirVersionIndependentConcept> theTarget, Set<String> theCodeFilter) {
881                for (CodeSystem.ConceptDefinitionComponent next : theSource) {
882                        if (isNotBlank(next.getCode())) {
883                                if (theCodeFilter == null || theCodeFilter.contains(next.getCode())) {
884                                        theTarget.add(new FhirVersionIndependentConcept(theCodeSystemUrl, next.getCode(), next.getDisplay(), theCodeSystemVersion));
885                                }
886                        }
887                        addCodes(theCodeSystemUrl, theCodeSystemVersion, next.getConcept(), theTarget, theCodeFilter);
888                }
889        }
890
891        private String optionallyPopulateVersionFromUrl(String theSystemUrl, String theVersion) {
892                if(contains(theSystemUrl, OUR_PIPE_CHARACTER) && isBlank(theVersion)){
893                        theVersion = substringAfter(theSystemUrl, OUR_PIPE_CHARACTER);
894                }
895                return theVersion;
896        }
897
898        public enum FailureType {
899
900                UNKNOWN_CODE_SYSTEM,
901                OTHER
902
903        }
904
905        public static class ExpansionCouldNotBeCompletedInternallyException extends Exception {
906
907                private static final long serialVersionUID = -2226561628771483085L;
908                private final FailureType myFailureType;
909
910                public ExpansionCouldNotBeCompletedInternallyException(String theMessage, FailureType theFailureType) {
911                        super(theMessage);
912                        myFailureType = theFailureType;
913                }
914
915                public FailureType getFailureType() {
916                        return myFailureType;
917                }
918        }
919
920        private static void flattenAndConvertCodesDstu2(List<ca.uhn.fhir.model.dstu2.resource.ValueSet.ExpansionContains> theInput, List<FhirVersionIndependentConcept> theFhirVersionIndependentConcepts) {
921                for (ca.uhn.fhir.model.dstu2.resource.ValueSet.ExpansionContains next : theInput) {
922                        theFhirVersionIndependentConcepts.add(new FhirVersionIndependentConcept(next.getSystem(), next.getCode(), next.getDisplay()));
923                        flattenAndConvertCodesDstu2(next.getContains(), theFhirVersionIndependentConcepts);
924                }
925        }
926
927        private static void flattenAndConvertCodesDstu2Hl7Org(List<org.hl7.fhir.dstu2.model.ValueSet.ValueSetExpansionContainsComponent> theInput, List<FhirVersionIndependentConcept> theFhirVersionIndependentConcepts) {
928                for (org.hl7.fhir.dstu2.model.ValueSet.ValueSetExpansionContainsComponent next : theInput) {
929                        theFhirVersionIndependentConcepts.add(new FhirVersionIndependentConcept(next.getSystem(), next.getCode(), next.getDisplay()));
930                        flattenAndConvertCodesDstu2Hl7Org(next.getContains(), theFhirVersionIndependentConcepts);
931                }
932        }
933
934        private static void flattenAndConvertCodesDstu3(List<org.hl7.fhir.dstu3.model.ValueSet.ValueSetExpansionContainsComponent> theInput, List<FhirVersionIndependentConcept> theFhirVersionIndependentConcepts) {
935                for (org.hl7.fhir.dstu3.model.ValueSet.ValueSetExpansionContainsComponent next : theInput) {
936                        theFhirVersionIndependentConcepts.add(new FhirVersionIndependentConcept(next.getSystem(), next.getCode(), next.getDisplay(), next.getVersion()));
937                        flattenAndConvertCodesDstu3(next.getContains(), theFhirVersionIndependentConcepts);
938                }
939        }
940
941        private static void flattenAndConvertCodesR4(List<org.hl7.fhir.r4.model.ValueSet.ValueSetExpansionContainsComponent> theInput, List<FhirVersionIndependentConcept> theFhirVersionIndependentConcepts) {
942                for (org.hl7.fhir.r4.model.ValueSet.ValueSetExpansionContainsComponent next : theInput) {
943                        theFhirVersionIndependentConcepts.add(new FhirVersionIndependentConcept(next.getSystem(), next.getCode(), next.getDisplay(), next.getVersion()));
944                        flattenAndConvertCodesR4(next.getContains(), theFhirVersionIndependentConcepts);
945                }
946        }
947
948        private static void flattenAndConvertCodesR4B(List<org.hl7.fhir.r4b.model.ValueSet.ValueSetExpansionContainsComponent> theInput, List<FhirVersionIndependentConcept> theFhirVersionIndependentConcepts) {
949                for (org.hl7.fhir.r4b.model.ValueSet.ValueSetExpansionContainsComponent next : theInput) {
950                        theFhirVersionIndependentConcepts.add(new FhirVersionIndependentConcept(next.getSystem(), next.getCode(), next.getDisplay(), next.getVersion()));
951                        flattenAndConvertCodesR4B(next.getContains(), theFhirVersionIndependentConcepts);
952                }
953        }
954
955        private static void flattenAndConvertCodesR5(List<org.hl7.fhir.r5.model.ValueSet.ValueSetExpansionContainsComponent> theInput, List<FhirVersionIndependentConcept> theFhirVersionIndependentConcepts) {
956                for (org.hl7.fhir.r5.model.ValueSet.ValueSetExpansionContainsComponent next : theInput) {
957                        theFhirVersionIndependentConcepts.add(new FhirVersionIndependentConcept(next.getSystem(), next.getCode(), next.getDisplay(), next.getVersion()));
958                        flattenAndConvertCodesR5(next.getContains(), theFhirVersionIndependentConcepts);
959                }
960        }
961
962}