001package org.hl7.fhir.r5.terminologies;
002
003import java.util.Calendar;
004import java.util.Collections;
005import java.util.Comparator;
006import java.util.HashSet;
007import java.util.List;
008import java.util.Set;
009
010/*
011  Copyright (c) 2011+, HL7, Inc.
012  All rights reserved.
013  
014  Redistribution and use in source and binary forms, with or without modification, 
015  are permitted provided that the following conditions are met:
016    
017   * Redistributions of source code must retain the above copyright notice, this 
018     list of conditions and the following disclaimer.
019   * Redistributions in binary form must reproduce the above copyright notice, 
020     this list of conditions and the following disclaimer in the documentation 
021     and/or other materials provided with the distribution.
022   * Neither the name of HL7 nor the names of its contributors may be used to 
023     endorse or promote products derived from this software without specific 
024     prior written permission.
025  
026  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 
027  ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 
028  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 
029  IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 
030  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 
031  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
032  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 
033  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
034  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
035  POSSIBILITY OF SUCH DAMAGE.
036  
037 */
038
039
040import org.hl7.fhir.exceptions.FHIRException;
041import org.hl7.fhir.exceptions.FHIRFormatError;
042import org.hl7.fhir.r5.context.IWorkerContext;
043import org.hl7.fhir.r5.model.BooleanType;
044import org.hl7.fhir.r5.model.CanonicalType;
045import org.hl7.fhir.r5.model.CodeSystem;
046import org.hl7.fhir.r5.model.DateTimeType;
047import org.hl7.fhir.r5.model.StringType;
048import org.hl7.fhir.r5.model.IntegerType;
049import org.hl7.fhir.r5.model.Enumerations.FilterOperator;
050import org.hl7.fhir.r5.model.Enumerations.PublicationStatus;
051import org.hl7.fhir.r5.model.Parameters.ParametersParameterComponent;
052import org.hl7.fhir.r5.model.Identifier;
053import org.hl7.fhir.r5.model.Meta;
054import org.hl7.fhir.r5.model.Parameters;
055import org.hl7.fhir.r5.model.UriType;
056import org.hl7.fhir.r5.model.ValueSet;
057import org.hl7.fhir.r5.model.CodeSystem.ConceptDefinitionComponent;
058import org.hl7.fhir.r5.model.CodeSystem.ConceptPropertyComponent;
059import org.hl7.fhir.r5.model.CodeType;
060import org.hl7.fhir.r5.model.Coding;
061import org.hl7.fhir.r5.model.DataType;
062import org.hl7.fhir.r5.model.ValueSet.ConceptReferenceComponent;
063import org.hl7.fhir.r5.model.ValueSet.ConceptSetComponent;
064import org.hl7.fhir.r5.model.ValueSet.ValueSetComposeComponent;
065import org.hl7.fhir.r5.model.ValueSet.ValueSetExpansionComponent;
066import org.hl7.fhir.r5.model.ValueSet.ValueSetExpansionContainsComponent;
067import org.hl7.fhir.r5.model.ValueSet.ValueSetExpansionPropertyComponent;
068import org.hl7.fhir.r5.terminologies.CodeSystemUtilities.ConceptDefinitionComponentSorter;
069import org.hl7.fhir.r5.terminologies.CodeSystemUtilities.ConceptStatus;
070import org.hl7.fhir.r5.terminologies.utilities.TerminologyCache.SourcedValueSet;
071import org.hl7.fhir.r5.utils.CanonicalResourceUtilities;
072import org.hl7.fhir.r5.utils.ToolingExtensions;
073import org.hl7.fhir.r5.utils.UserDataNames;
074import org.hl7.fhir.utilities.StandardsStatus;
075import org.hl7.fhir.utilities.Utilities;
076import org.hl7.fhir.utilities.VersionUtilities;
077
078public class ValueSetUtilities extends TerminologyUtilities {
079
080
081  public static class ValueSetSorter implements Comparator<ValueSet> {
082
083    @Override
084    public int compare(ValueSet o1, ValueSet o2) {
085      String url1 = o1.getUrl();
086      String url2 = o2.getUrl();
087      int c = compareString(url1, url2);
088      if (c == 0) {
089        String ver1 = o1.getVersion();
090        String ver2 = o2.getVersion();
091        c = VersionUtilities.compareVersions(ver1, ver2);
092        if (c == 0) {
093          String d1 = o1.getDateElement().asStringValue();
094          String d2 = o2.getDateElement().asStringValue();
095          c = compareString(url1, url2);
096        }
097      }
098      return c;
099    }
100
101    private int compareString(String s1, String s2) {
102      if (s1 == null) {
103        return s2 == null ? 0 : 1;
104      } else {
105        return s1.compareTo(s2);
106      }
107    }
108
109  }
110
111
112  public static boolean isServerSide(String url) {
113    return Utilities.existsInList(url, "http://hl7.org/fhir/sid/cvx");
114  }
115  
116  public static ValueSet makeShareable(ValueSet vs) {
117    if (!vs.hasExperimental()) {
118      vs.setExperimental(false);
119    }
120    if (!vs.hasMeta())
121      vs.setMeta(new Meta());
122    for (UriType t : vs.getMeta().getProfile()) 
123      if (t.getValue().equals("http://hl7.org/fhir/StructureDefinition/shareablevalueset"))
124        return vs;
125    vs.getMeta().getProfile().add(new CanonicalType("http://hl7.org/fhir/StructureDefinition/shareablevalueset"));
126    return vs;
127  }
128
129  public static boolean makeVSShareable(ValueSet vs) {
130    if (!vs.hasMeta())
131      vs.setMeta(new Meta());
132    for (UriType t : vs.getMeta().getProfile()) 
133      if (t.getValue().equals("http://hl7.org/fhir/StructureDefinition/shareablevalueset"))
134        return false;
135    vs.getMeta().getProfile().add(new CanonicalType("http://hl7.org/fhir/StructureDefinition/shareablevalueset"));
136    return true;
137  }
138
139  public static void checkShareable(ValueSet vs) {
140    if (!vs.hasMeta())
141      throw new Error("ValueSet "+vs.getUrl()+" is not shareable");
142    for (UriType t : vs.getMeta().getProfile()) {
143      if (t.getValue().equals("http://hl7.org/fhir/StructureDefinition/shareablevalueset"))
144        return;
145    }
146    throw new Error("ValueSet "+vs.getUrl()+" is not shareable");    
147  }
148
149  public static boolean hasOID(ValueSet vs) {
150    return getOID(vs) != null;
151  }
152
153  public static String getOID(ValueSet vs) {
154    for (Identifier id : vs.getIdentifier()) {
155      if ("urn:ietf:rfc:3986".equals(id.getSystem()) && id.hasValue() && id.getValue().startsWith("urn:oid:"))
156        return id.getValue().substring(8);
157    }
158    return null;
159  }
160
161  public static void setOID(ValueSet vs, String oid) {
162    if (!oid.startsWith("urn:oid:"))
163      oid = "urn:oid:" + oid;
164    for (Identifier id : vs.getIdentifier()) {
165      if ("urn:ietf:rfc:3986".equals(id.getSystem()) && id.hasValue() && id.getValue().startsWith("urn:oid:")) {
166        id.setValue(oid);
167        return;
168      }
169    }
170    vs.addIdentifier().setSystem("urn:ietf:rfc:3986").setValue(oid);
171  }
172
173  public static void markStatus(ValueSet vs, String wg, StandardsStatus status, String pckage, String fmm, IWorkerContext context, String normativeVersion) throws FHIRException {
174    if (vs.hasUserData(UserDataNames.render_external_link))
175      return;
176    
177    if (wg != null) {
178      if (!ToolingExtensions.hasExtension(vs, ToolingExtensions.EXT_WORKGROUP) || 
179          (!Utilities.existsInList(ToolingExtensions.readStringExtension(vs, ToolingExtensions.EXT_WORKGROUP), "fhir", "vocab") && Utilities.existsInList(wg, "fhir", "vocab"))) {
180        CanonicalResourceUtilities.setHl7WG(vs, wg);
181      }
182    }
183    if (status != null) {
184      StandardsStatus ss = ToolingExtensions.getStandardsStatus(vs);
185      if (ss == null || ss.isLowerThan(status)) 
186        ToolingExtensions.setStandardsStatus(vs, status, normativeVersion);
187      if (pckage != null) {
188        if (!vs.hasUserData(UserDataNames.kindling_ballot_package))        
189          vs.setUserData(UserDataNames.kindling_ballot_package, pckage);
190        else if (!pckage.equals(vs.getUserString(UserDataNames.kindling_ballot_package)))
191          if (!"infrastructure".equals(vs.getUserString(UserDataNames.kindling_ballot_package)))
192          System.out.println("Value Set "+vs.getUrl()+": ownership clash "+pckage+" vs "+vs.getUserString(UserDataNames.kindling_ballot_package));
193      }
194      if (status == StandardsStatus.NORMATIVE) {
195        vs.setStatus(PublicationStatus.ACTIVE);
196      }
197    }
198    if (fmm != null) {
199      String sfmm = ToolingExtensions.readStringExtension(vs, ToolingExtensions.EXT_FMM_LEVEL);
200      if (Utilities.noString(sfmm) || Integer.parseInt(sfmm) < Integer.parseInt(fmm))  {
201        ToolingExtensions.setIntegerExtension(vs, ToolingExtensions.EXT_FMM_LEVEL, Integer.parseInt(fmm));
202      }
203    }
204    if (vs.hasUserData(UserDataNames.TX_ASSOCIATED_CODESYSTEM))
205      CodeSystemUtilities.markStatus((CodeSystem) vs.getUserData(UserDataNames.TX_ASSOCIATED_CODESYSTEM), wg, status, pckage, fmm, normativeVersion);
206    else if (status == StandardsStatus.NORMATIVE && context != null) {
207      for (ConceptSetComponent csc : vs.getCompose().getInclude()) {
208        if (csc.hasSystem()) {
209          CodeSystem cs = context.fetchCodeSystem(csc.getSystem());
210          if (cs != null) {
211            CodeSystemUtilities.markStatus(cs, wg, status, pckage, fmm, normativeVersion);
212          }
213        }
214      }
215    }
216  }
217
218  private static int ssval(String status) {
219    if ("Draft".equals("status")) 
220      return 1;
221    if ("Informative".equals("status")) 
222      return 2;
223    if ("External".equals("status")) 
224      return 3;
225    if ("Trial Use".equals("status")) 
226      return 3;
227    if ("Normative".equals("status")) 
228      return 4;
229    return -1;
230  }
231
232  public static ValueSet generateImplicitValueSet(String uri) {
233    if (uri.startsWith("http://snomed.info/sct"))
234      return generateImplicitSnomedValueSet(uri);
235    if (uri.startsWith("http://loinc.org/vs"))
236      return generateImplicitLoincValueSet(uri);
237    if (uri.equals("http://hl7.org/fhir/ValueSet/mimetypes")) {
238      return generateImplicitMimetypesValueSet(uri);
239    }
240    return null;
241  }
242
243  private static ValueSet generateImplicitMimetypesValueSet(String theUri) {
244    ValueSet valueSet = new ValueSet();
245    valueSet.setStatus(PublicationStatus.ACTIVE);
246    valueSet.setUrl(theUri);
247    valueSet.setDescription("This value set includes all possible codes from BCP-13 (http://tools.ietf.org/html/bcp13)");
248    valueSet.getCompose()
249      .addInclude().setSystem("urn:ietf:bcp:13");
250    return valueSet;
251  }
252
253  private static ValueSet generateImplicitLoincValueSet(String uri) {
254    if ("http://loinc.org/vs".equals(uri))
255      return makeLoincValueSet();
256    if (uri.startsWith("http://loinc.org/vs/LL"))
257      return makeAnswerList(makeLoincValueSet(), uri);
258    return null;
259  }
260
261  private static ValueSet makeAnswerList(ValueSet vs, String uri) {
262    vs.setUrl(uri);
263    String c = uri.substring(20);
264    vs.setName("LOINCAnswers"+c);
265    vs.setTitle("LOINC Answer Codes for "+c);
266    vs.getCompose().getIncludeFirstRep().addFilter().setProperty("LIST").setOp(FilterOperator.EQUAL).setValue(c);
267    return vs;
268  }
269
270  private static ValueSet makeLoincValueSet() {
271    ValueSet vs = new ValueSet();
272    vs.setUrl("http://loinc.org/vs");
273    vs.setName("LOINCCodes");
274    vs.setTitle("All LOINC codes");
275    vs.setCopyright("This content LOINC® is copyright © 1995 Regenstrief Institute, Inc. and the LOINC Committee, and available at no cost under the license at http://loinc.org/terms-of-use");
276    vs.setStatus(PublicationStatus.ACTIVE);
277    vs.getCompose().addInclude().setSystem("http://loinc.org");
278    return vs;
279  }
280
281  private static ValueSet generateImplicitSnomedValueSet(String uri) {
282    if ("http://snomed.info/sct?fhir_vs".equals(uri))
283      return makeImplicitSnomedValueSet(uri);
284    return null;
285  }
286
287  private static ValueSet makeImplicitSnomedValueSet(String uri) {
288    ValueSet vs = new ValueSet();
289    vs.setUrl(uri);
290    vs.setName("SCTValueSet");
291    vs.setTitle("SCT ValueSet");
292    vs.setDescription("All SNOMED CT Concepts");
293    vs.setCopyright("This value set includes content from SNOMED CT, which is copyright © 2002+ International Health Terminology Standards Development Organisation (SNOMED International), and distributed by agreement between SNOMED International and HL7. Implementer use of SNOMED CT is not covered by this agreement");
294    vs.setStatus(PublicationStatus.ACTIVE);
295    vs.getCompose().addInclude().setSystem("http://snomed.info/sct");
296    return vs;
297  }
298
299  public static void setDeprecated(List<ValueSetExpansionPropertyComponent> vsProp,  ValueSetExpansionContainsComponent n) {
300    n.addProperty().setCode("status").setValue(new CodeType("deprecated"));
301    for (ValueSetExpansionPropertyComponent o : vsProp) {
302      if ("status".equals(o.getCode())) {
303        return;
304      }
305    }
306    vsProp.add(new ValueSetExpansionPropertyComponent().setCode("status").setUri("http://hl7.org/fhir/concept-properties#status"));
307  }
308
309
310  public static class ConceptReferenceComponentSorter implements Comparator<ConceptReferenceComponent> {
311
312    @Override
313    public int compare(ConceptReferenceComponent o1, ConceptReferenceComponent o2) {
314      return o1.getCode().compareToIgnoreCase(o2.getCode());
315    }
316  }
317
318
319  public static void sortInclude(ConceptSetComponent inc) {
320    Collections.sort(inc.getConcept(), new ConceptReferenceComponentSorter());
321  }
322
323  public static String getAllCodesSystem(ValueSet vs) {
324    if (vs.hasCompose()) {
325      ValueSetComposeComponent c = vs.getCompose();
326      if (c.getExclude().isEmpty() && c.getInclude().size() == 1) {
327        ConceptSetComponent i = c.getIncludeFirstRep();
328        if (i.hasSystem() && !i.hasValueSet() && !i.hasConcept() && !i.hasFilter()) {
329          return i.getSystem();
330        }
331      }
332    }
333    return null;
334  }
335
336  public static boolean isDeprecated(ValueSet vs, ValueSetExpansionContainsComponent c) {
337    try {
338      for (org.hl7.fhir.r5.model.ValueSet.ConceptPropertyComponent p : c.getProperty()) {
339        if ("status".equals(p.getCode()) && p.hasValue() && p.hasValueCodeType() && "deprecated".equals(p.getValueCodeType().getCode())) {
340          return true;
341        }      
342        // this, though status should also be set
343        if ("deprecationDate".equals(p.getCode()) && p.hasValue() && p.getValue() instanceof DateTimeType) 
344          return ((DateTimeType) p.getValue()).before(new DateTimeType(Calendar.getInstance()));
345        // legacy  
346        if ("deprecated".equals(p.getCode()) && p.hasValue() && p.getValue() instanceof BooleanType) 
347          return ((BooleanType) p.getValue()).getValue();
348      }
349      StandardsStatus ss = ToolingExtensions.getStandardsStatus(c);
350      if (ss == StandardsStatus.DEPRECATED) {
351        return true;
352      }
353      return false;
354    } catch (FHIRException e) {
355      return false;
356    }  
357  }
358
359  public static boolean hasCodeInExpansion(ValueSet vs, Coding code) {
360    return hasCodeInExpansion(vs.getExpansion().getContains(), code);
361  }
362
363  private static boolean hasCodeInExpansion(List<ValueSetExpansionContainsComponent> list, Coding code) {
364    for (ValueSetExpansionContainsComponent c : list) {
365      if (c.getSystem().equals(code.getSystem()) && c.getCode().equals(code.getCode())) {
366        return true;
367      }
368      if (hasCodeInExpansion(c.getContains(), code)) {
369        return true;
370      }
371    }
372    return false;
373  }
374
375  public static org.hl7.fhir.r5.model.ValueSet.ConceptPropertyComponent addProperty(ValueSet vs, ValueSetExpansionContainsComponent ctxt, String url, String code, String value) {
376    if (value != null) {
377      return addProperty(vs, ctxt, url, code, new StringType(value));
378    } else {
379      return null;
380    }
381  }
382
383  public static org.hl7.fhir.r5.model.ValueSet.ConceptPropertyComponent addProperty(ValueSet vs, ValueSetExpansionContainsComponent ctxt, String url, String code, Integer value) {
384    if (value != null) {
385      return addProperty(vs, ctxt, url, code, new IntegerType(value));
386    } else {
387      return null;
388    }
389  }
390
391  public static org.hl7.fhir.r5.model.ValueSet.ConceptPropertyComponent addProperty(ValueSet vs, ValueSetExpansionContainsComponent ctxt, String url, String code, DataType value) {
392    code = defineProperty(vs, url, code);
393    org.hl7.fhir.r5.model.ValueSet.ConceptPropertyComponent p = getProperty(ctxt.getProperty(),  code);
394    if (p != null) {
395      p.setValue(value);
396    } else {
397      p = ctxt.addProperty().setCode(code).setValue(value);
398    }
399    return p;
400  }
401
402  private static org.hl7.fhir.r5.model.ValueSet.ConceptPropertyComponent getProperty(List<org.hl7.fhir.r5.model.ValueSet.ConceptPropertyComponent> list, String code) {
403    for (org.hl7.fhir.r5.model.ValueSet.ConceptPropertyComponent t : list) {
404      if (code.equals(t.getCode())) {
405        return t;
406      }
407    }
408    return null;
409  }
410
411  private static String defineProperty(ValueSet vs, String url, String code) {
412    for (ValueSetExpansionPropertyComponent p : vs.getExpansion().getProperty()) {
413      if (p.hasUri() && p.getUri().equals(url)) {
414        return p.getCode();
415      }
416    }
417    for (ValueSetExpansionPropertyComponent p : vs.getExpansion().getProperty()) {
418      if (p.hasCode() && p.getCode().equals(code)) {
419        p.setUri(url);
420        return code;
421      }
422    }
423    ValueSetExpansionPropertyComponent p = vs.getExpansion().addProperty();
424    p.setUri(url);
425    p.setCode(code);
426    return code;  
427  }
428
429  public static int countExpansion(ValueSet valueset) {
430    int i = valueset.getExpansion().getContains().size();
431    for (ValueSetExpansionContainsComponent t : valueset.getExpansion().getContains()) {
432      i = i + countExpansion(t);
433    }
434    return i;
435  }
436
437  public static int countExpansion(List<ValueSetExpansionContainsComponent> list) {
438    int i = list.size();
439    for (ValueSetExpansionContainsComponent t : list) {
440      i = i + countExpansion(t);
441    }
442    return i;
443  }
444
445  private static int countExpansion(ValueSetExpansionContainsComponent c) {
446    int i = c.getContains().size();
447    for (ValueSetExpansionContainsComponent t : c.getContains()) {
448      i = i + countExpansion(t);
449    }
450    return i;
451  }
452
453  public static Set<String> listSystems(IWorkerContext ctxt, ValueSet vs) {
454    Set<String> systems = new HashSet<>();
455    for (ConceptSetComponent inc : vs.getCompose().getInclude()) {
456      for (CanonicalType ct : inc.getValueSet()) {
457        ValueSet vsr = ctxt.findTxResource(ValueSet.class, ct.asStringValue(), vs);
458        if (vsr != null) {
459          systems.addAll(listSystems(ctxt, vsr));
460        }
461      }
462      if (inc.hasSystem()) {
463        systems.add(inc.getSystem());
464      }
465    }
466    return systems;
467  }
468  
469
470  public static boolean isIncompleteExpansion(ValueSet valueSet) {
471    if (valueSet.hasExpansion()) {
472      ValueSetExpansionComponent exp = valueSet.getExpansion();
473      if (exp.hasTotal()) {
474        if (exp.getTotal() != countExpansion(exp.getContains())) {
475          return true;
476        }
477      }
478    }
479    return false;
480  }
481
482
483  public static Set<String> codes(ValueSet vs, CodeSystem cs) {
484    Set<String> res = new HashSet<>();
485    for (ConceptSetComponent inc : vs.getCompose().getInclude()) {
486      if (inc.getSystem().equals(cs.getUrl())) {
487        addCodes(res, inc, cs.getConcept());
488      }
489    }
490    return res;
491  }
492
493  private static void addCodes(Set<String> res, ConceptSetComponent inc, List<ConceptDefinitionComponent> list) {
494    for (ConceptDefinitionComponent cd : list) {
495      if (cd.hasCode() && (!inc.hasConcept() || inc.hasConcept(cd.getCode()))) {
496        res.add(cd.getCode());
497      }
498      if (cd.hasConcept()) {
499        addCodes(res, inc, cd.getConcept());
500      }
501    }    
502  }
503  
504  public static String versionFromExpansionParams(Parameters expParameters, String system, String defaultVersion) {
505    if (expParameters != null) {
506      for (ParametersParameterComponent p : expParameters.getParameter()) {
507        if ("system-version".equals(p.getName()) || "force-system-version".equals(p.getName())) {
508          String v = p.getValue().primitiveValue();
509          if (v.startsWith(system+"|")) {
510            String ver = v.substring(v.indexOf("|")+1);
511            if (defaultVersion == null || ver.startsWith(defaultVersion) || "force-system-version".equals(p.getName())) {
512              return ver;
513            }
514          }
515        }
516      }
517    }
518    return defaultVersion;
519  }
520
521  public static boolean isImplicitLoincValueSet(String url) {
522    return url.startsWith("http://loinc.org/vs");    
523  }
524
525  public static boolean isImplicitSCTValueSet(String url) {
526    return url.startsWith("http://snomed.info/sct") && url.contains("?fhir_vs");
527  }
528
529  public static ValueSet makeImplicitValueSet(String url, String version) {
530    if (url.startsWith("http://snomed.info/sct")) {
531      return makeImplicitSCTVS(url, version);
532    } else if (url.startsWith("http://loinc.org/vs")) {
533      return makeImplicitLoincVS(url, version);
534    } else {
535      throw new FHIRException("Unknown implicit value set URL "+url);
536    }
537  }
538
539  private static ValueSet makeImplicitSCTVS(String url, String version) {
540    String query = url.substring(url.indexOf("?")+1);
541    if ("fhir_vs".equals(query)) {
542      ValueSet vs = new ValueSet();
543      vs.setUrl(url);
544      vs.setVersion(version);
545      vs.getCompose().addInclude().setSystem("http://snomed.info/sct");
546      return vs;
547    } else if (query.startsWith("fhir_vs=isa/")) {
548      ValueSet vs = new ValueSet();
549      vs.setUrl(url);
550      vs.setVersion(version);
551      vs.getCompose().addInclude().setSystem("http://snomed.info/sct").addFilter().setProperty("concept").setOp(FilterOperator.ISA).setValue(query.substring(12));
552      return vs;
553    } else if (query.equals("fhir_vs=refset")) {
554      ValueSet vs = new ValueSet();
555      vs.setUrl(url);
556      vs.setVersion(version);
557      vs.getCompose().addInclude().setSystem("http://snomed.info/sct").addFilter().setProperty("concept").setOp(FilterOperator.ISA).setValue("refset-base");
558      return vs;      
559    } else if (query.startsWith("fhir_vs=refset/")) {
560      ValueSet vs = new ValueSet();
561      vs.setUrl(url);
562      vs.setVersion(version);
563      vs.getCompose().addInclude().setSystem("http://snomed.info/sct").addFilter().setProperty("concept").setOp(FilterOperator.IN).setValue(query.substring(15));
564      return vs;      
565    } else {
566      throw new FHIRException("Unknown implicit SNOMED CT value set URL "+url);
567    }
568  }
569
570  private static ValueSet makeImplicitLoincVS(String url, String version) {
571    if (url.equals("http://loinc.org/vs")) {
572      ValueSet vs = new ValueSet();
573      vs.setUrl(url);
574      vs.setVersion(version);
575      vs.getCompose().addInclude().setSystem("http://loinc.org");
576      return vs;
577    } else if (url.startsWith("http://loinc.org/vs/LP")) {
578      ValueSet vs = new ValueSet();
579      vs.setUrl(url);
580      vs.setVersion(version);
581      vs.getCompose().addInclude().setSystem("http://loinc.org").addFilter().setProperty("ancestor").setOp(FilterOperator.EQUAL).setValue(url.substring(21));
582      return vs;      
583    } else if (url.startsWith("http://loinc.org/vs/LL")) {
584      ValueSet vs = new ValueSet();
585      vs.setUrl(url);
586      vs.setVersion(version);
587      // this isn't the actual definition, but it won't matter to us internally
588      vs.getCompose().addInclude().setSystem("http://loinc.org").addFilter().setProperty("answer-list").setOp(FilterOperator.EQUAL).setValue(url.substring(21));      
589      return vs;
590    } else {
591      throw new FHIRException("Unknown implicit LOINC value set URL "+url);
592    }
593  }
594
595  
596}