org.hl7.fhir.r4.context.BaseWorkerContext Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of org.hl7.fhir.r4 Show documentation
Show all versions of org.hl7.fhir.r4 Show documentation
Builds the hapi fhir r4. Requires hapi-fhir-base and hapi-fhir-utilities be built first and be
excluded from any other poms requiring it.
The newest version!
package org.hl7.fhir.r4.context;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.ResourceBundle;
import java.util.Set;
import org.apache.commons.lang3.StringUtils;
import org.fhir.ucum.UcumService;
import org.hl7.fhir.exceptions.DefinitionException;
import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.exceptions.TerminologyServiceException;
import org.hl7.fhir.r4.conformance.ProfileUtilities;
import org.hl7.fhir.r4.context.TerminologyCache.CacheToken;
import org.hl7.fhir.r4.model.BooleanType;
import org.hl7.fhir.r4.model.CapabilityStatement;
import org.hl7.fhir.r4.model.CodeSystem;
import org.hl7.fhir.r4.model.CodeSystem.CodeSystemContentMode;
import org.hl7.fhir.r4.model.CodeSystem.ConceptDefinitionComponent;
import org.hl7.fhir.r4.model.CodeableConcept;
import org.hl7.fhir.r4.model.Coding;
import org.hl7.fhir.r4.model.ConceptMap;
import org.hl7.fhir.r4.model.Constants;
import org.hl7.fhir.r4.model.ElementDefinition.ElementDefinitionBindingComponent;
import org.hl7.fhir.r4.model.ImplementationGuide;
import org.hl7.fhir.r4.model.IntegerType;
import org.hl7.fhir.r4.model.MetadataResource;
import org.hl7.fhir.r4.model.NamingSystem;
import org.hl7.fhir.r4.model.NamingSystem.NamingSystemIdentifierType;
import org.hl7.fhir.r4.model.NamingSystem.NamingSystemUniqueIdComponent;
import org.hl7.fhir.r4.model.OperationDefinition;
import org.hl7.fhir.r4.model.Parameters;
import org.hl7.fhir.r4.model.Parameters.ParametersParameterComponent;
import org.hl7.fhir.r4.model.PlanDefinition;
import org.hl7.fhir.r4.model.Questionnaire;
import org.hl7.fhir.r4.model.Reference;
import org.hl7.fhir.r4.model.Resource;
import org.hl7.fhir.r4.model.SearchParameter;
import org.hl7.fhir.r4.model.StringType;
import org.hl7.fhir.r4.model.StructureDefinition;
import org.hl7.fhir.r4.model.StructureMap;
import org.hl7.fhir.r4.model.TerminologyCapabilities;
import org.hl7.fhir.r4.model.TerminologyCapabilities.TerminologyCapabilitiesCodeSystemComponent;
import org.hl7.fhir.r4.model.ValueSet;
import org.hl7.fhir.r4.model.ValueSet.ConceptSetComponent;
import org.hl7.fhir.r4.model.ValueSet.ValueSetComposeComponent;
import org.hl7.fhir.r4.terminologies.TerminologyClient;
import org.hl7.fhir.r4.terminologies.ValueSetCheckerSimple;
import org.hl7.fhir.r4.terminologies.ValueSetExpander.TerminologyServiceErrorClass;
import org.hl7.fhir.r4.terminologies.ValueSetExpander.ValueSetExpansionOutcome;
import org.hl7.fhir.r4.terminologies.ValueSetExpanderSimple;
import org.hl7.fhir.r4.utils.ToolingExtensions;
import org.hl7.fhir.utilities.OIDUtils;
import org.hl7.fhir.utilities.TranslationServices;
import org.hl7.fhir.utilities.Utilities;
import org.hl7.fhir.utilities.i18n.I18nBase;
import org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity;
import org.hl7.fhir.utilities.validation.ValidationMessage.IssueType;
import org.hl7.fhir.utilities.validation.ValidationOptions;
/*
Copyright (c) 2011+, HL7, Inc.
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of HL7 nor the names of its contributors may be used to
endorse or promote products derived from this software without specific
prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
import com.google.gson.JsonObject;
public abstract class BaseWorkerContext extends I18nBase implements IWorkerContext {
private Object lock = new Object(); // used as a lock for the data that follows
private Map> allResourcesById = new HashMap>();
// all maps are to the full URI
private Map codeSystems = new HashMap();
private Set supportedCodeSystems = new HashSet();
private Map valueSets = new HashMap();
private Map maps = new HashMap();
protected Map transforms = new HashMap();
private Map structures = new HashMap();
private Map guides = new HashMap();
private Map capstmts = new HashMap();
private Map searchParameters = new HashMap();
private Map questionnaires = new HashMap();
private Map operations = new HashMap();
private Map plans = new HashMap();
private List systems = new ArrayList();
private UcumService ucumService;
protected Map> validationCache = new HashMap>();
protected String tsServer;
protected String name;
private boolean allowLoadingDuplicates;
protected TerminologyClient txClient;
protected HTMLClientLogger txLog;
private TerminologyCapabilities txcaps;
private boolean canRunWithoutTerminology;
protected boolean noTerminologyServer;
private int expandCodesLimit = 1000;
protected ILoggingService logger;
protected Parameters expParameters;
private TranslationServices translator = new NullTranslator();
protected TerminologyCache txCache;
private boolean tlogging = true;
private Locale locale;
private ResourceBundle i18Nmessages;
public BaseWorkerContext() throws FileNotFoundException, IOException, FHIRException {
super();
txCache = new TerminologyCache(lock, null);
}
public BaseWorkerContext(Map codeSystems, Map valueSets,
Map maps, Map profiles, Map guides)
throws FileNotFoundException, IOException, FHIRException {
super();
this.codeSystems = codeSystems;
this.valueSets = valueSets;
this.maps = maps;
this.structures = profiles;
this.guides = guides;
txCache = new TerminologyCache(lock, null);
}
protected void copy(BaseWorkerContext other) {
synchronized (other.lock) { // tricky, because you need to lock this as well, but it's really not in use yet
allResourcesById.putAll(other.allResourcesById);
translator = other.translator;
codeSystems.putAll(other.codeSystems);
txcaps = other.txcaps;
valueSets.putAll(other.valueSets);
maps.putAll(other.maps);
transforms.putAll(other.transforms);
structures.putAll(other.structures);
searchParameters.putAll(other.searchParameters);
plans.putAll(other.plans);
questionnaires.putAll(other.questionnaires);
operations.putAll(other.operations);
systems.addAll(other.systems);
guides.putAll(other.guides);
capstmts.putAll(other.capstmts);
allowLoadingDuplicates = other.allowLoadingDuplicates;
tsServer = other.tsServer;
name = other.name;
txClient = other.txClient;
txLog = other.txLog;
txcaps = other.txcaps;
canRunWithoutTerminology = other.canRunWithoutTerminology;
noTerminologyServer = other.noTerminologyServer;
if (other.txCache != null)
txCache = other.txCache.copy();
expandCodesLimit = other.expandCodesLimit;
logger = other.logger;
expParameters = other.expParameters;
}
}
public void cacheResource(Resource r) throws FHIRException {
synchronized (lock) {
Map map = allResourcesById.get(r.fhirType());
if (map == null) {
map = new HashMap();
allResourcesById.put(r.fhirType(), map);
}
map.put(r.getId(), r);
if (r instanceof MetadataResource) {
MetadataResource m = (MetadataResource) r;
String url = m.getUrl();
if (!allowLoadingDuplicates && hasResource(r.getClass(), url))
throw new DefinitionException("Duplicate Resource " + url);
if (r instanceof StructureDefinition)
seeMetadataResource((StructureDefinition) m, structures, false);
else if (r instanceof ValueSet)
seeMetadataResource((ValueSet) m, valueSets, false);
else if (r instanceof CodeSystem)
seeMetadataResource((CodeSystem) m, codeSystems, false);
else if (r instanceof ImplementationGuide)
seeMetadataResource((ImplementationGuide) m, guides, false);
else if (r instanceof CapabilityStatement)
seeMetadataResource((CapabilityStatement) m, capstmts, false);
else if (r instanceof SearchParameter)
seeMetadataResource((SearchParameter) m, searchParameters, false);
else if (r instanceof PlanDefinition)
seeMetadataResource((PlanDefinition) m, plans, false);
else if (r instanceof OperationDefinition)
seeMetadataResource((OperationDefinition) m, operations, false);
else if (r instanceof Questionnaire)
seeMetadataResource((Questionnaire) m, questionnaires, true);
else if (r instanceof ConceptMap)
seeMetadataResource((ConceptMap) m, maps, false);
else if (r instanceof StructureMap)
seeMetadataResource((StructureMap) m, transforms, false);
else if (r instanceof NamingSystem)
systems.add((NamingSystem) r);
}
}
}
/*
* Compare business versions, returning "true" if the candidate newer version is
* in fact newer than the oldVersion Comparison will work for strictly numeric
* versions as well as multi-level versions separated by ., -, _, : or space
* Failing that, it will do unicode-based character ordering. E.g. 1.5.3 <
* 1.14.3 2017-3-10 < 2017-12-7 A3 < T2
*/
private boolean laterVersion(String newVersion, String oldVersion) {
// Compare business versions, retur
newVersion = newVersion.trim();
oldVersion = oldVersion.trim();
if (StringUtils.isNumeric(newVersion) && StringUtils.isNumeric(oldVersion))
return Double.parseDouble(newVersion) > Double.parseDouble(oldVersion);
else if (hasDelimiter(newVersion, oldVersion, "."))
return laterDelimitedVersion(newVersion, oldVersion, "\\.");
else if (hasDelimiter(newVersion, oldVersion, "-"))
return laterDelimitedVersion(newVersion, oldVersion, "\\-");
else if (hasDelimiter(newVersion, oldVersion, "_"))
return laterDelimitedVersion(newVersion, oldVersion, "\\_");
else if (hasDelimiter(newVersion, oldVersion, ":"))
return laterDelimitedVersion(newVersion, oldVersion, "\\:");
else if (hasDelimiter(newVersion, oldVersion, " "))
return laterDelimitedVersion(newVersion, oldVersion, "\\ ");
else {
return newVersion.compareTo(oldVersion) > 0;
}
}
/*
* Returns true if both strings include the delimiter and have the same number
* of occurrences of it
*/
private boolean hasDelimiter(String s1, String s2, String delimiter) {
return s1.contains(delimiter) && s2.contains(delimiter) && s1.split(delimiter).length == s2.split(delimiter).length;
}
private boolean laterDelimitedVersion(String newVersion, String oldVersion, String delimiter) {
String[] newParts = newVersion.split(delimiter);
String[] oldParts = oldVersion.split(delimiter);
for (int i = 0; i < newParts.length; i++) {
if (!newParts[i].equals(oldParts[i]))
return laterVersion(newParts[i], oldParts[i]);
}
// This should never happen
throw new Error(
"Delimited versions have exact match for delimiter '" + delimiter + "' : " + newParts + " vs " + oldParts);
}
protected void seeMetadataResource(T r, Map map, boolean addId)
throws FHIRException {
if (addId)
map.put(r.getId(), r); // todo: why?
if (!map.containsKey(r.getUrl()))
map.put(r.getUrl(), r);
else {
// If this resource already exists, see if it's the newest business version. The
// default resource to return if not qualified is the most recent business
// version
MetadataResource existingResource = (MetadataResource) map.get(r.getUrl());
if (r.hasVersion() && existingResource.hasVersion() && !r.getVersion().equals(existingResource.getVersion())) {
if (laterVersion(r.getVersion(), existingResource.getVersion())) {
map.remove(r.getUrl());
map.put(r.getUrl(), r);
}
} else
map.remove(r.getUrl());
map.put(r.getUrl(), r);
// throw new FHIRException("Multiple declarations of resource with same canonical URL (" + r.getUrl() + ") and version (" + (r.hasVersion() ? r.getVersion() : "" ) + ")");
}
if (r.hasVersion())
map.put(r.getUrl() + "|" + r.getVersion(), r);
}
@Override
public CodeSystem fetchCodeSystem(String system) {
synchronized (lock) {
return codeSystems.get(system);
}
}
@Override
public boolean supportsSystem(String system) throws TerminologyServiceException {
synchronized (lock) {
if (codeSystems.containsKey(system) && codeSystems.get(system).getContent() != CodeSystemContentMode.NOTPRESENT)
return true;
else if (supportedCodeSystems.contains(system))
return true;
else if (system.startsWith("http://example.org") || system.startsWith("http://acme.com")
|| system.startsWith("http://hl7.org/fhir/valueset-") || system.startsWith("urn:oid:"))
return false;
else {
if (noTerminologyServer)
return false;
if (txcaps == null) {
try {
log("Terminology server: Check for supported code systems for " + system);
txcaps = txClient.getTerminologyCapabilities();
} catch (Exception e) {
if (canRunWithoutTerminology) {
noTerminologyServer = true;
log("==============!! Running without terminology server !! ==============");
if (txClient != null) {
log("txServer = " + txClient.getAddress());
log("Error = " + e.getMessage() + "");
}
log("=====================================================================");
return false;
} else
throw new TerminologyServiceException(e);
}
if (txcaps != null) {
for (TerminologyCapabilitiesCodeSystemComponent tccs : txcaps.getCodeSystem()) {
supportedCodeSystems.add(tccs.getUri());
}
}
if (supportedCodeSystems.contains(system))
return true;
}
}
return false;
}
}
private void log(String message) {
if (logger != null)
logger.logMessage(message);
else
System.out.println(message);
}
protected void tlog(String msg) {
if (tlogging)
System.out.println("-tx cache miss: " + msg);
}
// --- expansion support
// ------------------------------------------------------------------------------------------------------------
public int getExpandCodesLimit() {
return expandCodesLimit;
}
public void setExpandCodesLimit(int expandCodesLimit) {
this.expandCodesLimit = expandCodesLimit;
}
@Override
public ValueSetExpansionOutcome expandVS(ElementDefinitionBindingComponent binding, boolean cacheOk,
boolean heirarchical) throws FHIRException {
ValueSet vs = null;
vs = fetchResource(ValueSet.class, binding.getValueSet());
if (vs == null)
throw new FHIRException("Unable to resolve value Set " + binding.getValueSet());
return expandVS(vs, cacheOk, heirarchical);
}
@Override
public ValueSetExpansionOutcome expandVS(ConceptSetComponent inc, boolean heirachical)
throws TerminologyServiceException {
ValueSet vs = new ValueSet();
vs.setCompose(new ValueSetComposeComponent());
vs.getCompose().getInclude().add(inc);
CacheToken cacheToken = txCache.generateExpandToken(vs, heirachical);
ValueSetExpansionOutcome res;
res = txCache.getExpansion(cacheToken);
if (res != null)
return res;
Parameters p = expParameters.copy();
p.setParameter("includeDefinition", false);
p.setParameter("excludeNested", !heirachical);
if (noTerminologyServer)
return new ValueSetExpansionOutcome("Error expanding ValueSet: running without terminology services",
TerminologyServiceErrorClass.NOSERVICE);
p.addParameter().setName("_limit").setValue(new IntegerType(expandCodesLimit));
p.addParameter().setName("_incomplete").setValue(new BooleanType("true"));
tlog("$expand on " + txCache.summary(vs));
try {
ValueSet result = txClient.expandValueset(vs, p);
res = new ValueSetExpansionOutcome(result).setTxLink(txLog.getLastId());
} catch (Exception e) {
res = new ValueSetExpansionOutcome(e.getMessage() == null ? e.getClass().getName() : e.getMessage(),
TerminologyServiceErrorClass.UNKNOWN);
if (txLog != null)
res.setTxLink(txLog.getLastId());
}
txCache.cacheExpansion(cacheToken, res, TerminologyCache.PERMANENT);
return res;
}
@Override
public ValueSetExpansionOutcome expandVS(ValueSet vs, boolean cacheOk, boolean heirarchical) {
if (expParameters == null)
throw new Error("No Expansion Parameters provided");
Parameters p = expParameters.copy();
return expandVS(vs, cacheOk, heirarchical, p);
}
public ValueSetExpansionOutcome expandVS(ValueSet vs, boolean cacheOk, boolean heirarchical, Parameters p) {
if (p == null)
throw new Error("No Parameters provided to expandVS");
if (vs.hasExpansion()) {
return new ValueSetExpansionOutcome(vs.copy());
}
if (!vs.hasUrl())
throw new Error("no value set");
CacheToken cacheToken = txCache.generateExpandToken(vs, heirarchical);
ValueSetExpansionOutcome res;
if (cacheOk) {
res = txCache.getExpansion(cacheToken);
if (res != null)
return res;
}
p.setParameter("includeDefinition", false);
p.setParameter("excludeNested", !heirarchical);
// ok, first we try to expand locally
try {
ValueSetExpanderSimple vse = new ValueSetExpanderSimple(this);
res = vse.doExpand(vs, p);
if (!res.getValueset().hasUrl())
throw new Error("no url in expand value set");
txCache.cacheExpansion(cacheToken, res, TerminologyCache.TRANSIENT);
return res;
} catch (Exception e) {
}
// if that failed, we try to expand on the server
if (noTerminologyServer)
return new ValueSetExpansionOutcome("Error expanding ValueSet: running without terminology services",
TerminologyServiceErrorClass.NOSERVICE);
p.addParameter().setName("_limit").setValue(new IntegerType(expandCodesLimit));
p.addParameter().setName("_incomplete").setValue(new BooleanType("true"));
tlog("$expand on " + txCache.summary(vs));
try {
ValueSet result = txClient.expandValueset(vs, p);
if (result != null) {
if (!result.hasUrl())
result.setUrl(vs.getUrl());
if (!result.hasUrl())
throw new Error("no url in expand value set 2");
}
res = new ValueSetExpansionOutcome(result).setTxLink(txLog.getLastId());
} catch (Exception e) {
res = new ValueSetExpansionOutcome(e.getMessage() == null ? e.getClass().getName() : e.getMessage(),
TerminologyServiceErrorClass.UNKNOWN).setTxLink(txLog == null ? null : txLog.getLastId());
}
txCache.cacheExpansion(cacheToken, res, TerminologyCache.PERMANENT);
return res;
}
private boolean hasTooCostlyExpansion(ValueSet valueset) {
return valueset != null && valueset.hasExpansion() && ToolingExtensions.hasExtension(valueset.getExpansion(),
"http://hl7.org/fhir/StructureDefinition/valueset-toocostly");
}
// --- validate code
// -------------------------------------------------------------------------------
@Override
public ValidationResult validateCode(ValidationOptions options, String system, String code, String display) {
Coding c = new Coding(system, code, display);
return validateCode(options, c, null);
}
@Override
public ValidationResult validateCode(ValidationOptions options, String system, String code, String display,
ValueSet vs) {
Coding c = new Coding(system, code, display);
return validateCode(options, c, vs);
}
@Override
public ValidationResult validateCode(ValidationOptions options, String code, ValueSet vs) {
Coding c = new Coding(null, code, null);
return doValidateCode(options, c, vs, true);
}
@Override
public ValidationResult validateCode(ValidationOptions options, String system, String code, String display,
ConceptSetComponent vsi) {
Coding c = new Coding(system, code, display);
ValueSet vs = new ValueSet();
vs.setUrl(Utilities.makeUuidUrn());
vs.getCompose().addInclude(vsi);
return validateCode(options, c, vs);
}
@Override
public ValidationResult validateCode(ValidationOptions options, Coding code, ValueSet vs) {
return doValidateCode(options, code, vs, false);
}
public ValidationResult doValidateCode(ValidationOptions options, Coding code, ValueSet vs, boolean inferSystem) {
CacheToken cacheToken = txCache != null ? txCache.generateValidationToken(options, code, vs) : null;
ValidationResult res = null;
if (txCache != null)
res = txCache.getValidation(cacheToken);
if (res != null)
return res;
// ok, first we try to validate locally
try {
ValueSetCheckerSimple vsc = new ValueSetCheckerSimple(options, vs, this);
res = vsc.validateCode(code);
if (txCache != null)
txCache.cacheValidation(cacheToken, res, TerminologyCache.TRANSIENT);
return res;
} catch (Exception e) {
}
// if that failed, we try to validate on the server
if (noTerminologyServer)
return new ValidationResult(IssueSeverity.ERROR, "Error validating code: running without terminology services",
TerminologyServiceErrorClass.NOSERVICE);
String csumm = txCache != null ? txCache.summary(code) : null;
if (txCache != null)
tlog("$validate " + csumm + " for " + txCache.summary(vs));
else
tlog("$validate " + csumm + " before cache exists");
try {
Parameters pIn = new Parameters();
pIn.addParameter().setName("coding").setValue(code);
if (inferSystem)
pIn.addParameter().setName("inferSystem").setValue(new BooleanType(true));
if (options != null)
setTerminologyOptions(options, pIn);
res = validateOnServer(vs, pIn);
} catch (Exception e) {
res = new ValidationResult(IssueSeverity.ERROR, e.getMessage() == null ? e.getClass().getName() : e.getMessage())
.setTxLink(txLog == null ? null : txLog.getLastId());
}
if (txCache != null)
txCache.cacheValidation(cacheToken, res, TerminologyCache.PERMANENT);
return res;
}
private void setTerminologyOptions(ValidationOptions options, Parameters pIn) {
if (options != null && options.hasLanguages()) {
pIn.addParameter("displayLanguage", options.getLanguages().toString());
}
}
@Override
public ValidationResult validateCode(ValidationOptions options, CodeableConcept code, ValueSet vs) {
CacheToken cacheToken = txCache.generateValidationToken(options, code, vs);
ValidationResult res = txCache.getValidation(cacheToken);
if (res != null)
return res;
// ok, first we try to validate locally
try {
ValueSetCheckerSimple vsc = new ValueSetCheckerSimple(options, vs, this);
res = vsc.validateCode(code);
txCache.cacheValidation(cacheToken, res, TerminologyCache.TRANSIENT);
return res;
} catch (Exception e) {
}
// if that failed, we try to validate on the server
if (noTerminologyServer)
return new ValidationResult(IssueSeverity.ERROR, "Error validating code: running without terminology services",
TerminologyServiceErrorClass.NOSERVICE);
tlog("$validate " + txCache.summary(code) + " for " + txCache.summary(vs));
try {
Parameters pIn = new Parameters();
pIn.addParameter().setName("codeableConcept").setValue(code);
if (options != null)
setTerminologyOptions(options, pIn);
res = validateOnServer(vs, pIn);
} catch (Exception e) {
res = new ValidationResult(IssueSeverity.ERROR, e.getMessage() == null ? e.getClass().getName() : e.getMessage())
.setTxLink(txLog.getLastId());
}
txCache.cacheValidation(cacheToken, res, TerminologyCache.PERMANENT);
return res;
}
private ValidationResult validateOnServer(ValueSet vs, Parameters pin) throws FHIRException {
if (vs != null)
pin.addParameter().setName("valueSet").setResource(vs);
for (ParametersParameterComponent pp : pin.getParameter())
if (pp.getName().equals("profile"))
throw new Error("Can only specify profile in the context");
if (expParameters == null)
throw new Error("No ExpansionProfile provided");
pin.addParameter().setName("profile").setResource(expParameters);
txLog.clearLastId();
Parameters pOut;
if (vs == null)
pOut = txClient.validateCS(pin);
else
pOut = txClient.validateVS(pin);
boolean ok = false;
String message = "No Message returned";
String display = null;
TerminologyServiceErrorClass err = TerminologyServiceErrorClass.UNKNOWN;
for (ParametersParameterComponent p : pOut.getParameter()) {
if (p.getName().equals("result"))
ok = ((BooleanType) p.getValue()).getValue().booleanValue();
else if (p.getName().equals("message"))
message = ((StringType) p.getValue()).getValue();
else if (p.getName().equals("display"))
display = ((StringType) p.getValue()).getValue();
else if (p.getName().equals("cause")) {
try {
IssueType it = IssueType.fromCode(((StringType) p.getValue()).getValue());
if (it == IssueType.UNKNOWN)
err = TerminologyServiceErrorClass.UNKNOWN;
else if (it == IssueType.NOTSUPPORTED)
err = TerminologyServiceErrorClass.VALUESET_UNSUPPORTED;
} catch (FHIRException e) {
}
}
}
if (!ok)
return new ValidationResult(IssueSeverity.ERROR, message, err).setTxLink(txLog.getLastId())
.setTxLink(txLog.getLastId());
else if (message != null && !message.equals("No Message returned"))
return new ValidationResult(IssueSeverity.WARNING, message, new ConceptDefinitionComponent().setDisplay(display))
.setTxLink(txLog.getLastId()).setTxLink(txLog.getLastId());
else if (display != null)
return new ValidationResult(new ConceptDefinitionComponent().setDisplay(display)).setTxLink(txLog.getLastId())
.setTxLink(txLog.getLastId());
else
return new ValidationResult(new ConceptDefinitionComponent()).setTxLink(txLog.getLastId())
.setTxLink(txLog.getLastId());
}
// --------------------------------------------------------------------------------------------------------------------------------------------------------
public void initTS(String cachePath) throws Exception {
txCache = new TerminologyCache(lock, cachePath);
}
@Override
public List findMapsForSource(String url) throws FHIRException {
synchronized (lock) {
List res = new ArrayList();
for (ConceptMap map : maps.values())
if (((Reference) map.getSource()).getReference().equals(url))
res.add(map);
return res;
}
}
public boolean isCanRunWithoutTerminology() {
return canRunWithoutTerminology;
}
public void setCanRunWithoutTerminology(boolean canRunWithoutTerminology) {
this.canRunWithoutTerminology = canRunWithoutTerminology;
}
public void setLogger(ILoggingService logger) {
this.logger = logger;
}
public Parameters getExpansionParameters() {
return expParameters;
}
public void setExpansionProfile(Parameters expParameters) {
this.expParameters = expParameters;
}
@Override
public boolean isNoTerminologyServer() {
return noTerminologyServer;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public Set getResourceNamesAsSet() {
Set res = new HashSet();
res.addAll(getResourceNames());
return res;
}
public boolean isAllowLoadingDuplicates() {
return allowLoadingDuplicates;
}
public void setAllowLoadingDuplicates(boolean allowLoadingDuplicates) {
this.allowLoadingDuplicates = allowLoadingDuplicates;
}
@SuppressWarnings("unchecked")
@Override
public T fetchResourceWithException(Class class_, String uri) throws FHIRException {
if (class_ == StructureDefinition.class)
uri = ProfileUtilities.sdNs(uri, getOverrideVersionNs());
synchronized (lock) {
if (uri.startsWith("http:") || uri.startsWith("https:")) {
String version = null;
if (uri.contains("|")) {
version = uri.substring(uri.lastIndexOf("|") + 1);
uri = uri.substring(0, uri.lastIndexOf("|"));
}
if (uri.contains("#"))
uri = uri.substring(0, uri.indexOf("#"));
if (class_ == Resource.class || class_ == null) {
if (structures.containsKey(uri))
return (T) structures.get(uri);
if (guides.containsKey(uri))
return (T) guides.get(uri);
if (capstmts.containsKey(uri))
return (T) capstmts.get(uri);
if (valueSets.containsKey(uri))
return (T) valueSets.get(uri);
if (codeSystems.containsKey(uri))
return (T) codeSystems.get(uri);
if (operations.containsKey(uri))
return (T) operations.get(uri);
if (searchParameters.containsKey(uri))
return (T) searchParameters.get(uri);
if (plans.containsKey(uri))
return (T) plans.get(uri);
if (maps.containsKey(uri))
return (T) maps.get(uri);
if (transforms.containsKey(uri))
return (T) transforms.get(uri);
if (questionnaires.containsKey(uri))
return (T) questionnaires.get(uri);
for (Map rt : allResourcesById.values()) {
for (Resource r : rt.values()) {
if (r instanceof MetadataResource) {
MetadataResource mr = (MetadataResource) r;
if (uri.equals(mr.getUrl()))
return (T) mr;
}
}
}
return null;
} else if (class_ == ImplementationGuide.class) {
return (T) guides.get(uri);
} else if (class_ == CapabilityStatement.class) {
return (T) capstmts.get(uri);
} else if (class_ == StructureDefinition.class) {
return (T) structures.get(uri);
} else if (class_ == StructureMap.class) {
return (T) transforms.get(uri);
} else if (class_ == ValueSet.class) {
if (valueSets.containsKey(uri + "|" + version))
return (T) valueSets.get(uri + "|" + version);
else
return (T) valueSets.get(uri);
} else if (class_ == CodeSystem.class) {
if (codeSystems.containsKey(uri + "|" + version))
return (T) codeSystems.get(uri + "|" + version);
else
return (T) codeSystems.get(uri);
} else if (class_ == ConceptMap.class) {
return (T) maps.get(uri);
} else if (class_ == PlanDefinition.class) {
return (T) plans.get(uri);
} else if (class_ == OperationDefinition.class) {
OperationDefinition od = operations.get(uri);
return (T) od;
} else if (class_ == SearchParameter.class) {
SearchParameter res = searchParameters.get(uri);
if (res == null) {
StringBuilder b = new StringBuilder();
for (String s : searchParameters.keySet()) {
b.append(s);
b.append("\r\n");
}
}
return (T) res;
}
}
if (class_ == CodeSystem.class && codeSystems.containsKey(uri))
return (T) codeSystems.get(uri);
if (class_ == Questionnaire.class)
return (T) questionnaires.get(uri);
if (class_ == null) {
if (uri.matches(Constants.URI_REGEX) && !uri.contains("ValueSet"))
return null;
// it might be a special URL.
if (Utilities.isAbsoluteUrl(uri) || uri.startsWith("ValueSet/")) {
Resource res = null; // findTxValueSet(uri);
if (res != null)
return (T) res;
}
return null;
}
if (supportedCodeSystems.contains(uri))
return null;
throw new FHIRException("not done yet: can't fetch " + uri);
}
}
private Set notCanonical = new HashSet();
private String overrideVersionNs;
// private MetadataResource findTxValueSet(String uri) {
// MetadataResource res = expansionCache.getStoredResource(uri);
// if (res != null)
// return res;
// synchronized (lock) {
// if (notCanonical.contains(uri))
// return null;
// }
// try {
// tlog("get canonical "+uri);
// res = txServer.getCanonical(ValueSet.class, uri);
// } catch (Exception e) {
// synchronized (lock) {
// notCanonical.add(uri);
// }
// return null;
// }
// if (res != null)
// try {
// expansionCache.storeResource(res);
// } catch (IOException e) {
// }
// return res;
// }
@Override
public Resource fetchResourceById(String type, String uri) {
synchronized (lock) {
String[] parts = uri.split("\\/");
if (!Utilities.noString(type) && parts.length == 1) {
if (allResourcesById.containsKey(type))
return allResourcesById.get(type).get(parts[0]);
else
return null;
}
if (parts.length >= 2) {
if (!Utilities.noString(type))
if (!type.equals(parts[parts.length - 2]))
throw new Error("Resource type mismatch for " + type + " / " + uri);
return allResourcesById.get(parts[parts.length - 2]).get(parts[parts.length - 1]);
} else
throw new Error("Unable to process request for resource for " + type + " / " + uri);
}
}
public T fetchResource(Class class_, String uri) {
try {
return fetchResourceWithException(class_, uri);
} catch (FHIRException e) {
throw new Error(e);
}
}
public T fetchResource(Class class_, String uri, String version) {
try {
return fetchResourceWithException(class_, uri+"|"+version);
} catch (FHIRException e) {
throw new Error(e);
}
}
@Override
public boolean hasResource(Class class_, String uri) {
try {
return fetchResourceWithException(class_, uri) != null;
} catch (Exception e) {
return false;
}
}
public TranslationServices translator() {
return translator;
}
public void setTranslator(TranslationServices translator) {
this.translator = translator;
}
public class NullTranslator implements TranslationServices {
@Override
public String translate(String context, String value, String targetLang) {
return value;
}
@Override
public String translate(String context, String value) {
return value;
}
@Override
public String toStr(float value) {
return null;
}
@Override
public String toStr(Date value) {
return null;
}
@Override
public String translateAndFormat(String contest, String lang, String value, Object... args) {
return String.format(value, args);
}
@Override
public Map translations(String value) {
// TODO Auto-generated method stub
return null;
}
@Override
public Set listTranslations(String category) {
// TODO Auto-generated method stub
return null;
}
}
public void reportStatus(JsonObject json) {
synchronized (lock) {
json.addProperty("codeystem-count", codeSystems.size());
json.addProperty("valueset-count", valueSets.size());
json.addProperty("conceptmap-count", maps.size());
json.addProperty("transforms-count", transforms.size());
json.addProperty("structures-count", structures.size());
json.addProperty("guides-count", guides.size());
json.addProperty("statements-count", capstmts.size());
}
}
public void dropResource(Resource r) throws FHIRException {
dropResource(r.fhirType(), r.getId());
}
public void dropResource(String fhirType, String id) {
synchronized (lock) {
Map map = allResourcesById.get(fhirType);
if (map == null) {
map = new HashMap();
allResourcesById.put(fhirType, map);
}
if (map.containsKey(id))
map.remove(id);
if (fhirType.equals("StructureDefinition"))
dropMetadataResource(structures, id);
else if (fhirType.equals("ImplementationGuide"))
dropMetadataResource(guides, id);
else if (fhirType.equals("CapabilityStatement"))
dropMetadataResource(capstmts, id);
else if (fhirType.equals("ValueSet"))
dropMetadataResource(valueSets, id);
else if (fhirType.equals("CodeSystem"))
dropMetadataResource(codeSystems, id);
else if (fhirType.equals("OperationDefinition"))
dropMetadataResource(operations, id);
else if (fhirType.equals("Questionnaire"))
dropMetadataResource(questionnaires, id);
else if (fhirType.equals("ConceptMap"))
dropMetadataResource(maps, id);
else if (fhirType.equals("StructureMap"))
dropMetadataResource(transforms, id);
else if (fhirType.equals("NamingSystem"))
for (int i = systems.size() - 1; i >= 0; i--) {
if (systems.get(i).getId().equals(id))
systems.remove(i);
}
}
}
private void dropMetadataResource(Map map, String id) {
T res = map.get(id);
if (res != null) {
map.remove(id);
if (map.containsKey(res.getUrl()))
map.remove(res.getUrl());
if (res.getVersion() != null)
if (map.containsKey(res.getUrl() + "|" + res.getVersion()))
map.remove(res.getUrl() + "|" + res.getVersion());
}
}
@Override
public List allConformanceResources() {
synchronized (lock) {
List result = new ArrayList();
result.addAll(structures.values());
result.addAll(guides.values());
result.addAll(capstmts.values());
result.addAll(codeSystems.values());
result.addAll(valueSets.values());
result.addAll(maps.values());
result.addAll(transforms.values());
result.addAll(plans.values());
result.addAll(questionnaires.values());
return result;
}
}
public String listSupportedSystems() {
synchronized (lock) {
String sl = null;
for (String s : supportedCodeSystems)
sl = sl == null ? s : sl + "\r\n" + s;
return sl;
}
}
public int totalCount() {
synchronized (lock) {
return valueSets.size() + maps.size() + structures.size() + transforms.size();
}
}
public List listMaps() {
List m = new ArrayList();
synchronized (lock) {
m.addAll(maps.values());
}
return m;
}
public List listTransforms() {
List m = new ArrayList();
synchronized (lock) {
m.addAll(transforms.values());
}
return m;
}
public StructureMap getTransform(String code) {
synchronized (lock) {
return transforms.get(code);
}
}
public List listStructures() {
List m = new ArrayList();
synchronized (lock) {
m.addAll(structures.values());
}
return m;
}
public StructureDefinition getStructure(String code) {
synchronized (lock) {
return structures.get(code);
}
}
@Override
public String oid2Uri(String oid) {
synchronized (lock) {
String uri = OIDUtils.getUriForOid(oid);
if (uri != null)
return uri;
for (NamingSystem ns : systems) {
if (hasOid(ns, oid)) {
uri = getUri(ns);
if (uri != null)
return null;
}
}
}
return null;
}
private String getUri(NamingSystem ns) {
for (NamingSystemUniqueIdComponent id : ns.getUniqueId()) {
if (id.getType() == NamingSystemIdentifierType.URI)
return id.getValue();
}
return null;
}
private boolean hasOid(NamingSystem ns, String oid) {
for (NamingSystemUniqueIdComponent id : ns.getUniqueId()) {
if (id.getType() == NamingSystemIdentifierType.OID && id.getValue().equals(oid))
return true;
}
return false;
}
public void cacheVS(JsonObject json, Map t) {
synchronized (lock) {
validationCache.put(json.get("url").getAsString(), t);
}
}
public SearchParameter getSearchParameter(String code) {
synchronized (lock) {
return searchParameters.get(code);
}
}
@Override
public String getOverrideVersionNs() {
return overrideVersionNs;
}
@Override
public void setOverrideVersionNs(String value) {
overrideVersionNs = value;
}
@Override
public ILoggingService getLogger() {
return logger;
}
@Override
public StructureDefinition fetchTypeDefinition(String typeName) {
if (Utilities.isAbsoluteUrl(typeName))
return fetchResource(StructureDefinition.class, typeName);
else
return fetchResource(StructureDefinition.class, "http://hl7.org/fhir/StructureDefinition/" + typeName);
}
public boolean isPrimitiveType(String type) {
return Utilities.existsInList(type, "boolean", "integer", "integer64", "string", "decimal", "uri", "base64Binary", "instant", "date", "dateTime", "time", "code", "oid", "id", "markdown", "unsignedInt", "positiveInt", "uuid", "xhtml", "url", "canonical");
}
public boolean isDataType(String type) {
return Utilities.existsInList(type, "Address", "Age", "Annotation", "Attachment", "CodeableConcept", "Coding", "ContactPoint", "Count", "Distance", "Duration", "HumanName", "Identifier", "Money", "Period", "Quantity", "Range", "Ratio", "Reference", "SampledData", "Signature", "Timing",
"ContactDetail", "Contributor", "DataRequirement", "Expression", "ParameterDefinition", "RelatedArtifact", "TriggerDefinition", "UsageContext");
}
public boolean isTlogging() {
return tlogging;
}
public void setTlogging(boolean tlogging) {
this.tlogging = tlogging;
}
public UcumService getUcumService() {
return ucumService;
}
public void setUcumService(UcumService ucumService) {
this.ucumService = ucumService;
}
@Override
public List getStructures() {
List res = new ArrayList<>();
synchronized (lock) { // tricky, because you need to lock this as well, but it's really not in use yet
res.addAll(structures.values());
}
return res;
}
public String getLinkForUrl(String corePath, String url) {
for (CodeSystem r : codeSystems.values())
if (url.equals(r.getUrl()))
return r.getUserString("path");
for (ValueSet r : valueSets.values())
if (url.equals(r.getUrl()))
return r.getUserString("path");
for (ConceptMap r : maps.values())
if (url.equals(r.getUrl()))
return r.getUserString("path");
for (StructureMap r : transforms.values())
if (url.equals(r.getUrl()))
return r.getUserString("path");
for (StructureDefinition r : structures.values())
if (url.equals(r.getUrl()))
return r.getUserString("path");
for (ImplementationGuide r : guides.values())
if (url.equals(r.getUrl()))
return r.getUserString("path");
for (CapabilityStatement r : capstmts.values())
if (url.equals(r.getUrl()))
return r.getUserString("path");
for (SearchParameter r : searchParameters.values())
if (url.equals(r.getUrl()))
return r.getUserString("path");
for (Questionnaire r : questionnaires.values())
if (url.equals(r.getUrl()))
return r.getUserString("path");
for (OperationDefinition r : operations.values())
if (url.equals(r.getUrl()))
return r.getUserString("path");
for (PlanDefinition r : plans.values())
if (url.equals(r.getUrl()))
return r.getUserString("path");
if (url.equals("http://loinc.org"))
return corePath + "loinc.html";
if (url.equals("http://unitsofmeasure.org"))
return corePath + "ucum.html";
if (url.equals("http://snomed.info/sct"))
return corePath + "snomed.html";
return null;
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy