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.rest.server.exceptions.ResourceNotFoundException;
025import ca.uhn.fhir.util.ParametersUtil;
026import org.apache.commons.lang3.Validate;
027import org.apache.commons.lang3.builder.EqualsBuilder;
028import org.apache.commons.lang3.builder.HashCodeBuilder;
029import org.hl7.fhir.instance.model.api.IBase;
030import org.hl7.fhir.instance.model.api.IBaseParameters;
031import org.hl7.fhir.instance.model.api.IBaseResource;
032import org.hl7.fhir.instance.model.api.IPrimitiveType;
033
034import javax.annotation.Nonnull;
035import javax.annotation.Nullable;
036import java.util.ArrayList;
037import java.util.Arrays;
038import java.util.Collections;
039import java.util.Iterator;
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         * Fetch the given ValueSet by URL
220         */
221        @Nullable
222        default IBaseResource fetchValueSet(String theValueSetUrl) {
223                return null;
224        }
225
226        /**
227         * Validates that the given code exists and if possible returns a display
228         * name. This method is called to check codes which are found in "example"
229         * binding fields (e.g. <code>Observation.code</code> in the default profile.
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 theOptions                  Provides options controlling the validation
234         * @param theCodeSystem               The code system, e.g. "<code>http://loinc.org</code>"
235         * @param theCode                     The code, e.g. "<code>1234-5</code>"
236         * @param theDisplay                  The display name, if it should also be validated
237         * @return Returns a validation result object
238         */
239        @Nullable
240        default CodeValidationResult validateCode(ValidationSupportContext theValidationSupportContext, ConceptValidationOptions theOptions, String theCodeSystem, String theCode, String theDisplay, String theValueSetUrl) {
241                return null;
242        }
243
244        /**
245         * Validates that the given code exists and if possible returns a display
246         * name. This method is called to check codes which are found in "example"
247         * binding fields (e.g. <code>Observation.code</code> in the default profile.
248         *
249         * @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
250         *                                    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.
251         * @param theCodeSystem               The code system, e.g. "<code>http://loinc.org</code>"
252         * @param theCode                     The code, e.g. "<code>1234-5</code>"
253         * @param theDisplay                  The display name, if it should also be validated
254         * @param theValueSet                 The ValueSet to validate against. Must not be null, and must be a ValueSet resource.
255         * @return Returns a validation result object, or <code>null</code> if this validation support module can not handle this kind of request
256         */
257        @Nullable
258        default CodeValidationResult validateCodeInValueSet(ValidationSupportContext theValidationSupportContext, ConceptValidationOptions theOptions, String theCodeSystem, String theCode, String theDisplay, @Nonnull IBaseResource theValueSet) {
259                return null;
260        }
261
262        /**
263         * Look up a code using the system and code value
264         *
265         * @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
266         *                                    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.
267         * @param theSystem                   The CodeSystem URL
268         * @param theCode                     The code
269         * @param theDisplayLanguage          to filter out the designation by the display language, to return all designation, the this value to null
270         */
271        @Nullable
272        default LookupCodeResult lookupCode(ValidationSupportContext theValidationSupportContext, String theSystem, String theCode, String theDisplayLanguage) {
273                return null;
274        }
275
276        /**
277         * Look up a code using the system and code value
278         *
279         * @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
280         *                                    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.
281         * @param theSystem                   The CodeSystem URL
282         * @param theCode                     The code
283         */
284        @Nullable
285        default LookupCodeResult lookupCode(ValidationSupportContext theValidationSupportContext, String theSystem, String theCode) {
286                return lookupCode(theValidationSupportContext, theSystem, theCode, null);
287        }
288        
289        /**
290         * Returns <code>true</code> if the given valueset can be validated by the given
291         * validation support module
292         *
293         * @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
294         *                                    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.
295         * @param theValueSetUrl              The ValueSet canonical URL
296         */
297        default boolean isValueSetSupported(ValidationSupportContext theValidationSupportContext, String theValueSetUrl) {
298                return false;
299        }
300
301        /**
302         * Generate a snapshot from the given differential profile.
303         *
304         * @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
305         *                                    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.
306         * @return Returns null if this module does not know how to handle this request
307         */
308        @Nullable
309        default IBaseResource generateSnapshot(ValidationSupportContext theValidationSupportContext, IBaseResource theInput, String theUrl, String theWebUrl, String theProfileName) {
310                return null;
311        }
312
313        /**
314         * Returns the FHIR Context associated with this module
315         */
316        FhirContext getFhirContext();
317
318        /**
319         * This method clears any temporary caches within the validation support. It is mainly intended for unit tests,
320         * but could be used in non-test scenarios as well.
321         */
322        default void invalidateCaches() {
323                // nothing
324        }
325
326        /**
327         * Attempt to translate the given concept from one code system to another
328         */
329        @Nullable
330        default TranslateConceptResults translateConcept(TranslateCodeRequest theRequest) {
331                return null;
332        }
333
334
335        enum IssueSeverity {
336                /**
337                 * The issue caused the action to fail, and no further checking could be performed.
338                 */
339                FATAL,
340                /**
341                 * The issue is sufficiently important to cause the action to fail.
342                 */
343                ERROR,
344                /**
345                 * 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.
346                 */
347                WARNING,
348                /**
349                 * The issue has no relation to the degree of success of the action.
350                 */
351                INFORMATION
352        }
353
354        class ConceptDesignation {
355
356                private String myLanguage;
357                private String myUseSystem;
358                private String myUseCode;
359                private String myUseDisplay;
360                private String myValue;
361
362                public String getLanguage() {
363                        return myLanguage;
364                }
365
366                public ConceptDesignation setLanguage(String theLanguage) {
367                        myLanguage = theLanguage;
368                        return this;
369                }
370
371                public String getUseSystem() {
372                        return myUseSystem;
373                }
374
375                public ConceptDesignation setUseSystem(String theUseSystem) {
376                        myUseSystem = theUseSystem;
377                        return this;
378                }
379
380                public String getUseCode() {
381                        return myUseCode;
382                }
383
384                public ConceptDesignation setUseCode(String theUseCode) {
385                        myUseCode = theUseCode;
386                        return this;
387                }
388
389                public String getUseDisplay() {
390                        return myUseDisplay;
391                }
392
393                public ConceptDesignation setUseDisplay(String theUseDisplay) {
394                        myUseDisplay = theUseDisplay;
395                        return this;
396                }
397
398                public String getValue() {
399                        return myValue;
400                }
401
402                public ConceptDesignation setValue(String theValue) {
403                        myValue = theValue;
404                        return this;
405                }
406        }
407
408        abstract class BaseConceptProperty {
409                private final String myPropertyName;
410
411                /**
412                 * Constructor
413                 */
414                protected BaseConceptProperty(String thePropertyName) {
415                        myPropertyName = thePropertyName;
416                }
417
418                public String getPropertyName() {
419                        return myPropertyName;
420                }
421        }
422
423        class StringConceptProperty extends BaseConceptProperty {
424                private final String myValue;
425
426                /**
427                 * Constructor
428                 *
429                 * @param theName The name
430                 */
431                public StringConceptProperty(String theName, String theValue) {
432                        super(theName);
433                        myValue = theValue;
434                }
435
436                public String getValue() {
437                        return myValue;
438                }
439        }
440
441        class CodingConceptProperty extends BaseConceptProperty {
442                private final String myCode;
443                private final String myCodeSystem;
444                private final String myDisplay;
445
446                /**
447                 * Constructor
448                 *
449                 * @param theName The name
450                 */
451                public CodingConceptProperty(String theName, String theCodeSystem, String theCode, String theDisplay) {
452                        super(theName);
453                        myCodeSystem = theCodeSystem;
454                        myCode = theCode;
455                        myDisplay = theDisplay;
456                }
457
458                public String getCode() {
459                        return myCode;
460                }
461
462                public String getCodeSystem() {
463                        return myCodeSystem;
464                }
465
466                public String getDisplay() {
467                        return myDisplay;
468                }
469        }
470
471        class CodeValidationResult {
472                private String myCode;
473                private String myMessage;
474                private IssueSeverity mySeverity;
475                private String myCodeSystemName;
476                private String myCodeSystemVersion;
477                private List<BaseConceptProperty> myProperties;
478                private String myDisplay;
479
480                public CodeValidationResult() {
481                        super();
482                }
483
484                public String getDisplay() {
485                        return myDisplay;
486                }
487
488                public CodeValidationResult setDisplay(String theDisplay) {
489                        myDisplay = theDisplay;
490                        return this;
491                }
492
493                public String getCode() {
494                        return myCode;
495                }
496
497                public CodeValidationResult setCode(String theCode) {
498                        myCode = theCode;
499                        return this;
500                }
501
502                String getCodeSystemName() {
503                        return myCodeSystemName;
504                }
505
506                public CodeValidationResult setCodeSystemName(String theCodeSystemName) {
507                        myCodeSystemName = theCodeSystemName;
508                        return this;
509                }
510
511                public String getCodeSystemVersion() {
512                        return myCodeSystemVersion;
513                }
514
515                public CodeValidationResult setCodeSystemVersion(String theCodeSystemVersion) {
516                        myCodeSystemVersion = theCodeSystemVersion;
517                        return this;
518                }
519
520                public String getMessage() {
521                        return myMessage;
522                }
523
524                public CodeValidationResult setMessage(String theMessage) {
525                        myMessage = theMessage;
526                        return this;
527                }
528
529                public List<BaseConceptProperty> getProperties() {
530                        return myProperties;
531                }
532
533                public void setProperties(List<BaseConceptProperty> theProperties) {
534                        myProperties = theProperties;
535                }
536
537                public IssueSeverity getSeverity() {
538                        return mySeverity;
539                }
540
541                public CodeValidationResult setSeverity(IssueSeverity theSeverity) {
542                        mySeverity = theSeverity;
543                        return this;
544                }
545
546                public boolean isOk() {
547                        return isNotBlank(myCode);
548                }
549
550                public LookupCodeResult asLookupCodeResult(String theSearchedForSystem, String theSearchedForCode) {
551                        LookupCodeResult retVal = new LookupCodeResult();
552                        retVal.setSearchedForSystem(theSearchedForSystem);
553                        retVal.setSearchedForCode(theSearchedForCode);
554                        if (isOk()) {
555                                retVal.setFound(true);
556                                retVal.setCodeDisplay(myDisplay);
557                                retVal.setCodeSystemDisplayName(getCodeSystemName());
558                                retVal.setCodeSystemVersion(getCodeSystemVersion());
559                        }
560                        return retVal;
561                }
562
563                /**
564                 * Convenience method that returns {@link #getSeverity()} as an IssueSeverity code string
565                 */
566                public String getSeverityCode() {
567                        String retVal = null;
568                        if (getSeverity() != null) {
569                                retVal = getSeverity().name().toLowerCase();
570                        }
571                        return retVal;
572                }
573
574                /**
575                 * Sets an issue severity as a string code. Value must be the name of
576                 * one of the enum values in {@link IssueSeverity}. Value is case-insensitive.
577                 */
578                public CodeValidationResult setSeverityCode(@Nonnull String theIssueSeverity) {
579                        setSeverity(IssueSeverity.valueOf(theIssueSeverity.toUpperCase()));
580                        return this;
581                }
582        }
583
584        class ValueSetExpansionOutcome {
585
586                private final IBaseResource myValueSet;
587                private final String myError;
588
589                public ValueSetExpansionOutcome(String theError) {
590                        myValueSet = null;
591                        myError = theError;
592                }
593
594                public ValueSetExpansionOutcome(IBaseResource theValueSet) {
595                        myValueSet = theValueSet;
596                        myError = null;
597                }
598
599                public String getError() {
600                        return myError;
601                }
602
603                public IBaseResource getValueSet() {
604                        return myValueSet;
605                }
606        }
607
608        class LookupCodeResult {
609
610                private String myCodeDisplay;
611                private boolean myCodeIsAbstract;
612                private String myCodeSystemDisplayName;
613                private String myCodeSystemVersion;
614                private boolean myFound;
615                private String mySearchedForCode;
616                private String mySearchedForSystem;
617                private List<IValidationSupport.BaseConceptProperty> myProperties;
618                private List<ConceptDesignation> myDesignations;
619
620                /**
621                 * Constructor
622                 */
623                public LookupCodeResult() {
624                        super();
625                }
626
627                public List<BaseConceptProperty> getProperties() {
628                        if (myProperties == null) {
629                                myProperties = new ArrayList<>();
630                        }
631                        return myProperties;
632                }
633
634                public void setProperties(List<IValidationSupport.BaseConceptProperty> theProperties) {
635                        myProperties = theProperties;
636                }
637
638                @Nonnull
639                public List<ConceptDesignation> getDesignations() {
640                        if (myDesignations == null) {
641                                myDesignations = new ArrayList<>();
642                        }
643                        return myDesignations;
644                }
645
646                public String getCodeDisplay() {
647                        return myCodeDisplay;
648                }
649
650                public void setCodeDisplay(String theCodeDisplay) {
651                        myCodeDisplay = theCodeDisplay;
652                }
653
654                public String getCodeSystemDisplayName() {
655                        return myCodeSystemDisplayName;
656                }
657
658                public void setCodeSystemDisplayName(String theCodeSystemDisplayName) {
659                        myCodeSystemDisplayName = theCodeSystemDisplayName;
660                }
661
662                public String getCodeSystemVersion() {
663                        return myCodeSystemVersion;
664                }
665
666                public void setCodeSystemVersion(String theCodeSystemVersion) {
667                        myCodeSystemVersion = theCodeSystemVersion;
668                }
669
670                public String getSearchedForCode() {
671                        return mySearchedForCode;
672                }
673
674                public LookupCodeResult setSearchedForCode(String theSearchedForCode) {
675                        mySearchedForCode = theSearchedForCode;
676                        return this;
677                }
678
679                public String getSearchedForSystem() {
680                        return mySearchedForSystem;
681                }
682
683                public LookupCodeResult setSearchedForSystem(String theSearchedForSystem) {
684                        mySearchedForSystem = theSearchedForSystem;
685                        return this;
686                }
687
688                public boolean isCodeIsAbstract() {
689                        return myCodeIsAbstract;
690                }
691
692                public void setCodeIsAbstract(boolean theCodeIsAbstract) {
693                        myCodeIsAbstract = theCodeIsAbstract;
694                }
695
696                public boolean isFound() {
697                        return myFound;
698                }
699
700                public LookupCodeResult setFound(boolean theFound) {
701                        myFound = theFound;
702                        return this;
703                }
704
705                public void throwNotFoundIfAppropriate() {
706                        if (isFound() == false) {
707                                throw new ResourceNotFoundException("Unable to find code[" + getSearchedForCode() + "] in system[" + getSearchedForSystem() + "]");
708                        }
709                }
710
711                public IBaseParameters toParameters(FhirContext theContext, List<? extends IPrimitiveType<String>> theProperties) {
712
713                        IBaseParameters retVal = ParametersUtil.newInstance(theContext);
714                        if (isNotBlank(getCodeSystemDisplayName())) {
715                                ParametersUtil.addParameterToParametersString(theContext, retVal, "name", getCodeSystemDisplayName());
716                        }
717                        if (isNotBlank(getCodeSystemVersion())) {
718                                ParametersUtil.addParameterToParametersString(theContext, retVal, "version", getCodeSystemVersion());
719                        }
720                        ParametersUtil.addParameterToParametersString(theContext, retVal, "display", getCodeDisplay());
721                        ParametersUtil.addParameterToParametersBoolean(theContext, retVal, "abstract", isCodeIsAbstract());
722
723                        if (myProperties != null) {
724
725                                Set<String> properties = Collections.emptySet();
726                                if (theProperties != null) {
727                                        properties = theProperties
728                                                .stream()
729                                                .map(IPrimitiveType::getValueAsString)
730                                                .collect(Collectors.toSet());
731                                }
732
733                                for (IValidationSupport.BaseConceptProperty next : myProperties) {
734
735                                        if (!properties.isEmpty()) {
736                                                if (!properties.contains(next.getPropertyName())) {
737                                                        continue;
738                                                }
739                                        }
740
741                                        IBase property = ParametersUtil.addParameterToParameters(theContext, retVal, "property");
742                                        ParametersUtil.addPartCode(theContext, property, "code", next.getPropertyName());
743
744                                        if (next instanceof IValidationSupport.StringConceptProperty) {
745                                                IValidationSupport.StringConceptProperty prop = (IValidationSupport.StringConceptProperty) next;
746                                                ParametersUtil.addPartString(theContext, property, "value", prop.getValue());
747                                        } else if (next instanceof IValidationSupport.CodingConceptProperty) {
748                                                IValidationSupport.CodingConceptProperty prop = (IValidationSupport.CodingConceptProperty) next;
749                                                ParametersUtil.addPartCoding(theContext, property, "value", prop.getCodeSystem(), prop.getCode(), prop.getDisplay());
750                                        } else {
751                                                throw new IllegalStateException("Don't know how to handle " + next.getClass());
752                                        }
753                                }
754                        }
755
756                        if (myDesignations != null) {
757                                for (ConceptDesignation next : myDesignations) {
758
759                                        IBase property = ParametersUtil.addParameterToParameters(theContext, retVal, "designation");
760                                        ParametersUtil.addPartCode(theContext, property, "language", next.getLanguage());
761                                        ParametersUtil.addPartCoding(theContext, property, "use", next.getUseSystem(), next.getUseCode(), next.getUseDisplay());
762                                        ParametersUtil.addPartString(theContext, property, "value", next.getValue());
763                                }
764                        }
765
766                        return retVal;
767                }
768
769                public static LookupCodeResult notFound(String theSearchedForSystem, String theSearchedForCode) {
770                        return new LookupCodeResult()
771                                .setFound(false)
772                                .setSearchedForSystem(theSearchedForSystem)
773                                .setSearchedForCode(theSearchedForCode);
774                }
775        }
776
777
778        class TranslateCodeRequest {
779                private final String mySourceSystemUrl;
780                private final String mySourceCode;
781                private final String myTargetSystemUrl;
782                private final int myHashCode;
783
784                public TranslateCodeRequest(String theSourceSystemUrl, String theSourceCode, String theTargetSystemUrl) {
785                        mySourceSystemUrl = theSourceSystemUrl;
786                        mySourceCode = theSourceCode;
787                        myTargetSystemUrl = theTargetSystemUrl;
788
789                        myHashCode = new HashCodeBuilder(17, 37)
790                                .append(mySourceSystemUrl)
791                                .append(mySourceCode)
792                                .append(myTargetSystemUrl)
793                                .toHashCode();
794                }
795
796                @Override
797                public boolean equals(Object theO) {
798                        if (this == theO) {
799                                return true;
800                        }
801
802                        if (theO == null || getClass() != theO.getClass()) {
803                                return false;
804                        }
805
806                        TranslateCodeRequest that = (TranslateCodeRequest) theO;
807
808                        return new EqualsBuilder()
809                                .append(mySourceSystemUrl, that.mySourceSystemUrl)
810                                .append(mySourceCode, that.mySourceCode)
811                                .append(myTargetSystemUrl, that.myTargetSystemUrl)
812                                .isEquals();
813                }
814
815                @Override
816                public int hashCode() {
817                        return myHashCode;
818                }
819
820                public String getSourceSystemUrl() {
821                        return mySourceSystemUrl;
822                }
823
824                public String getSourceCode() {
825                        return mySourceCode;
826                }
827
828                public String getTargetSystemUrl() {
829                        return myTargetSystemUrl;
830                }
831        }
832
833
834
835}