001package org.hl7.fhir.dstu3.terminologies;
002
003import static org.apache.commons.lang3.StringUtils.isNotBlank;
004
005import java.io.FileNotFoundException;
006import java.io.IOException;
007import java.util.ArrayList;
008import java.util.HashMap;
009import java.util.HashSet;
010import java.util.List;
011import java.util.Map;
012import java.util.Set;
013import java.util.UUID;
014
015/*
016  Copyright (c) 2011+, HL7, Inc.
017  All rights reserved.
018  
019  Redistribution and use in source and binary forms, with or without modification, 
020  are permitted provided that the following conditions are met:
021    
022   * Redistributions of source code must retain the above copyright notice, this 
023     list of conditions and the following disclaimer.
024   * Redistributions in binary form must reproduce the above copyright notice, 
025     this list of conditions and the following disclaimer in the documentation 
026     and/or other materials provided with the distribution.
027   * Neither the name of HL7 nor the names of its contributors may be used to 
028     endorse or promote products derived from this software without specific 
029     prior written permission.
030  
031  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 
032  ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 
033  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 
034  IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 
035  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 
036  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
037  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 
038  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
039  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
040  POSSIBILITY OF SUCH DAMAGE.
041  
042 */
043
044
045
046import org.apache.commons.lang3.NotImplementedException;
047import org.hl7.fhir.dstu3.context.IWorkerContext;
048import org.hl7.fhir.dstu3.model.CodeSystem;
049import org.hl7.fhir.dstu3.model.CodeSystem.CodeSystemContentMode;
050import org.hl7.fhir.dstu3.model.CodeSystem.ConceptDefinitionComponent;
051import org.hl7.fhir.dstu3.model.CodeSystem.ConceptDefinitionDesignationComponent;
052import org.hl7.fhir.dstu3.model.DateTimeType;
053import org.hl7.fhir.dstu3.model.ExpansionProfile;
054import org.hl7.fhir.dstu3.model.Factory;
055import org.hl7.fhir.dstu3.model.PrimitiveType;
056import org.hl7.fhir.dstu3.model.Type;
057import org.hl7.fhir.dstu3.model.UriType;
058import org.hl7.fhir.dstu3.model.ValueSet;
059import org.hl7.fhir.dstu3.model.ValueSet.ConceptReferenceComponent;
060import org.hl7.fhir.dstu3.model.ValueSet.ConceptReferenceDesignationComponent;
061import org.hl7.fhir.dstu3.model.ValueSet.ConceptSetComponent;
062import org.hl7.fhir.dstu3.model.ValueSet.ConceptSetFilterComponent;
063import org.hl7.fhir.dstu3.model.ValueSet.FilterOperator;
064import org.hl7.fhir.dstu3.model.ValueSet.ValueSetComposeComponent;
065import org.hl7.fhir.dstu3.model.ValueSet.ValueSetExpansionComponent;
066import org.hl7.fhir.dstu3.model.ValueSet.ValueSetExpansionContainsComponent;
067import org.hl7.fhir.dstu3.model.ValueSet.ValueSetExpansionParameterComponent;
068import org.hl7.fhir.dstu3.utils.ToolingExtensions;
069import org.hl7.fhir.exceptions.FHIRException;
070import org.hl7.fhir.exceptions.FHIRFormatError;
071import org.hl7.fhir.exceptions.NoTerminologyServiceException;
072import org.hl7.fhir.exceptions.TerminologyServiceException;
073import org.hl7.fhir.utilities.Utilities;
074
075/*
076 * Copyright (c) 2011+, HL7, Inc
077 * All rights reserved.
078 *
079 * Redistribution and use in source and binary forms, with or without modification,
080 * are permitted provided that the following conditions are met:
081 *
082 * Redistributions of source code must retain the above copyright notice, this
083 * list of conditions and the following disclaimer.
084 * Redistributions in binary form must reproduce the above copyright notice,
085 * this list of conditions and the following disclaimer in the documentation
086 * and/or other materials provided with the distribution.
087 * Neither the name of HL7 nor the names of its contributors may be used to
088 * endorse or promote products derived from this software without specific
089 * prior written permission.
090 *
091 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
092 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
093 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
094 * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
095 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
096 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
097 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
098 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
099 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
100 * POSSIBILITY OF SUCH DAMAGE.
101 *
102 */
103
104public class ValueSetExpanderSimple implements ValueSetExpander {
105
106  private List<ValueSetExpansionContainsComponent> codes = new ArrayList<ValueSet.ValueSetExpansionContainsComponent>();
107  private List<ValueSetExpansionContainsComponent> roots = new ArrayList<ValueSet.ValueSetExpansionContainsComponent>();
108  private Map<String, ValueSetExpansionContainsComponent> map = new HashMap<String, ValueSet.ValueSetExpansionContainsComponent>();
109  private IWorkerContext context;
110  private boolean canBeHeirarchy = true;
111  private Set<String> excludeKeys = new HashSet<String>();
112  private Set<String> excludeSystems = new HashSet<String>();
113  private ValueSetExpanderFactory factory;
114  private ValueSet focus;
115  private int maxExpansionSize = 500;
116
117  private int total;
118
119  public ValueSetExpanderSimple(IWorkerContext context, ValueSetExpanderFactory factory) {
120    super();
121    this.context = context;
122    this.factory = factory;
123  }
124
125  public void setMaxExpansionSize(int theMaxExpansionSize) {
126    maxExpansionSize = theMaxExpansionSize;
127  }
128
129  private ValueSetExpansionContainsComponent addCode(String system, String code, String display, ValueSetExpansionContainsComponent parent, List<ConceptDefinitionDesignationComponent> designations,
130                                                     ExpansionProfile profile, boolean isAbstract, boolean inactive, List<ValueSet> filters) {
131    if (filters != null && !filters.isEmpty() && !filterContainsCode(filters, system, code))
132      return null;
133    ValueSetExpansionContainsComponent n = new ValueSet.ValueSetExpansionContainsComponent();
134    n.setSystem(system);
135    n.setCode(code);
136    if (isAbstract)
137      n.setAbstract(true);
138    if (inactive)
139      n.setInactive(true);
140
141    if (profile.getIncludeDesignations() && designations != null) {
142      for (ConceptDefinitionDesignationComponent t : designations) {
143        ToolingExtensions.addLanguageTranslation(n, t.getLanguage(), t.getValue());
144      }
145    }
146    ConceptDefinitionDesignationComponent t = profile.hasLanguage() ? getMatchingLang(designations, profile.getLanguage()) : null;
147    if (t == null)
148      n.setDisplay(display);
149    else
150      n.setDisplay(t.getValue());
151
152    String s = key(n);
153    if (map.containsKey(s) || excludeKeys.contains(s)) {
154      canBeHeirarchy = false;
155    } else {
156      codes.add(n);
157      map.put(s, n);
158      total++;
159    }
160    if (canBeHeirarchy && parent != null) {
161      parent.getContains().add(n);
162    } else {
163      roots.add(n);
164    }
165    return n;
166  }
167
168  private boolean filterContainsCode(List<ValueSet> filters, String system, String code) {
169    for (ValueSet vse : filters)
170      if (expansionContainsCode(vse.getExpansion().getContains(), system, code))
171        return true;
172    return false;
173  }
174
175  private boolean expansionContainsCode(List<ValueSetExpansionContainsComponent> contains, String system, String code) {
176    for (ValueSetExpansionContainsComponent cc : contains) {
177      if (system.equals(cc.getSystem()) && code.equals(cc.getCode()))
178        return true;
179      if (expansionContainsCode(cc.getContains(), system, code))
180        return true;
181    }
182    return false;
183  }
184
185  private ConceptDefinitionDesignationComponent getMatchingLang(List<ConceptDefinitionDesignationComponent> list, String lang) {
186    for (ConceptDefinitionDesignationComponent t : list)
187      if (t.getLanguage().equals(lang))
188        return t;
189    for (ConceptDefinitionDesignationComponent t : list)
190      if (t.getLanguage().startsWith(lang))
191        return t;
192    return null;
193  }
194
195  private void addCodeAndDescendents(CodeSystem cs, String system, ConceptDefinitionComponent def, ValueSetExpansionContainsComponent parent, ExpansionProfile profile, List<ValueSet> filters)
196    throws FHIRException {
197    if (!CodeSystemUtilities.isDeprecated(cs, def)) {
198      ValueSetExpansionContainsComponent np = null;
199      boolean abs = CodeSystemUtilities.isNotSelectable(cs, def);
200      boolean inc = CodeSystemUtilities.isInactive(cs, def);
201      if (canBeHeirarchy || !abs)
202        np = addCode(system, def.getCode(), def.getDisplay(), parent, def.getDesignation(), profile, abs, inc, filters);
203      for (ConceptDefinitionComponent c : def.getConcept())
204        addCodeAndDescendents(cs, system, c, np, profile, filters);
205    } else
206      for (ConceptDefinitionComponent c : def.getConcept())
207        addCodeAndDescendents(cs, system, c, null, profile, filters);
208
209  }
210
211  private void addCodes(ValueSetExpansionComponent expand, List<ValueSetExpansionParameterComponent> params, ExpansionProfile profile, List<ValueSet> filters) throws ETooCostly {
212    if (expand.getContains().size() > maxExpansionSize)
213      throw new ETooCostly("Too many codes to display (>" + Integer.toString(expand.getContains().size()) + ")");
214    for (ValueSetExpansionParameterComponent p : expand.getParameter()) {
215      if (!existsInParams(params, p.getName(), p.getValue()))
216        params.add(p);
217    }
218
219    copyImportContains(expand.getContains(), null, profile, filters);
220  }
221
222  private void excludeCode(String theSystem, String theCode) {
223    ValueSetExpansionContainsComponent n = new ValueSet.ValueSetExpansionContainsComponent();
224    n.setSystem(theSystem);
225    n.setCode(theCode);
226    String s = key(n);
227    excludeKeys.add(s);
228  }
229
230  private void excludeCodes(ConceptSetComponent exc, List<ValueSetExpansionParameterComponent> params) throws TerminologyServiceException {
231    if (exc.hasSystem() && exc.getConcept().size() == 0 && exc.getFilter().size() == 0) {
232      excludeSystems.add(exc.getSystem());
233    }
234
235    if (exc.hasValueSet())
236      throw new Error("Processing Value set references in exclude is not yet done");
237    // importValueSet(imp.getValue(), params, profile);
238
239    CodeSystem cs = context.fetchCodeSystem(exc.getSystem());
240    if ((cs == null || cs.getContent() != CodeSystemContentMode.COMPLETE) && context.supportsSystem(exc.getSystem())) {
241      excludeCodes(context.expandVS(exc, false), params);
242      return;
243    }
244
245    for (ConceptReferenceComponent c : exc.getConcept()) {
246      excludeCode(exc.getSystem(), c.getCode());
247    }
248
249    if (exc.getFilter().size() > 0)
250      throw new NotImplementedException("not done yet");
251  }
252
253  private void excludeCodes(ValueSetExpansionComponent expand, List<ValueSetExpansionParameterComponent> params) {
254    for (ValueSetExpansionContainsComponent c : expand.getContains()) {
255      excludeCode(c.getSystem(), c.getCode());
256    }
257  }
258
259  private boolean existsInParams(List<ValueSetExpansionParameterComponent> params, String name, Type value) {
260    for (ValueSetExpansionParameterComponent p : params) {
261      if (p.getName().equals(name) && PrimitiveType.compareDeep(p.getValue(), value, false))
262        return true;
263    }
264    return false;
265  }
266
267  @Override
268  public ValueSetExpansionOutcome expand(ValueSet source, ExpansionProfile profile) {
269
270    if (profile == null)
271      profile = makeDefaultExpansion();
272    try {
273      focus = source.copy();
274      focus.setExpansion(new ValueSet.ValueSetExpansionComponent());
275      focus.getExpansion().setTimestampElement(DateTimeType.now());
276      focus.getExpansion().setIdentifier(Factory.createUUID());
277      if (!profile.getUrl().startsWith("urn:uuid:"))
278        focus.getExpansion().addParameter().setName("profile").setValue(new UriType(profile.getUrl()));
279
280      if (source.hasCompose())
281        handleCompose(source.getCompose(), focus.getExpansion().getParameter(), profile);
282
283      if (canBeHeirarchy) {
284        for (ValueSetExpansionContainsComponent c : roots) {
285          focus.getExpansion().getContains().add(c);
286        }
287      } else {
288        for (ValueSetExpansionContainsComponent c : codes) {
289          if (map.containsKey(key(c)) && !c.getAbstract()) { // we may have added abstract codes earlier while we still thought it might be heirarchical, but later we gave up, so now ignore them
290            focus.getExpansion().getContains().add(c);
291            c.getContains().clear(); // make sure any heirarchy is wiped
292          }
293        }
294      }
295
296      if (total > 0) {
297        focus.getExpansion().setTotal(total);
298      }
299
300      return new ValueSetExpansionOutcome(focus);
301    } catch (NoTerminologyServiceException e) {
302      // well, we couldn't expand, so we'll return an interface to a checker that can check membership of the set
303      // that might fail too, but it might not, later.
304      return new ValueSetExpansionOutcome(new ValueSetCheckerSimple(source, factory, context), e.getMessage(), TerminologyServiceErrorClass.NOSERVICE);
305    } catch (RuntimeException e) {
306      // TODO: we should put something more specific instead of just Exception below, since
307      // it swallows bugs.. what would be expected to be caught there?
308      throw e;
309    } catch (Exception e) {
310      // well, we couldn't expand, so we'll return an interface to a checker that can check membership of the set
311      // that might fail too, but it might not, later.
312      return new ValueSetExpansionOutcome(new ValueSetCheckerSimple(source, factory, context), e.getMessage(), TerminologyServiceErrorClass.UNKNOWN);
313    }
314  }
315
316  private ExpansionProfile makeDefaultExpansion() {
317    ExpansionProfile res = new ExpansionProfile();
318    res.setUrl("urn:uuid:" + UUID.randomUUID().toString().toLowerCase());
319    res.setExcludeNested(true);
320    res.setIncludeDesignations(false);
321    return res;
322  }
323
324  private void addToHeirarchy(List<ValueSetExpansionContainsComponent> target, List<ValueSetExpansionContainsComponent> source) {
325    for (ValueSetExpansionContainsComponent s : source) {
326      target.add(s);
327    }
328  }
329
330  private String getCodeDisplay(CodeSystem cs, String code) throws TerminologyServiceException {
331    ConceptDefinitionComponent def = getConceptForCode(cs.getConcept(), code);
332    if (def == null)
333      throw new TerminologyServiceException("Unable to find code '" + code + "' in code system " + cs.getUrl());
334    return def.getDisplay();
335  }
336
337  private ConceptDefinitionComponent getConceptForCode(List<ConceptDefinitionComponent> clist, String code) {
338    for (ConceptDefinitionComponent c : clist) {
339      if (code.equals(c.getCode()))
340        return c;
341      ConceptDefinitionComponent v = getConceptForCode(c.getConcept(), code);
342      if (v != null)
343        return v;
344    }
345    return null;
346  }
347
348  private void handleCompose(ValueSetComposeComponent compose, List<ValueSetExpansionParameterComponent> params, ExpansionProfile profile)
349    throws ETooCostly, FileNotFoundException, IOException, FHIRException {
350    // Exclude comes first because we build up a map of things to exclude
351    for (ConceptSetComponent inc : compose.getExclude())
352      excludeCodes(inc, params);
353    canBeHeirarchy = !profile.getExcludeNested() && excludeKeys.isEmpty() && excludeSystems.isEmpty();
354    boolean first = true;
355    for (ConceptSetComponent inc : compose.getInclude()) {
356      if (first == true)
357        first = false;
358      else
359        canBeHeirarchy = false;
360      includeCodes(inc, params, profile);
361    }
362
363  }
364
365  private ValueSet importValueSet(String value, List<ValueSetExpansionParameterComponent> params, ExpansionProfile profile)
366    throws ETooCostly, TerminologyServiceException, FileNotFoundException, IOException, FHIRFormatError {
367    if (value == null)
368      throw new TerminologyServiceException("unable to find value set with no identity");
369    ValueSet vs = context.fetchResource(ValueSet.class, value);
370    if (vs == null)
371      throw new TerminologyServiceException("Unable to find imported value set " + value);
372    ValueSetExpansionOutcome vso = factory.getExpander().expand(vs, profile);
373    if (vso.getError() != null)
374      throw new TerminologyServiceException("Unable to expand imported value set: " + vso.getError());
375    if (vso.getService() != null)
376      throw new TerminologyServiceException("Unable to expand imported value set " + value);
377    if (vs.hasVersion())
378      if (!existsInParams(params, "version", new UriType(vs.getUrl() + "|" + vs.getVersion())))
379        params.add(new ValueSetExpansionParameterComponent().setName("version").setValue(new UriType(vs.getUrl() + "|" + vs.getVersion())));
380    for (ValueSetExpansionParameterComponent p : vso.getValueset().getExpansion().getParameter()) {
381      if (!existsInParams(params, p.getName(), p.getValue()))
382        params.add(p);
383    }
384    canBeHeirarchy = false; // if we're importing a value set, we have to be combining, so we won't try for a heirarchy
385    return vso.getValueset();
386  }
387
388  private void copyImportContains(List<ValueSetExpansionContainsComponent> list, ValueSetExpansionContainsComponent parent, ExpansionProfile profile, List<ValueSet> filter) {
389    for (ValueSetExpansionContainsComponent c : list) {
390      ValueSetExpansionContainsComponent np = addCode(c.getSystem(), c.getCode(), c.getDisplay(), parent, null, profile, c.getAbstract(), c.getInactive(), filter);
391      copyImportContains(c.getContains(), np, profile, filter);
392    }
393  }
394
395  private void includeCodes(ConceptSetComponent inc, List<ValueSetExpansionParameterComponent> params, ExpansionProfile profile) throws ETooCostly, FileNotFoundException, IOException, FHIRException {
396    List<ValueSet> imports = new ArrayList<ValueSet>();
397    for (UriType imp : inc.getValueSet())
398      imports.add(importValueSet(imp.getValue(), params, profile));
399
400    if (!inc.hasSystem()) {
401      if (imports.isEmpty()) // though this is not supposed to be the case
402        return;
403      ValueSet base = imports.get(0);
404      imports.remove(0);
405      copyImportContains(base.getExpansion().getContains(), null, profile, imports);
406    } else {
407      CodeSystem cs = context.fetchCodeSystem(inc.getSystem());
408      if ((cs == null || cs.getContent() != CodeSystemContentMode.COMPLETE) && context.supportsSystem(inc.getSystem())) {
409        addCodes(context.expandVS(inc, canBeHeirarchy), params, profile, imports);
410        return;
411      }
412
413      if (cs == null) {
414        if (context.isNoTerminologyServer())
415          throw new NoTerminologyServiceException("unable to find code system " + inc.getSystem().toString());
416        else
417          throw new TerminologyServiceException("unable to find code system " + inc.getSystem().toString());
418      }
419      if (cs.getContent() != CodeSystemContentMode.COMPLETE)
420        throw new TerminologyServiceException("Code system " + inc.getSystem().toString() + " is incomplete");
421      if (cs.hasVersion())
422        if (!existsInParams(params, "version", new UriType(cs.getUrl() + "|" + cs.getVersion())))
423          params.add(new ValueSetExpansionParameterComponent().setName("version").setValue(new UriType(cs.getUrl() + "|" + cs.getVersion())));
424
425      if (inc.getConcept().size() == 0 && inc.getFilter().size() == 0) {
426        // special case - add all the code system
427        for (ConceptDefinitionComponent def : cs.getConcept()) {
428          addCodeAndDescendents(cs, inc.getSystem(), def, null, profile, imports);
429        }
430      }
431
432      if (!inc.getConcept().isEmpty()) {
433        canBeHeirarchy = false;
434        for (ConceptReferenceComponent c : inc.getConcept()) {
435          addCode(inc.getSystem(), c.getCode(), Utilities.noString(c.getDisplay()) ? getCodeDisplay(cs, c.getCode()) : c.getDisplay(), null, convertDesignations(c.getDesignation()), profile, false,
436            CodeSystemUtilities.isInactive(cs, c.getCode()), imports);
437        }
438      }
439      if (inc.getFilter().size() > 1) {
440        canBeHeirarchy = false; // which will bt the case if we get around to supporting this
441        throw new TerminologyServiceException("Multiple filters not handled yet"); // need to and them, and this isn't done yet. But this shouldn't arise in non loinc and snomed value sets
442      }
443      if (inc.getFilter().size() == 1) {
444        ConceptSetFilterComponent fc = inc.getFilter().get(0);
445        if ("concept".equals(fc.getProperty()) && fc.getOp() == FilterOperator.ISA) {
446          // special: all codes in the target code system under the value
447          ConceptDefinitionComponent def = getConceptForCode(cs.getConcept(), fc.getValue());
448          if (def == null)
449            throw new TerminologyServiceException("Code '" + fc.getValue() + "' not found in system '" + inc.getSystem() + "'");
450          addCodeAndDescendents(cs, inc.getSystem(), def, null, profile, imports);
451        } else if ("concept".equals(fc.getProperty()) && fc.getOp() == FilterOperator.DESCENDENTOF) {
452          // special: all codes in the target code system under the value
453          ConceptDefinitionComponent def = getConceptForCode(cs.getConcept(), fc.getValue());
454          if (def == null)
455            throw new TerminologyServiceException("Code '" + fc.getValue() + "' not found in system '" + inc.getSystem() + "'");
456          for (ConceptDefinitionComponent c : def.getConcept())
457            addCodeAndDescendents(cs, inc.getSystem(), c, null, profile, imports);
458        } else if ("display".equals(fc.getProperty()) && fc.getOp() == FilterOperator.EQUAL) {
459          // gg; note: wtf is this: if the filter is display=v, look up the code 'v', and see if it's diplsay is 'v'?
460          canBeHeirarchy = false;
461          ConceptDefinitionComponent def = getConceptForCode(cs.getConcept(), fc.getValue());
462          if (def != null) {
463            if (isNotBlank(def.getDisplay()) && isNotBlank(fc.getValue())) {
464              if (def.getDisplay().contains(fc.getValue())) {
465                addCode(inc.getSystem(), def.getCode(), def.getDisplay(), null, def.getDesignation(), profile, CodeSystemUtilities.isNotSelectable(cs, def), CodeSystemUtilities.isInactive(cs, def),
466                  imports);
467              }
468            }
469          }
470        } else
471          throw new NotImplementedException("Search by property[" + fc.getProperty() + "] and op[" + fc.getOp() + "] is not supported yet");
472      }
473    }
474  }
475
476  private List<ConceptDefinitionDesignationComponent> convertDesignations(List<ConceptReferenceDesignationComponent> list) {
477    List<ConceptDefinitionDesignationComponent> res = new ArrayList<CodeSystem.ConceptDefinitionDesignationComponent>();
478    for (ConceptReferenceDesignationComponent t : list) {
479      ConceptDefinitionDesignationComponent c = new ConceptDefinitionDesignationComponent();
480      c.setLanguage(t.getLanguage());
481      c.setUse(t.getUse());
482      c.setValue(t.getValue());
483    }
484    return res;
485  }
486
487  private String key(String uri, String code) {
488    return "{" + uri + "}" + code;
489  }
490
491  private String key(ValueSetExpansionContainsComponent c) {
492    return key(c.getSystem(), c.getCode());
493  }
494
495}