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