add -debug parameter to validator, and add additional (failing) validator tests, and update R4 code

This commit is contained in:
Grahame Grieve 2019-08-04 07:34:13 +10:00
parent 163a369984
commit 4ffa343395
43 changed files with 2065 additions and 279 deletions

1
.gitignore vendored
View File

@ -289,3 +289,4 @@ local.properties
/org.hl7.fhir.r5/src/test/resources/snapshot-generation/t9-actual.xml
/org.hl7.fhir.r5/src/test/resources/snapshot-generation/dv1-actual.xml
/org.hl7.fhir.r5/src/test/resources/snapshot-generation/t45-actual.xml
/org.hl7.fhir.r4b

17
.project Normal file
View File

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>org.hl7.fhir.core</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.m2e.core.maven2Builder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.m2e.core.maven2Nature</nature>
</natures>
</projectDescription>

View File

@ -144,7 +144,7 @@ public class ExtensionDefinitionGenerator {
List<StructureDefinition> definitions = loadSource();
List<StructureDefinition> extensions = buildExtensions(definitions);
for (StructureDefinition ext : extensions)
pu.generateSnapshot(extbase, ext, ext.getUrl(), ext.getName());
pu.generateSnapshot(extbase, ext, ext.getUrl(), "http://hl7.org/fhir/R4", ext.getName());
savePackage(extensions);
}

View File

@ -1246,7 +1246,7 @@ public class ProfileComparer {
// // then we produce value set pages for each value set
//
// // TODO Auto-generated method stub
return null;
return Utilities.path(dest, getId()+".html");
}
private void producePage(String src, String path, Map<String, String> vars) throws IOException {

View File

@ -38,6 +38,7 @@ 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;
@ -68,6 +69,7 @@ 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.TerminologyServiceOptions;
import org.hl7.fhir.r4.terminologies.ValueSetCheckerSimple;
import org.hl7.fhir.r4.terminologies.ValueSetExpander.TerminologyServiceErrorClass;
import org.hl7.fhir.r4.terminologies.ValueSetExpander.ValueSetExpansionOutcome;
@ -94,6 +96,7 @@ public abstract class BaseWorkerContext implements IWorkerContext {
protected Map<String, StructureMap> transforms = new HashMap<String, StructureMap>();
private Map<String, StructureDefinition> structures = new HashMap<String, StructureDefinition>();
private Map<String, ImplementationGuide> guides = new HashMap<String, ImplementationGuide>();
private Map<String, CapabilityStatement> capstmts = new HashMap<String, CapabilityStatement>();
private Map<String, SearchParameter> searchParameters = new HashMap<String, SearchParameter>();
private Map<String, Questionnaire> questionnaires = new HashMap<String, Questionnaire>();
private Map<String, OperationDefinition> operations = new HashMap<String, OperationDefinition>();
@ -150,6 +153,7 @@ public abstract class BaseWorkerContext implements IWorkerContext {
operations.putAll(other.operations);
systems.addAll(other.systems);
guides.putAll(other.guides);
capstmts.putAll(other.capstmts);
allowLoadingDuplicates = other.allowLoadingDuplicates;
tsServer = other.tsServer;
@ -189,6 +193,8 @@ public abstract class BaseWorkerContext implements IWorkerContext {
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)
@ -381,7 +387,9 @@ public abstract class BaseWorkerContext implements IWorkerContext {
ValueSet result = txClient.expandValueset(vs, p, params);
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.getLastId());
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;
@ -456,39 +464,39 @@ public abstract class BaseWorkerContext implements IWorkerContext {
// --- validate code -------------------------------------------------------------------------------
@Override
public ValidationResult validateCode(String system, String code, String display) {
public ValidationResult validateCode(TerminologyServiceOptions options, String system, String code, String display) {
Coding c = new Coding(system, code, display);
return validateCode(c, null);
return validateCode(options, c, null);
}
@Override
public ValidationResult validateCode(String system, String code, String display, ValueSet vs) {
public ValidationResult validateCode(TerminologyServiceOptions options, String system, String code, String display, ValueSet vs) {
Coding c = new Coding(system, code, display);
return validateCode(c, vs);
return validateCode(options, c, vs);
}
@Override
public ValidationResult validateCode(String code, ValueSet vs) {
public ValidationResult validateCode(TerminologyServiceOptions options, String code, ValueSet vs) {
Coding c = new Coding(null, code, null);
return doValidateCode(c, vs, true);
return doValidateCode(options, c, vs, true);
}
@Override
public ValidationResult validateCode(String system, String code, String display, ConceptSetComponent vsi) {
public ValidationResult validateCode(TerminologyServiceOptions 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(c, vs);
return validateCode(options, c, vs);
}
@Override
public ValidationResult validateCode(Coding code, ValueSet vs) {
return doValidateCode(code, vs, false);
public ValidationResult validateCode(TerminologyServiceOptions options, Coding code, ValueSet vs) {
return doValidateCode(options, code, vs, false);
}
public ValidationResult doValidateCode(Coding code, ValueSet vs, boolean implySystem) {
CacheToken cacheToken = txCache != null ? txCache.generateValidationToken(code, vs) : null;
public ValidationResult doValidateCode(TerminologyServiceOptions options, Coding code, ValueSet vs, boolean implySystem) {
CacheToken cacheToken = txCache != null ? txCache.generateValidationToken(options, code, vs) : null;
ValidationResult res = null;
if (txCache != null)
res = txCache.getValidation(cacheToken);
@ -497,7 +505,7 @@ public abstract class BaseWorkerContext implements IWorkerContext {
// ok, first we try to validate locally
try {
ValueSetCheckerSimple vsc = new ValueSetCheckerSimple(vs, this);
ValueSetCheckerSimple vsc = new ValueSetCheckerSimple(options, vs, this);
res = vsc.validateCode(code);
if (txCache != null)
txCache.cacheValidation(cacheToken, res, TerminologyCache.TRANSIENT);
@ -518,6 +526,8 @@ public abstract class BaseWorkerContext implements IWorkerContext {
pIn.addParameter().setName("coding").setValue(code);
if (implySystem)
pIn.addParameter().setName("implySystem").setValue(new BooleanType(true));
if (options != null)
options.updateParameters(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());
@ -528,15 +538,15 @@ public abstract class BaseWorkerContext implements IWorkerContext {
}
@Override
public ValidationResult validateCode(CodeableConcept code, ValueSet vs) {
CacheToken cacheToken = txCache.generateValidationToken(code, vs);
public ValidationResult validateCode(TerminologyServiceOptions 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(vs, this);
ValueSetCheckerSimple vsc = new ValueSetCheckerSimple(options, vs, this);
res = vsc.validateCode(code);
txCache.cacheValidation(cacheToken, res, TerminologyCache.TRANSIENT);
return res;
@ -550,6 +560,8 @@ public abstract class BaseWorkerContext implements IWorkerContext {
try {
Parameters pIn = new Parameters();
pIn.addParameter().setName("codeableConcept").setValue(code);
if (options != null)
options.updateParameters(pIn);
res = validateOnServer(vs, pIn);
} catch (Exception e) {
res = new ValidationResult(IssueSeverity.ERROR, e.getMessage() == null ? e.getClass().getName() : e.getMessage()).setTxLink(txLog.getLastId());
@ -679,6 +691,10 @@ public abstract class BaseWorkerContext implements IWorkerContext {
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) {
@ -686,6 +702,8 @@ public abstract class BaseWorkerContext implements IWorkerContext {
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))
@ -702,16 +720,33 @@ public abstract class BaseWorkerContext implements IWorkerContext {
return (T) transforms.get(uri);
if (questionnaires.containsKey(uri))
return (T) questionnaires.get(uri);
for (Map<String, Resource> 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);
@ -729,7 +764,6 @@ public abstract class BaseWorkerContext implements IWorkerContext {
b.append("\r\n");
}
}
if (res != null)
return (T) res;
}
}
@ -789,8 +823,12 @@ public abstract class BaseWorkerContext implements IWorkerContext {
public Resource fetchResourceById(String type, String uri) {
synchronized (lock) {
String[] parts = uri.split("\\/");
if (!Utilities.noString(type) && parts.length == 1)
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]))
@ -876,6 +914,7 @@ public abstract class BaseWorkerContext implements IWorkerContext {
json.addProperty("transforms-count", transforms.size());
json.addProperty("structures-count", structures.size());
json.addProperty("guides-count", guides.size());
json.addProperty("statements-count", capstmts.size());
}
}
@ -899,6 +938,8 @@ public abstract class BaseWorkerContext implements IWorkerContext {
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"))
@ -937,6 +978,7 @@ public abstract class BaseWorkerContext implements IWorkerContext {
List<MetadataResource> result = new ArrayList<MetadataResource>();
result.addAll(structures.values());
result.addAll(guides.values());
result.addAll(capstmts.values());
result.addAll(codeSystems.values());
result.addAll(valueSets.values());
result.addAll(maps.values());
@ -1082,5 +1124,15 @@ public abstract class BaseWorkerContext implements IWorkerContext {
public void setUcumService(UcumService ucumService) {
this.ucumService = ucumService;
}
@Override
public List<StructureDefinition> getStructures() {
List<StructureDefinition> 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;
}
}

View File

@ -22,9 +22,11 @@ package org.hl7.fhir.r4.context;
import java.util.List;
import java.util.Map;
import java.util.Set;
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.formats.IParser;
@ -42,6 +44,7 @@ import org.hl7.fhir.r4.model.StructureDefinition;
import org.hl7.fhir.r4.model.StructureMap;
import org.hl7.fhir.r4.model.ValueSet;
import org.hl7.fhir.r4.model.ValueSet.ConceptSetComponent;
import org.hl7.fhir.r4.terminologies.TerminologyServiceOptions;
import org.hl7.fhir.r4.terminologies.ValueSetExpander.TerminologyServiceErrorClass;
import org.hl7.fhir.r4.terminologies.ValueSetExpander.ValueSetExpansionOutcome;
import org.hl7.fhir.r4.utils.INarrativeGenerator;
@ -209,8 +212,10 @@ public interface IWorkerContext {
public List<String> getResourceNames();
public Set<String> getResourceNamesAsSet();
public List<String> getTypeNames();
public List<StructureDefinition> allStructures();
public List<StructureDefinition> allStructures(); // ensure snapshot exists...
public List<StructureDefinition> getStructures();
public List<MetadataResource> allConformanceResources();
public void generateSnapshot(StructureDefinition p) throws DefinitionException, FHIRException;
// -- Terminology services ------------------------------------------------------
@ -371,7 +376,7 @@ public interface IWorkerContext {
* @param display
* @return
*/
public ValidationResult validateCode(String system, String code, String display);
public ValidationResult validateCode(TerminologyServiceOptions options, String system, String code, String display);
/**
* Validation of a code - consult the terminology service
@ -387,10 +392,10 @@ public interface IWorkerContext {
* @param display
* @return
*/
public ValidationResult validateCode(String system, String code, String display, ValueSet vs);
public ValidationResult validateCode(String code, ValueSet vs);
public ValidationResult validateCode(Coding code, ValueSet vs);
public ValidationResult validateCode(CodeableConcept code, ValueSet vs);
public ValidationResult validateCode(TerminologyServiceOptions options, String system, String code, String display, ValueSet vs);
public ValidationResult validateCode(TerminologyServiceOptions options, String code, ValueSet vs);
public ValidationResult validateCode(TerminologyServiceOptions options, Coding code, ValueSet vs);
public ValidationResult validateCode(TerminologyServiceOptions options, CodeableConcept code, ValueSet vs);
/**
* Validation of a code - consult the terminology service
@ -406,7 +411,7 @@ public interface IWorkerContext {
* @param display
* @return
*/
public ValidationResult validateCode(String system, String code, String display, ConceptSetComponent vsi);
public ValidationResult validateCode(TerminologyServiceOptions options, String system, String code, String display, ConceptSetComponent vsi);
/**
* returns the recommended tla for the type

View File

@ -289,7 +289,7 @@ public class SimpleWorkerContext extends BaseWorkerContext implements IWorkerCon
}
}
}
this.version = pi.version();
version = pi.version();
}
public void loadFromFile(String file, IContextResourceLoader loader) throws IOException, FHIRException {
@ -436,6 +436,11 @@ public class SimpleWorkerContext extends BaseWorkerContext implements IWorkerCon
return null;
}
@Override
public BindingResolution resolveBinding(StructureDefinition profile, String url, String path) {
return null;
}
@Override
public String getLinkForProfile(StructureDefinition profile, String url) {
return null;
@ -583,7 +588,7 @@ public class SimpleWorkerContext extends BaseWorkerContext implements IWorkerCon
pu.sortDifferential(sd, p, p.getUrl(), errors);
for (String err : errors)
msgs.add(new ValidationMessage(Source.ProfileValidator, IssueType.EXCEPTION, p.getUserString("path"), "Error sorting Differential: "+err, ValidationMessage.IssueSeverity.ERROR));
pu.generateSnapshot(sd, p, p.getUrl(), p.getName());
pu.generateSnapshot(sd, p, p.getUrl(), Utilities.extractBaseUrl(sd.getUserString("path")), p.getName());
for (ValidationMessage msg : msgs) {
if ((!ignoreProfileErrors && msg.getLevel() == ValidationMessage.IssueSeverity.ERROR) || msg.getLevel() == ValidationMessage.IssueSeverity.FATAL)
throw new DefinitionException("Profile "+p.getName()+" ("+p.getUrl()+"). Error generating snapshot: "+msg.getMessage());

View File

@ -43,6 +43,7 @@ import org.hl7.fhir.r4.model.ValueSet;
import org.hl7.fhir.r4.model.ValueSet.ConceptSetComponent;
import org.hl7.fhir.r4.model.ValueSet.ConceptSetFilterComponent;
import org.hl7.fhir.r4.model.ValueSet.ValueSetExpansionContainsComponent;
import org.hl7.fhir.r4.terminologies.TerminologyServiceOptions;
import org.hl7.fhir.r4.terminologies.ValueSetExpander.TerminologyServiceErrorClass;
import org.hl7.fhir.r4.terminologies.ValueSetExpander.ValueSetExpansionOutcome;
import org.hl7.fhir.utilities.CommaSeparatedStringBuilder;
@ -111,7 +112,7 @@ public class TerminologyCache {
load();
}
public CacheToken generateValidationToken(Coding code, ValueSet vs) {
public CacheToken generateValidationToken(TerminologyServiceOptions options, Coding code, ValueSet vs) {
CacheToken ct = new CacheToken();
if (code.hasSystem())
ct.name = getNameForSystem(code.getSystem());
@ -121,7 +122,7 @@ public class TerminologyCache {
json.setOutputStyle(OutputStyle.PRETTY);
ValueSet vsc = getVSEssense(vs);
try {
ct.request = "{\"code\" : "+json.composeString(code, "code")+", \"valueSet\" :"+(vsc == null ? "null" : json.composeString(vsc))+"}";
ct.request = "{\"code\" : "+json.composeString(code, "code")+", \"valueSet\" :"+(vsc == null ? "null" : json.composeString(vsc))+(options == null ? "" : ", "+options.toJson())+"}";
} catch (IOException e) {
throw new Error(e);
}
@ -129,7 +130,7 @@ public class TerminologyCache {
return ct;
}
public CacheToken generateValidationToken(CodeableConcept code, ValueSet vs) {
public CacheToken generateValidationToken(TerminologyServiceOptions options, CodeableConcept code, ValueSet vs) {
CacheToken ct = new CacheToken();
for (Coding c : code.getCoding()) {
if (c.hasSystem())
@ -139,7 +140,7 @@ public class TerminologyCache {
json.setOutputStyle(OutputStyle.PRETTY);
ValueSet vsc = getVSEssense(vs);
try {
ct.request = "{\"code\" : "+json.composeString(code, "codeableConcept")+", \"valueSet\" :"+json.composeString(vsc)+"}";
ct.request = "{\"code\" : "+json.composeString(code, "codeableConcept")+", \"valueSet\" :"+json.composeString(vsc)+(options == null ? "" : ", "+options.toJson())+"}";
} catch (IOException e) {
throw new Error(e);
}
@ -327,12 +328,14 @@ public class TerminologyCache {
NamedCache nc = new NamedCache();
nc.name = title;
caches.put(title, nc);
System.out.print(" - load "+title+".cache");
String src = TextFile.fileToString(Utilities.path(folder, fn));
if (src.startsWith("?"))
src = src.substring(1);
int i = src.indexOf(ENTRY_MARKER);
while (i > -1) {
String s = src.substring(0, i);
System.out.print(".");
src = src.substring(i+ENTRY_MARKER.length()+1);
i = src.indexOf(ENTRY_MARKER);
if (!Utilities.noString(s)) {
@ -360,6 +363,7 @@ public class TerminologyCache {
nc.list.add(ce);
}
}
System.out.println("done");
} catch (Exception e) {
throw new FHIRException("Error loading "+fn+": "+e.getMessage(), e);
}

View File

@ -578,7 +578,10 @@ public class Element extends Base {
@Override
public boolean isEmpty() {
if (value != null && !"".equals(value)) {
// GG: this used to also test !"".equals(value).
// the condition where "" is empty and there are no children is an error, and so this really only manifested as an issue in corner cases technical testing of the validator / FHIRPath.
// it should not cause any problems in real life.
if (value != null) {
return false;
}
for (Element next : getChildren()) {

View File

@ -28,6 +28,7 @@ import java.io.OutputStreamWriter;
import java.math.BigDecimal;
import java.util.HashMap;
import java.util.HashSet;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
@ -86,7 +87,7 @@ public class JsonParser extends ParserBase {
@Override
public Element parse(InputStream stream) throws IOException, FHIRException {
// if we're parsing at this point, then we're going to use the custom parser
map = new HashMap<JsonElement, LocationData>();
map = new IdentityHashMap<JsonElement, LocationData>();
String source = TextFile.streamToString(stream);
if (policy == ValidationPolicy.EVERYTHING) {
JsonObject obj = null;

View File

@ -99,11 +99,11 @@ public abstract class ParserBase {
return null;
}
for (StructureDefinition sd : context.allStructures()) {
if (name.equals(sd.getType()) && sd.getDerivation() == TypeDerivationRule.SPECIALIZATION && !sd.getUrl().startsWith("http://hl7.org/fhir/StructureDefinition/de-")) {
if((ns == null || ns.equals(FormatUtilities.FHIR_NS)) && !ToolingExtensions.hasExtension(sd, "http://hl7.org/fhir/StructureDefinition/elementdefinition-namespace"))
if (sd.getDerivation() == TypeDerivationRule.SPECIALIZATION && !sd.getUrl().startsWith("http://hl7.org/fhir/StructureDefinition/de-")) {
if(name.equals(sd.getType()) && (ns == null || ns.equals(FormatUtilities.FHIR_NS)) && !ToolingExtensions.hasExtension(sd, "http://hl7.org/fhir/StructureDefinition/elementdefinition-namespace"))
return sd;
String sns = ToolingExtensions.readStringExtension(sd, "http://hl7.org/fhir/StructureDefinition/elementdefinition-namespace");
if (ns != null && ns.equals(sns))
if (name.equals(sd.getType()) && ns != null && ns.equals(sns))
return sd;
}
}
@ -117,13 +117,15 @@ public abstract class ParserBase {
return null;
}
// first pass: only look at base definitions
for (StructureDefinition sd : context.allStructures()) {
for (StructureDefinition sd : context.getStructures()) {
if (sd.getUrl().equals("http://hl7.org/fhir/StructureDefinition/"+name)) {
context.generateSnapshot(sd);
return sd;
}
}
for (StructureDefinition sd : context.allStructures()) {
for (StructureDefinition sd : context.getStructures()) {
if (name.equals(sd.getType()) && sd.getDerivation() == TypeDerivationRule.SPECIALIZATION) {
context.generateSnapshot(sd);
return sd;
}
}

View File

@ -501,7 +501,7 @@ public class TurtleParser extends ParserBase {
String system = coding.getChildValue("system");
String code = coding.getChildValue("code");
if (system == null)
if (system == null || code == null)
return;
if ("http://snomed.info/sct".equals(system)) {
t.prefix("sct", "http://snomed.info/id/");

View File

@ -58836,7 +58836,7 @@ public class JsonParser extends JsonParserBase {
else if (type instanceof ParameterDefinition)
composeParameterDefinitionInner((ParameterDefinition) type);
else
throw new Error("Unhandled type");
throw new Error("Unhandled type: "+type.fhirType());
}
}

View File

@ -488,6 +488,18 @@ Modifier extensions SHALL NOT change the meaning of any elements on Resource or
}
public Resource getContained(String ref) {
if (ref == null)
return null;
if (ref.startsWith("#"))
ref = ref.substring(1);
for (Resource r : getContained()) {
if (r.getId().equals(ref))
return r;
}
return null;
}
// end addition

View File

@ -7527,6 +7527,10 @@ When pattern[x] is used to constrain a complex object, it means that each proper
setIsModifier(modifier);
setIsSummary(inSummary);
}
}
public String present() {
return hasId() ? getId() : getPath();
}

View File

@ -10127,6 +10127,9 @@ The primary difference between a medication statement and a medication administr
public String toCode(int len) {
return toCode().substring(0, len);
}
public static boolean isR4Plus(String version) {
return false;
}
}
public static class FHIRVersionEnumFactory implements EnumFactory<FHIRVersion> {

View File

@ -61,7 +61,7 @@ public class ExpressionNode {
Empty, Not, Exists, SubsetOf, SupersetOf, IsDistinct, Distinct, Count, Where, Select, All, Repeat, Aggregate, Item /*implicit from name[]*/, As, Is, Single,
First, Last, Tail, Skip, Take, Union, Combine, Intersect, Exclude, Iif, Upper, Lower, ToChars, Substring, StartsWith, EndsWith, Matches, ReplaceMatches, Contains, Replace, Length,
Children, Descendants, MemberOf, Trace, Today, Now, Resolve, Extension, AllFalse, AnyFalse, AllTrue, AnyTrue,
Children, Descendants, MemberOf, Trace, Check, Today, Now, Resolve, Extension, AllFalse, AnyFalse, AllTrue, AnyTrue,
HasValue, AliasAs, Alias, HtmlChecks, OfType, Type,
ConvertsToBoolean, ConvertsToInteger, ConvertsToString, ConvertsToDecimal, ConvertsToQuantity, ConvertsToDateTime, ConvertsToTime, ToBoolean, ToInteger, ToString, ToDecimal, ToQuantity, ToDateTime, ToTime, ConformsTo;
@ -108,6 +108,7 @@ public class ExpressionNode {
if (name.equals("descendants")) return Function.Descendants;
if (name.equals("memberOf")) return Function.MemberOf;
if (name.equals("trace")) return Function.Trace;
if (name.equals("check")) return Function.Check;
if (name.equals("today")) return Function.Today;
if (name.equals("now")) return Function.Now;
if (name.equals("resolve")) return Function.Resolve;
@ -183,6 +184,7 @@ public class ExpressionNode {
case Descendants : return "descendants";
case MemberOf : return "memberOf";
case Trace : return "trace";
case Check : return "check";
case Today : return "today";
case Now : return "now";
case Resolve : return "resolve";
@ -374,8 +376,10 @@ public class ExpressionNode {
b.append(" '");
b.append(Utilities.escapeJson(q.getUnit()));
b.append("'");
} else
} else if (constant.primitiveValue() != null)
b.append(Utilities.escapeJson(constant.primitiveValue()));
else
b.append(Utilities.escapeJson(constant.toString()));
break;
case Group:
b.append("(");

View File

@ -0,0 +1,57 @@
package org.hl7.fhir.r4.terminologies;
/*-
* #%L
* org.hl7.fhir.r4
* %%
* Copyright (C) 2014 - 2019 Health Level 7
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
import org.fhir.ucum.Utilities;
import org.hl7.fhir.r4.model.Parameters;
public class TerminologyServiceOptions {
private String language;
public TerminologyServiceOptions() {
super();
}
public TerminologyServiceOptions(String language) {
super();
this.language = language;
}
public String getLanguage() {
return language;
}
public void setLanguage(String language) {
this.language = language;
}
public String toJson() {
return "\"lang\":\""+language+"\"";
}
public void updateParameters(Parameters pIn) {
if (!Utilities.noString(language))
pIn.addParameter("displayLanguage", language);
}
}

View File

@ -22,11 +22,14 @@ package org.hl7.fhir.r4.terminologies;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.r4.context.IWorkerContext;
import org.hl7.fhir.r4.context.IWorkerContext.ValidationResult;
import org.hl7.fhir.r4.model.CanonicalType;
import org.hl7.fhir.r4.model.CodeSystem;
import org.hl7.fhir.r4.model.CodeSystem.CodeSystemContentMode;
import org.hl7.fhir.r4.model.CodeSystem.ConceptDefinitionComponent;
@ -36,7 +39,9 @@ import org.hl7.fhir.r4.model.Coding;
import org.hl7.fhir.r4.model.UriType;
import org.hl7.fhir.r4.model.ValueSet;
import org.hl7.fhir.r4.model.ValueSet.ConceptReferenceComponent;
import org.hl7.fhir.r4.model.ValueSet.ConceptReferenceDesignationComponent;
import org.hl7.fhir.r4.model.ValueSet.ConceptSetComponent;
import org.hl7.fhir.r4.model.ValueSet.ConceptSetFilterComponent;
import org.hl7.fhir.r4.model.ValueSet.ValueSetExpansionContainsComponent;
import org.hl7.fhir.utilities.CommaSeparatedStringBuilder;
import org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity;
@ -45,10 +50,13 @@ public class ValueSetCheckerSimple implements ValueSetChecker {
private ValueSet valueset;
private IWorkerContext context;
private Map<String, ValueSetCheckerSimple> inner = new HashMap<>();
private TerminologyServiceOptions options;
public ValueSetCheckerSimple(ValueSet source, IWorkerContext context) {
public ValueSetCheckerSimple(TerminologyServiceOptions options, ValueSet source, IWorkerContext context) {
this.valueset = source;
this.context = context;
this.options = options;
}
public ValidationResult validateCode(CodeableConcept code) throws FHIRException {
@ -145,7 +153,7 @@ public class ValueSetCheckerSimple implements ValueSetChecker {
private ValidationResult validateCode(Coding code, CodeSystem cs) {
ConceptDefinitionComponent cc = findCodeInConcept(cs.getConcept(), code.getCode());
if (cc == null)
return new ValidationResult(IssueSeverity.ERROR, "Unknown Code "+code+" in "+cs.getUrl());
return new ValidationResult(IssueSeverity.ERROR, "Unknown Code "+gen(code)+" in "+cs.getUrl());
if (code.getDisplay() == null)
return new ValidationResult(cc);
CommaSeparatedStringBuilder b = new CommaSeparatedStringBuilder();
@ -159,7 +167,56 @@ public class ValueSetCheckerSimple implements ValueSetChecker {
if (code.getDisplay().equalsIgnoreCase(ds.getValue()))
return new ValidationResult(cc);
}
return new ValidationResult(IssueSeverity.WARNING, "Display Name for "+code.getSystem()+"#"+code.getCode()+" must be one of '"+b.toString()+"'", cc);
// also check to see if the value set has another display
ConceptReferenceComponent vs = findValueSetRef(code.getSystem(), code.getCode());
if (vs != null && (vs.hasDisplay() ||vs.hasDesignation())) {
if (vs.hasDisplay()) {
b.append(vs.getDisplay());
if (code.getDisplay().equalsIgnoreCase(vs.getDisplay()))
return new ValidationResult(cc);
}
for (ConceptReferenceDesignationComponent ds : vs.getDesignation()) {
b.append(ds.getValue());
if (code.getDisplay().equalsIgnoreCase(ds.getValue()))
return new ValidationResult(cc);
}
}
return new ValidationResult(IssueSeverity.WARNING, "Display Name for "+code.getSystem()+"#"+code.getCode()+" should be one of '"+b.toString()+"'", cc);
}
private ConceptReferenceComponent findValueSetRef(String system, String code) {
if (valueset == null)
return null;
// if it has an expansion
for (ValueSetExpansionContainsComponent exp : valueset.getExpansion().getContains()) {
if (system.equals(exp.getSystem()) && code.equals(exp.getCode())) {
ConceptReferenceComponent cc = new ConceptReferenceComponent();
cc.setDisplay(exp.getDisplay());
cc.setDesignation(exp.getDesignation());
return cc;
}
}
for (ConceptSetComponent inc : valueset.getCompose().getInclude()) {
if (system.equals(inc.getSystem())) {
for (ConceptReferenceComponent cc : inc.getConcept()) {
if (cc.getCode().equals(code))
return cc;
}
}
for (CanonicalType url : inc.getValueSet()) {
ConceptReferenceComponent cc = getVs(url.asStringValue()).findValueSetRef(system, code);
if (cc != null)
return cc;
}
}
return null;
}
private String gen(Coding code) {
if (code.hasSystem())
return code.getSystem()+"#"+code.getCode();
else
return null;
}
private String getValueSetSystem() throws FHIRException {
@ -288,15 +345,62 @@ public class ValueSetCheckerSimple implements ValueSetChecker {
if (!system.equals(vsi.getSystem()))
return false;
if (vsi.hasFilter())
throw new FHIRException("Filters - not done yet");
if (vsi.hasFilter()) {
boolean ok = true;
for (ConceptSetFilterComponent f : vsi.getFilter())
if (!codeInFilter(system, f, code)) {
ok = false;
break;
}
if (ok)
return true;
}
CodeSystem def = context.fetchCodeSystem(system);
if (def.getContent() != CodeSystemContentMode.COMPLETE)
throw new FHIRException("Unable to resolve system "+vsi.getSystem()+" - system is not complete");
List<ConceptDefinitionComponent> list = def.getConcept();
return validateCodeInConceptList(code, def, list);
boolean ok = validateCodeInConceptList(code, def, list);
if (ok && vsi.hasConcept()) {
for (ConceptReferenceComponent cc : vsi.getConcept())
if (cc.getCode().equals(code))
return true;
return false;
} else
return ok;
}
private boolean codeInFilter(String system, ConceptSetFilterComponent f, String code) throws FHIRException {
CodeSystem cs = context.fetchCodeSystem(system);
if (cs == null)
throw new FHIRException("Unable to evaluate filters on unknown code system '"+system+"'");
if ("concept".equals(f.getProperty()))
return codeInConceptFilter(cs, f, code);
else {
System.out.println("todo: handle filters with property = "+f.getProperty());
throw new FHIRException("Unable to handle system "+cs.getUrl()+" filter with property = "+f.getProperty());
}
}
private boolean codeInConceptFilter(CodeSystem cs, ConceptSetFilterComponent f, String code) throws FHIRException {
switch (f.getOp()) {
case ISA: return codeInConceptIsAFilter(cs, f, code);
case ISNOTA: return !codeInConceptIsAFilter(cs, f, code);
default:
System.out.println("todo: handle concept filters with op = "+f.getOp());
throw new FHIRException("Unable to handle system "+cs.getUrl()+" concept filter with op = "+f.getOp());
}
}
private boolean codeInConceptIsAFilter(CodeSystem cs, ConceptSetFilterComponent f, String code) {
if (code.equals(f.getProperty()))
return true;
ConceptDefinitionComponent cc = findCodeInConcept(cs.getConcept(), f.getValue());
if (cc == null)
return false;
cc = findCodeInConcept(cc.getConcept(), code);
return cc != null;
}
public boolean validateCodeInConceptList(String code, CodeSystem def, List<ConceptDefinitionComponent> list) {
@ -318,10 +422,18 @@ public class ValueSetCheckerSimple implements ValueSetChecker {
return false;
}
private ValueSetCheckerSimple getVs(String url) {
if (inner.containsKey(url)) {
return inner.get(url);
}
ValueSet vs = context.fetchResource(ValueSet.class, url);
ValueSetCheckerSimple vsc = new ValueSetCheckerSimple(options, vs, context);
inner.put(url, vsc);
return vsc;
}
private boolean inImport(String uri, String system, String code) throws FHIRException {
ValueSet vs = context.fetchResource(ValueSet.class, uri);
ValueSetCheckerSimple vsc = new ValueSetCheckerSimple(vs, context);
return vsc.codeInValueSet(system, code);
return getVs(uri).codeInValueSet(system, code);
}
}

View File

@ -0,0 +1,40 @@
package org.hl7.fhir.r4.utils;
/*-
* #%L
* org.hl7.fhir.r4
* %%
* Copyright (C) 2014 - 2019 Health Level 7
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
import org.hl7.fhir.r4.model.ElementDefinition;
import org.hl7.fhir.r4.model.ElementDefinition.TypeRefComponent;
public class ElementDefinitionUtilities {
public static boolean hasType(ElementDefinition ed, String name) {
if (name == null)
return false;
for (TypeRefComponent tr : ed.getType()) {
if (name.equals(tr.getCode()))
return true;
}
return false;
}
}

View File

@ -17,6 +17,7 @@ import org.hl7.fhir.r4.model.ExpressionNode.*;
import org.hl7.fhir.r4.model.StructureDefinition.StructureDefinitionKind;
import org.hl7.fhir.r4.model.StructureDefinition.TypeDerivationRule;
import org.hl7.fhir.r4.model.TypeDetails.ProfiledType;
import org.hl7.fhir.r4.terminologies.TerminologyServiceOptions;
import org.hl7.fhir.r4.utils.FHIRLexer.FHIRLexerException;
import org.hl7.fhir.r4.utils.FHIRPathEngine.IEvaluationContext.FunctionDetails;
import org.hl7.fhir.utilities.Utilities;
@ -82,6 +83,11 @@ public class FHIRPathEngine {
public String getValue() {
return value;
}
@Override
public String primitiveValue() {
return value;
}
}
private class ClassTypeInfo extends Base {
@ -144,6 +150,7 @@ public class FHIRPathEngine {
private Set<String> primitiveTypes = new HashSet<String>();
private Map<String, StructureDefinition> allTypes = new HashMap<String, StructureDefinition>();
private boolean legacyMode; // some R2 and R3 constraints assume that != is valid for emptty sets, so when running for R2/R3, this is set ot true
private TerminologyServiceOptions terminologyServiceOptions = new TerminologyServiceOptions();
// if the fhir path expressions are allowed to use constants beyond those defined in the specification
// the application can implement them by providing a constant resolver
@ -219,7 +226,8 @@ public class FHIRPathEngine {
/**
* Implementation of resolve() function. Passed a string, return matching resource, if one is known - else null
* @param url
* @appContext - passed in by the host to the FHIRPathEngine
* @param url the reference (Reference.reference or the value of the canonical
* @return
* @throws FHIRException
*/
@ -1072,6 +1080,7 @@ public class FHIRPathEngine {
case Descendants: return checkParamCount(lexer, location, exp, 0);
case MemberOf: return checkParamCount(lexer, location, exp, 1);
case Trace: return checkParamCount(lexer, location, exp, 1, 2);
case Check: return checkParamCount(lexer, location, exp, 2);
case Today: return checkParamCount(lexer, location, exp, 0);
case Now: return checkParamCount(lexer, location, exp, 0);
case Resolve: return checkParamCount(lexer, location, exp, 0);
@ -1862,13 +1871,13 @@ public class FHIRPathEngine {
if (vs != null) {
for (Base l : left) {
if (l.fhirType().equals("code")) {
if (worker.validateCode(l.castToCoding(l), vs).isOk())
if (worker.validateCode(terminologyServiceOptions , l.castToCoding(l), vs).isOk())
ans = true;
} else if (l.fhirType().equals("Coding")) {
if (worker.validateCode(l.castToCoding(l), vs).isOk())
if (worker.validateCode(terminologyServiceOptions, l.castToCoding(l), vs).isOk())
ans = true;
} else if (l.fhirType().equals("CodeableConcept")) {
if (worker.validateCode(l.castToCodeableConcept(l), vs).isOk())
if (worker.validateCode(terminologyServiceOptions, l.castToCodeableConcept(l), vs).isOk())
ans = true;
}
}
@ -2511,6 +2520,10 @@ public class FHIRPathEngine {
checkParamTypes(exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String));
return focus;
}
case Check : {
checkParamTypes(exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String));
return focus;
}
case Today :
return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_DateTime);
case Now :
@ -2703,6 +2716,7 @@ public class FHIRPathEngine {
case Descendants : return funcDescendants(context, focus, exp);
case MemberOf : return funcMemberOf(context, focus, exp);
case Trace : return funcTrace(context, focus, exp);
case Check : return funcCheck(context, focus, exp);
case Today : return funcToday(context, focus, exp);
case Now : return funcNow(context, focus, exp);
case Resolve : return funcResolve(context, focus, exp);
@ -3446,6 +3460,16 @@ public class FHIRPathEngine {
return focus;
}
private List<Base> funcCheck(ExecutionContext context, List<Base> focus, ExpressionNode exp) throws FHIRException {
List<Base> n1 = execute(context, focus, exp.getParameters().get(0), true);
if (!convertToBoolean(n1)) {
List<Base> n2 = execute(context, focus, exp.getParameters().get(1), true);
String name = n2.get(0).primitiveValue();
throw new FHIRException("check failed: "+name);
}
return focus;
}
private List<Base> funcDistinct(ExecutionContext context, List<Base> focus, ExpressionNode exp) {
if (focus.size() <= 1)
return focus;
@ -3739,7 +3763,7 @@ public class FHIRPathEngine {
else
return null;
} else {
if (Utilities.isDecimal(s, false))
if (Utilities.isDecimal(s, true))
return new Quantity().setValue(new BigDecimal(s)).setSystem("http://unitsofmeasure.org").setCode("1");
else
return null;
@ -4092,6 +4116,16 @@ public class FHIRPathEngine {
ElementDefinition focus = null;
if (expr.getKind() == Kind.Name) {
if (element.hasSlicing()) {
ElementDefinition slice = pickMandatorySlice(sd, element);
if (slice == null)
throw new DefinitionException("Error in discriminator at "+element.getId()+": found a sliced element while resolving the fixed value for one of the slices");
element = slice;
}
if (expr.getName().equals("$this")) {
focus = element;
} else {
List<ElementDefinition> childDefinitions;
childDefinitions = ProfileUtilities.getChildMap(sd, element);
// if that's empty, get the children of the type
@ -4107,6 +4141,7 @@ public class FHIRPathEngine {
break;
}
}
}
} else if (expr.getKind() == Kind.Function) {
if ("resolve".equals(expr.getName())) {
if (!element.hasType())
@ -4127,10 +4162,12 @@ public class FHIRPathEngine {
List<ElementDefinition> childDefinitions = ProfileUtilities.getChildMap(sd, element);
for (ElementDefinition t : childDefinitions) {
if (t.getPath().endsWith(".extension") && t.hasSliceName()) {
sd = worker.fetchResource(StructureDefinition.class, t.getType().get(0).getProfile().get(0).getValue());
while (sd!=null && !sd.getBaseDefinition().equals("http://hl7.org/fhir/StructureDefinition/Extension"))
sd = worker.fetchResource(StructureDefinition.class, sd.getBaseDefinition());
if (sd.getUrl().equals(targetUrl)) {
StructureDefinition exsd = worker.fetchResource(StructureDefinition.class, t.getType().get(0).getProfile().get(0).getValue());
while (exsd!=null && !exsd.getBaseDefinition().equals("http://hl7.org/fhir/StructureDefinition/Extension"))
exsd = worker.fetchResource(StructureDefinition.class, exsd.getBaseDefinition());
if (exsd.getUrl().equals(targetUrl)) {
if (ProfileUtilities.getChildMap(sd, t).isEmpty())
sd = exsd;
focus = t;
break;
}
@ -4145,12 +4182,23 @@ public class FHIRPathEngine {
}
if (focus == null)
throw new DefinitionException("Unable to resolve discriminator");
throw new DefinitionException("Unable to resolve discriminator in definitions: "+expr.toString());
else if (expr.getInner() == null)
return focus;
else
else {
return evaluateDefinition(expr.getInner(), sd, focus);
}
}
private ElementDefinition pickMandatorySlice(StructureDefinition sd, ElementDefinition element) throws DefinitionException {
List<ElementDefinition> list = ProfileUtilities.getSliceList(sd, element);
for (ElementDefinition ed : list) {
if (ed.getMin() > 0)
return ed;
}
return null;
}
private StructureDefinition fetchStructureByType(ElementDefinition ed) throws DefinitionException {
if (ed.getType().size() == 0)
@ -4159,8 +4207,6 @@ public class FHIRPathEngine {
throw new DefinitionException("Error in discriminator at "+ed.getId()+": no children, multiple types");
if (ed.getType().get(0).getProfile().size() > 1)
throw new DefinitionException("Error in discriminator at "+ed.getId()+": no children, multiple type profiles");
if (ed.hasSlicing())
throw new DefinitionException("Error in discriminator at "+ed.getId()+": slicing found");
if (ed.getType().get(0).hasProfile())
return worker.fetchResource(StructureDefinition.class, ed.getType().get(0).getProfile().get(0).getValue());
else
@ -4176,7 +4222,8 @@ public class FHIRPathEngine {
return true;
else if (t.getType().size() == 1 && t.getType().get(0).getCode() != null && t.getPath() != null && t.getPath().toUpperCase().endsWith(t.getType().get(0).getCode().toUpperCase()))
return tail.startsWith(d);
else if (t.getPath().endsWith("[x]") && tail.startsWith(d))
return true;
return false;
}
@ -4241,7 +4288,7 @@ public class FHIRPathEngine {
return Equality.False;
else if (Utilities.isInteger(item.primitiveValue()))
return asBoolFromInt(item.primitiveValue());
else if (Utilities.isDecimal(item.primitiveValue(), false))
else if (Utilities.isDecimal(item.primitiveValue(), true))
return asBoolFromDec(item.primitiveValue());
else
return Equality.Null;
@ -4252,5 +4299,10 @@ public class FHIRPathEngine {
private Equality boolToTriState(boolean b) {
return b ? Equality.True : Equality.False;
}
public TerminologyServiceOptions getTerminologyServiceOptions() {
return terminologyServiceOptions;
}
}

View File

@ -96,6 +96,7 @@ public class GraphQLSchemaGenerator {
public void generateResource(OutputStream stream, StructureDefinition sd, List<SearchParameter> parameters, EnumSet<FHIROperationType> operations) throws IOException, FHIRException {
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(stream));
writer.write("# FHIR GraphQL Schema. Version "+Constants.VERSION+"\r\n\r\n");
writer.write("# import the types from 'types.graphql'\r\n\r\n");
generateType(writer, sd);
if (operations.contains(FHIROperationType.READ))
generateIdAccess(writer, sd.getName());
@ -116,7 +117,7 @@ public class GraphQLSchemaGenerator {
private void generateCreate(BufferedWriter writer, String name) throws IOException {
writer.write("type "+name+"CreateType {\r\n");
writer.write(" "+name+"Create(");
param(writer, "resource", name, false, false);
param(writer, "resource", name+"Input", false, false);
writer.write(") : "+name+"Creation\r\n");
writer.write("}\r\n");
writer.write("\r\n");
@ -132,7 +133,8 @@ public class GraphQLSchemaGenerator {
writer.write("type "+name+"UpdateType {\r\n");
writer.write(" "+name+"Update(");
param(writer, "id", "ID", false, false);
param(writer, "resource", name, false, false);
writer.write(", ");
param(writer, "resource", name+"Input", false, false);
writer.write(") : "+name+"Update\r\n");
writer.write("}\r\n");
writer.write("\r\n");
@ -224,7 +226,7 @@ public class GraphQLSchemaGenerator {
private void generateElementBase(BufferedWriter writer) throws IOException {
writer.write("type ElementBase {\r\n");
writer.write(" id : ID\r\n");
writer.write(" extension : [Extension]{\r\n");
writer.write(" extension: [Extension]\r\n");
writer.write("}\r\n");
writer.write("\r\n");
@ -241,7 +243,7 @@ public class GraphQLSchemaGenerator {
b.append(sd.getName());
b.append(" {\r\n");
ElementDefinition ed = sd.getSnapshot().getElementFirstRep();
generateProperties(list, b, sd.getName(), sd, ed, "type");
generateProperties(list, b, sd.getName(), sd, ed, "type", "");
b.append("}");
b.append("\r\n");
b.append("\r\n");
@ -252,9 +254,9 @@ public class GraphQLSchemaGenerator {
list.add(b);
b.append("input ");
b.append(sd.getName());
b.append(" {\r\n");
b.append("Input {\r\n");
ed = sd.getSnapshot().getElementFirstRep();
generateProperties(list, b, sd.getName(), sd, ed, "input");
generateProperties(list, b, sd.getName(), sd, ed, "input", "Input");
b.append("}");
b.append("\r\n");
b.append("\r\n");
@ -262,22 +264,22 @@ public class GraphQLSchemaGenerator {
writer.write(bs.toString());
}
private void generateProperties(List<StringBuilder> list, StringBuilder b, String typeName, StructureDefinition sd, ElementDefinition ed, String mode) throws IOException {
private void generateProperties(List<StringBuilder> list, StringBuilder b, String typeName, StructureDefinition sd, ElementDefinition ed, String mode, String suffix) throws IOException {
List<ElementDefinition> children = ProfileUtilities.getChildList(sd, ed);
for (ElementDefinition child : children) {
if (child.hasContentReference()) {
ElementDefinition ref = resolveContentReference(sd, child.getContentReference());
generateProperty(list, b, typeName, sd, child, ref.getType().get(0), false, ref, mode);
generateProperty(list, b, typeName, sd, child, ref.getType().get(0), false, ref, mode, suffix);
} else if (child.getType().size() == 1) {
generateProperty(list, b, typeName, sd, child, child.getType().get(0), false, null, mode);
generateProperty(list, b, typeName, sd, child, child.getType().get(0), false, null, mode, suffix);
} else {
boolean ref = false;
for (TypeRefComponent t : child.getType()) {
if (!t.hasTarget())
generateProperty(list, b, typeName, sd, child, t, true, null, mode);
generateProperty(list, b, typeName, sd, child, t, true, null, mode, suffix);
else if (!ref) {
ref = true;
generateProperty(list, b, typeName, sd, child, t, true, null, mode);
generateProperty(list, b, typeName, sd, child, t, true, null, mode, suffix);
}
}
}
@ -293,7 +295,7 @@ public class GraphQLSchemaGenerator {
throw new Error("Unable to find "+id);
}
private void generateProperty(List<StringBuilder> list, StringBuilder b, String typeName, StructureDefinition sd, ElementDefinition child, TypeRefComponent typeDetails, boolean suffix, ElementDefinition cr, String mode) throws IOException {
private void generateProperty(List<StringBuilder> list, StringBuilder b, String typeName, StructureDefinition sd, ElementDefinition child, TypeRefComponent typeDetails, boolean suffix, ElementDefinition cr, String mode, String suffixS) throws IOException {
if (isPrimitive(typeDetails)) {
String n = getGqlname(typeDetails.getCode());
b.append(" ");
@ -323,11 +325,11 @@ public class GraphQLSchemaGenerator {
b.append("[");
String type = typeDetails.getCode();
if (cr != null)
b.append(generateInnerType(list, sd, typeName, cr, mode));
b.append(generateInnerType(list, sd, typeName, cr, mode, suffixS));
else if (Utilities.existsInList(type, "Element", "BackboneElement"))
b.append(generateInnerType(list, sd, typeName, child, mode));
b.append(generateInnerType(list, sd, typeName, child, mode, suffixS));
else
b.append(type);
b.append(type+suffixS);
if (!child.getMax().equals("1"))
b.append("]");
if (child.getMin() != 0 && !suffix)
@ -336,7 +338,7 @@ public class GraphQLSchemaGenerator {
}
}
private String generateInnerType(List<StringBuilder> list, StructureDefinition sd, String name, ElementDefinition child, String mode) throws IOException {
private String generateInnerType(List<StringBuilder> list, StructureDefinition sd, String name, ElementDefinition child, String mode, String suffix) throws IOException {
if (child.hasUserData(INNER_TYPE_NAME+"."+mode))
return child.getUserString(INNER_TYPE_NAME+"."+mode);
@ -347,12 +349,13 @@ public class GraphQLSchemaGenerator {
b.append(mode);
b.append(" ");
b.append(typeName);
b.append(suffix);
b.append(" {\r\n");
generateProperties(list, b, typeName, sd, child, mode);
generateProperties(list, b, typeName, sd, child, mode, suffix);
b.append("}");
b.append("\r\n");
b.append("\r\n");
return typeName;
return typeName+suffix;
}
private String tail(String path, boolean suffix) {
@ -380,7 +383,7 @@ public class GraphQLSchemaGenerator {
private void generatePrimitive(BufferedWriter writer, StructureDefinition sd) throws IOException, FHIRException {
String gqlName = getGqlname(sd.getName());
if (gqlName.equals(sd.getName())) {
writer.write("Scalar ");
writer.write("scalar ");
writer.write(sd.getName());
writer.write(" # JSON Format: ");
writer.write(getJsonFormat(sd));

View File

@ -133,6 +133,9 @@ public interface IResourceValidator {
public boolean isErrorForUnknownProfiles();
public void setErrorForUnknownProfiles(boolean errorForUnknownProfiles);
public String getValidationLanguage();
public void setValidationLanguage(String value);
/**
* Validate suite
*

View File

@ -78,7 +78,7 @@ public class LiquidEngine implements IEvaluationContext {
this.includeResolver = includeResolver;
}
public LiquidDocument parse(String source, String sourceName) throws Exception {
public LiquidDocument parse(String source, String sourceName) throws FHIRException {
return new LiquidParser(source).parse(sourceName);
}

View File

@ -23,8 +23,10 @@ package org.hl7.fhir.r4.utils;
import java.io.BufferedOutputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.HashSet;
@ -45,6 +47,7 @@ import org.hl7.fhir.r4.model.ImplementationGuide;
import org.hl7.fhir.r4.model.ImplementationGuide.ImplementationGuideDependsOnComponent;
import org.hl7.fhir.utilities.CommaSeparatedStringBuilder;
import org.hl7.fhir.utilities.TextFile;
import org.hl7.fhir.utilities.Utilities;
import org.hl7.fhir.utilities.cache.PackageGenerator.PackageType;
import org.hl7.fhir.utilities.cache.ToolsVersion;
@ -56,36 +59,60 @@ import com.google.gson.JsonObject;
public class NPMPackageGenerator {
public enum Category {
RESOURCE, OPENAPI, SCHEMATRON, RDF, OTHER, TOOL;
RESOURCE, EXAMPLE, OPENAPI, SCHEMATRON, RDF, OTHER, TOOL, TEMPLATE, JEKYLL;
private String getDirectory() {
switch (this) {
case RESOURCE: return "/package/";
case EXAMPLE: return "/example/";
case OPENAPI: return "/openapi/";
case SCHEMATRON: return "/xml/";
case RDF: return "/rdf/";
case OTHER: return "/other/";
case TEMPLATE: return "/other/";
case JEKYLL: return "/jekyll/";
case TOOL: return "/bin/";
}
return "/";
}
}
private String destFile;
private Set<String> created = new HashSet<String>();
private TarArchiveOutputStream tar;
private ByteArrayOutputStream OutputStream;
private BufferedOutputStream bufferedOutputStream;
private GzipCompressorOutputStream gzipOutputStream;
private JsonObject packageJ;
public NPMPackageGenerator(String destFile, String canonical, String url, PackageType kind, ImplementationGuide ig, String genDate) throws FHIRException, IOException {
super();
System.out.println("create package file at "+destFile);
this.destFile = destFile;
start();
buildPackageJson(canonical, kind, url, genDate, ig);
List<String> fhirVersion = new ArrayList<>();
for (Enumeration<FHIRVersion> v : ig.getFhirVersion())
fhirVersion.add(v.asStringValue());
buildPackageJson(canonical, kind, url, genDate, ig, fhirVersion);
}
public static NPMPackageGenerator subset(NPMPackageGenerator master, String destFile, String id, String name) throws FHIRException, IOException {
JsonObject p = master.packageJ.deepCopy();
p.remove("name");
p.addProperty("name", id);
p.remove("type");
p.addProperty("type", PackageType.SUBSET.getCode());
p.remove("title");
p.addProperty("title", name);
return new NPMPackageGenerator(destFile, p);
}
public NPMPackageGenerator(String destFile, String canonical, String url, PackageType kind, ImplementationGuide ig, String genDate, List<String> fhirVersion) throws FHIRException, IOException {
super();
System.out.println("create package file at "+destFile);
this.destFile = destFile;
start();
buildPackageJson(canonical, kind, url, genDate, ig, fhirVersion);
}
public NPMPackageGenerator(String destFile, JsonObject npm) throws FHIRException, IOException {
@ -99,10 +126,10 @@ public class NPMPackageGenerator {
addFile(Category.RESOURCE, "package.json", json.getBytes("UTF-8"));
} catch (UnsupportedEncodingException e) {
}
packageJ = npm;
}
private void buildPackageJson(String canonical, PackageType kind, String web, String genDate, ImplementationGuide ig) throws FHIRException, IOException {
private void buildPackageJson(String canonical, PackageType kind, String web, String genDate, ImplementationGuide ig, List<String> fhirVersion) throws FHIRException, IOException {
CommaSeparatedStringBuilder b = new CommaSeparatedStringBuilder();
if (!ig.hasPackageId())
b.append("packageId");
@ -134,8 +161,8 @@ public class NPMPackageGenerator {
if (kind != PackageType.CORE) {
JsonObject dep = new JsonObject();
npm.add("dependencies", dep);
for (Enumeration<FHIRVersion> v : ig.getFhirVersion()) { // TODO: fix for multiple versions
dep.addProperty("hl7.fhir.core", v.asStringValue());
for (String v : fhirVersion) { // TODO: fix for multiple versions
dep.addProperty("hl7.fhir.core", v);
}
for (ImplementationGuideDependsOnComponent d : ig.getDependsOn()) {
dep.addProperty(d.getPackageId(), d.getVersion());
@ -161,12 +188,17 @@ public class NPMPackageGenerator {
npm.add("maintainers", m);
if (ig.getManifest().hasRendering())
npm.addProperty("homepage", ig.getManifest().getRendering());
JsonObject dir = new JsonObject();
npm.add("directories", dir);
dir.addProperty("lib", "package");
dir.addProperty("example", "example");
Gson gson = new GsonBuilder().setPrettyPrinting().create();
String json = gson.toJson(npm);
try {
addFile(Category.RESOURCE, "package.json", json.getBytes("UTF-8"));
} catch (UnsupportedEncodingException e) {
}
packageJ = npm;
}
@ -233,4 +265,33 @@ public class NPMPackageGenerator {
public String filename() {
return destFile;
}
public void loadDir(String rootDir, String name) throws IOException {
loadFiles(rootDir, new File(Utilities.path(rootDir, name)));
}
public void loadFiles(String root, File dir, String... noload) throws IOException {
for (File f : dir.listFiles()) {
if (!Utilities.existsInList(f.getName(), noload)) {
if (f.isDirectory()) {
loadFiles(root, f);
} else {
String path = f.getAbsolutePath().substring(root.length()+1);
byte[] content = TextFile.fileToBytes(f);
if (created.contains(path))
System.out.println("Duplicate package file "+path);
else {
created.add(path);
TarArchiveEntry entry = new TarArchiveEntry(path);
entry.setSize(content.length);
tar.putArchiveEntry(entry);
tar.write(content);
tar.closeArchiveEntry();
}
}
}
}
}
}

View File

@ -63,6 +63,7 @@ import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.LinkedTransferQueue;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.io.output.ByteArrayOutputStream;
@ -150,6 +151,7 @@ import org.hl7.fhir.r4.model.Questionnaire;
import org.hl7.fhir.r4.model.Range;
import org.hl7.fhir.r4.model.Ratio;
import org.hl7.fhir.r4.model.Reference;
import org.hl7.fhir.r4.model.RelatedArtifact;
import org.hl7.fhir.r4.model.Resource;
import org.hl7.fhir.r4.model.SampledData;
import org.hl7.fhir.r4.model.Signature;
@ -173,7 +175,11 @@ import org.hl7.fhir.r4.model.ValueSet.ValueSetExpansionComponent;
import org.hl7.fhir.r4.model.ValueSet.ValueSetExpansionContainsComponent;
import org.hl7.fhir.r4.model.ValueSet.ValueSetExpansionParameterComponent;
import org.hl7.fhir.r4.terminologies.CodeSystemUtilities;
import org.hl7.fhir.r4.terminologies.TerminologyServiceOptions;
import org.hl7.fhir.r4.terminologies.ValueSetExpander.ValueSetExpansionOutcome;
import org.hl7.fhir.r4.utils.FHIRPathEngine.IEvaluationContext;
import org.hl7.fhir.r4.utils.LiquidEngine.LiquidDocument;
import org.hl7.fhir.r4.utils.NarrativeGenerator.ILiquidTemplateProvider;
import org.hl7.fhir.utilities.CommaSeparatedStringBuilder;
import org.hl7.fhir.utilities.MarkDownProcessor;
import org.hl7.fhir.utilities.MarkDownProcessor.Dialect;
@ -188,6 +194,12 @@ import org.w3c.dom.Element;
public class NarrativeGenerator implements INarrativeGenerator {
public interface ILiquidTemplateProvider {
String findTemplate(ResourceContext rcontext, DomainResource r);
}
public interface ITypeParser {
Base parseType(String xml, String type) throws FHIRFormatError, IOException, FHIRException ;
}
@ -236,7 +248,7 @@ public class NarrativeGenerator implements INarrativeGenerator {
}
}
public class ResourceContext {
public static class ResourceContext {
Bundle bundleResource;
DomainResource resourceResource;
@ -290,6 +302,8 @@ public class NarrativeGenerator implements INarrativeGenerator {
private ProfileKnowledgeProvider pkp;
private MarkDownProcessor markdown = new MarkDownProcessor(Dialect.COMMON_MARK);
private ITypeParser parser; // when generating for an element model
private ILiquidTemplateProvider templateProvider;
private IEvaluationContext services;
public boolean generate(Bundle b, boolean evenIfAlreadyHasNarrative, Set<String> outputTracker) throws EOperationOutcome, FHIRException, IOException {
boolean res = false;
@ -312,6 +326,12 @@ public class NarrativeGenerator implements INarrativeGenerator {
if (rcontext == null)
rcontext = new ResourceContext(null, r);
if (templateProvider != null) {
String liquidTemplate = templateProvider.findTemplate(rcontext, r);
if (liquidTemplate != null) {
return generateByLiquid(rcontext, r, liquidTemplate, outputTracker);
}
}
if (r instanceof ConceptMap) {
return generate(rcontext, (ConceptMap) r); // Maintainer = Grahame
} else if (r instanceof ValueSet) {
@ -350,6 +370,24 @@ public class NarrativeGenerator implements INarrativeGenerator {
}
}
private boolean generateByLiquid(ResourceContext rcontext, DomainResource r, String liquidTemplate, Set<String> outputTracker) {
LiquidEngine engine = new LiquidEngine(context, services);
XhtmlNode x;
try {
LiquidDocument doc = engine.parse(liquidTemplate, "template");
String html = engine.evaluate(doc, r, rcontext);
x = new XhtmlParser().parseFragment(html);
if (!x.getName().equals("div"))
throw new FHIRException("Error in template: Root element is not 'div'");
} catch (FHIRException | IOException e) {
x = new XhtmlNode(NodeType.Element, "div");
x.para().b().setAttribute("style", "color: maroon").tx("Exception generating Narrative: "+e.getMessage());
}
inject(r, x, NarrativeStatus.GENERATED);
return true;
}
private interface PropertyWrapper {
public String getName();
public boolean hasValues();
@ -965,6 +1003,7 @@ public class NarrativeGenerator implements INarrativeGenerator {
private List<ConceptMapRenderInstructions> renderingMaps = new ArrayList<ConceptMapRenderInstructions>();
private boolean pretty;
private boolean canonicalUrlsAsLinks;
private TerminologyServiceOptions terminologyServiceOptions = new TerminologyServiceOptions();
public NarrativeGenerator(String prefix, String basePath, IWorkerContext context) {
super();
@ -974,6 +1013,12 @@ public class NarrativeGenerator implements INarrativeGenerator {
init();
}
public NarrativeGenerator setLiquidServices(ILiquidTemplateProvider templateProvider, IEvaluationContext services) {
this.templateProvider = templateProvider;
this.services = services;
return this;
}
public Base parseType(String xml, String type) throws IOException, FHIRException {
if (parser != null)
return parser.parseType(xml, type);
@ -1352,9 +1397,10 @@ public class NarrativeGenerator implements INarrativeGenerator {
return;
else if (e instanceof InstantType)
x.addText(((InstantType) e).toHumanDisplay());
else if (e instanceof DateTimeType)
else if (e instanceof DateTimeType) {
if (e.hasPrimitiveValue())
x.addText(((DateTimeType) e).toHumanDisplay());
else if (e instanceof Base64BinaryType)
} else if (e instanceof Base64BinaryType)
x.addText(new Base64().encodeAsString(((Base64BinaryType) e).getValue()));
else if (e instanceof org.hl7.fhir.r4.model.DateType)
x.addText(((org.hl7.fhir.r4.model.DateType) e).toHumanDisplay());
@ -1561,6 +1607,8 @@ public class NarrativeGenerator implements INarrativeGenerator {
return false;
} else if (e instanceof UsageContext) {
return false;
} else if (e instanceof RelatedArtifact) {
return false;
} else if (e instanceof ElementDefinition) {
return false;
} else if (!(e instanceof Attachment))
@ -1793,7 +1841,7 @@ public class NarrativeGenerator implements INarrativeGenerator {
}
private String lookupCode(String system, String code) {
ValidationResult t = context.validateCode(system, code, null);
ValidationResult t = context.validateCode(terminologyServiceOptions , system, code, null);
if (t != null && t.getDisplay() != null)
return t.getDisplay();
@ -1936,6 +1984,9 @@ public class NarrativeGenerator implements INarrativeGenerator {
StructureDefinition sd = context.fetchTypeDefinition(path.substring(0, path.length()-4));
if (sd == null)
return false;
if (Utilities.existsInList(path.substring(0, path.length()-4), "CapabilityStatement", "StructureDefinition", "ImplementationGuide", "SearchParameter", "MessageDefinition", "OperationDefinition", "CompartmentDefinition", "StructureMap", "GraphDefinition",
"ExampleScenario", "CodeSystem", "ValueSet", "ConceptMap", "NamingSystem", "TerminologyCapabilities"))
return true;
return sd.getBaseDefinitionElement().hasExtension("http://hl7.org/fhir/StructureDefinition/structuredefinition-codegen-super");
}
@ -2522,7 +2573,7 @@ public class NarrativeGenerator implements INarrativeGenerator {
private String getDisplayForConcept(String system, String value) {
if (value == null || system == null)
return null;
ValidationResult cl = context.validateCode(system, value, null);
ValidationResult cl = context.validateCode(terminologyServiceOptions, system, value, null);
return cl == null ? null : cl.getDisplay();
}
@ -2785,7 +2836,7 @@ public class NarrativeGenerator implements INarrativeGenerator {
}
private boolean isSource(ValueSet vs, Type source) {
return vs.hasUrl() && vs.getUrl().equals(source.primitiveValue());
return vs.hasUrl() && source != null && vs.getUrl().equals(source.primitiveValue());
}
private Integer countMembership(ValueSet vs) {
@ -3323,7 +3374,7 @@ public class NarrativeGenerator implements INarrativeGenerator {
return "??Lang";
}
private boolean addDefineRowToTable(XhtmlNode t, ConceptDefinitionComponent c, int i, boolean hasHierarchy, boolean hasDisplay, boolean comment, boolean version, boolean deprecated, List<UsedConceptMap> maps, String system, CodeSystem cs, String lang) {
private boolean addDefineRowToTable(XhtmlNode t, ConceptDefinitionComponent c, int i, boolean hasHierarchy, boolean hasDisplay, boolean comment, boolean version, boolean deprecated, List<UsedConceptMap> maps, String system, CodeSystem cs, String lang) throws FHIRFormatError, DefinitionException, IOException {
boolean hasExtensions = false;
XhtmlNode tr = t.tr();
XhtmlNode td = tr.td();
@ -3371,6 +3422,9 @@ public class NarrativeGenerator implements INarrativeGenerator {
if (c != null &&
c.hasDefinitionElement()) {
if (lang == null) {
if (hasMarkdownInDefinitions(cs))
addMarkdown(td, c.getDefinition());
else
td.addText(c.getDefinition());
} else if (lang.equals("*")) {
boolean sl = false;
@ -3488,6 +3542,10 @@ public class NarrativeGenerator implements INarrativeGenerator {
}
private boolean hasMarkdownInDefinitions(CodeSystem cs) {
return ToolingExtensions.readBoolExtension(cs, "http://hl7.org/fhir/StructureDefinition/codesystem-use-markdown");
}
private String makeAnchor(String codeSystem, String code) {
String s = codeSystem+'-'+code;
StringBuilder b = new StringBuilder();
@ -3712,7 +3770,7 @@ public class NarrativeGenerator implements INarrativeGenerator {
li.ah(href).addText(f.getValue());
} else if ("concept".equals(f.getProperty()) && inc.hasSystem()) {
li.addText(f.getValue());
ValidationResult vr = context.validateCode(inc.getSystem(), f.getValue(), null);
ValidationResult vr = context.validateCode(terminologyServiceOptions, inc.getSystem(), f.getValue(), null);
if (vr.isOk()) {
li.tx(" ("+vr.getDisplay()+")");
}
@ -3794,7 +3852,7 @@ public class NarrativeGenerator implements INarrativeGenerator {
}
}
return context.validateCode(inc.getSystem(), code, null).asConceptDefinition();
return context.validateCode(terminologyServiceOptions, inc.getSystem(), code, null).asConceptDefinition();
}
@ -4171,18 +4229,37 @@ public class NarrativeGenerator implements INarrativeGenerator {
addTableRow(t, "System History", showOp(rest, SystemRestfulInteraction.HISTORYSYSTEM));
addTableRow(t, "System Search", showOp(rest, SystemRestfulInteraction.SEARCHSYSTEM));
boolean hasVRead = false;
boolean hasPatch = false;
boolean hasDelete = false;
boolean hasHistory = false;
boolean hasUpdates = false;
for (CapabilityStatementRestResourceComponent r : rest.getResource()) {
hasVRead = hasVRead || hasOp(r, TypeRestfulInteraction.VREAD);
hasPatch = hasPatch || hasOp(r, TypeRestfulInteraction.PATCH);
hasDelete = hasDelete || hasOp(r, TypeRestfulInteraction.DELETE);
hasHistory = hasHistory || hasOp(r, TypeRestfulInteraction.HISTORYTYPE);
hasUpdates = hasUpdates || hasOp(r, TypeRestfulInteraction.HISTORYINSTANCE);
}
t = x.table(null);
XhtmlNode tr = t.tr();
tr.th().b().tx("Resource Type");
tr.th().b().tx("Profile");
tr.th().b().tx("Read");
tr.th().b().tx("V-Read");
tr.th().b().tx("Search");
tr.th().b().tx("Update");
tr.th().b().tx("Updates");
tr.th().b().tx("Create");
tr.th().b().tx("Delete");
tr.th().b().tx("History");
tr.th().b().attribute("title", "GET a resource (read interaction)").tx("Read");
if (hasVRead)
tr.th().b().attribute("title", "GET past versions of resources (vread interaction)").tx("V-Read");
tr.th().b().attribute("title", "GET all set of resources of the type (search interaction)").tx("Search");
tr.th().b().attribute("title", "PUT a new resource version (update interaction)").tx("Update");
if (hasPatch)
tr.th().b().attribute("title", "PATCH a new resource version (patch interaction)").tx("Patch");
tr.th().b().attribute("title", "POST a new resource (create interaction)").tx("Create");
if (hasDelete)
tr.th().b().attribute("title", "DELETE a resource (delete interaction)").tx("Delete");
if (hasUpdates)
tr.th().b().attribute("title", "GET changes to a resource (history interaction on instance)").tx("Updates");
if (hasHistory)
tr.th().b().attribute("title", "GET changes for all resources of the type (history interaction on type)").tx("History");
for (CapabilityStatementRestResourceComponent r : rest.getResource()) {
tr = t.tr();
@ -4191,12 +4268,18 @@ public class NarrativeGenerator implements INarrativeGenerator {
tr.td().ah(prefix+r.getProfile()).addText(r.getProfile());
}
tr.td().addText(showOp(r, TypeRestfulInteraction.READ));
if (hasVRead)
tr.td().addText(showOp(r, TypeRestfulInteraction.VREAD));
tr.td().addText(showOp(r, TypeRestfulInteraction.SEARCHTYPE));
tr.td().addText(showOp(r, TypeRestfulInteraction.UPDATE));
tr.td().addText(showOp(r, TypeRestfulInteraction.HISTORYINSTANCE));
if (hasPatch)
tr.td().addText(showOp(r, TypeRestfulInteraction.PATCH));
tr.td().addText(showOp(r, TypeRestfulInteraction.CREATE));
if (hasDelete)
tr.td().addText(showOp(r, TypeRestfulInteraction.DELETE));
if (hasUpdates)
tr.td().addText(showOp(r, TypeRestfulInteraction.HISTORYINSTANCE));
if (hasHistory)
tr.td().addText(showOp(r, TypeRestfulInteraction.HISTORYTYPE));
}
}
@ -4205,6 +4288,14 @@ public class NarrativeGenerator implements INarrativeGenerator {
return true;
}
private boolean hasOp(CapabilityStatementRestResourceComponent r, TypeRestfulInteraction on) {
for (ResourceInteractionComponent op : r.getInteraction()) {
if (op.getCode() == on)
return true;
}
return false;
}
private String showOp(CapabilityStatementRestResourceComponent r, TypeRestfulInteraction on) {
for (ResourceInteractionComponent op : r.getInteraction()) {
if (op.getCode() == on)
@ -4666,5 +4757,13 @@ public class NarrativeGenerator implements INarrativeGenerator {
return this;
}
public TerminologyServiceOptions getTerminologyServiceOptions() {
return terminologyServiceOptions;
}
public void setTerminologyServiceOptions(TerminologyServiceOptions terminologyServiceOptions) {
this.terminologyServiceOptions = terminologyServiceOptions;
}
}

View File

@ -22,6 +22,7 @@ package org.hl7.fhir.r4.utils;
import org.hl7.fhir.r4.model.CodeableConcept;
import org.hl7.fhir.r4.model.IntegerType;
import org.hl7.fhir.r4.model.OperationOutcome;
import org.hl7.fhir.r4.model.OperationOutcome.IssueSeverity;
import org.hl7.fhir.r4.model.OperationOutcome.IssueType;
@ -36,14 +37,16 @@ public class OperationOutcomeUtilities {
public static OperationOutcomeIssueComponent convertToIssue(ValidationMessage message, OperationOutcome op) {
OperationOutcomeIssueComponent issue = new OperationOutcome.OperationOutcomeIssueComponent();
issue.setCode(convert(message.getType()));
if (message.getLocation() != null) {
// message location has a fhirPath in it. We need to populate the expression
issue.addExpression(message.getLocation());
// also, populate the XPath variant
StringType s = new StringType();
s.setValue(Utilities.fhirPathToXPath(message.getLocation())+(message.getLine()>= 0 && message.getCol() >= 0 ? " (line "+Integer.toString(message.getLine())+", col"+Integer.toString(message.getCol())+")" : "") );
issue.getLocation().add(s);
}
// pass through line/col if they're present
if (message.getLine() != 0)
issue.addExtension().setUrl(ToolingExtensions.EXT_ISSUE_LINE).setValue(new IntegerType(message.getLine()));
if (message.getCol() != 0)
issue.addExtension().setUrl(ToolingExtensions.EXT_ISSUE_COL).setValue(new IntegerType(message.getCol()));
issue.setSeverity(convert(message.getLevel()));
CodeableConcept c = new CodeableConcept();
c.setText(message.getMessage());

View File

@ -34,6 +34,11 @@ import java.util.Map;
import java.util.Set;
import java.util.UUID;
import org.apache.commons.lang3.NotImplementedException;
import org.hl7.fhir.exceptions.DefinitionException;
import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.exceptions.FHIRFormatError;
import org.hl7.fhir.exceptions.PathEngineException;
import org.hl7.fhir.r4.conformance.ProfileUtilities;
import org.hl7.fhir.r4.conformance.ProfileUtilities.ProfileKnowledgeProvider;
import org.hl7.fhir.r4.context.IWorkerContext;
@ -86,10 +91,10 @@ import org.hl7.fhir.r4.model.StructureMap.StructureMapGroupRuleTargetComponent;
import org.hl7.fhir.r4.model.StructureMap.StructureMapGroupRuleTargetParameterComponent;
import org.hl7.fhir.r4.model.StructureMap.StructureMapGroupTypeMode;
import org.hl7.fhir.r4.model.StructureMap.StructureMapInputMode;
import org.hl7.fhir.r4.model.StructureMap.StructureMapSourceListMode;
import org.hl7.fhir.r4.model.StructureMap.StructureMapTargetListMode;
import org.hl7.fhir.r4.model.StructureMap.StructureMapModelMode;
import org.hl7.fhir.r4.model.StructureMap.StructureMapSourceListMode;
import org.hl7.fhir.r4.model.StructureMap.StructureMapStructureComponent;
import org.hl7.fhir.r4.model.StructureMap.StructureMapTargetListMode;
import org.hl7.fhir.r4.model.StructureMap.StructureMapTransform;
import org.hl7.fhir.r4.model.Type;
import org.hl7.fhir.r4.model.TypeDetails;
@ -97,14 +102,10 @@ import org.hl7.fhir.r4.model.TypeDetails.ProfiledType;
import org.hl7.fhir.r4.model.UriType;
import org.hl7.fhir.r4.model.ValueSet;
import org.hl7.fhir.r4.model.ValueSet.ValueSetExpansionContainsComponent;
import org.hl7.fhir.r4.terminologies.TerminologyServiceOptions;
import org.hl7.fhir.r4.terminologies.ValueSetExpander.ValueSetExpansionOutcome;
import org.hl7.fhir.r4.utils.FHIRLexer.FHIRLexerException;
import org.hl7.fhir.r4.utils.FHIRPathEngine.IEvaluationContext;
import org.apache.commons.lang3.NotImplementedException;
import org.hl7.fhir.exceptions.DefinitionException;
import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.exceptions.FHIRFormatError;
import org.hl7.fhir.exceptions.PathEngineException;
import org.hl7.fhir.utilities.CommaSeparatedStringBuilder;
import org.hl7.fhir.utilities.Utilities;
import org.hl7.fhir.utilities.validation.ValidationMessage;
@ -222,6 +223,7 @@ public class StructureMapUtilities {
private ITransformerServices services;
private ProfileKnowledgeProvider pkp;
private Map<String, Integer> ids = new HashMap<String, Integer>();
private TerminologyServiceOptions terminologyServiceOptions = new TerminologyServiceOptions();
public StructureMapUtilities(IWorkerContext worker, ITransformerServices services, ProfileKnowledgeProvider pkp) {
super();
@ -414,6 +416,7 @@ public class StructureMapUtilities {
b.append(g.getExtends());
}
if (g.hasTypeMode()) {
switch (g.getTypeMode()) {
case TYPES:
b.append(" <<types>>");
@ -423,6 +426,7 @@ public class StructureMapUtilities {
break;
default: // NONE, NULL
}
}
b.append(" {\r\n");
for (StructureMapGroupRuleComponent r : g.getRule()) {
renderRule(b, r, 2);
@ -1014,7 +1018,11 @@ public class StructureMapUtilities {
}
if (newFmt) {
if (lexer.isConstant()) {
if (lexer.isStringConstant()) {
rule.setName(lexer.readConstant("ruleName"));
} else {
rule.setName(lexer.take());
}
} else {
if (rule.getSource().size() != 1 || !rule.getSourceFirstRep().hasElement())
throw lexer.error("Complex rules must have an explicit name");
@ -1194,7 +1202,7 @@ public class StructureMapUtilities {
private Type readConstant(String s, FHIRLexer lexer) throws FHIRLexerException {
if (Utilities.isInteger(s))
return new IntegerType(s);
else if (Utilities.isDecimal(s, true))
else if (Utilities.isDecimal(s, false))
return new DecimalType(s);
else if (Utilities.existsInList(s, "true", "false"))
return new BooleanType(s.equals("true"));
@ -1402,7 +1410,7 @@ public class StructureMapUtilities {
}
} else if (rule.getSource().size() == 1 && rule.getSourceFirstRep().hasVariable() && rule.getTarget().size() == 1 && rule.getTargetFirstRep().hasVariable() && rule.getTargetFirstRep().getTransform() == StructureMapTransform.CREATE && !rule.getTargetFirstRep().hasParameter()) {
// simple inferred, map by type
log(v.summary());
System.out.println(v.summary());
Base src = v.get(VariableMode.INPUT, rule.getSourceFirstRep().getVariable());
Base tgt = v.get(VariableMode.OUTPUT, rule.getTargetFirstRep().getVariable());
String srcType = src.fhirType();
@ -1493,11 +1501,9 @@ public class StructureMapUtilities {
if (value.contains("*")) {
for (StructureMap sm : worker.listTransforms()) {
if (urlMatches(value, sm.getUrl())) {
if (!res.contains(sm)) {
res.add(sm);
}
}
}
} else {
StructureMap sm = worker.getTransform(value);
if (sm != null)
@ -1967,7 +1973,7 @@ public class StructureMapUtilities {
throw new FHIRException("The code '"+code+"' is not in the value set '"+uri+"' (valid codes: "+b.toString()+"; also checked displays)");
} else
system = uri;
ValidationResult vr = worker.validateCode(system, code, null);
ValidationResult vr = worker.validateCode(terminologyServiceOptions, system, code, null);
if (vr != null && vr.getDisplay() != null)
display = vr.getDisplay();
return new Coding().setSystem(system).setCode(code).setDisplay(display);
@ -2291,7 +2297,7 @@ public class StructureMapUtilities {
* @return
* @throws Exception
*/
public StructureMapAnalysis analyse(Object appInfo, StructureMap map) throws Exception {
public StructureMapAnalysis analyse(Object appInfo, StructureMap map) throws FHIRException {
ids.clear();
StructureMapAnalysis result = new StructureMapAnalysis();
TransformContext context = new TransformContext(appInfo);
@ -2319,7 +2325,7 @@ public class StructureMapUtilities {
}
private void analyseGroup(String indent, TransformContext context, StructureMap map, VariablesForProfiling vars, StructureMapGroupComponent group, StructureMapAnalysis result) throws Exception {
private void analyseGroup(String indent, TransformContext context, StructureMap map, VariablesForProfiling vars, StructureMapGroupComponent group, StructureMapAnalysis result) throws FHIRException {
log(indent+"Analyse Group : "+group.getName());
// todo: extends
// todo: check inputs
@ -2344,7 +2350,7 @@ public class StructureMapUtilities {
xs.addText("Input: "+v.property.getPath());
}
private void analyseRule(String indent, TransformContext context, StructureMap map, VariablesForProfiling vars, StructureMapGroupComponent group, StructureMapGroupRuleComponent rule, StructureMapAnalysis result) throws Exception {
private void analyseRule(String indent, TransformContext context, StructureMap map, VariablesForProfiling vars, StructureMapGroupComponent group, StructureMapGroupRuleComponent rule, StructureMapAnalysis result) throws FHIRException {
log(indent+"Analyse rule : "+rule.getName());
XhtmlNode tr = result.summary.addTag("tr");
XhtmlNode xs = tr.addTag("td");
@ -2352,7 +2358,7 @@ public class StructureMapUtilities {
VariablesForProfiling srcVars = vars.copy();
if (rule.getSource().size() != 1)
throw new Exception("Rule \""+rule.getName()+"\": not handled yet");
throw new FHIRException("Rule \""+rule.getName()+"\": not handled yet");
VariablesForProfiling source = analyseSource(rule.getName(), context, srcVars, rule.getSourceFirstRep(), xs);
TargetWriter tw = new TargetWriter();
@ -2415,7 +2421,7 @@ public class StructureMapUtilities {
}
}
private VariablesForProfiling analyseSource(String ruleId, TransformContext context, VariablesForProfiling vars, StructureMapGroupRuleSourceComponent src, XhtmlNode td) throws Exception {
private VariablesForProfiling analyseSource(String ruleId, TransformContext context, VariablesForProfiling vars, StructureMapGroupRuleSourceComponent src, XhtmlNode td) throws FHIRException {
VariableForProfiling var = vars.get(VariableMode.INPUT, src.getContext());
if (var == null)
throw new FHIRException("Rule \""+ruleId+"\": Unknown input variable "+src.getContext());
@ -2431,7 +2437,7 @@ public class StructureMapUtilities {
if (src.hasElement()) {
Property element = prop.getBaseProperty().getChild(prop.types.getType(), src.getElement());
if (element == null)
throw new Exception("Rule \""+ruleId+"\": Unknown element name "+src.getElement());
throw new FHIRException("Rule \""+ruleId+"\": Unknown element name "+src.getElement());
if (element.getDefinition().getMin() == 0)
optional = true;
if (element.getDefinition().getMax().equals("*"))
@ -2459,14 +2465,14 @@ public class StructureMapUtilities {
}
private void analyseTarget(String ruleId, TransformContext context, VariablesForProfiling vars, StructureMap map, StructureMapGroupRuleTargetComponent tgt, String tv, TargetWriter tw, List<StructureDefinition> profiles, String sliceName) throws Exception {
private void analyseTarget(String ruleId, TransformContext context, VariablesForProfiling vars, StructureMap map, StructureMapGroupRuleTargetComponent tgt, String tv, TargetWriter tw, List<StructureDefinition> profiles, String sliceName) throws FHIRException {
VariableForProfiling var = null;
if (tgt.hasContext()) {
var = vars.get(VariableMode.OUTPUT, tgt.getContext());
if (var == null)
throw new Exception("Rule \""+ruleId+"\": target context not known: "+tgt.getContext());
throw new FHIRException("Rule \""+ruleId+"\": target context not known: "+tgt.getContext());
if (!tgt.hasElement())
throw new Exception("Rule \""+ruleId+"\": Not supported yet");
throw new FHIRException("Rule \""+ruleId+"\": Not supported yet");
}
@ -2477,7 +2483,7 @@ public class StructureMapUtilities {
} else {
Property vp = var.property.baseProperty.getChild(tgt.getElement(), tgt.getElement());
if (vp == null)
throw new Exception("Unknown Property "+tgt.getElement()+" on "+var.property.path);
throw new FHIRException("Unknown Property "+tgt.getElement()+" on "+var.property.path);
type = new TypeDetails(CollectionStatus.SINGLETON, vp.getType(tgt.getElement()));
}
@ -2871,19 +2877,19 @@ public class StructureMapUtilities {
return prop;
}
private PropertyWithType resolveType(StructureMap map, String type, StructureMapInputMode mode) throws Exception {
private PropertyWithType resolveType(StructureMap map, String type, StructureMapInputMode mode) throws FHIRException {
for (StructureMapStructureComponent imp : map.getStructure()) {
if ((imp.getMode() == StructureMapModelMode.SOURCE && mode == StructureMapInputMode.SOURCE) ||
(imp.getMode() == StructureMapModelMode.TARGET && mode == StructureMapInputMode.TARGET)) {
StructureDefinition sd = worker.fetchResource(StructureDefinition.class, imp.getUrl());
if (sd == null)
throw new Exception("Import "+imp.getUrl()+" cannot be resolved");
throw new FHIRException("Import "+imp.getUrl()+" cannot be resolved");
if (sd.getId().equals(type)) {
return new PropertyWithType(sd.getType(), new Property(worker, sd.getSnapshot().getElement().get(0), sd), null, new TypeDetails(CollectionStatus.SINGLETON, sd.getUrl()));
}
}
}
throw new Exception("Unable to find structure definition for "+type+" in imports");
throw new FHIRException("Unable to find structure definition for "+type+" in imports");
}
@ -2960,5 +2966,13 @@ public class StructureMapUtilities {
}
return null;
}
public TerminologyServiceOptions getTerminologyServiceOptions() {
return terminologyServiceOptions;
}
public void setTerminologyServiceOptions(TerminologyServiceOptions terminologyServiceOptions) {
this.terminologyServiceOptions = terminologyServiceOptions;
}
}

View File

@ -74,6 +74,7 @@ import org.hl7.fhir.r4.model.Factory;
import org.hl7.fhir.r4.model.Identifier;
import org.hl7.fhir.r4.model.IntegerType;
import org.hl7.fhir.r4.model.MarkdownType;
import org.hl7.fhir.r4.model.OperationOutcome.OperationOutcomeIssueComponent;
import org.hl7.fhir.r4.model.PrimitiveType;
import org.hl7.fhir.r4.model.Questionnaire.QuestionnaireItemComponent;
import org.hl7.fhir.r4.model.Questionnaire.QuestionnaireItemType;
@ -83,6 +84,9 @@ import org.hl7.fhir.r4.model.UriType;
import org.hl7.fhir.r4.model.ValueSet.ConceptReferenceComponent;
import org.hl7.fhir.r4.model.ValueSet.ConceptSetComponent;
import org.hl7.fhir.utilities.StandardsStatus;
import org.hl7.fhir.utilities.validation.ValidationMessage;
import org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity;
import org.hl7.fhir.utilities.validation.ValidationMessage.IssueType;
import org.hl7.fhir.utilities.validation.ValidationMessage.Source;
@ -97,6 +101,8 @@ public class ToolingExtensions {
private static final String EXT_IDENTIFIER = "http://hl7.org/fhir/StructureDefinition/identifier";
public static final String EXT_TRANSLATION = "http://hl7.org/fhir/StructureDefinition/translation";
public static final String EXT_ISSUE_SOURCE = "http://hl7.org/fhir/StructureDefinition/operationoutcome-issue-source";
public static final String EXT_ISSUE_LINE = "http://hl7.org/fhir/StructureDefinition/operationoutcome-issue-line";
public static final String EXT_ISSUE_COL = "http://hl7.org/fhir/StructureDefinition/operationoutcome-issue-col";
public static final String EXT_DISPLAY_HINT = "http://hl7.org/fhir/StructureDefinition/structuredefinition-display-hint";
public static final String EXT_REPLACED_BY = "http://hl7.org/fhir/StructureDefinition/valueset-replacedby";
public static final String EXT_JSON_TYPE = "http://hl7.org/fhir/StructureDefinition/structuredefinition-json-type";
@ -142,10 +148,20 @@ public class ToolingExtensions {
public static final String EXT_IGP_RESOURCES = "http://hl7.org/fhir/StructureDefinition/igpublisher-folder-resource";
public static final String EXT_IGP_PAGES = "http://hl7.org/fhir/StructureDefinition/igpublisher-folder-pages";
public static final String EXT_IGP_SPREADSHEET = "http://hl7.org/fhir/StructureDefinition/igpublisher-spreadsheet";
public static final String EXT_IGP_MAPPING_CSV = "http://hl7.org/fhir/StructureDefinition/igpublisher-mapping-csv";
public static final String EXT_IGP_BUNDLE = "http://hl7.org/fhir/StructureDefinition/igpublisher-bundle";
public static final String EXT_IGP_RESOURCE_INFO = "http://hl7.org/fhir/tools/StructureDefinition/resource-information";
public static final String EXT_IGP_LOADVERSION = "http://hl7.org/fhir/StructureDefinition/igpublisher-loadversion";
public static final String EXT_MAX_VALUESET = "http://hl7.org/fhir/StructureDefinition/elementdefinition-maxValueSet";
public static final String EXT_MIN_VALUESET = "http://hl7.org/fhir/StructureDefinition/elementdefinition-minValueSet";
public static final String EXT_PROFILE_ELEMENT = "http://hl7.org/fhir/StructureDefinition/elementdefinition-profile-element";
public static final String EXT_LIST_PACKAGE = "http://hl7.org/fhir/StructureDefinition/list-packageId";
public static final String EXT_MAPPING_NAME = "http://hl7.org/fhir/tools/StructureDefinition/conceptmap-source-name";
public static final String EXT_MAPPING_TYPE = "http://hl7.org/fhir/tools/StructureDefinition/conceptmap-source-type";
public static final String EXT_MAPPING_CARD = "http://hl7.org/fhir/tools/StructureDefinition/conceptmap-source-cardinality";
public static final String EXT_MAPPING_TGTTYPE = "http://hl7.org/fhir/tools/StructureDefinition/conceptmap-target-type";
public static final String EXT_MAPPING_TGTCARD = "http://hl7.org/fhir/tools/StructureDefinition/conceptmap-target-cardinality";
public static final String EXT_PRIVATE_BASE = "http://hl7.org/fhir/tools/";
// specific extension helpers
@ -654,6 +670,15 @@ public class ToolingExtensions {
throw new Error("Unable to read extension "+uri+" as an integer");
}
public static int readIntegerExtension(Element e, String uri, int defaultValue) {
Extension ex = ExtensionHelper.getExtension(e, uri);
if (ex == null)
return defaultValue;
if (ex.getValue() instanceof IntegerType)
return ((IntegerType) ex.getValue()).getValue();
throw new Error("Unable to read extension "+uri+" as an integer");
}
public static Map<String, String> getLanguageTranslations(Element e) {
Map<String, String> res = new HashMap<String, String>();
for (Extension ext : e.getExtension()) {
@ -692,6 +717,71 @@ public class ToolingExtensions {
ToolingExtensions.setCodeExtension(dr, ToolingExtensions.EXT_NORMATIVE_VERSION, normativeVersion);
}
public static ValidationMessage readValidationMessage(OperationOutcomeIssueComponent issue, Source source) {
ValidationMessage vm = new ValidationMessage();
vm.setSource(source);
vm.setLevel(mapSeverity(issue.getSeverity()));
vm.setType(mapType(issue.getCode()));
if (issue.hasExtension(ToolingExtensions.EXT_ISSUE_LINE))
vm.setLine(ToolingExtensions.readIntegerExtension(issue, ToolingExtensions.EXT_ISSUE_LINE, 0));
if (issue.hasExtension(ToolingExtensions.EXT_ISSUE_COL))
vm.setCol(ToolingExtensions.readIntegerExtension(issue, ToolingExtensions.EXT_ISSUE_COL, 0));
if (issue.hasExpression())
vm.setLocation(issue.getExpression().get(0).asStringValue());
vm.setMessage(issue.getDetails().getText());
if (issue.hasExtension("http://hl7.org/fhir/StructureDefinition/rendering-xhtml"))
vm.setHtml(ToolingExtensions.readStringExtension(issue, "http://hl7.org/fhir/StructureDefinition/rendering-xhtml"));
return vm;
}
private static IssueType mapType(org.hl7.fhir.r4.model.OperationOutcome.IssueType code) {
switch (code) {
case BUSINESSRULE: return IssueType.BUSINESSRULE;
case CODEINVALID: return IssueType.CODEINVALID;
case CONFLICT: return IssueType.CONFLICT;
case DELETED: return IssueType.DELETED;
case DUPLICATE: return IssueType.DUPLICATE;
case EXCEPTION: return IssueType.EXCEPTION;
case EXPIRED: return IssueType.EXPIRED;
case EXTENSION: return IssueType.EXTENSION;
case FORBIDDEN: return IssueType.FORBIDDEN;
case INCOMPLETE: return IssueType.INCOMPLETE;
case INFORMATIONAL: return IssueType.INFORMATIONAL;
case INVALID: return IssueType.INVALID;
case INVARIANT: return IssueType.INVARIANT;
case LOCKERROR: return IssueType.LOCKERROR;
case LOGIN: return IssueType.LOGIN;
case MULTIPLEMATCHES: return IssueType.MULTIPLEMATCHES;
case NOSTORE: return IssueType.NOSTORE;
case NOTFOUND: return IssueType.NOTFOUND;
case NOTSUPPORTED: return IssueType.NOTSUPPORTED;
case NULL: return IssueType.NULL;
case PROCESSING: return IssueType.PROCESSING;
case REQUIRED: return IssueType.REQUIRED;
case SECURITY: return IssueType.SECURITY;
case STRUCTURE: return IssueType.STRUCTURE;
case SUPPRESSED: return IssueType.SUPPRESSED;
case THROTTLED: return IssueType.THROTTLED;
case TIMEOUT: return IssueType.TIMEOUT;
case TOOCOSTLY: return IssueType.TOOCOSTLY;
case TOOLONG: return IssueType.TOOLONG;
case TRANSIENT: return IssueType.TRANSIENT;
case UNKNOWN: return IssueType.UNKNOWN;
case VALUE: return IssueType.VALUE;
default: return null;
}
}
private static IssueSeverity mapSeverity(org.hl7.fhir.r4.model.OperationOutcome.IssueSeverity severity) {
switch (severity) {
case ERROR: return IssueSeverity.ERROR;
case FATAL: return IssueSeverity.FATAL;
case INFORMATION: return IssueSeverity.INFORMATION;
case WARNING: return IssueSeverity.WARNING;
default: return null;
}
}
// public static boolean hasOID(ValueSet vs) {
// return hasExtension(vs, EXT_OID);
// }

View File

@ -43,7 +43,7 @@ public class ProfileUtilitiesTests {
focus.setType("Patient");
focus.setDerivation(TypeDerivationRule.CONSTRAINT);
List<ValidationMessage> messages = new ArrayList<ValidationMessage>();
new ProfileUtilities(TestingUtilities.context(), messages, null).generateSnapshot(base, focus, focus.getUrl(), "Simple Test");
new ProfileUtilities(TestingUtilities.context(), messages, null).generateSnapshot(base, focus, focus.getUrl(), "http://hl7.org/fhir/R4", "Simple Test");
boolean ok = base.getSnapshot().getElement().size() == focus.getSnapshot().getElement().size();
for (int i = 0; i < base.getSnapshot().getElement().size(); i++) {
@ -86,7 +86,7 @@ public class ProfileUtilitiesTests {
focus.setSnapshot(null);
focus.setDifferential(null);
List<ValidationMessage> messages = new ArrayList<ValidationMessage>();
new ProfileUtilities(TestingUtilities.context(), messages, null).generateSnapshot(base, focus, focus.getUrl(), "Simple Test" );
new ProfileUtilities(TestingUtilities.context(), messages, null).generateSnapshot(base, focus, focus.getUrl(), "http://hl7.org/fhir/R4", "Simple Test" );
boolean ok = base.getSnapshot().getElement().size() == focus.getSnapshot().getElement().size();
for (int i = 0; i < base.getSnapshot().getElement().size(); i++) {

View File

@ -104,6 +104,12 @@ public class SnapShotGenerationTests {
return false;
}
@Override
public BindingResolution resolveBinding(StructureDefinition def, String url, String path) throws FHIRException {
// TODO Auto-generated method stub
return null;
}
}
private static class SnapShotGenerationTestsContext implements IEvaluationContext {
@ -312,7 +318,7 @@ public class SnapShotGenerationTests {
if (TestingUtilities.context().fetchResource(StructureDefinition.class, sd.getUrl()) == null) {
sd.setUserData("path", "test-"+sd.getId()+".html");
StructureDefinition extd = TestingUtilities.context().fetchResource(StructureDefinition.class, sd.getBaseDefinition());
new ProfileUtilities(TestingUtilities.context(), null, null).generateSnapshot(extd, sd, sd.getUrl(), sd.getName());
new ProfileUtilities(TestingUtilities.context(), null, null).generateSnapshot(extd, sd, sd.getUrl(), "http://hl7.org/fhir/R4", sd.getName());
TestingUtilities.context().cacheResource(sd);
debugSaveResource(sd);
}
@ -347,7 +353,7 @@ public class SnapShotGenerationTests {
throw new FHIRException("Sort failed: "+errors.toString());
}
pu.generateSnapshot(base, output, source.getUrl(), source.getName());
pu.generateSnapshot(base, output, source.getUrl(), "http://hl7.org/fhir/R4", source.getName());
debugSaveResource(output);
context.fixtures.put(op.getResponseId(), output);
context.snapshots.put(output.getUrl(), output);
@ -440,7 +446,7 @@ public class SnapShotGenerationTests {
pu.sortDifferential(getSD(p.getBaseDefinition()), p, url, errors);
if (!errors.isEmpty())
throw new FHIRException(errors.get(0));
pu.generateSnapshot(getSD(p.getBaseDefinition()), p, p.getUrl(), p.getName());
pu.generateSnapshot(getSD(p.getBaseDefinition()), p, p.getUrl(), "http://hl7.org/fhir/R4", p.getName());
debugSaveResource(p);
return p;
}

View File

@ -701,7 +701,6 @@ public class PackageCacheManager {
for (String s : ini.getPropertyNames("urls"))
allUrls.add(ini.getStringProperty("urls", s));
try {
System.out.println("Listing known Implementation Guides");
URL url = new URL("https://raw.githubusercontent.com/FHIR/ig-registry/master/fhir-ig-list.json?nocache=" + System.currentTimeMillis());
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("GET");
@ -715,7 +714,7 @@ public class PackageCacheManager {
allUrls.add(gi.get("canonical").getAsString());
}
} catch (Exception e) {
System.out.println(" .. failed: "+e.getMessage());
System.out.println("Listing known Implementation Guides failed: "+e.getMessage());
}
}
return allUrls;

View File

@ -120,6 +120,7 @@ import org.hl7.fhir.r4.utils.IResourceValidator;
import org.hl7.fhir.r4.utils.ToolingExtensions;
import org.hl7.fhir.r4.utils.ValidationProfileSet;
import org.hl7.fhir.r4.utils.ValidationProfileSet.ProfileRegistration;
import org.hl7.fhir.r4.terminologies.TerminologyServiceOptions;
import org.hl7.fhir.utilities.CommaSeparatedStringBuilder;
import org.hl7.fhir.utilities.Utilities;
import org.hl7.fhir.utilities.validation.ValidationMessage;
@ -807,7 +808,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
txTime = txTime + (System.nanoTime() - t);
if (ss) {
t = System.nanoTime();
ValidationResult s = context.validateCode(system, code, display);
ValidationResult s = context.validateCode(new TerminologyServiceOptions("en"), system, code, display);
txTime = txTime + (System.nanoTime() - t);
if (s == null)
return true;
@ -908,7 +909,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
if (!atLeastOneSystemIsSupported && binding.getStrength() == BindingStrength.EXAMPLE) {
// ignore this since we can't validate but it doesn't matter..
} else {
ValidationResult vr = context.validateCode(cc, valueset);
ValidationResult vr = context.validateCode(new TerminologyServiceOptions("en"), cc, valueset);
if (!vr.isOk()) {
bindingsOk = false;
if (vr.getErrorClass() != null && vr.getErrorClass().isInfrastructure()) {
@ -942,7 +943,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
String nextCode = nextCoding.getCode();
String nextSystem = nextCoding.getSystem();
if (isNotBlank(nextCode) && isNotBlank(nextSystem) && context.supportsSystem(nextSystem)) {
ValidationResult vr = context.validateCode(nextSystem, nextCode, null);
ValidationResult vr = context.validateCode(new TerminologyServiceOptions("en"), nextSystem, nextCode, null);
if (!vr.isOk()) {
txWarning(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "Code {0} is not a valid code in code system {1}", nextCode, nextSystem);
}
@ -971,7 +972,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
if (warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, valueset != null, "ValueSet " + describeReference(maxVSUrl) + " not found")) {
try {
long t = System.nanoTime();
ValidationResult vr = context.validateCode(cc, valueset);
ValidationResult vr = context.validateCode(new TerminologyServiceOptions("en"), cc, valueset);
txTime = txTime + (System.nanoTime() - t);
if (!vr.isOk()) {
if (vr.getErrorClass() != null && vr.getErrorClass().isInfrastructure())
@ -991,7 +992,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
if (warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, valueset != null, "ValueSet " + describeReference(maxVSUrl) + " not found")) {
try {
long t = System.nanoTime();
ValidationResult vr = context.validateCode(c, valueset);
ValidationResult vr = context.validateCode(new TerminologyServiceOptions("en"), c, valueset);
txTime = txTime + (System.nanoTime() - t);
if (!vr.isOk()) {
if (vr.getErrorClass() != null && vr.getErrorClass().isInfrastructure())
@ -1011,7 +1012,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
if (warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, valueset != null, "ValueSet " + describeReference(maxVSUrl) + " not found")) {
try {
long t = System.nanoTime();
ValidationResult vr = context.validateCode(value, valueset);
ValidationResult vr = context.validateCode(new TerminologyServiceOptions("en"), value, valueset);
txTime = txTime + (System.nanoTime() - t);
if (!vr.isOk()) {
if (vr.getErrorClass() != null && vr.getErrorClass().isInfrastructure())
@ -1060,7 +1061,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
long t = System.nanoTime();
ValidationResult vr = null;
if (binding.getStrength() != BindingStrength.EXAMPLE) {
vr = context.validateCode(c, valueset);
vr = context.validateCode(new TerminologyServiceOptions("en"), c, valueset);
}
txTime = txTime + (System.nanoTime() - t);
if (vr != null && !vr.isOk()) {
@ -1678,7 +1679,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
long t = System.nanoTime();
ValidationResult vr = null;
if (binding.getStrength() != BindingStrength.EXAMPLE) {
vr = context.validateCode(value, vs);
vr = context.validateCode(new TerminologyServiceOptions("en"), value, vs);
}
txTime = txTime + (System.nanoTime() - t);
if (vr != null && !vr.isOk()) {
@ -3037,7 +3038,7 @@ private String misplacedItemError(QuestionnaireItemComponent qItem) {
}
long t = System.nanoTime();
ValidationResult res = context.validateCode(c, vs);
ValidationResult res = context.validateCode(new TerminologyServiceOptions("en"), c, vs);
txTime = txTime + (System.nanoTime() - t);
if (!res.isOk()) {
txRule(errors, res.getTxLink(), IssueType.CODEINVALID, value.line(), value.col(), stack.getLiteralPath(), false, "The value provided (" + c.getSystem() + "::" + c.getCode() + ") is not in the options value set in the questionnaire");
@ -4414,5 +4415,17 @@ private String misplacedItemError(QuestionnaireItemComponent qItem) {
return externalHostServices;
}
@Override
public String getValidationLanguage() {
// TODO Auto-generated method stub
return null;
}
@Override
public void setValidationLanguage(String value) {
// TODO Auto-generated method stub
}
}

View File

@ -246,6 +246,7 @@ public class ValidationEngine {
private String version;
private PackageCacheManager pcm;
private PrintWriter mapLog;
private boolean debug;
private class AsteriskFilter implements FilenameFilter {
String dir;
@ -666,7 +667,8 @@ public class ValidationEngine {
for (Entry<String, byte[]> t : source.entrySet()) {
String fn = t.getKey();
if (!exemptFile(fn)) {
System.out.print(" ..file: "+fn);
if (debug)
System.out.print("* load file: "+fn);
Resource r = null;
try {
if (version.equals("3.0.1") || version.equals("3.0.0")) {
@ -712,8 +714,11 @@ public class ValidationEngine {
throw new Exception("Unsupported format for "+fn);
} else
throw new Exception("Unsupported version "+version);
if (debug)
System.out.println(" .. success");
} catch (Exception e) {
if (debug)
System.out.println(" .. failed: "+e.getMessage());
throw new Exception("Error parsing "+fn+": "+e.getMessage(), e);
}
if (r != null) {
@ -1022,7 +1027,7 @@ public class ValidationEngine {
StructureDefinition sd = (StructureDefinition) res;
StructureDefinition base = context.fetchResource(StructureDefinition.class, sd.getBaseDefinition());
new ProfileUtilities(context, null, null).generateSnapshot(base, sd, sd.getUrl(), sd.getName());
new ProfileUtilities(context, null, null).generateSnapshot(base, sd, sd.getUrl(), "http://hl7.org/fhir/R4", sd.getName());
return sd;
}
@ -1055,5 +1060,13 @@ public class ValidationEngine {
this.mapLog = new PrintWriter(mapLog);
}
public boolean isDebug() {
return debug;
}
public void setDebug(boolean debug) {
this.debug = debug;
}
}

View File

@ -158,6 +158,8 @@ public class Validator {
System.out.println(" no default value. This parameter can appear any number of times");
System.out.println("-output [file]: a filename for the results (OperationOutcome)");
System.out.println(" Default: results are sent to the std out.");
System.out.println("-debug");
System.out.println(" Produce additional information about the loading/validation process");
System.out.println("-native: use schema for validation as well");
System.out.println(" * XML: w3c schema+schematron");
System.out.println(" * JSON: json.schema");
@ -267,6 +269,7 @@ public class Validator {
String sv = null;
String txLog = null;
String mapLog = null;
boolean doDebug = false;
// load the parameters - so order doesn't matter
for (int i = 0; i < args.length; i++) {
@ -309,6 +312,8 @@ public class Validator {
questionnaires.add(args[++i]);
else if (args[i].equals("-native"))
doNative = true;
else if (args[i].equals("-debug"))
doDebug = true;
else if (args[i].equals("-strictExtensions"))
anyExtensionsAllowed = false;
else if (args[i].equals("-hintAboutNonMustSupport"))
@ -376,6 +381,7 @@ public class Validator {
validator.setNative(doNative);
validator.setHintAboutNonMustSupport(hintAboutNonMustSupport);
validator.setAnyExtensionsAllowed(anyExtensionsAllowed);
validator.setDebug(doDebug);
XmlParser x = new XmlParser();
if (mode == EngineMode.TRANSFORM) {

View File

@ -248,6 +248,7 @@ public class ValidationEngine {
private String language;
private PackageCacheManager pcm;
private PrintWriter mapLog;
private boolean debug;
private class AsteriskFilter implements FilenameFilter {
String dir;
@ -693,7 +694,8 @@ public class ValidationEngine {
for (Entry<String, byte[]> t : source.entrySet()) {
String fn = t.getKey();
if (!exemptFile(fn)) {
System.out.print(" ..file: "+fn);
if (debug)
System.out.print("* load file: "+fn);
Resource r = null;
try {
if (version.equals("3.0.1") || version.equals("3.0.0")) {
@ -750,7 +752,8 @@ public class ValidationEngine {
throw new Exception("Unsupported format for "+fn);
} else
throw new Exception("Unsupported version "+version);
if (debug)
System.out.println(" .. success");
} catch (Exception e) {
System.out.println(" - ignored due to error: "+e.getMessage());
}
@ -1122,5 +1125,12 @@ public class ValidationEngine {
}
public boolean isDebug() {
return debug;
}
public void setDebug(boolean debug) {
this.debug = debug;
}
}

View File

@ -161,6 +161,8 @@ public class Validator {
System.out.println(" no default value. This parameter can appear any number of times");
System.out.println("-output [file]: a filename for the results (OperationOutcome)");
System.out.println(" Default: results are sent to the std out.");
System.out.println("-debug");
System.out.println(" Produce additional information about the loading/validation process");
System.out.println("-native: use schema for validation as well");
System.out.println(" * XML: w3c schema+schematron");
System.out.println(" * JSON: json.schema");
@ -275,6 +277,7 @@ public class Validator {
String txLog = null;
String mapLog = null;
String lang = null;
boolean doDebug = false;
// load the parameters - so order doesn't matter
for (int i = 0; i < args.length; i++) {
@ -317,7 +320,9 @@ public class Validator {
else
questionnaires.add(args[++i]);
else if (args[i].equals("-native"))
doNative = true;
doNative = true;
else if (args[i].equals("-debug"))
doDebug = true;
else if (args[i].equals("-strictExtensions"))
anyExtensionsAllowed = false;
else if (args[i].equals("-hintAboutNonMustSupport"))
@ -391,6 +396,7 @@ public class Validator {
validator.setHintAboutNonMustSupport(hintAboutNonMustSupport);
validator.setAnyExtensionsAllowed(anyExtensionsAllowed);
validator.setLanguage(lang);
validator.setDebug(doDebug);
IParser x;
if (output != null && output.endsWith(".json"))

View File

@ -221,7 +221,7 @@ public class InstanceValidatorR4Test {
}
});
when(myWorkerContext.validateCode(nullable(String.class), nullable(String.class), nullable(String.class))).thenAnswer(new Answer<CodeValidationResult>() {
when(myWorkerContext.validateCode(null, nullable(String.class), nullable(String.class), nullable(String.class))).thenAnswer(new Answer<CodeValidationResult>() {
@Override
public CodeValidationResult answer(InvocationOnMock theInvocation) throws Throwable {
String system = (String) theInvocation.getArguments()[0];
@ -276,7 +276,7 @@ public class InstanceValidatorR4Test {
IWorkerContext worker = new HapiWorkerContext(ourCtx, myDefaultProfileValidationSupport);
List<ValidationMessage> issues = new ArrayList<>();
ProfileUtilities profileUtilities = new ProfileUtilities(worker, issues, null);
profileUtilities.generateSnapshot(base, derived, "", "");
profileUtilities.generateSnapshot(base, derived, "", "", "");
return derived;
}
@ -500,7 +500,7 @@ public class InstanceValidatorR4Test {
@Test
public void testLargeBase64() throws IOException {
when(myWorkerContext.validateCode(nullable(CodeableConcept.class), nullable(ValueSet.class))).thenReturn(
when(myWorkerContext.validateCode(null, nullable(CodeableConcept.class), nullable(ValueSet.class))).thenReturn(
new IWorkerContext.ValidationResult(ValidationMessage.IssueSeverity.INFORMATION, "Code http://loinc.org/1-8 was not validated because the code system is not present")
);

View File

@ -853,6 +853,17 @@
"bundle-local-refs.xml" : {
"errorCount": 0,
"warningCount": 0
},
"nictiz-med.xml" : {
"version" : "3.0",
"profiles": [
"nictiz-med-extension.xml"
],
"errorCount": 0,
"profile": {
"source": "nictiz-med-profile.xml",
"errorCount": 1
}
}
}
}

View File

@ -0,0 +1,40 @@
<StructureDefinition xmlns="http://hl7.org/fhir">
<id value="zib-Product-Description" />
<url value="http://nictiz.nl/fhir/StructureDefinition/zib-Product-Description" />
<version value="2.0.0" />
<name value="Zib Product Description" />
<title value="HCIM Product Description" />
<status value="active" />
<publisher value="Nictiz" />
<contact>
<name value="Nictiz" />
<telecom>
<system value="email" />
<value value="info@nictiz.nl" />
<use value="work" />
</telecom>
</contact>
<description value="A textual description of the type of medication (including relevant properties of the composition and preparation method if possible), which is only used if no coded indication from the G Standard is available (magistral preparation)." />
<copyright value="CC0" />
<fhirVersion value="3.0.1" />
<kind value="primitive-type" />
<abstract value="false" />
<contextType value="resource" />
<context value="Medication" />
<type value="Extension" />
<baseDefinition value="http://hl7.org/fhir/StructureDefinition/Extension" />
<derivation value="constraint" />
<differential>
<element id="Extension.url">
<path value="Extension.url" />
<fixedUri value="http://nictiz.nl/fhir/StructureDefinition/zib-Product-Description" />
</element>
<element id="Extension.value[x]:valueString">
<path value="Extension.valueString" />
<sliceName value="valueString" />
<type>
<code value="string" />
</type>
</element>
</differential>
</StructureDefinition>

View File

@ -0,0 +1,493 @@
<StructureDefinition xmlns="http://hl7.org/fhir">
<id value="zib-Product" />
<url value="http://nictiz.nl/fhir/StructureDefinition/zib-Product" />
<version value="2.0.3" />
<name value="Zib PharmaceuticalProduct" />
<title value="HCIM PharmaceuticalProduct" />
<status value="active" />
<publisher value="Nictiz" />
<contact>
<name value="Nictiz" />
<telecom>
<system value="email" />
<value value="info@nictiz.nl" />
<use value="work" />
</telecom>
</contact>
<description value="This profile is based on Medicatieproces V9.0.7 and the related Dutch Health and Care Information models (Dutch: Zorginformatiebouwsteen or ZIB) nl.zorg.part.PharmacauticalProduct version 2.0, year 2017. It was first modeled after nl.zorg.part.Product from the prerelease version 1.0 of 2017 (https://zibs.nl/wiki/Product-v1.0(2017EN)) which is why the canonical uri is different. This will updated in the next iteration of the HCIMs/profiles" />
<purpose value="The purpose of PharmaceuticalProduct is to unambiguously describe the medication to be used." />
<copyright value="CC0" />
<fhirVersion value="3.0.1" />
<mapping>
<identity value="hcim-pharmaceuticalproduct-v2.0-2017EN" />
<uri value="https://zibs.nl/wiki/PharmaceuticalProduct-v2.0(2017EN)" />
<name value="HCIM PharmaceuticalProduct-v2.0(2017EN)" />
</mapping>
<kind value="resource" />
<abstract value="false" />
<type value="Medication" />
<baseDefinition value="http://hl7.org/fhir/StructureDefinition/Medication" />
<derivation value="constraint" />
<differential>
<element id="Medication">
<path value="Medication" />
<short value="Product" />
<definition value="Root concept of the Product partial information model. This root concept contains all data elements of the Product partial information model.
The prescribed product is usually a medicine. However, medical aids and bandages can also be prescribed and supplied via the pharmacy. Strictly speaking, food and blood products do not belong in the medication category, but can be prescribed and supplied by a pharmacy as well.
A type of medication can be indicated with a single codeThat code can be chosen from several possible coding systems (concretely: GPK, PRK, HPK or article numbers). Correct use of these codes in the software systems will sufficiently record the composition of the product used, making a complete product specification unnecessary.
In addition to a primary code, alternative codes from other coding systems can also be entered (so that the GPK can be sent along in the event that the patient was registered based on PRK, for example).
Entering multiple ingredients will enable you to display a compound product. If one of the composite parts is liquid, the dosage will be given in milliliters; otherwise it will be given in units.
In that case, the composition of the medicationcan be specified implicitly (with the use of a medication code) or explicitly, for example by listing the (active) substance(s) of the medication.
Prescriptions to be prepared by the pharmacy can be entered as well. This can be done by means of the option listed above to enter coded ingredients and/or by entering the composition and preparation method as free text." />
<alias value="Geneesmiddel" />
<mapping>
<identity value="hcim-pharmaceuticalproduct-v2.0-2017EN" />
<map value="NL-CM:9.7.19926" />
<comment value="FarmaceuticalProduct" />
</mapping>
</element>
<element id="Medication.extension">
<path value="Medication.extension" />
<slicing>
<discriminator>
<type value="value" />
<path value="url" />
</discriminator>
<rules value="open" />
</slicing>
</element>
<element id="Medication.extension:description">
<path value="Medication.extension" />
<sliceName value="description" />
<short value="Description" />
<definition value="A textual description of the type of medication (including relevant properties of the composition and preparation method if possible), which is only used if no coded indication from the G Standard is available (magistral preparation)." />
<alias value="Omschrijving" />
<max value="1" />
<type>
<code value="Extension" />
<profile value="http://nictiz.nl/fhir/StructureDefinition/zib-Product-Description" />
</type>
<mapping>
<identity value="hcim-pharmaceuticalproduct-v2.0-2017EN" />
<map value="NL-CM:9.7.19784" />
<comment value="Description" />
</mapping>
</element>
<element id="Medication.code">
<path value="Medication.code" />
<example>
<label value="Example of Medication code" />
<valueCodeableConcept>
<coding>
<system value="urn:oid:2.16.840.1.113883.2.4.4.8" />
<code value="13610554" />
<display value="Carbasalaatcalcium Sandoz 600 30 sac" />
</coding>
</valueCodeableConcept>
</example>
</element>
<element id="Medication.code.coding">
<path value="Medication.code.coding" />
<slicing>
<discriminator>
<type value="value" />
<path value="system" />
</discriminator>
<rules value="open" />
</slicing>
<short value="MedicationCode" />
<definition value="Coding medication in the Netherlands is done on the basis of the G standard (issued by Z-index), which is filled under the direction of KNMP.
The coded medication can be expressed as:
- GTIN International Article Number
- KNMP article number = ATKODE (2.16.840.1.113883.2.4.4.8)
- Trade product code (HPK)
- Prescription code (PRK)
- Generic product code (GPK)
- Anatomic Therapeutic Classification code (ATC)
- SNOMED CT code
- Substance Name Code (SNK)
- Substance Name Code, in combination with Route of Administration (SSK)
- 90.000.000 number (individual code setting) (or similar from the facility)
The GTIN enables identification of the product including its origin with a barcode.
The ATKODE is the number with which wholesalers link the article to pharmacy systems (e.g. a box with 3 strips of 10 tablets).
The HPK is the code for the trade product (with the brand name) as used per dose/per time the medication is taken (1 pill, 1 puff, 1ml)
The PRK codes for the same product as the HPK does, but is not linked to a manufacturer (no brand name, no characteristics such as color, geometrical shape etc.). This code will enable a generic prescription, while still defining which trade product can be taken (e.g. a 200ml bag).
The generic product code defines the composition of a product, and is sufficient for recording the prescription, but not the order.
The prescription code (PRK) was developed and added to the older generic (GPK) and supplier-specific (HPK, ATKODE) coding to enable a generic product to be entered without listing a specific brand on the one hand, and to enable providing enough information to support the pharmacy supplying it on the other.
The Substance Name Code (SNK) and the Substance Name Code, in combination with Route of Administration (SSK) are used to prescribe at a more generic level.
The GTIN coding is used for the implementation of a barcode scanning standard and to be able to trace the origin of the product.
The 90.000.000 number is used in accordance with national agreements." />
<comment value="It is possible to capture multiple product codes with .coding elements. These product codes need to be related to each other. It should not be possible that two different product codes result in two medication products: only more generic product codes are allowed next to the original product code." />
<alias value="ProductCode" />
<alias value="Geneesmiddel code" />
<mapping>
<identity value="hcim-pharmaceuticalproduct-v2.0-2017EN" />
<map value="NL-CM:9.7.19927" />
<comment value="MedicationCode" />
</mapping>
</element>
<element id="Medication.code.coding:productCodeZICodelijst">
<path value="Medication.code.coding" />
<sliceName value="productCodeZICodelijst" />
<max value="1" />
<binding>
<strength value="required" />
<description value="ProductCodeZICodelijst" />
<valueSetReference>
<reference value="http://decor.nictiz.nl/fhir/ValueSet/2.16.840.1.113883.2.4.3.11.60.40.2.9.7.1--20171231000000" />
<display value="ProductCodeZICodelijst" />
</valueSetReference>
</binding>
</element>
<element id="Medication.code.coding:productCodeHPKCodelijst">
<path value="Medication.code.coding" />
<sliceName value="productCodeHPKCodelijst" />
<max value="1" />
<binding>
<strength value="required" />
<description value="ProductCodeHPKCodelijst" />
<valueSetReference>
<reference value="http://decor.nictiz.nl/fhir/ValueSet/2.16.840.1.113883.2.4.3.11.60.40.2.9.7.5--20171231000000" />
<display value="ProductCodeHPKCodelijst" />
</valueSetReference>
</binding>
</element>
<element id="Medication.code.coding:productCodeGTINCodelijst">
<path value="Medication.code.coding" />
<sliceName value="productCodeGTINCodelijst" />
<max value="1" />
<binding>
<strength value="required" />
<description value="ProductCodeGTINCodelijst" />
<valueSetReference>
<reference value="http://decor.nictiz.nl/fhir/ValueSet/2.16.840.1.113883.2.4.3.11.60.40.2.9.7.2--20171231000000" />
<display value="ProductCodeGTINCodelijst" />
</valueSetReference>
</binding>
</element>
<element id="Medication.code.coding:productCodeGPKCodelijst">
<path value="Medication.code.coding" />
<sliceName value="productCodeGPKCodelijst" />
<max value="1" />
<binding>
<strength value="required" />
<description value="ProductCodeGPKCodelijst" />
<valueSetReference>
<reference value="http://decor.nictiz.nl/fhir/ValueSet/2.16.840.1.113883.2.4.3.11.60.40.2.9.7.6--20171231000000" />
<display value="ProductCodeGPKCodelijst" />
</valueSetReference>
</binding>
</element>
<element id="Medication.code.coding:productCodePRKCodelijst">
<path value="Medication.code.coding" />
<sliceName value="productCodePRKCodelijst" />
<max value="1" />
<binding>
<strength value="required" />
<description value="ProductCodePRKCodelijst" />
<valueSetReference>
<reference value="http://decor.nictiz.nl/fhir/ValueSet/2.16.840.1.113883.2.4.3.11.60.40.2.9.7.3--20171231000000" />
<display value="ProductCodePRKCodelijst" />
</valueSetReference>
</binding>
</element>
<element id="Medication.code.coding:productCodeATCCodelijst">
<path value="Medication.code.coding" />
<sliceName value="productCodeATCCodelijst" />
<max value="1" />
<binding>
<strength value="required" />
<description value="ProductCodeATCCodelijst" />
<valueSetReference>
<reference value="http://decor.nictiz.nl/fhir/ValueSet/2.16.840.1.113883.2.4.3.11.60.40.2.9.7.7--20171231000000" />
<display value="ProductCodeATCCodelijst" />
</valueSetReference>
</binding>
</element>
<element id="Medication.code.text">
<extension url="http://hl7.org/fhir/StructureDefinition/elementdefinition-translatable">
<valueBoolean value="true" />
</extension>
<path value="Medication.code.text" />
<short value="Medication" />
<definition value="There is no code for medication entered in free text. In these cases, enter the complete description." />
<alias value="ProductNaam" />
<alias value="Geneesmiddel" />
<example>
<label value="Example of Medication as free text" />
<valueString value="PARACETAMOL 500MG TABLET" />
</example>
<mapping>
<identity value="hcim-pharmaceuticalproduct-v2.0-2017EN" />
<map value="NL-CM:9.7.19929" />
<comment value="Medication" />
</mapping>
</element>
<element id="Medication.form">
<path value="Medication.form" />
<short value="PharmaceuticalForm" />
<definition value="The pharmaceutical form indicates the form of the medication in accordance with the route of administration. Examples include: tablet, suppository, infusion liquid, ointment. If the product has a generic code in the G standard, the form will be known in the G standard. For products without a code (free text, preparation by the pharmacy), the means of administration can be entered." />
<alias value="FarmaceutischeVorm" />
<example>
<label value="Example of Pharmaceutical form" />
<valueCodeableConcept>
<coding>
<system value="urn:oid:2.16.840.1.113883.2.4.4.11" />
<code value="230" />
<display value="TABLET" />
</coding>
</valueCodeableConcept>
</example>
<binding>
<strength value="required" />
<description value="FarmaceutischeVormCodelijst" />
<valueSetReference>
<reference value="http://decor.nictiz.nl/fhir/ValueSet/2.16.840.1.113883.2.4.3.11.60.40.2.9.7.8--20171231000000" />
<display value="FarmaceutischeVormCodelijst" />
</valueSetReference>
</binding>
<mapping>
<identity value="hcim-pharmaceuticalproduct-v2.0-2017EN" />
<map value="NL-CM:9.7.19931" />
<comment value="PharmaceuticalForm" />
</mapping>
</element>
<element id="Medication.ingredient">
<path value="Medication.ingredient" />
<short value="Ingredient" />
<definition value="Container of the Ingredient concept. This container contains all data elements of the Ingredient concept.
A product contains one or more active substances and excipients. These are usually determined by the product code. For medication prepared or compounded by the local pharmacy, each ingredient must be entered separately.
The active substances play an important role, as they:
a) determine the pharmacotherapeutic effect of the medication and
b) serve as the basis for the indication of the strength of the medication (e.g. 200mg)." />
<alias value="Ingredient" />
<mapping>
<identity value="hcim-pharmaceuticalproduct-v2.0-2017EN" />
<map value="NL-CM:9.7.19932" />
<comment value="Ingredient" />
</mapping>
</element>
<element id="Medication.ingredient.item[x]:itemCodeableConcept">
<path value="Medication.ingredient.itemCodeableConcept" />
<sliceName value="itemCodeableConcept" />
<short value="Substance" />
<definition value="Active substance or excipient.
Here, the same codes can be used as for the ProductCode (for dilutions and compounds in particular), but now, the ATC, SSK and SNK codes can also be used to indicate a substance (to list ingredients of local products prepared by the pharmacy).
- GTIN International Article Number
- KNMP article number
- Trade product code (HPK)
- Prescription code (PRK)
- Generic product code (GPK)
- ATC (anatomic therapeutic classification)
- SSK (substance name code with route of administration)
- SNK (substance name code)
The ATC is an international classification of pharmaceutical substances without a reference to specific products on the market. Therefore, the ATC code of a generic product will not contain a reference to a certain dose, pharmaceutical form or route of administration; it will only contain a reference to the ingredients (not the amount/concentration/strength)." />
<alias value="IngredientCode" />
<type>
<code value="CodeableConcept" />
</type>
<example>
<label value="Example of Ingredient substance" />
<valueCodeableConcept>
<coding>
<system value="http://www.whocc.no/atc" />
<code value="B01AC08" />
<display value="carbasalate calcium" />
</coding>
</valueCodeableConcept>
</example>
<mapping>
<identity value="hcim-pharmaceuticalproduct-v2.0-2017EN" />
<map value="NL-CM:9.7.19934" />
<comment value="SubstanceCode" />
</mapping>
</element>
<element id="Medication.ingredient.item[x]:itemCodeableConcept.coding">
<path value="Medication.ingredient.itemCodeableConcept.coding" />
<slicing>
<discriminator>
<type value="value" />
<path value="system" />
</discriminator>
<rules value="open" />
</slicing>
</element>
<element id="Medication.ingredient.item[x]:itemCodeableConcept.coding:ingredientCodeZICodelijst">
<path value="Medication.ingredient.itemCodeableConcept.coding" />
<sliceName value="ingredientCodeZICodelijst" />
<binding>
<strength value="required" />
<description value="IngredientCodeZICodelijst" />
<valueSetReference>
<reference value="http://decor.nictiz.nl/fhir/ValueSet/2.16.840.1.113883.2.4.3.11.60.40.2.9.7.12--20171231000000" />
<display value="IngredientCodeZICodelijst" />
</valueSetReference>
</binding>
</element>
<element id="Medication.ingredient.item[x]:itemCodeableConcept.coding:ingredientCodeGTINCodeLijst">
<path value="Medication.ingredient.itemCodeableConcept.coding" />
<sliceName value="ingredientCodeGTINCodeLijst" />
<binding>
<strength value="required" />
<description value="IngredientCodeGTINCodeLijst" />
<valueSetReference>
<reference value="http://decor.nictiz.nl/fhir/ValueSet/2.16.840.1.113883.2.4.3.11.60.40.2.9.7.16--20171231000000" />
<display value="IngredientCodeGTINCodeLijst" />
</valueSetReference>
</binding>
</element>
<element id="Medication.ingredient.item[x]:itemCodeableConcept.coding:ingredientCodeATCCodelijst">
<path value="Medication.ingredient.itemCodeableConcept.coding" />
<sliceName value="ingredientCodeATCCodelijst" />
<binding>
<strength value="required" />
<description value="IngredientCodeATCCodelijst" />
<valueSetReference>
<reference value="http://decor.nictiz.nl/fhir/ValueSet/2.16.840.1.113883.2.4.3.11.60.40.2.9.7.13--20171231000000" />
<display value="IngredientCodeATCCodelijst" />
</valueSetReference>
</binding>
</element>
<element id="Medication.ingredient.item[x]:itemCodeableConcept.coding:ingredientCodeHPKLijst">
<path value="Medication.ingredient.itemCodeableConcept.coding" />
<sliceName value="ingredientCodeHPKLijst" />
<binding>
<strength value="required" />
<description value="IngredientCodeHPKLijst" />
<valueSetReference>
<reference value="http://decor.nictiz.nl/fhir/ValueSet/2.16.840.1.113883.2.4.3.11.60.40.2.9.7.10--20171231000000" />
<display value="IngredientCodeHPKLijst" />
</valueSetReference>
</binding>
</element>
<element id="Medication.ingredient.item[x]:itemCodeableConcept.coding:ingredientCodeGPKLijst">
<path value="Medication.ingredient.itemCodeableConcept.coding" />
<sliceName value="ingredientCodeGPKLijst" />
<binding>
<strength value="required" />
<description value="IngredientCodeGPKLijst" />
<valueSetReference>
<reference value="http://decor.nictiz.nl/fhir/ValueSet/2.16.840.1.113883.2.4.3.11.60.40.2.9.7.9--20171231000000" />
<display value="IngredientCodeGPKLijst" />
</valueSetReference>
</binding>
</element>
<element id="Medication.ingredient.item[x]:itemCodeableConcept.coding:ingredientCodeSNKLijst">
<path value="Medication.ingredient.itemCodeableConcept.coding" />
<sliceName value="ingredientCodeSNKLijst" />
<binding>
<strength value="required" />
<description value="IngredientCodeSNKLijst" />
<valueSetReference>
<reference value="http://decor.nictiz.nl/fhir/ValueSet/2.16.840.1.113883.2.4.3.11.60.40.2.9.7.14--20171231000000" />
<display value="IngredientCodeSNKLijst" />
</valueSetReference>
</binding>
</element>
<element id="Medication.ingredient.item[x]:itemCodeableConcept.coding:ingredientCodePRKLijst">
<path value="Medication.ingredient.itemCodeableConcept.coding" />
<sliceName value="ingredientCodePRKLijst" />
<binding>
<strength value="required" />
<description value="IngredientCodePRKLijst" />
<valueSetReference>
<reference value="http://decor.nictiz.nl/fhir/ValueSet/2.16.840.1.113883.2.4.3.11.60.40.2.9.7.11--20171231000000" />
<display value="IngredientCodePRKLijst" />
</valueSetReference>
</binding>
</element>
<element id="Medication.ingredient.item[x]:itemCodeableConcept.coding:ingredientCodeSSKLijst">
<path value="Medication.ingredient.itemCodeableConcept.coding" />
<sliceName value="ingredientCodeSSKLijst" />
<binding>
<strength value="required" />
<description value="IngredientCodeSSKLijst" />
<valueSetReference>
<reference value="http://decor.nictiz.nl/fhir/ValueSet/2.16.840.1.113883.2.4.3.11.60.40.2.9.7.15--20171231000000" />
<display value="IngredientCodeSSKLijst" />
</valueSetReference>
</binding>
</element>
<element id="Medication.ingredient.amount">
<path value="Medication.ingredient.amount" />
<short value="Concentration" />
<definition value="The relative amount of this ingredient in this product.
Calculation of Concentration = Ingredient Amount ÷ Product Amount.
This could be a concentration if the medication is dissolved in liquid, for example: 25mg/tablet or 50IE/ml of 200mg/500ml." />
<alias value="Sterkte" />
<alias value="Concentratie" />
<alias value="Hoeveelheid" />
<example>
<label value="Example of Concentration" />
<valueRatio>
<numerator>
<value value="5" />
<unit value="ml" />
<system value="http://unitsofmeasure.org" />
<code value="mL" />
</numerator>
<denominator>
<value value="200" />
<unit value="ml" />
<system value="http://unitsofmeasure.org" />
<code value="mL" />
</denominator>
</valueRatio>
</example>
<mapping>
<identity value="hcim-pharmaceuticalproduct-v2.0-2017EN" />
<map value="NL-CM:9.7.19933" />
<comment value="Concentration" />
</mapping>
</element>
<element id="Medication.ingredient.amount.numerator">
<path value="Medication.ingredient.amount.numerator" />
<short value="IngredientAmount" />
<definition value="The amount of this ingredient. This is the numerator for the calculation of the concentration. The unit should be selected from the G-Standard (Table 902)" />
<alias value="IngredientHoeveelheid" />
<mapping>
<identity value="hcim-pharmaceuticalproduct-v2.0-2017EN" />
<map value="NL-CM:9.7.22277" />
<comment value="IngredientAmount" />
</mapping>
</element>
<element id="Medication.ingredient.amount.denominator">
<path value="Medication.ingredient.amount.denominator" />
<short value="ProductAmount" />
<definition value="Amount of the product. This is the denominator for the calculation of the concentration" />
<alias value="ProductHoeveelheid" />
<mapping>
<identity value="hcim-pharmaceuticalproduct-v2.0-2017EN" />
<map value="NL-CM:9.7.22278" />
<comment value="ProductAmount" />
</mapping>
</element>
<element id="Medication.package.content.item[x]">
<path value="Medication.package.content.item[x]" />
<type>
<code value="CodeableConcept" />
</type>
<type>
<code value="Reference" />
<targetProfile value="http://nictiz.nl/fhir/StructureDefinition/zib-Product" />
</type>
</element>
</differential>
</StructureDefinition>

View File

@ -0,0 +1,22 @@
<Medication xmlns="http://hl7.org/fhir">
<id value="HCIM-PharmaceuticalProduct--zindex-voorschrift--705155"/>
<meta>
<profile value="http://nictiz.nl/fhir/StructureDefinition/zib-Product"/>
</meta>
<extension url="http://nictiz.nl/fhir/StructureDefinition/zib-Product-Description">
<valueString value="description"/>
</extension>
<code>
<coding>
<system value="urn:oid:2.16.840.1.113883.2.4.4.10"/> <code value="32336"/> <display value="32336"/>
</coding>
<coding>
<system value="urn:oid:2.16.840.1.113883.2.4.4.1"/> <code value="82724"/> <display value="82724"/>
</coding>
<coding>
<system value="http://www.whocc.no/atc"/> <code value="J02AC02"/> <display value="J02AC02"/>
</coding>
<text value="ITRACONAZOL SDZ 100MG CAPS"/>
</code>
<status value="active"/> <isBrand value="false"/>
</Medication>