@Override
  public ValueSet expandByIdentifier(String theUri, String theFilter) {
    if (isBlank(theUri)) {
      throw new InvalidRequestException("URI must not be blank or missing");
    }

    ValueSet source = new ValueSet();

    source.getCompose().addImport(theUri);

    if (isNotBlank(theFilter)) {
      ConceptSetComponent include = source.getCompose().addInclude();
      ConceptSetFilterComponent filter = include.addFilter();
      filter.setProperty("display");
      filter.setOp(FilterOperator.EQUAL);
      filter.setValue(theFilter);
    }

    ValueSet retVal = doExpand(source);
    return retVal;

    // if (defaultValueSet != null) {
    // source = getContext().newJsonParser().parseResource(ValueSet.class,
    // getContext().newJsonParser().encodeResourceToString(defaultValueSet));
    // } else {
    // IBundleProvider ids = search(ValueSet.SP_URL, new UriParam(theUri));
    // if (ids.size() == 0) {
    // throw new InvalidRequestException("Unknown ValueSet URI: " + theUri);
    // }
    // source = (ValueSet) ids.getResources(0, 1).get(0);
    // }
    //
    // return expand(defaultValueSet, theFilter);

  }
 private String getVSSummary(ValueSet vs) {
   CommaSeparatedStringBuilder b = new CommaSeparatedStringBuilder();
   for (ConceptSetComponent cc : vs.getCompose().getInclude())
     b.append("Include " + getIncSummary(cc));
   for (ConceptSetComponent cc : vs.getCompose().getExclude())
     b.append("Exclude " + getIncSummary(cc));
   return b.toString();
 }
 private boolean notcomplete(ValueSet vs) {
   if (!vs.hasExpansion()) return true;
   if (!vs.getExpansion()
       .getExtensionsByUrl("http://hl7.org/fhir/StructureDefinition/valueset-unclosed")
       .isEmpty()) return true;
   if (!vs.getExpansion()
       .getExtensionsByUrl("http://hl7.org/fhir/StructureDefinition/valueset-toocostly")
       .isEmpty()) return true;
   return false;
 }
  /**
   * Only produce the v3 vocabulary for appending to rim.ttl
   *
   * @throws Exception
   */
  public void executeV3(Map<String, ValueSet> valuesets, Map<String, CodeSystem> codeSystems)
      throws Exception {
    for (ValueSet vs : this.valuesets.values()) {
      valuesets.put("vs:" + tail(vs.getUrl()), vs);
    }

    //    for (String n : sorted(valuesets.keySet()))
    //      gen(n, valuesets.get(n));
    commit(false);
  }
 private ValidationResult verifyCodeInExpansion(ValueSet vs, String code) throws FHIRException {
   if (vs.getExpansion()
       .hasExtension("http://hl7.org/fhir/StructureDefinition/valueset-toocostly")) {
     throw new FHIRException("Unable to validate core - value set is too costly to expand");
   } else {
     ValueSetExpansionContainsComponent cc = findCode(vs.getExpansion().getContains(), code);
     if (cc == null)
       return new ValidationResult(
           IssueSeverity.ERROR, "Unknown Code " + code + " in " + vs.getUrl());
     return null;
   }
 }
 @Override
 public ValueSetExpansionComponent expandVS(ConceptSetComponent inc, boolean heirachical)
     throws TerminologyServiceException {
   ValueSet vs = new ValueSet();
   vs.setCompose(new ValueSetComposeComponent());
   vs.getCompose().getInclude().add(inc);
   ValueSetExpansionOutcome vse = expandVS(vs, true, heirachical);
   ValueSet valueset = vse.getValueset();
   if (valueset == null)
     throw new TerminologyServiceException("Error Expanding ValueSet: " + vse.getError());
   return valueset.getExpansion();
 }
 @Override
 public ValidationResult validateCode(
     String system, String code, String display, ConceptSetComponent vsi) {
   try {
     ValueSet vs = new ValueSet();
     vs.setUrl(Utilities.makeUuidUrn());
     vs.getCompose().addInclude(vsi);
     return verifyCodeExternal(
         vs, new Coding().setSystem(system).setCode(code).setDisplay(display), true);
   } catch (Exception e) {
     return new ValidationResult(
         IssueSeverity.FATAL,
         "Error validating code \"" + code + "\" in system \"" + system + "\": " + e.getMessage());
   }
 }
  private ValueSet doExpand(ValueSet theSource) {

    validateIncludes("include", theSource.getCompose().getInclude());
    validateIncludes("exclude", theSource.getCompose().getExclude());

    HapiWorkerContext workerContext = new HapiWorkerContext(getContext(), myValidationSupport);

    ValueSetExpansionOutcome outcome = workerContext.expand(theSource);
    ValueSetExpansionComponent expansion = outcome.getValueset().getExpansion();

    ValueSet retVal = new ValueSet();
    retVal.getMeta().setLastUpdated(new Date());
    retVal.setExpansion(expansion);
    return retVal;
  }
 @Override
 public ValueSetExpansionOutcome expandVS(ValueSet vs, boolean cacheOk, boolean heirarchical) {
   try {
     if (vs.hasExpansion()) {
       return new ValueSetExpansionOutcome(vs.copy());
     }
     String cacheFn = null;
     if (cache != null) {
       cacheFn = Utilities.path(cache, determineCacheId(vs, heirarchical) + ".json");
       if (new File(cacheFn).exists()) return loadFromCache(vs.copy(), cacheFn);
     }
     if (cacheOk && vs.hasUrl()) {
       if (expProfile == null) throw new Exception("No ExpansionProfile provided");
       ValueSetExpansionOutcome vse =
           expansionCache.getExpander().expand(vs, expProfile.setExcludeNested(!heirarchical));
       if (vse.getValueset() != null) {
         if (cache != null) {
           FileOutputStream s = new FileOutputStream(cacheFn);
           newJsonParser().compose(new FileOutputStream(cacheFn), vse.getValueset());
           s.close();
         }
       }
       return vse;
     } else {
       ValueSetExpansionOutcome res = expandOnServer(vs, cacheFn);
       if (cacheFn != null) {
         if (res.getValueset() != null) {
           saveToCache(res.getValueset(), cacheFn);
         } else {
           OperationOutcome oo = new OperationOutcome();
           oo.addIssue().getDetails().setText(res.getError());
           saveToCache(oo, cacheFn);
         }
       }
       return res;
     }
   } catch (NoTerminologyServiceException e) {
     return new ValueSetExpansionOutcome(
         e.getMessage() == null ? e.getClass().getName() : e.getMessage(),
         TerminologyServiceErrorClass.NOSERVICE);
   } catch (Exception e) {
     return new ValueSetExpansionOutcome(
         e.getMessage() == null ? e.getClass().getName() : e.getMessage(),
         TerminologyServiceErrorClass.UNKNOWN);
   }
 }
 private ValidationResult verifyCodeInternal(ValueSet vs, String code)
     throws FileNotFoundException, ETooCostly, IOException, FHIRException {
   if (vs.hasExpansion()) return verifyCodeInExpansion(vs, code);
   else {
     ValueSetExpansionOutcome vse = expansionCache.getExpander().expand(vs, null);
     if (vse.getValueset() == null)
       return new ValidationResult(IssueSeverity.ERROR, vse.getError(), vse.getErrorClass());
     else return verifyCodeInExpansion(vse.getValueset(), code);
   }
 }
 private ValidationResult verifyCodeInExpansion(
     ValueSet vs, String system, String code, String display) {
   ValueSetExpansionContainsComponent cc = findCode(vs.getExpansion().getContains(), code);
   if (cc == null)
     return new ValidationResult(
         IssueSeverity.ERROR, "Unknown Code " + code + " in " + vs.getUrl());
   if (display == null)
     return new ValidationResult(
         new ConceptDefinitionComponent().setCode(code).setDisplay(cc.getDisplay()));
   if (cc.hasDisplay()) {
     if (display.equalsIgnoreCase(cc.getDisplay()))
       return new ValidationResult(
           new ConceptDefinitionComponent().setCode(code).setDisplay(cc.getDisplay()));
     return new ValidationResult(
         IssueSeverity.WARNING,
         "Display Name for " + code + " must be '" + cc.getDisplay() + "'",
         new ConceptDefinitionComponent().setCode(code).setDisplay(cc.getDisplay()));
   }
   return null;
 }
 private ValidationResult verifyCodeInternal(
     ValueSet vs, String system, String code, String display) throws Exception {
   if (vs.hasExpansion()) return verifyCodeInExpansion(vs, system, code, display);
   else {
     ValueSetExpansionOutcome vse = expansionCache.getExpander().expand(vs, null);
     if (vse.getValueset() != null)
       return verifyCodeExternal(
           vs, new Coding().setSystem(system).setCode(code).setDisplay(display), false);
     else return verifyCodeInExpansion(vse.getValueset(), system, code, display);
   }
 }
  private ValidationResult handleByCache(ValueSet vs, Coding coding, boolean tryCache) {
    String cacheId = cacheId(coding);
    Map<String, ValidationResult> cache = validationCache.get(vs.getUrl());
    if (cache == null) {
      cache = new HashMap<String, IWorkerContext.ValidationResult>();
      validationCache.put(vs.getUrl(), cache);
    }
    if (cache.containsKey(cacheId)) return cache.get(cacheId);
    if (!tryCache) return null;
    if (!cacheValidation) return null;
    if (failed.contains(vs.getUrl())) return null;
    ValueSetExpansionOutcome vse = expandVS(vs, true, false);
    if (vse.getValueset() == null || notcomplete(vse.getValueset())) {
      failed.add(vs.getUrl());
      return null;
    }

    ValidationResult res = validateCode(coding, vse.getValueset());
    cache.put(cacheId, res);
    return res;
  }
 private ValidationResult verifyCodeExternal(ValueSet vs, CodeableConcept cc, boolean tryCache)
     throws Exception {
   ValidationResult res = handleByCache(vs, cc, tryCache);
   if (res != null) return res;
   Parameters pin = new Parameters();
   pin.addParameter().setName("codeableConcept").setValue(cc);
   pin.addParameter().setName("valueSet").setResource(vs);
   res = serverValidateCode(pin, false);
   Map<String, ValidationResult> cache = validationCache.get(vs.getUrl());
   cache.put(cacheId(cc), res);
   return res;
 }
 private String determineCacheId(ValueSet vs, boolean heirarchical) throws Exception {
   // just the content logical definition is hashed
   ValueSet vsid = new ValueSet();
   vsid.setCompose(vs.getCompose());
   JsonParser parser = new JsonParser();
   parser.setOutputStyle(OutputStyle.NORMAL);
   ByteArrayOutputStream b = new ByteArrayOutputStream();
   parser.compose(b, vsid);
   b.close();
   String s = new String(b.toByteArray(), Charsets.UTF_8);
   // any code systems we can find, we add these too.
   for (ConceptSetComponent inc : vs.getCompose().getInclude()) {
     CodeSystem cs = fetchCodeSystem(inc.getSystem());
     if (cs != null) {
       String css = cacheValue(cs);
       s = s + css;
     }
   }
   s = s + "-" + Boolean.toString(heirarchical);
   String r = Integer.toString(s.hashCode());
   //    TextFile.stringToFile(s, Utilities.path(cache, r+".id.json"));
   return r;
 }
 private ValidationResult verifyCodeExternal(ValueSet vs, Coding coding, boolean tryCache)
     throws Exception {
   ValidationResult res = vs == null ? null : handleByCache(vs, coding, tryCache);
   if (res != null) return res;
   Parameters pin = new Parameters();
   pin.addParameter().setName("coding").setValue(coding);
   if (vs != null) pin.addParameter().setName("valueSet").setResource(vs);
   res = serverValidateCode(pin, vs == null);
   if (vs != null) {
     Map<String, ValidationResult> cache = validationCache.get(vs.getUrl());
     cache.put(cacheId(coding), res);
   }
   return res;
 }
 private ValueSetExpansionOutcome loadFromCache(ValueSet vs, String cacheFn)
     throws FileNotFoundException, Exception {
   JsonParser parser = new JsonParser();
   Resource r = parser.parse(new FileInputStream(cacheFn));
   if (r instanceof OperationOutcome)
     return new ValueSetExpansionOutcome(
         ((OperationOutcome) r).getIssue().get(0).getDetails().getText(),
         TerminologyServiceErrorClass.UNKNOWN);
   else {
     vs.setExpansion(
         ((ValueSet) r)
             .getExpansion()); // because what is cached might be from a different value set
     return new ValueSetExpansionOutcome(vs);
   }
 }
 @Override
 public ValidationResult validateCode(String system, String code, String display, ValueSet vs) {
   try {
     if (system == null && display == null) return verifyCodeInternal(vs, code);
     if ((codeSystems.containsKey(system) && codeSystems.get(system) != null) || vs.hasExpansion())
       return verifyCodeInternal(vs, system, code, display);
     else
       return verifyCodeExternal(
           vs, new Coding().setSystem(system).setCode(code).setDisplay(display), true);
   } catch (Exception e) {
     return new ValidationResult(
         IssueSeverity.FATAL,
         "Error validating code \"" + code + "\" in system \"" + system + "\": " + e.getMessage(),
         TerminologyServiceErrorClass.SERVER_ERROR);
   }
 }
 @Override
 public ValidationResult validateCode(Coding code, ValueSet vs) {
   if (codeSystems.containsKey(code.getSystem()) && codeSystems.get(code.getSystem()) != null)
     try {
       return verifyCodeInCodeSystem(
           codeSystems.get(code.getSystem()), code.getSystem(), code.getCode(), code.getDisplay());
     } catch (Exception e) {
       return new ValidationResult(
           IssueSeverity.FATAL,
           "Error validating code \""
               + code
               + "\" in system \""
               + code.getSystem()
               + "\": "
               + e.getMessage());
     }
   else if (vs.hasExpansion())
     try {
       return verifyCodeInternal(vs, code.getSystem(), code.getCode(), code.getDisplay());
     } catch (Exception e) {
       return new ValidationResult(
           IssueSeverity.FATAL,
           "Error validating code \""
               + code
               + "\" in system \""
               + code.getSystem()
               + "\": "
               + e.getMessage());
     }
   else
     try {
       return verifyCodeExternal(vs, code, true);
     } catch (Exception e) {
       return new ValidationResult(
           IssueSeverity.WARNING,
           "Error validating code \""
               + code
               + "\" in system \""
               + code.getSystem()
               + "\": "
               + e.getMessage());
     }
 }
  public ValueSetExpansionOutcome expandOnServer(ValueSet vs, String fn) throws Exception {
    if (noTerminologyServer)
      return new ValueSetExpansionOutcome(
          "Error expanding ValueSet: running without terminology services",
          TerminologyServiceErrorClass.NOSERVICE);
    if (expProfile == null) throw new Exception("No ExpansionProfile provided");

    try {
      Map<String, String> params = new HashMap<String, String>();
      params.put("_limit", Integer.toString(expandCodesLimit));
      params.put("_incomplete", "true");
      log("Terminology Server: $expand on " + getVSSummary(vs));
      ValueSet result = txServer.expandValueset(vs, expProfile.setIncludeDefinition(false), params);
      return new ValueSetExpansionOutcome(result);
    } catch (Exception e) {
      return new ValueSetExpansionOutcome(
          "Error expanding ValueSet \"" + vs.getUrl() + ": " + e.getMessage(),
          TerminologyServiceErrorClass.UNKNOWN);
    }
  }
  private String gen(Section section, CodeSystem cs, ValueSet vs) {
    String bn = getPNameForUri(cs.getUrl());
    if (!bn.startsWith("<")) {
      section.triple(bn + ".system", "a", "fhir:CodeSystem");
      if (cs.hasVersion()) section.triple(bn + ".system", "fhir:version", literal(cs.getVersion()));
      if (vs.hasName()) section.label(bn + ".system", vs.getName());
      if (vs.hasDescription())
        section.comment(
            bn + ".system",
            vs.getDescription()
                .replace("value set", "code system")
                .replace("Value Set", "Code System")
                .replace("Value set", "Code system"));
      if (vs.hasCopyright())
        section.triple(bn + ".system", "dc:rights", literal(vs.getCopyright()));
      if (vs.hasDate()) section.triple(bn + ".system", "dc:date", literal(vs.getDate().toString()));

      section.triple(bn, "a", "fhir:Concept");

      gen(section, bn, bn, cs.getConcept());
    }
    return bn;
  }
  @Override
  public ValueSet expand(ValueSet source, String theFilter) {
    ValueSet toExpand = new ValueSet();
    for (UriType next : source.getCompose().getImport()) {
      ConceptSetComponent include = toExpand.getCompose().addInclude();
      include.setSystem(next.getValue());
      addFilterIfPresent(theFilter, include);
    }

    for (ConceptSetComponent next : source.getCompose().getInclude()) {
      toExpand.getCompose().addInclude(next);
      addFilterIfPresent(theFilter, next);
    }

    if (toExpand.getCompose().isEmpty()) {
      throw new InvalidRequestException(
          "ValueSet does not have any compose.include or compose.import values, can not expand");
    }

    toExpand.getCompose().getExclude().addAll(source.getCompose().getExclude());

    ValueSet retVal = doExpand(toExpand);
    return retVal;
  }
 @Override
 public ValidationResult validateCode(CodeableConcept code, ValueSet vs) {
   try {
     if (vs.hasExpansion()) return verifyCodeInternal(vs, code);
     else {
       // we'll try expanding first; if that doesn't work, then we'll just pass it to the server to
       // validate
       // ... could be a problem if the server doesn't have the code systems we have locally, so we
       // try not to depend on the server
       try {
         ValueSetExpansionOutcome vse = expandVS(vs, true, false);
         if (vse.getValueset() != null) return verifyCodeInternal(vse.getValueset(), code);
       } catch (Exception e) {
         // failed? we'll just try the server
       }
       return verifyCodeExternal(vs, code, true);
     }
   } catch (Exception e) {
     return new ValidationResult(
         IssueSeverity.FATAL,
         "Error validating code \"" + code.toString() + "\": " + e.getMessage(),
         TerminologyServiceErrorClass.SERVER_ERROR);
   }
 }
  private void gen(String bn, ValueSet vs) throws FHIRException {
    Section section = section(bn);
    section.triple(bn, "a", "fhir:ValueSet");
    if (vs.hasVersion()) section.triple(bn, "fhir:version", literal(vs.getVersion()));
    if (vs.hasName()) section.label(bn, vs.getName());
    if (vs.hasDescription())
      section.comment(
          bn,
          vs.getDescription()
              .replace("code system", "value set")
              .replace("Code System", "Value Set")
              .replace("Code system", "Value set"));
    if (vs.hasCopyright()) section.triple(bn, "dc:rights", literal(vs.getCopyright()));
    if (vs.hasDate()) section.triple(bn, "dc:date", literal(vs.getDateElement().asStringValue()));

    for (UsageContext cc : vs.getUseContext())
      codedTriple(section, bn, "fhir:useContext", cc.getValueCodeableConcept());
    section.triple(
        bn,
        "fhir:status",
        complex().predicate("a", "fhir:conformance-resource-status\\#" + vs.getStatus().toCode()));
    section.triple(
        bn,
        "fhir:canonicalStatus",
        complex().predicate("a", getCanonicalStatus("ValueSet.status", vs.getStatus().toCode())));
  }
  @Override
  public ca.uhn.fhir.jpa.dao.IFhirResourceDaoValueSet.ValidateCodeResult validateCode(
      IPrimitiveType<String> theValueSetIdentifier,
      IIdType theId,
      IPrimitiveType<String> theCode,
      IPrimitiveType<String> theSystem,
      IPrimitiveType<String> theDisplay,
      Coding theCoding,
      CodeableConcept theCodeableConcept,
      RequestDetails theRequestDetails) {

    List<IIdType> valueSetIds = Collections.emptyList();

    boolean haveCodeableConcept =
        theCodeableConcept != null && theCodeableConcept.getCoding().size() > 0;
    boolean haveCoding = theCoding != null && theCoding.isEmpty() == false;
    boolean haveCode = theCode != null && theCode.isEmpty() == false;

    if (!haveCodeableConcept && !haveCoding && !haveCode) {
      throw new InvalidRequestException("No code, coding, or codeableConcept provided to validate");
    }
    if (!LogicUtil.multiXor(haveCodeableConcept, haveCoding, haveCode)) {
      throw new InvalidRequestException(
          "$validate-code can only validate (system AND code) OR (coding) OR (codeableConcept)");
    }

    boolean haveIdentifierParam =
        theValueSetIdentifier != null && theValueSetIdentifier.isEmpty() == false;
    if (theId != null) {
      valueSetIds = Collections.singletonList(theId);
    } else if (haveIdentifierParam) {
      Set<Long> ids =
          searchForIds(
              ValueSet.SP_IDENTIFIER, new TokenParam(null, theValueSetIdentifier.getValue()));
      valueSetIds = new ArrayList<IIdType>();
      for (Long next : ids) {
        valueSetIds.add(new IdType("ValueSet", next));
      }
    } else {
      if (theCode == null || theCode.isEmpty()) {
        throw new InvalidRequestException(
            "Either ValueSet ID or ValueSet identifier or system and code must be provided. Unable to validate.");
      }
      // String code = theCode.getValue();
      // String system = toStringOrNull(theSystem);
      LookupCodeResult result = myCodeSystemDao.lookupCode(theCode, theSystem, null, null);
      if (result.isFound()) {
        ca.uhn.fhir.jpa.dao.IFhirResourceDaoValueSet.ValidateCodeResult retVal =
            new ValidateCodeResult(true, "Found code", result.getCodeDisplay());
        return retVal;
      }
    }

    //		if (valueSetIds.isEmpty()) {
    //			if (haveIdentifierParam) {
    //				myValidationSupport.expandValueSet(getContext(), include);
    //				if (myValidationSupport.isCodeSystemSupported(getContext(),
    // theValueSetIdentifier.getValue())) {
    //					String system = toStringOrNull(theSystem);
    //					String code = toStringOrNull(theCode);
    //					String display = toStringOrNull(theDisplay);
    //					CodeValidationResult result = myValidationSupport.validateCode(getContext(), system,
    // code, display);
    //					if (result != null) {
    //						if (theDisplay != null && isNotBlank(theDisplay.getValue()) &&
    // isNotBlank(result.getDisplay())) {
    //							if (!theDisplay.getValue().equals(result.getDisplay())) {
    //								return new ValidateCodeResult(false, "Display for code does not match",
    // result.getDisplay());
    //							}
    //						}
    //						return new ValidateCodeResult(true, "Code validates", result.getDisplay());
    //					}
    //				}
    //			}
    //		}

    for (IIdType nextId : valueSetIds) {
      ValueSet expansion = expand(nextId, null, theRequestDetails);
      List<ValueSetExpansionContainsComponent> contains = expansion.getExpansion().getContains();
      ValidateCodeResult result =
          validateCodeIsInContains(
              contains,
              toStringOrNull(theSystem),
              toStringOrNull(theCode),
              theCoding,
              theCodeableConcept);
      if (result != null) {
        if (theDisplay != null
            && isNotBlank(theDisplay.getValue())
            && isNotBlank(result.getDisplay())) {
          if (!theDisplay.getValue().equals(result.getDisplay())) {
            return new ValidateCodeResult(
                false, "Display for code does not match", result.getDisplay());
          }
        }
        return result;
      }
    }

    return new ValidateCodeResult(false, "Code not found", null);
  }