001package ca.uhn.fhir.context.support;
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.context.FhirContext;
024import ca.uhn.fhir.i18n.Msg;
025import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
026import ca.uhn.fhir.util.ParametersUtil;
027import ca.uhn.fhir.util.UrlUtil;
028import org.apache.commons.lang3.Validate;
029import org.apache.commons.lang3.builder.EqualsBuilder;
030import org.apache.commons.lang3.builder.HashCodeBuilder;
031import org.hl7.fhir.instance.model.api.IBase;
032import org.hl7.fhir.instance.model.api.IBaseCoding;
033import org.hl7.fhir.instance.model.api.IBaseParameters;
034import org.hl7.fhir.instance.model.api.IBaseResource;
035import org.hl7.fhir.instance.model.api.IPrimitiveType;
036
037import javax.annotation.Nonnull;
038import javax.annotation.Nullable;
039import java.util.ArrayList;
040import java.util.Arrays;
041import java.util.Collections;
042import java.util.List;
043import java.util.Set;
044import java.util.function.Supplier;
045import java.util.stream.Collectors;
046
047import static org.apache.commons.lang3.StringUtils.defaultString;
048import static org.apache.commons.lang3.StringUtils.isNotBlank;
049
050/**
051 * This interface is a version-independent representation of the
052 * various functions that can be provided by validation and terminology
053 * services.
054 * <p>
055 * This interface is invoked directly by internal parts of the HAPI FHIR API, including the
056 * Validator and the FHIRPath evaluator. It is used to supply artifacts required for validation
057 * (e.g. StructureDefinition resources, ValueSet resources, etc.) and also to provide
058 * terminology functions such as code validation, ValueSet expansion, etc.
059 * </p>
060 * <p>
061 * Implementations are not required to implement all of the functions
062 * in this interface; in fact it is expected that most won't. Any
063 * methods which are not implemented may simply return <code>null</code>
064 * and calling code is expected to be able to handle this. Generally, a
065 * series of implementations of this interface will be joined together using
066 * the
067 * <a href="https://hapifhir.io/hapi-fhir/apidocs/hapi-fhir-validation/org/hl7/fhir/common/hapi/validation/ValidationSupportChain2.html">ValidationSupportChain</a>
068 * class.
069 * </p>
070 * <p>
071 * See <a href="https://hapifhir.io/hapi-fhir/docs/validation/validation_support_modules.html">Validation Support Modules</a>
072 * for information on how to assemble and configure implementations of this interface. See also
073 * the <code>org.hl7.fhir.common.hapi.validation.support</code>
074 * <a href="https://hapifhir.io/hapi-fhir/apidocs/hapi-fhir-validation/org/hl7/fhir/common/hapi/validation/package-summary.html">package summary</a>
075 * in the <code>hapi-fhir-validation</code> module for many implementations of this interface.
076 * </p>
077 *
078 * @since 5.0.0
079 */
080public interface IValidationSupport {
081        String URL_PREFIX_VALUE_SET = "http://hl7.org/fhir/ValueSet/";
082
083
084        /**
085         * Expands the given portion of a ValueSet
086         *
087         * @param theValidationSupportContext The validation support module will be passed in to this method. This is convenient in cases where the operation needs to make calls to
088         *                                    other method in the support chain, so that they can be passed through the entire chain. Implementations of this interface may always safely ignore this parameter.
089         * @param theExpansionOptions         If provided (can be <code>null</code>), contains options controlling the expansion
090         * @param theValueSetToExpand         The valueset that should be expanded
091         * @return The expansion, or null
092         */
093        @Nullable
094        default ValueSetExpansionOutcome expandValueSet(ValidationSupportContext theValidationSupportContext, @Nullable ValueSetExpansionOptions theExpansionOptions, @Nonnull IBaseResource theValueSetToExpand) {
095                return null;
096        }
097
098        /**
099         * Expands the given portion of a ValueSet by canonical URL.
100         *
101         * @param theValidationSupportContext The validation support module will be passed in to this method. This is convenient in cases where the operation needs to make calls to
102         *                                    other method in the support chain, so that they can be passed through the entire chain. Implementations of this interface may always safely ignore this parameter.
103         * @param theExpansionOptions         If provided (can be <code>null</code>), contains options controlling the expansion
104         * @param theValueSetUrlToExpand      The valueset that should be expanded
105         * @return The expansion, or null
106         * @throws ResourceNotFoundException If no ValueSet can be found with the given URL
107         * @since 6.0.0
108         */
109        @Nullable
110        default ValueSetExpansionOutcome expandValueSet(ValidationSupportContext theValidationSupportContext, @Nullable ValueSetExpansionOptions theExpansionOptions, @Nonnull String theValueSetUrlToExpand) throws ResourceNotFoundException {
111                Validate.notBlank(theValueSetUrlToExpand, "theValueSetUrlToExpand must not be null or blank");
112                IBaseResource valueSet = fetchValueSet(theValueSetUrlToExpand);
113                if (valueSet == null) {
114                        throw new ResourceNotFoundException(Msg.code(2024) + "Unknown ValueSet: " + UrlUtil.escapeUrlParam(theValueSetUrlToExpand));
115                }
116                return expandValueSet(theValidationSupportContext, theExpansionOptions, valueSet);
117        }
118
119        /**
120         * Load and return all conformance resources associated with this
121         * validation support module. This method may return null if it doesn't
122         * make sense for a given module.
123         */
124        @Nullable
125        default List<IBaseResource> fetchAllConformanceResources() {
126                return null;
127        }
128
129        /**
130         * Load and return all possible structure definitions
131         */
132        @Nullable
133        default <T extends IBaseResource> List<T> fetchAllStructureDefinitions() {
134                return null;
135        }
136
137        /**
138         * Load and return all possible structure definitions aside from resource definitions themselves
139         */
140        @Nullable
141        default <T extends IBaseResource> List<T> fetchAllNonBaseStructureDefinitions() {
142                List<T> retVal = fetchAllStructureDefinitions();
143                if (retVal != null) {
144                        List<T> newList = new ArrayList<>(retVal.size());
145                        for (T next : retVal) {
146                                String url = defaultString(getFhirContext().newTerser().getSinglePrimitiveValueOrNull(next, "url"));
147                                if (url.startsWith("http://hl7.org/fhir/StructureDefinition/")) {
148                                        String lastPart = url.substring("http://hl7.org/fhir/StructureDefinition/".length());
149                                        if (getFhirContext().getResourceTypes().contains(lastPart)) {
150                                                continue;
151                                        }
152                                }
153
154                                newList.add(next);
155                        }
156
157                        retVal = newList;
158                }
159
160                return retVal;
161        }
162
163        /**
164         * Fetch a code system by ID
165         *
166         * @param theSystem The code system
167         * @return The valueset (must not be null, but can be an empty ValueSet)
168         */
169        @Nullable
170        default IBaseResource fetchCodeSystem(String theSystem) {
171                return null;
172        }
173
174        /**
175         * Loads a resource needed by the validation (a StructureDefinition, or a
176         * ValueSet)
177         *
178         * <p>
179         * Note: Since 5.3.0, {@literal theClass} can be {@literal null}
180         * </p>
181         *
182         * @param theClass The type of the resource to load, or <code>null</code> to return any resource with the given canonical URI
183         * @param theUri   The resource URI
184         * @return Returns the resource, or <code>null</code> if no resource with the
185         * given URI can be found
186         */
187        @SuppressWarnings("unchecked")
188        @Nullable
189        default <T extends IBaseResource> T fetchResource(@Nullable Class<T> theClass, String theUri) {
190                Validate.notBlank(theUri, "theUri must not be null or blank");
191
192                if (theClass == null) {
193                        Supplier<IBaseResource>[] sources = new Supplier[]{
194                                () -> fetchStructureDefinition(theUri),
195                                () -> fetchValueSet(theUri),
196                                () -> fetchCodeSystem(theUri)
197                        };
198                        return (T) Arrays
199                                .stream(sources)
200                                .map(t -> t.get())
201                                .filter(t -> t != null)
202                                .findFirst()
203                                .orElse(null);
204                }
205
206                switch (getFhirContext().getResourceType(theClass)) {
207                        case "StructureDefinition":
208                                return theClass.cast(fetchStructureDefinition(theUri));
209                        case "ValueSet":
210                                return theClass.cast(fetchValueSet(theUri));
211                        case "CodeSystem":
212                                return theClass.cast(fetchCodeSystem(theUri));
213                }
214
215                if (theUri.startsWith(URL_PREFIX_VALUE_SET)) {
216                        return theClass.cast(fetchValueSet(theUri));
217                }
218
219                return null;
220        }
221
222        @Nullable
223        default IBaseResource fetchStructureDefinition(String theUrl) {
224                return null;
225        }
226
227        /**
228         * Returns <code>true</code> if codes in the given code system can be expanded
229         * or validated
230         *
231         * @param theValidationSupportContext The validation support module will be passed in to this method. This is convenient in cases where the operation needs to make calls to
232         *                                    other method in the support chain, so that they can be passed through the entire chain. Implementations of this interface may always safely ignore this parameter.
233         * @param theSystem                   The URI for the code system, e.g. <code>"http://loinc.org"</code>
234         * @return Returns <code>true</code> if codes in the given code system can be
235         * validated
236         */
237        default boolean isCodeSystemSupported(ValidationSupportContext theValidationSupportContext, String theSystem) {
238                return false;
239        }
240
241        /**
242         * Returns <code>true</code> if a Remote Terminology Service is currently configured
243         *
244         * @return Returns <code>true</code> if a Remote Terminology Service is currently configured
245         */
246        default boolean isRemoteTerminologyServiceConfigured() {
247                return false;
248        }
249
250        /**
251         * Fetch the given ValueSet by URL, or returns null if one can't be found for the given URL
252         */
253        @Nullable
254        default IBaseResource fetchValueSet(String theValueSetUrl) {
255                return null;
256        }
257
258        /**
259         * Validates that the given code exists and if possible returns a display
260         * name. This method is called to check codes which are found in "example"
261         * binding fields (e.g. <code>Observation.code</code>) in the default profile.
262         *
263         * @param theValidationSupportContext The validation support module will be passed in to this method. This is convenient in cases where the operation needs to make calls to
264         *                                    other method in the support chain, so that they can be passed through the entire chain. Implementations of this interface may always safely ignore this parameter.
265         * @param theOptions                  Provides options controlling the validation
266         * @param theCodeSystem               The code system, e.g. "<code>http://loinc.org</code>"
267         * @param theCode                     The code, e.g. "<code>1234-5</code>"
268         * @param theDisplay                  The display name, if it should also be validated
269         * @return Returns a validation result object
270         */
271        @Nullable
272        default CodeValidationResult validateCode(@Nonnull ValidationSupportContext theValidationSupportContext, @Nonnull ConceptValidationOptions theOptions, String theCodeSystem, String theCode, String theDisplay, String theValueSetUrl) {
273                return null;
274        }
275
276        /**
277         * Validates that the given code exists and if possible returns a display
278         * name. This method is called to check codes which are found in "example"
279         * binding fields (e.g. <code>Observation.code</code>) in the default profile.
280         *
281         * @param theValidationSupportContext The validation support module will be passed in to this method. This is convenient in cases where the operation needs to make calls to
282         *                                    other method in the support chain, so that they can be passed through the entire chain. Implementations of this interface may always safely ignore this parameter.
283         * @param theCodeSystem               The code system, e.g. "<code>http://loinc.org</code>"
284         * @param theCode                     The code, e.g. "<code>1234-5</code>"
285         * @param theDisplay                  The display name, if it should also be validated
286         * @param theValueSet                 The ValueSet to validate against. Must not be null, and must be a ValueSet resource.
287         * @return Returns a validation result object, or <code>null</code> if this validation support module can not handle this kind of request
288         */
289        @Nullable
290        default CodeValidationResult validateCodeInValueSet(ValidationSupportContext theValidationSupportContext, ConceptValidationOptions theOptions, String theCodeSystem, String theCode, String theDisplay, @Nonnull IBaseResource theValueSet) {
291                return null;
292        }
293
294        /**
295         * Look up a code using the system and code value
296         *
297         * @param theValidationSupportContext The validation support module will be passed in to this method. This is convenient in cases where the operation needs to make calls to
298         *                                    other method in the support chain, so that they can be passed through the entire chain. Implementations of this interface may always safely ignore this parameter.
299         * @param theSystem                   The CodeSystem URL
300         * @param theCode                     The code
301         * @param theDisplayLanguage          to filter out the designation by the display language. To return all designation, set this value to <code>null</code>.
302         */
303        @Nullable
304        default LookupCodeResult lookupCode(ValidationSupportContext theValidationSupportContext, String theSystem, String theCode, String theDisplayLanguage) {
305                return null;
306        }
307
308        /**
309         * Look up a code using the system and code value
310         *
311         * @param theValidationSupportContext The validation support module will be passed in to this method. This is convenient in cases where the operation needs to make calls to
312         *                                    other method in the support chain, so that they can be passed through the entire chain. Implementations of this interface may always safely ignore this parameter.
313         * @param theSystem                   The CodeSystem URL
314         * @param theCode                     The code
315         */
316        @Nullable
317        default LookupCodeResult lookupCode(ValidationSupportContext theValidationSupportContext, String theSystem, String theCode) {
318                return lookupCode(theValidationSupportContext, theSystem, theCode, null);
319        }
320
321        /**
322         * Returns <code>true</code> if the given valueset can be validated by the given
323         * validation support module
324         *
325         * @param theValidationSupportContext The validation support module will be passed in to this method. This is convenient in cases where the operation needs to make calls to
326         *                                    other method in the support chain, so that they can be passed through the entire chain. Implementations of this interface may always safely ignore this parameter.
327         * @param theValueSetUrl              The ValueSet canonical URL
328         */
329        default boolean isValueSetSupported(ValidationSupportContext theValidationSupportContext, String theValueSetUrl) {
330                return false;
331        }
332
333        /**
334         * Generate a snapshot from the given differential profile.
335         *
336         * @param theValidationSupportContext The validation support module will be passed in to this method. This is convenient in cases where the operation needs to make calls to
337         *                                    other method in the support chain, so that they can be passed through the entire chain. Implementations of this interface may always safely ignore this parameter.
338         * @return Returns null if this module does not know how to handle this request
339         */
340        @Nullable
341        default IBaseResource generateSnapshot(ValidationSupportContext theValidationSupportContext, IBaseResource theInput, String theUrl, String theWebUrl, String theProfileName) {
342                return null;
343        }
344
345        /**
346         * Returns the FHIR Context associated with this module
347         */
348        FhirContext getFhirContext();
349
350        /**
351         * This method clears any temporary caches within the validation support. It is mainly intended for unit tests,
352         * but could be used in non-test scenarios as well.
353         */
354        default void invalidateCaches() {
355                // nothing
356        }
357
358        /**
359         * Attempt to translate the given concept from one code system to another
360         */
361        @Nullable
362        default TranslateConceptResults translateConcept(TranslateCodeRequest theRequest) {
363                return null;
364        }
365
366        enum IssueSeverity {
367                /**
368                 * The issue caused the action to fail, and no further checking could be performed.
369                 */
370                FATAL,
371                /**
372                 * The issue is sufficiently important to cause the action to fail.
373                 */
374                ERROR,
375                /**
376                 * The issue is not important enough to cause the action to fail, but may cause it to be performed suboptimally or in a way that is not as desired.
377                 */
378                WARNING,
379                /**
380                 * The issue has no relation to the degree of success of the action.
381                 */
382                INFORMATION
383        }
384
385        class ConceptDesignation {
386
387                private String myLanguage;
388                private String myUseSystem;
389                private String myUseCode;
390                private String myUseDisplay;
391                private String myValue;
392
393                public String getLanguage() {
394                        return myLanguage;
395                }
396
397                public ConceptDesignation setLanguage(String theLanguage) {
398                        myLanguage = theLanguage;
399                        return this;
400                }
401
402                public String getUseSystem() {
403                        return myUseSystem;
404                }
405
406                public ConceptDesignation setUseSystem(String theUseSystem) {
407                        myUseSystem = theUseSystem;
408                        return this;
409                }
410
411                public String getUseCode() {
412                        return myUseCode;
413                }
414
415                public ConceptDesignation setUseCode(String theUseCode) {
416                        myUseCode = theUseCode;
417                        return this;
418                }
419
420                public String getUseDisplay() {
421                        return myUseDisplay;
422                }
423
424                public ConceptDesignation setUseDisplay(String theUseDisplay) {
425                        myUseDisplay = theUseDisplay;
426                        return this;
427                }
428
429                public String getValue() {
430                        return myValue;
431                }
432
433                public ConceptDesignation setValue(String theValue) {
434                        myValue = theValue;
435                        return this;
436                }
437        }
438
439        abstract class BaseConceptProperty {
440                private final String myPropertyName;
441
442                /**
443                 * Constructor
444                 */
445                protected BaseConceptProperty(String thePropertyName) {
446                        myPropertyName = thePropertyName;
447                }
448
449                public String getPropertyName() {
450                        return myPropertyName;
451                }
452        }
453
454        class StringConceptProperty extends BaseConceptProperty {
455                private final String myValue;
456
457                /**
458                 * Constructor
459                 *
460                 * @param theName The name
461                 */
462                public StringConceptProperty(String theName, String theValue) {
463                        super(theName);
464                        myValue = theValue;
465                }
466
467                public String getValue() {
468                        return myValue;
469                }
470        }
471
472        class CodingConceptProperty extends BaseConceptProperty {
473                private final String myCode;
474                private final String myCodeSystem;
475                private final String myDisplay;
476
477                /**
478                 * Constructor
479                 *
480                 * @param theName The name
481                 */
482                public CodingConceptProperty(String theName, String theCodeSystem, String theCode, String theDisplay) {
483                        super(theName);
484                        myCodeSystem = theCodeSystem;
485                        myCode = theCode;
486                        myDisplay = theDisplay;
487                }
488
489                public String getCode() {
490                        return myCode;
491                }
492
493                public String getCodeSystem() {
494                        return myCodeSystem;
495                }
496
497                public String getDisplay() {
498                        return myDisplay;
499                }
500        }
501
502        class CodeValidationResult {
503                private String myCode;
504                private String myMessage;
505                private IssueSeverity mySeverity;
506                private String myCodeSystemName;
507                private String myCodeSystemVersion;
508                private List<BaseConceptProperty> myProperties;
509                private String myDisplay;
510
511                public CodeValidationResult() {
512                        super();
513                }
514
515                public String getDisplay() {
516                        return myDisplay;
517                }
518
519                public CodeValidationResult setDisplay(String theDisplay) {
520                        myDisplay = theDisplay;
521                        return this;
522                }
523
524                public String getCode() {
525                        return myCode;
526                }
527
528                public CodeValidationResult setCode(String theCode) {
529                        myCode = theCode;
530                        return this;
531                }
532
533                String getCodeSystemName() {
534                        return myCodeSystemName;
535                }
536
537                public CodeValidationResult setCodeSystemName(String theCodeSystemName) {
538                        myCodeSystemName = theCodeSystemName;
539                        return this;
540                }
541
542                public String getCodeSystemVersion() {
543                        return myCodeSystemVersion;
544                }
545
546                public CodeValidationResult setCodeSystemVersion(String theCodeSystemVersion) {
547                        myCodeSystemVersion = theCodeSystemVersion;
548                        return this;
549                }
550
551                public String getMessage() {
552                        return myMessage;
553                }
554
555                public CodeValidationResult setMessage(String theMessage) {
556                        myMessage = theMessage;
557                        return this;
558                }
559
560                public List<BaseConceptProperty> getProperties() {
561                        return myProperties;
562                }
563
564                public void setProperties(List<BaseConceptProperty> theProperties) {
565                        myProperties = theProperties;
566                }
567
568                public IssueSeverity getSeverity() {
569                        return mySeverity;
570                }
571
572                public CodeValidationResult setSeverity(IssueSeverity theSeverity) {
573                        mySeverity = theSeverity;
574                        return this;
575                }
576
577                public boolean isOk() {
578                        return isNotBlank(myCode);
579                }
580
581                public LookupCodeResult asLookupCodeResult(String theSearchedForSystem, String theSearchedForCode) {
582                        LookupCodeResult retVal = new LookupCodeResult();
583                        retVal.setSearchedForSystem(theSearchedForSystem);
584                        retVal.setSearchedForCode(theSearchedForCode);
585                        if (isOk()) {
586                                retVal.setFound(true);
587                                retVal.setCodeDisplay(myDisplay);
588                                retVal.setCodeSystemDisplayName(getCodeSystemName());
589                                retVal.setCodeSystemVersion(getCodeSystemVersion());
590                        }
591                        return retVal;
592                }
593
594                /**
595                 * Convenience method that returns {@link #getSeverity()} as an IssueSeverity code string
596                 */
597                public String getSeverityCode() {
598                        String retVal = null;
599                        if (getSeverity() != null) {
600                                retVal = getSeverity().name().toLowerCase();
601                        }
602                        return retVal;
603                }
604
605                /**
606                 * Sets an issue severity as a string code. Value must be the name of
607                 * one of the enum values in {@link IssueSeverity}. Value is case-insensitive.
608                 */
609                public CodeValidationResult setSeverityCode(@Nonnull String theIssueSeverity) {
610                        setSeverity(IssueSeverity.valueOf(theIssueSeverity.toUpperCase()));
611                        return this;
612                }
613        }
614
615        class ValueSetExpansionOutcome {
616
617                private final IBaseResource myValueSet;
618                private final String myError;
619
620                public ValueSetExpansionOutcome(String theError) {
621                        myValueSet = null;
622                        myError = theError;
623                }
624
625                public ValueSetExpansionOutcome(IBaseResource theValueSet) {
626                        myValueSet = theValueSet;
627                        myError = null;
628                }
629
630                public String getError() {
631                        return myError;
632                }
633
634                public IBaseResource getValueSet() {
635                        return myValueSet;
636                }
637        }
638
639        class LookupCodeResult {
640
641                private String myCodeDisplay;
642                private boolean myCodeIsAbstract;
643                private String myCodeSystemDisplayName;
644                private String myCodeSystemVersion;
645                private boolean myFound;
646                private String mySearchedForCode;
647                private String mySearchedForSystem;
648                private List<IValidationSupport.BaseConceptProperty> myProperties;
649                private List<ConceptDesignation> myDesignations;
650
651                /**
652                 * Constructor
653                 */
654                public LookupCodeResult() {
655                        super();
656                }
657
658                public List<BaseConceptProperty> getProperties() {
659                        if (myProperties == null) {
660                                myProperties = new ArrayList<>();
661                        }
662                        return myProperties;
663                }
664
665                public void setProperties(List<IValidationSupport.BaseConceptProperty> theProperties) {
666                        myProperties = theProperties;
667                }
668
669                @Nonnull
670                public List<ConceptDesignation> getDesignations() {
671                        if (myDesignations == null) {
672                                myDesignations = new ArrayList<>();
673                        }
674                        return myDesignations;
675                }
676
677                public String getCodeDisplay() {
678                        return myCodeDisplay;
679                }
680
681                public void setCodeDisplay(String theCodeDisplay) {
682                        myCodeDisplay = theCodeDisplay;
683                }
684
685                public String getCodeSystemDisplayName() {
686                        return myCodeSystemDisplayName;
687                }
688
689                public void setCodeSystemDisplayName(String theCodeSystemDisplayName) {
690                        myCodeSystemDisplayName = theCodeSystemDisplayName;
691                }
692
693                public String getCodeSystemVersion() {
694                        return myCodeSystemVersion;
695                }
696
697                public void setCodeSystemVersion(String theCodeSystemVersion) {
698                        myCodeSystemVersion = theCodeSystemVersion;
699                }
700
701                public String getSearchedForCode() {
702                        return mySearchedForCode;
703                }
704
705                public LookupCodeResult setSearchedForCode(String theSearchedForCode) {
706                        mySearchedForCode = theSearchedForCode;
707                        return this;
708                }
709
710                public String getSearchedForSystem() {
711                        return mySearchedForSystem;
712                }
713
714                public LookupCodeResult setSearchedForSystem(String theSearchedForSystem) {
715                        mySearchedForSystem = theSearchedForSystem;
716                        return this;
717                }
718
719                public boolean isCodeIsAbstract() {
720                        return myCodeIsAbstract;
721                }
722
723                public void setCodeIsAbstract(boolean theCodeIsAbstract) {
724                        myCodeIsAbstract = theCodeIsAbstract;
725                }
726
727                public boolean isFound() {
728                        return myFound;
729                }
730
731                public LookupCodeResult setFound(boolean theFound) {
732                        myFound = theFound;
733                        return this;
734                }
735
736                public void throwNotFoundIfAppropriate() {
737                        if (isFound() == false) {
738                                throw new ResourceNotFoundException(Msg.code(1738) + "Unable to find code[" + getSearchedForCode() + "] in system[" + getSearchedForSystem() + "]");
739                        }
740                }
741
742                public IBaseParameters toParameters(FhirContext theContext, List<? extends IPrimitiveType<String>> theProperties) {
743
744                        IBaseParameters retVal = ParametersUtil.newInstance(theContext);
745                        if (isNotBlank(getCodeSystemDisplayName())) {
746                                ParametersUtil.addParameterToParametersString(theContext, retVal, "name", getCodeSystemDisplayName());
747                        }
748                        if (isNotBlank(getCodeSystemVersion())) {
749                                ParametersUtil.addParameterToParametersString(theContext, retVal, "version", getCodeSystemVersion());
750                        }
751                        ParametersUtil.addParameterToParametersString(theContext, retVal, "display", getCodeDisplay());
752                        ParametersUtil.addParameterToParametersBoolean(theContext, retVal, "abstract", isCodeIsAbstract());
753
754                        if (myProperties != null) {
755
756                                Set<String> properties = Collections.emptySet();
757                                if (theProperties != null) {
758                                        properties = theProperties
759                                                .stream()
760                                                .map(IPrimitiveType::getValueAsString)
761                                                .collect(Collectors.toSet());
762                                }
763
764                                for (IValidationSupport.BaseConceptProperty next : myProperties) {
765
766                                        if (!properties.isEmpty()) {
767                                                if (!properties.contains(next.getPropertyName())) {
768                                                        continue;
769                                                }
770                                        }
771
772                                        IBase property = ParametersUtil.addParameterToParameters(theContext, retVal, "property");
773                                        ParametersUtil.addPartCode(theContext, property, "code", next.getPropertyName());
774
775                                        if (next instanceof IValidationSupport.StringConceptProperty) {
776                                                IValidationSupport.StringConceptProperty prop = (IValidationSupport.StringConceptProperty) next;
777                                                ParametersUtil.addPartString(theContext, property, "value", prop.getValue());
778                                        } else if (next instanceof IValidationSupport.CodingConceptProperty) {
779                                                IValidationSupport.CodingConceptProperty prop = (IValidationSupport.CodingConceptProperty) next;
780                                                ParametersUtil.addPartCoding(theContext, property, "value", prop.getCodeSystem(), prop.getCode(), prop.getDisplay());
781                                        } else {
782                                                throw new IllegalStateException(Msg.code(1739) + "Don't know how to handle " + next.getClass());
783                                        }
784                                }
785                        }
786
787                        if (myDesignations != null) {
788                                for (ConceptDesignation next : myDesignations) {
789
790                                        IBase property = ParametersUtil.addParameterToParameters(theContext, retVal, "designation");
791                                        ParametersUtil.addPartCode(theContext, property, "language", next.getLanguage());
792                                        ParametersUtil.addPartCoding(theContext, property, "use", next.getUseSystem(), next.getUseCode(), next.getUseDisplay());
793                                        ParametersUtil.addPartString(theContext, property, "value", next.getValue());
794                                }
795                        }
796
797                        return retVal;
798                }
799
800                public static LookupCodeResult notFound(String theSearchedForSystem, String theSearchedForCode) {
801                        return new LookupCodeResult()
802                                .setFound(false)
803                                .setSearchedForSystem(theSearchedForSystem)
804                                .setSearchedForCode(theSearchedForCode);
805                }
806        }
807
808
809        class TranslateCodeRequest {
810                private List<IBaseCoding> myCodings;
811                private final String myTargetSystemUrl;
812                private final String myConceptMapUrl;
813                private final String myConceptMapVersion;
814                private final String mySourceValueSetUrl;
815                private final String myTargetValueSetUrl;
816                private final Long myResourcePid;
817                private final boolean myReverse;
818
819                public TranslateCodeRequest(List<IBaseCoding> theCodings, String theTargetSystemUrl) {
820                        myCodings = theCodings;
821                        myTargetSystemUrl = theTargetSystemUrl;
822                        myConceptMapUrl = null;
823                        myConceptMapVersion = null;
824                        mySourceValueSetUrl = null;
825                        myTargetValueSetUrl = null;
826                        myResourcePid = null;
827                        myReverse = false;
828                }
829
830                public TranslateCodeRequest(
831                                List<IBaseCoding> theCodings,
832                                String theTargetSystemUrl,
833                                String theConceptMapUrl,
834                                String theConceptMapVersion,
835                                String theSourceValueSetUrl,
836                                String theTargetValueSetUrl,
837                                Long theResourcePid,
838                                boolean theReverse) {
839                        myCodings = theCodings;
840                        myTargetSystemUrl = theTargetSystemUrl;
841                        myConceptMapUrl = theConceptMapUrl;
842                        myConceptMapVersion = theConceptMapVersion;
843                        mySourceValueSetUrl = theSourceValueSetUrl;
844                        myTargetValueSetUrl = theTargetValueSetUrl;
845                        myResourcePid = theResourcePid;
846                        myReverse = theReverse;
847                }
848
849                @Override
850                public boolean equals(Object theO) {
851                        if (this == theO) {
852                                return true;
853                        }
854
855                        if (theO == null || getClass() != theO.getClass()) {
856                                return false;
857                        }
858
859                        TranslateCodeRequest that = (TranslateCodeRequest) theO;
860
861                        return new EqualsBuilder()
862                                .append(myCodings, that.myCodings)
863                                .append(myTargetSystemUrl, that.myTargetSystemUrl)
864                                .append(myConceptMapUrl, that.myConceptMapUrl)
865                                .append(myConceptMapVersion, that.myConceptMapVersion)
866                                .append(mySourceValueSetUrl, that.mySourceValueSetUrl)
867                                .append(myTargetValueSetUrl, that.myTargetValueSetUrl)
868                                .append(myResourcePid, that.myResourcePid)
869                                .append(myReverse, that.myReverse)
870                                .isEquals();
871                }
872
873                @Override
874                public int hashCode() {
875                        return new HashCodeBuilder(17, 37)
876                                .append(myCodings)
877                                .append(myTargetSystemUrl)
878                                .append(myConceptMapUrl)
879                                .append(myConceptMapVersion)
880                                .append(mySourceValueSetUrl)
881                                .append(myTargetValueSetUrl)
882                                .append(myResourcePid)
883                                .append(myReverse)
884                                .toHashCode();
885                }
886
887                public List<IBaseCoding> getCodings() {
888                        return myCodings;
889                }
890
891                public String getTargetSystemUrl() {
892                        return myTargetSystemUrl;
893                }
894
895                public String getConceptMapUrl() {
896                        return myConceptMapUrl;
897                }
898
899                public String getConceptMapVersion() {
900                        return myConceptMapVersion;
901                }
902
903                public String getSourceValueSetUrl() {
904                        return mySourceValueSetUrl;
905                }
906
907                public String getTargetValueSetUrl() {
908                        return myTargetValueSetUrl;
909                }
910
911                public Long getResourcePid() {
912                        return myResourcePid;
913                }
914
915                public boolean isReverse() {
916                        return myReverse;
917                }
918        }
919
920
921}