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