Merge branch 'master' of https://github.com/hapifhir/org.hl7.fhir.core
This commit is contained in:
commit
f2699a1d50
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>org.hl7.fhir.core</artifactId>
|
||||
<version>5.1.8-SNAPSHOT</version>
|
||||
<version>5.1.9-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -65,6 +65,7 @@ import org.hl7.fhir.convertors.conv10_40.ValueSet10_40;
|
|||
import org.hl7.fhir.dstu2.model.CodeableConcept;
|
||||
import org.hl7.fhir.dstu2.model.Parameters;
|
||||
import org.hl7.fhir.dstu2.model.Parameters.ParametersParameterComponent;
|
||||
import org.hl7.fhir.dstu2.model.PositiveIntType;
|
||||
import org.hl7.fhir.dstu2.model.Reference;
|
||||
import org.hl7.fhir.dstu2.utils.ToolingExtensions;
|
||||
import org.hl7.fhir.exceptions.FHIRException;
|
||||
|
@ -3423,4 +3424,10 @@ public class VersionConvertor_10_40 {
|
|||
public static org.hl7.fhir.dstu2.model.Resource convertResource(org.hl7.fhir.r4.model.Resource src) throws FHIRException {
|
||||
return convertResource(src, null);
|
||||
}
|
||||
|
||||
public static UnsignedIntType convertUnsignedIntToPositive(PositiveIntType src) {
|
||||
org.hl7.fhir.r4.model.UnsignedIntType tgt = src.hasValue() ? new org.hl7.fhir.r4.model.UnsignedIntType(src.getValue()) : new org.hl7.fhir.r4.model.UnsignedIntType();
|
||||
copyElement(src, tgt);
|
||||
return tgt;
|
||||
}
|
||||
}
|
|
@ -3742,7 +3742,7 @@ public class VersionConvertor_40_50 {
|
|||
return convertUsageContext((org.hl7.fhir.r4.model.UsageContext) src);
|
||||
if (src instanceof org.hl7.fhir.r4.model.ElementDefinition)
|
||||
return convertElementDefinition((org.hl7.fhir.r4.model.ElementDefinition) src);
|
||||
throw new Error("Unknown type " + src.fhirType());
|
||||
throw new FHIRException("Unknown type " + src.fhirType());
|
||||
}
|
||||
|
||||
public static org.hl7.fhir.r4.model.Type convertType(org.hl7.fhir.r5.model.DataType src) throws FHIRException {
|
||||
|
@ -3868,7 +3868,7 @@ public class VersionConvertor_40_50 {
|
|||
return convertUsageContext((org.hl7.fhir.r5.model.UsageContext) src);
|
||||
if (src instanceof org.hl7.fhir.r5.model.ElementDefinition)
|
||||
return convertElementDefinition((org.hl7.fhir.r5.model.ElementDefinition) src);
|
||||
throw new Error("Unknown type " + src.fhirType());
|
||||
throw new FHIRException("Unknown type " + src.fhirType());
|
||||
}
|
||||
|
||||
protected static void copyDomainResource(org.hl7.fhir.r4.model.DomainResource src, org.hl7.fhir.r5.model.DomainResource tgt) throws FHIRException {
|
||||
|
@ -4174,7 +4174,7 @@ public class VersionConvertor_40_50 {
|
|||
return VerificationResult40_50.convertVerificationResult((org.hl7.fhir.r4.model.VerificationResult) src);
|
||||
if (src instanceof org.hl7.fhir.r4.model.VisionPrescription)
|
||||
return VisionPrescription40_50.convertVisionPrescription((org.hl7.fhir.r4.model.VisionPrescription) src);
|
||||
throw new Error("Unknown resource " + src.fhirType());
|
||||
throw new FHIRException("Unknown resource " + src.fhirType());
|
||||
}
|
||||
|
||||
public static org.hl7.fhir.r4.model.Resource convertResource(org.hl7.fhir.r5.model.Resource src) throws FHIRException {
|
||||
|
@ -4441,7 +4441,7 @@ public class VersionConvertor_40_50 {
|
|||
return VerificationResult40_50.convertVerificationResult((org.hl7.fhir.r5.model.VerificationResult) src);
|
||||
if (src instanceof org.hl7.fhir.r5.model.VisionPrescription)
|
||||
return VisionPrescription40_50.convertVisionPrescription((org.hl7.fhir.r5.model.VisionPrescription) src);
|
||||
throw new Error("Unknown resource " + src.fhirType());
|
||||
throw new FHIRException("Unknown resource " + src.fhirType());
|
||||
}
|
||||
|
||||
protected static org.hl7.fhir.r5.model.CodeType convertResourceEnum(org.hl7.fhir.r4.model.CodeType src) {
|
||||
|
|
|
@ -9,6 +9,7 @@ public class AllergyIntolerance10_40 {
|
|||
if (src == null)
|
||||
return null;
|
||||
org.hl7.fhir.r4.model.AllergyIntolerance tgt = new org.hl7.fhir.r4.model.AllergyIntolerance();
|
||||
VersionConvertor_10_40.copyDomainResource(src, tgt);
|
||||
for (org.hl7.fhir.dstu2.model.Identifier t : src.getIdentifier()) tgt.addIdentifier(VersionConvertor_10_40.convertIdentifier(t));
|
||||
if (src.hasOnset())
|
||||
tgt.setOnset(VersionConvertor_10_40.convertType(src.getOnsetElement()));
|
||||
|
|
|
@ -15,7 +15,7 @@ public class MedicationRequest10_40 {
|
|||
tgt.setIntent(org.hl7.fhir.r4.model.MedicationRequest.MedicationRequestIntent.ORDER);
|
||||
for (org.hl7.fhir.dstu2.model.Identifier identifier : src.getIdentifier()) tgt.addIdentifier(VersionConvertor_10_40.convertIdentifier(identifier));
|
||||
if (src.hasDateWritten())
|
||||
tgt.setAuthoredOn(src.getDateWritten());
|
||||
tgt.setAuthoredOnElement(VersionConvertor_10_40.convertDateTime(src.getDateWrittenElement()));
|
||||
if (src.hasStatus())
|
||||
tgt.setStatus(org.hl7.fhir.r4.model.MedicationRequest.MedicationRequestStatus.fromCode(src.getStatus().toCode()));
|
||||
if (src.hasPatient())
|
||||
|
@ -48,7 +48,7 @@ public class MedicationRequest10_40 {
|
|||
return null;
|
||||
org.hl7.fhir.r4.model.Dosage tgt = new org.hl7.fhir.r4.model.Dosage();
|
||||
if (src.hasText())
|
||||
tgt.setText(src.getText());
|
||||
tgt.setTextElement(VersionConvertor_10_40.convertString(src.getTextElement()));
|
||||
if (src.hasAdditionalInstructions())
|
||||
tgt.addAdditionalInstruction(VersionConvertor_10_40.convertCodeableConcept(src.getAdditionalInstructions()));
|
||||
if (src.hasTiming())
|
||||
|
@ -79,7 +79,7 @@ public class MedicationRequest10_40 {
|
|||
if (src.hasValidityPeriod())
|
||||
tgt.setValidityPeriod(VersionConvertor_10_40.convertPeriod(src.getValidityPeriod()));
|
||||
if (src.hasNumberOfRepeatsAllowed())
|
||||
tgt.setNumberOfRepeatsAllowed(src.getNumberOfRepeatsAllowed());
|
||||
tgt.setNumberOfRepeatsAllowedElement(VersionConvertor_10_40.convertUnsignedIntToPositive(src.getNumberOfRepeatsAllowedElement()));
|
||||
if (src.hasQuantity())
|
||||
tgt.setQuantity(VersionConvertor_10_40.convertSimpleQuantity(src.getQuantity()));
|
||||
if (src.hasExpectedSupplyDuration())
|
||||
|
|
|
@ -1,26 +1,29 @@
|
|||
package org.hl7.fhir.convertors.misc;
|
||||
|
||||
import org.hl7.fhir.exceptions.FHIRException;
|
||||
import org.hl7.fhir.r5.formats.JsonParser;
|
||||
import org.hl7.fhir.r5.model.Enumerations.PublicationStatus;
|
||||
import org.hl7.fhir.r5.model.ValueSet;
|
||||
import org.hl7.fhir.r5.model.ValueSet.ConceptSetComponent;
|
||||
import org.hl7.fhir.r4.utils.client.FHIRToolingClient;
|
||||
import org.hl7.fhir.r4.formats.JsonParser;
|
||||
import org.hl7.fhir.r4.model.Enumerations.PublicationStatus;
|
||||
import org.hl7.fhir.r4.model.ValueSet;
|
||||
import org.hl7.fhir.r4.model.ValueSet.ConceptSetComponent;
|
||||
import org.hl7.fhir.utilities.CSVReader;
|
||||
import org.hl7.fhir.utilities.TextFile;
|
||||
import org.hl7.fhir.utilities.Utilities;
|
||||
import org.hl7.fhir.utilities.xml.XMLUtil;
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
import java.io.*;
|
||||
import java.net.URISyntaxException;
|
||||
import java.text.ParseException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class VSACImporter extends OIDBasedValueSetImporter {
|
||||
|
||||
public static void main(String[] args) throws FileNotFoundException, FHIRException, IOException, ParseException {
|
||||
public static void main(String[] args) throws FileNotFoundException, FHIRException, IOException, ParseException, URISyntaxException {
|
||||
// new PhinVadsImporter().importValueSet(TextFile.fileToBytes("C:\\work\\org.hl7.fhir\\packages\\us.cdc.phinvads-source\\source\\PHVS_BirthDefectsLateralityatDiagnosis_HL7_V1.txt"));
|
||||
VSACImporter self = new VSACImporter();
|
||||
self.process(args[0], args[1]);
|
||||
self.process(args[0], args[1], args[2], args[3]);
|
||||
}
|
||||
|
||||
public VSACImporter() throws FileNotFoundException, FHIRException, IOException {
|
||||
|
@ -28,69 +31,87 @@ public class VSACImporter extends OIDBasedValueSetImporter {
|
|||
init();
|
||||
}
|
||||
|
||||
private void process(String source, String dest) {
|
||||
for (File f : new File(source).listFiles()) {
|
||||
private void process(String source, String dest, String username, String password) throws FHIRException, FileNotFoundException, IOException, URISyntaxException {
|
||||
CSVReader csv = new CSVReader(new FileInputStream(source));
|
||||
csv.readHeaders();
|
||||
FHIRToolingClient client = new FHIRToolingClient("https://cts.nlm.nih.gov/fhir", username, password);
|
||||
int i = 0;
|
||||
while (csv.line()) {
|
||||
String oid = csv.cell("OID");
|
||||
try {
|
||||
System.out.println("Process " + f.getName());
|
||||
List<ValueSet> vsl = importValueSet(TextFile.fileToBytes(f));
|
||||
for (ValueSet vs : vsl) {
|
||||
if (vs.getId() != null) {
|
||||
new JsonParser().compose(new FileOutputStream(Utilities.path(dest, "ValueSet-" + vs.getId() + ".json")), vs);
|
||||
}
|
||||
ValueSet vs = client.read(ValueSet.class, oid);
|
||||
new JsonParser().compose(new FileOutputStream(Utilities.path(dest, "ValueSet-"+oid+".json")), vs);
|
||||
i++;
|
||||
if (i % 100 == 0) {
|
||||
System.out.println(i);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
System.out.println("Unable to fetch OID "+oid+": "+e.getMessage());
|
||||
}
|
||||
}
|
||||
System.out.println("Done. "+i+" ValueSets");
|
||||
// for (File f : new File(source).listFiles()) {
|
||||
// try {
|
||||
// System.out.println("Process " + f.getName());
|
||||
// List<ValueSet> vsl = importValueSet(TextFile.fileToBytes(f));
|
||||
// for (ValueSet vs : vsl) {
|
||||
// if (vs.getId() != null) {
|
||||
// new JsonParser().compose(new FileOutputStream(Utilities.path(dest, "ValueSet-" + vs.getId() + ".json")), vs);
|
||||
// }
|
||||
// }
|
||||
// } catch (Exception e) {
|
||||
// e.printStackTrace();
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
private List<ValueSet> importValueSet(byte[] source) throws Exception {
|
||||
List<ValueSet> res = new ArrayList<ValueSet>();
|
||||
Element x = loadXml(new ByteArrayInputStream(source)).getDocumentElement();
|
||||
List<Element> vl = XMLUtil.getNamedChildren(x, "DescribedValueSet");
|
||||
for (Element v : vl) {
|
||||
ValueSet vs = new ValueSet();
|
||||
vs.setId(v.getAttribute("ID"));
|
||||
vs.setUrl("http://vsac.nlm.nih.gov/fhir/ValueSet/" + vs.getId());
|
||||
vs.getMeta().setSource("https://vsac.nlm.nih.gov/valueset/" + vs.getId() + "/expansion");
|
||||
vs.setVersion(v.getAttribute("version"));
|
||||
vs.setTitle(v.getAttribute("displayName"));
|
||||
vs.setName(Utilities.titleize(vs.getTitle()).replace(" ", ""));
|
||||
Element d = XMLUtil.getNamedChild(v, "Purpose");
|
||||
if (d != null) {
|
||||
vs.setDescription(d.getTextContent());
|
||||
}
|
||||
Element s = XMLUtil.getNamedChild(v, "Status");
|
||||
if (s != null && "Active".equals(s.getTextContent())) {
|
||||
vs.setStatus(PublicationStatus.ACTIVE);
|
||||
} else {
|
||||
vs.setStatus(PublicationStatus.DRAFT);
|
||||
}
|
||||
Element dt = XMLUtil.getNamedChild(v, "RevisionDate");
|
||||
if (dt != null) {
|
||||
vs.getDateElement().setValueAsString(dt.getTextContent());
|
||||
}
|
||||
|
||||
Element cl = XMLUtil.getNamedChild(v, "ConceptList");
|
||||
Element cc = XMLUtil.getFirstChild(cl);
|
||||
|
||||
while (cc != null) {
|
||||
String code = cc.getAttribute("code");
|
||||
String display = cc.getAttribute("displayName");
|
||||
String csoid = cc.getAttribute("codeSystem");
|
||||
String csver = cc.getAttribute("codeSystemVersion");
|
||||
String url = context.oid2Uri(csoid);
|
||||
if (url == null) {
|
||||
url = "urn:oid:" + csoid;
|
||||
}
|
||||
csver = fixVersionforSystem(url, csver);
|
||||
ConceptSetComponent inc = getInclude(vs, url, csver);
|
||||
inc.addConcept().setCode(code).setDisplay(display);
|
||||
cc = XMLUtil.getNextSibling(cc);
|
||||
}
|
||||
|
||||
res.add(vs);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
// private List<ValueSet> importValueSet(byte[] source) throws Exception {
|
||||
// List<ValueSet> res = new ArrayList<ValueSet>();
|
||||
// Element x = loadXml(new ByteArrayInputStream(source)).getDocumentElement();
|
||||
// List<Element> vl = XMLUtil.getNamedChildren(x, "DescribedValueSet");
|
||||
// for (Element v : vl) {
|
||||
// ValueSet vs = new ValueSet();
|
||||
// vs.setId(v.getAttribute("ID"));
|
||||
// vs.setUrl("http://cts.nlm.nih.gov/fhir/ValueSet/" + vs.getId());
|
||||
// vs.getMeta().setSource("https://vsac.nlm.nih.gov/valueset/" + vs.getId() + "/expansion");
|
||||
// vs.setVersion(v.getAttribute("version"));
|
||||
// vs.setTitle(v.getAttribute("displayName"));
|
||||
// vs.setName(Utilities.titleize(vs.getTitle()).replace(" ", ""));
|
||||
// Element d = XMLUtil.getNamedChild(v, "Purpose");
|
||||
// if (d != null) {
|
||||
// vs.setDescription(d.getTextContent());
|
||||
// }
|
||||
// Element s = XMLUtil.getNamedChild(v, "Status");
|
||||
// if (s != null && "Active".equals(s.getTextContent())) {
|
||||
// vs.setStatus(PublicationStatus.ACTIVE);
|
||||
// } else {
|
||||
// vs.setStatus(PublicationStatus.DRAFT);
|
||||
// }
|
||||
// Element dt = XMLUtil.getNamedChild(v, "RevisionDate");
|
||||
// if (dt != null) {
|
||||
// vs.getDateElement().setValueAsString(dt.getTextContent());
|
||||
// }
|
||||
//
|
||||
// Element cl = XMLUtil.getNamedChild(v, "ConceptList");
|
||||
// Element cc = XMLUtil.getFirstChild(cl);
|
||||
//
|
||||
// while (cc != null) {
|
||||
// String code = cc.getAttribute("code");
|
||||
// String display = cc.getAttribute("displayName");
|
||||
// String csoid = cc.getAttribute("codeSystem");
|
||||
// String csver = cc.getAttribute("codeSystemVersion");
|
||||
// String url = context.oid2Uri(csoid);
|
||||
// if (url == null) {
|
||||
// url = "urn:oid:" + csoid;
|
||||
// }
|
||||
// csver = fixVersionforSystem(url, csver);
|
||||
// ConceptSetComponent inc = getInclude(vs, url, csver);
|
||||
// inc.addConcept().setCode(code).setDisplay(display);
|
||||
// cc = XMLUtil.getNextSibling(cc);
|
||||
// }
|
||||
//
|
||||
// res.add(vs);
|
||||
// }
|
||||
// return res;
|
||||
// }
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
{
|
||||
"resourceType" : "AllergyIntolerance",
|
||||
"id" : "TBwnNbrAqC0Qw5Ha7AFT-2AB",
|
||||
"clinicalStatus" : {
|
||||
"coding" : [
|
||||
{
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
{
|
||||
"resourceType" : "AllergyIntolerance",
|
||||
"id": "TKebKfLXzu6Sp.LY-IpvpmQB",
|
||||
"clinicalStatus" : {
|
||||
"coding" : [
|
||||
{
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>org.hl7.fhir.core</artifactId>
|
||||
<version>5.1.8-SNAPSHOT</version>
|
||||
<version>5.1.9-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>org.hl7.fhir.core</artifactId>
|
||||
<version>5.1.8-SNAPSHOT</version>
|
||||
<version>5.1.9-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>org.hl7.fhir.core</artifactId>
|
||||
<version>5.1.8-SNAPSHOT</version>
|
||||
<version>5.1.9-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>org.hl7.fhir.core</artifactId>
|
||||
<version>5.1.8-SNAPSHOT</version>
|
||||
<version>5.1.9-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>org.hl7.fhir.core</artifactId>
|
||||
<version>5.1.8-SNAPSHOT</version>
|
||||
<version>5.1.9-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -78,6 +78,9 @@ import org.hl7.fhir.r5.model.Enumeration;
|
|||
import org.hl7.fhir.r5.model.Enumerations.BindingStrength;
|
||||
import org.hl7.fhir.r5.model.Enumerations.FHIRVersion;
|
||||
import org.hl7.fhir.r5.model.Enumerations.PublicationStatus;
|
||||
import org.hl7.fhir.r5.model.ExpressionNode;
|
||||
import org.hl7.fhir.r5.model.ExpressionNode.Kind;
|
||||
import org.hl7.fhir.r5.model.ExpressionNode.Operation;
|
||||
import org.hl7.fhir.r5.model.Extension;
|
||||
import org.hl7.fhir.r5.model.IdType;
|
||||
import org.hl7.fhir.r5.model.IntegerType;
|
||||
|
@ -99,6 +102,8 @@ import org.hl7.fhir.r5.model.ValueSet.ValueSetExpansionComponent;
|
|||
import org.hl7.fhir.r5.model.ValueSet.ValueSetExpansionContainsComponent;
|
||||
import org.hl7.fhir.r5.renderers.TerminologyRenderer;
|
||||
import org.hl7.fhir.r5.terminologies.ValueSetExpander.ValueSetExpansionOutcome;
|
||||
import org.hl7.fhir.r5.utils.FHIRLexer;
|
||||
import org.hl7.fhir.r5.utils.FHIRPathEngine;
|
||||
import org.hl7.fhir.r5.utils.ToolingExtensions;
|
||||
import org.hl7.fhir.r5.utils.TranslatingUtilities;
|
||||
import org.hl7.fhir.r5.utils.XVerExtensionManager;
|
||||
|
@ -200,7 +205,31 @@ public class ProfileUtilities extends TranslatingUtilities {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
public static class ElementChoiceGroup {
|
||||
private Row row;
|
||||
private String name;
|
||||
private boolean mandatory;
|
||||
private List<String> elements = new ArrayList<>();
|
||||
|
||||
public ElementChoiceGroup(String name, boolean mandatory) {
|
||||
super();
|
||||
this.name = name;
|
||||
this.mandatory = mandatory;
|
||||
}
|
||||
public Row getRow() {
|
||||
return row;
|
||||
}
|
||||
public List<String> getElements() {
|
||||
return elements;
|
||||
}
|
||||
public void setRow(Row row) {
|
||||
this.row = row;
|
||||
}
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
}
|
||||
|
||||
private static final int MAX_RECURSION_LIMIT = 10;
|
||||
|
||||
public class ExtensionContext {
|
||||
|
@ -268,6 +297,7 @@ public class ProfileUtilities extends TranslatingUtilities {
|
|||
|
||||
// note that ProfileUtilities are used re-entrantly internally, so nothing with process state can be here
|
||||
private final IWorkerContext context;
|
||||
private FHIRPathEngine fpe;
|
||||
private List<ValidationMessage> messages;
|
||||
private List<String> snapshotStack = new ArrayList<String>();
|
||||
private ProfileKnowledgeProvider pkp;
|
||||
|
@ -284,6 +314,9 @@ public class ProfileUtilities extends TranslatingUtilities {
|
|||
this.context = context;
|
||||
this.messages = messages;
|
||||
this.pkp = pkp;
|
||||
if (context != null) {
|
||||
this.fpe = new FHIRPathEngine(context, this);
|
||||
}
|
||||
}
|
||||
|
||||
public static class UnusedTracker {
|
||||
|
@ -538,6 +571,7 @@ public class ProfileUtilities extends TranslatingUtilities {
|
|||
}
|
||||
processPaths("", derived.getSnapshot(), baseSnapshot, diff, baseCursor, diffCursor, baseSnapshot.getElement().size()-1,
|
||||
derived.getDifferential().hasElement() ? derived.getDifferential().getElement().size()-1 : -1, url, webUrl, derived.present(), null, null, false, base.getUrl(), null, false, null, new ArrayList<ElementRedirection>(), base);
|
||||
checkGroupConstraints(derived);
|
||||
if (derived.getDerivation() == TypeDerivationRule.SPECIALIZATION) {
|
||||
for (ElementDefinition e : diff.getElement()) {
|
||||
if (!e.hasUserData(GENERATED_IN_SNAPSHOT)) {
|
||||
|
@ -666,6 +700,81 @@ public class ProfileUtilities extends TranslatingUtilities {
|
|||
derived.clearUserData("profileutils.snapshot.generating");
|
||||
}
|
||||
|
||||
private void checkGroupConstraints(StructureDefinition derived) {
|
||||
List<ElementDefinition> toRemove = new ArrayList<>();
|
||||
// List<ElementDefinition> processed = new ArrayList<>();
|
||||
for (ElementDefinition element : derived.getSnapshot().getElement()) {
|
||||
if (!toRemove.contains(element) && !element.hasSlicing() && !"0".equals(element.getMax())) {
|
||||
checkForChildrenInGroup(derived, toRemove, element);
|
||||
}
|
||||
}
|
||||
derived.getSnapshot().getElement().removeAll(toRemove);
|
||||
}
|
||||
|
||||
public void checkForChildrenInGroup(StructureDefinition derived, List<ElementDefinition> toRemove, ElementDefinition element) throws Error {
|
||||
List<ElementDefinition> children = getChildren(derived, element);
|
||||
List<ElementChoiceGroup> groups = readChoices(element, children);
|
||||
for (ElementChoiceGroup group : groups) {
|
||||
System.out.println(children);
|
||||
String mandated = null;
|
||||
Set<String> names = new HashSet<>();
|
||||
for (ElementDefinition ed : children) {
|
||||
String name = tail(ed.getPath());
|
||||
if (names.contains(name)) {
|
||||
throw new Error("huh?");
|
||||
} else {
|
||||
names.add(name);
|
||||
}
|
||||
if (group.getElements().contains(name)) {
|
||||
if (ed.getMin() == 1) {
|
||||
if (mandated == null) {
|
||||
mandated = name;
|
||||
} else {
|
||||
throw new Error("Error: there are two mandatory elements in "+derived.getUrl()+" when there can only be one: "+mandated+" and "+name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (mandated != null) {
|
||||
for (ElementDefinition ed : children) {
|
||||
String name = tail(ed.getPath());
|
||||
if (group.getElements().contains(name) && !mandated.equals(name)) {
|
||||
ed.setMax("0");
|
||||
addAllChildren(derived, ed, toRemove);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private List<ElementDefinition> getChildren(StructureDefinition derived, ElementDefinition element) {
|
||||
List<ElementDefinition> elements = derived.getSnapshot().getElement();
|
||||
int index = elements.indexOf(element) + 1;
|
||||
String path = element.getPath()+".";
|
||||
List<ElementDefinition> list = new ArrayList<>();
|
||||
while (index < elements.size()) {
|
||||
ElementDefinition e = elements.get(index);
|
||||
String p = e.getPath();
|
||||
if (p.startsWith(path) && !e.hasSliceName()) {
|
||||
if (!p.substring(path.length()).contains(".")) {
|
||||
list.add(e);
|
||||
}
|
||||
index++;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
private void addAllChildren(StructureDefinition derived, ElementDefinition element, List<ElementDefinition> toRemove) {
|
||||
List<ElementDefinition> children = getChildList(derived, element);
|
||||
for (ElementDefinition child : children) {
|
||||
toRemove.add(child);
|
||||
addAllChildren(derived, child, toRemove);
|
||||
}
|
||||
}
|
||||
|
||||
private void checkDifferential(List<ElementDefinition> elements, String type, String url) {
|
||||
boolean first = true;
|
||||
for (ElementDefinition ed : elements) {
|
||||
|
@ -3620,12 +3729,17 @@ public class ProfileUtilities extends TranslatingUtilities {
|
|||
}
|
||||
|
||||
Row currRow = row;
|
||||
List<ElementChoiceGroup> groups = readChoices(element, children);
|
||||
boolean isExtension = Utilities.existsInList(tail(element.getPath()), "extension", "modifierExtension");
|
||||
for (ElementDefinition child : children) {
|
||||
if (!child.hasSliceName())
|
||||
if (!child.hasSliceName()) {
|
||||
currRow = row;
|
||||
if (logicalModel || !child.getPath().endsWith(".id") || (child.getPath().endsWith(".id") && (profile != null) && (profile.getDerivation() == TypeDerivationRule.CONSTRAINT)))
|
||||
currRow = genElement(defPath, gen, currRow.getSubRows(), child, all, profiles, showMissing, profileBaseFileName, isExtension, snapshot, corePath, imagePath, false, logicalModel, isConstraintMode, allInvariants, currRow, mustSupport);
|
||||
}
|
||||
Row childRow = chooseChildRowByGroup(gen, currRow, groups, child, element, isConstraintMode);
|
||||
|
||||
if (logicalModel || !child.getPath().endsWith(".id") || (child.getPath().endsWith(".id") && (profile != null) && (profile.getDerivation() == TypeDerivationRule.CONSTRAINT))) {
|
||||
currRow = genElement(defPath, gen, childRow.getSubRows(), child, all, profiles, showMissing, profileBaseFileName, isExtension, snapshot, corePath, imagePath, false, logicalModel, isConstraintMode, allInvariants, currRow, mustSupport);
|
||||
}
|
||||
}
|
||||
// if (!snapshot && (extensions == null || !extensions))
|
||||
// for (ElementDefinition child : children)
|
||||
|
@ -3639,6 +3753,34 @@ public class ProfileUtilities extends TranslatingUtilities {
|
|||
return slicingRow;
|
||||
}
|
||||
|
||||
private Row chooseChildRowByGroup(HierarchicalTableGenerator gen, Row row, List<ElementChoiceGroup> groups, ElementDefinition element, ElementDefinition parent, boolean isConstraintMode) {
|
||||
String name = tail(element.getPath());
|
||||
for (ElementChoiceGroup grp : groups) {
|
||||
if (grp.getElements().contains(name)) {
|
||||
if (grp.getRow() == null) {
|
||||
grp.setRow(makeChoiceElementRow(gen, row, grp, parent, isConstraintMode));
|
||||
}
|
||||
return grp.getRow();
|
||||
}
|
||||
}
|
||||
return row;
|
||||
}
|
||||
|
||||
private Row makeChoiceElementRow(HierarchicalTableGenerator gen, Row prow, ElementChoiceGroup grp, ElementDefinition parent, boolean isConstraintMode) {
|
||||
Row row = gen.new Row();
|
||||
row.setAnchor(parent.getPath()+"-"+grp.getName());
|
||||
row.setColor(getRowColor(parent, isConstraintMode));
|
||||
row.setLineColor(1);
|
||||
row.setIcon("icon_choice.gif", HierarchicalTableGenerator.TEXT_ICON_CHOICE);
|
||||
row.getCells().add(gen.new Cell(null, null, "(Choice of one)", "", null));
|
||||
row.getCells().add(gen.new Cell());
|
||||
row.getCells().add(gen.new Cell(null, null, (grp.mandatory ? "1" : "0")+"..1", "", null));
|
||||
row.getCells().add(gen.new Cell());
|
||||
row.getCells().add(gen.new Cell());
|
||||
prow.getSubRows().add(row);
|
||||
return row;
|
||||
}
|
||||
|
||||
public Cell genElementNameCell(HierarchicalTableGenerator gen, ElementDefinition element, String profileBaseFileName, boolean snapshot, String corePath,
|
||||
String imagePath, boolean root, boolean logicalModel, boolean allInvariants, StructureDefinition profile, Row typesRow, Row row, boolean hasDef,
|
||||
boolean ext, UnusedTracker used, String ref, String sName) throws IOException {
|
||||
|
@ -3985,7 +4127,7 @@ public class ProfileUtilities extends TranslatingUtilities {
|
|||
}
|
||||
if (root) {
|
||||
if (profile.getAbstract()) {
|
||||
if (!c.getPieces().isEmpty()) c.addPiece(gen.new Piece("br"));
|
||||
if (!c.getPieces().isEmpty()) { c.addPiece(gen.new Piece("br")); }
|
||||
c.addPiece(gen.new Piece(null, "This is an abstract profile", null));
|
||||
}
|
||||
}
|
||||
|
@ -3993,10 +4135,10 @@ public class ProfileUtilities extends TranslatingUtilities {
|
|||
c.getPieces().add(checkForNoChange(definition.getFixed(), gen.new Piece(null, "\""+buildJson(definition.getFixed())+"\"", null).addStyle("color: darkgreen")));
|
||||
} else {
|
||||
if (definition != null && definition.hasShort()) {
|
||||
if (!c.getPieces().isEmpty()) c.addPiece(gen.new Piece("br"));
|
||||
if (!c.getPieces().isEmpty()) { c.addPiece(gen.new Piece("br")); }
|
||||
c.addPiece(checkForNoChange(definition.getShortElement(), gen.new Piece(null, gt(definition.getShortElement()), null)));
|
||||
} else if (fallback != null && fallback.hasShort()) {
|
||||
if (!c.getPieces().isEmpty()) c.addPiece(gen.new Piece("br"));
|
||||
if (!c.getPieces().isEmpty()) { c.addPiece(gen.new Piece("br")); }
|
||||
c.addPiece(gen.new Piece(null, gt(fallback.getShortElement()), null).addStyle("opacity: 0.5"));
|
||||
}
|
||||
if (url != null) {
|
||||
|
@ -4041,7 +4183,7 @@ public class ProfileUtilities extends TranslatingUtilities {
|
|||
}
|
||||
|
||||
if (definition.hasSlicing()) {
|
||||
if (!c.getPieces().isEmpty()) c.addPiece(gen.new Piece("br"));
|
||||
if (!c.getPieces().isEmpty()) { c.addPiece(gen.new Piece("br")); }
|
||||
c.getPieces().add(gen.new Piece(null, translate("sd.table", "Slice")+": ", null).addStyle("font-weight:bold"));
|
||||
c.getPieces().add(gen.new Piece(null, describeSlice(definition.getSlicing()), null));
|
||||
}
|
||||
|
@ -4095,7 +4237,7 @@ public class ProfileUtilities extends TranslatingUtilities {
|
|||
}
|
||||
|
||||
if (definition.hasFixed()) {
|
||||
if (!c.getPieces().isEmpty()) c.addPiece(gen.new Piece("br"));
|
||||
if (!c.getPieces().isEmpty()) { c.addPiece(gen.new Piece("br")); }
|
||||
c.getPieces().add(checkForNoChange(definition.getFixed(), gen.new Piece(null, translate("sd.table", "Fixed Value")+": ", null).addStyle("font-weight:bold")));
|
||||
if (!useTableForFixedValues || definition.getFixed().isPrimitive()) {
|
||||
String s = buildJson(definition.getFixed());
|
||||
|
@ -4113,7 +4255,7 @@ public class ProfileUtilities extends TranslatingUtilities {
|
|||
c.getPieces().add(p);
|
||||
}
|
||||
} else if (definition.hasPattern()) {
|
||||
if (!c.getPieces().isEmpty()) c.addPiece(gen.new Piece("br"));
|
||||
if (!c.getPieces().isEmpty()) { c.addPiece(gen.new Piece("br")); }
|
||||
c.getPieces().add(checkForNoChange(definition.getPattern(), gen.new Piece(null, translate("sd.table", "Required Pattern")+": ", null).addStyle("font-weight:bold")));
|
||||
if (!useTableForFixedValues || definition.getPattern().isPrimitive())
|
||||
c.getPieces().add(checkForNoChange(definition.getPattern(), gen.new Piece(null, buildJson(definition.getPattern()), null).addStyle("color: darkgreen")));
|
||||
|
@ -4123,13 +4265,13 @@ public class ProfileUtilities extends TranslatingUtilities {
|
|||
}
|
||||
} else if (definition.hasExample()) {
|
||||
for (ElementDefinitionExampleComponent ex : definition.getExample()) {
|
||||
if (!c.getPieces().isEmpty()) c.addPiece(gen.new Piece("br"));
|
||||
if (!c.getPieces().isEmpty()) { c.addPiece(gen.new Piece("br")); }
|
||||
c.getPieces().add(checkForNoChange(ex, gen.new Piece(null, translate("sd.table", "Example")+("".equals("General")? "" : " "+ex.getLabel())+": ", null).addStyle("font-weight:bold")));
|
||||
c.getPieces().add(checkForNoChange(ex, gen.new Piece(null, buildJson(ex.getValue()), null).addStyle("color: darkgreen")));
|
||||
}
|
||||
}
|
||||
if (definition.hasMaxLength() && definition.getMaxLength()!=0) {
|
||||
if (!c.getPieces().isEmpty()) c.addPiece(gen.new Piece("br"));
|
||||
if (!c.getPieces().isEmpty()) { c.addPiece(gen.new Piece("br")); }
|
||||
c.getPieces().add(checkForNoChange(definition.getMaxLengthElement(), gen.new Piece(null, "Max Length: ", null).addStyle("font-weight:bold")));
|
||||
c.getPieces().add(checkForNoChange(definition.getMaxLengthElement(), gen.new Piece(null, Integer.toString(definition.getMaxLength()), null).addStyle("color: darkgreen")));
|
||||
}
|
||||
|
@ -4367,7 +4509,7 @@ public class ProfileUtilities extends TranslatingUtilities {
|
|||
}
|
||||
|
||||
if (definition.hasSlicing()) {
|
||||
if (!c.getPieces().isEmpty()) c.addPiece(gen.new Piece("br"));
|
||||
if (!c.getPieces().isEmpty()) { c.addPiece(gen.new Piece("br")); }
|
||||
c.getPieces().add(gen.new Piece(null, "Slice: ", null).addStyle("font-weight:bold"));
|
||||
c.getPieces().add(gen.new Piece(null, describeSlice(definition.getSlicing()), null));
|
||||
}
|
||||
|
@ -4389,12 +4531,16 @@ public class ProfileUtilities extends TranslatingUtilities {
|
|||
}
|
||||
}
|
||||
for (ElementDefinitionConstraintComponent inv : definition.getConstraint()) {
|
||||
if (!c.getPieces().isEmpty()) c.addPiece(gen.new Piece("br"));
|
||||
if (!c.getPieces().isEmpty()) { c.addPiece(gen.new Piece("br")); }
|
||||
c.getPieces().add(checkForNoChange(inv, gen.new Piece(null, inv.getKey()+": ", null).addStyle("font-weight:bold")));
|
||||
c.getPieces().add(checkForNoChange(inv, gen.new Piece(null, inv.getHuman(), null)));
|
||||
if (inv.getHumanElement().hasExtension("http://hl7.org/fhir/StructureDefinition/rendering-markdown")) {
|
||||
c.addMarkdown(inv.getHumanElement().getExtensionString("http://hl7.org/fhir/StructureDefinition/rendering-markdown"));
|
||||
} else {
|
||||
c.getPieces().add(checkForNoChange(inv, gen.new Piece(null, inv.getHuman(), null)));
|
||||
}
|
||||
}
|
||||
if (definition.hasFixed()) {
|
||||
if (!c.getPieces().isEmpty()) c.addPiece(gen.new Piece("br"));
|
||||
if (!c.getPieces().isEmpty()) { c.addPiece(gen.new Piece("br")); }
|
||||
c.getPieces().add(checkForNoChange(definition.getFixed(), gen.new Piece(null, "Fixed Value: ", null).addStyle("font-weight:bold")));
|
||||
String s = buildJson(definition.getFixed());
|
||||
String link = null;
|
||||
|
@ -4402,18 +4548,18 @@ public class ProfileUtilities extends TranslatingUtilities {
|
|||
link = pkp.getLinkForUrl(corePath, s);
|
||||
c.getPieces().add(checkForNoChange(definition.getFixed(), gen.new Piece(link, s, null).addStyle("color: darkgreen")));
|
||||
} else if (definition.hasPattern()) {
|
||||
if (!c.getPieces().isEmpty()) c.addPiece(gen.new Piece("br"));
|
||||
if (!c.getPieces().isEmpty()) { c.addPiece(gen.new Piece("br")); }
|
||||
c.getPieces().add(checkForNoChange(definition.getPattern(), gen.new Piece(null, "Required Pattern: ", null).addStyle("font-weight:bold")));
|
||||
c.getPieces().add(checkForNoChange(definition.getPattern(), gen.new Piece(null, buildJson(definition.getPattern()), null).addStyle("color: darkgreen")));
|
||||
} else if (definition.hasExample()) {
|
||||
for (ElementDefinitionExampleComponent ex : definition.getExample()) {
|
||||
if (!c.getPieces().isEmpty()) c.addPiece(gen.new Piece("br"));
|
||||
if (!c.getPieces().isEmpty()) { c.addPiece(gen.new Piece("br")); }
|
||||
c.getPieces().add(checkForNoChange(ex, gen.new Piece(null, "Example'"+("".equals("General")? "" : " "+ex.getLabel()+"'")+": ", null).addStyle("font-weight:bold")));
|
||||
c.getPieces().add(checkForNoChange(ex, gen.new Piece(null, buildJson(ex.getValue()), null).addStyle("color: darkgreen")));
|
||||
}
|
||||
}
|
||||
if (definition.hasMaxLength() && definition.getMaxLength()!=0) {
|
||||
if (!c.getPieces().isEmpty()) c.addPiece(gen.new Piece("br"));
|
||||
if (!c.getPieces().isEmpty()) { c.addPiece(gen.new Piece("br")); }
|
||||
c.getPieces().add(checkForNoChange(definition.getMaxLengthElement(), gen.new Piece(null, "Max Length: ", null).addStyle("font-weight:bold")));
|
||||
c.getPieces().add(checkForNoChange(definition.getMaxLengthElement(), gen.new Piece(null, Integer.toString(definition.getMaxLength()), null).addStyle("color: darkgreen")));
|
||||
}
|
||||
|
@ -4434,14 +4580,14 @@ public class ProfileUtilities extends TranslatingUtilities {
|
|||
}
|
||||
}
|
||||
if (definition.hasDefinition()) {
|
||||
if (!c.getPieces().isEmpty()) c.addPiece(gen.new Piece("br"));
|
||||
if (!c.getPieces().isEmpty()) { c.addPiece(gen.new Piece("br")); }
|
||||
c.getPieces().add(gen.new Piece(null, "Definition: ", null).addStyle("font-weight:bold"));
|
||||
c.addPiece(gen.new Piece("br"));
|
||||
c.addMarkdown(definition.getDefinition());
|
||||
// c.getPieces().add(checkForNoChange(definition.getCommentElement(), gen.new Piece(null, definition.getComment(), null)));
|
||||
}
|
||||
if (definition.getComment()!=null) {
|
||||
if (!c.getPieces().isEmpty()) c.addPiece(gen.new Piece("br"));
|
||||
if (!c.getPieces().isEmpty()) { c.addPiece(gen.new Piece("br")); }
|
||||
c.getPieces().add(gen.new Piece(null, "Comments: ", null).addStyle("font-weight:bold"));
|
||||
c.addPiece(gen.new Piece("br"));
|
||||
c.addMarkdown(definition.getComment());
|
||||
|
@ -5935,6 +6081,64 @@ public class ProfileUtilities extends TranslatingUtilities {
|
|||
}
|
||||
|
||||
|
||||
public List<ElementChoiceGroup> readChoices(ElementDefinition ed, List<ElementDefinition> children) {
|
||||
List<ElementChoiceGroup> result = new ArrayList<>();
|
||||
for (ElementDefinitionConstraintComponent c : ed.getConstraint()) {
|
||||
ElementChoiceGroup grp = processConstraint(children, c);
|
||||
if (grp != null) {
|
||||
result.add(grp);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private ElementChoiceGroup processConstraint(List<ElementDefinition> children, ElementDefinitionConstraintComponent c) {
|
||||
if (!c.hasExpression()) {
|
||||
return null;
|
||||
}
|
||||
ExpressionNode expr = fpe.parse(c.getExpression());
|
||||
if (expr.getKind() != Kind.Group || expr.getOpNext() == null || !(expr.getOperation() == Operation.Equals || expr.getOperation() == Operation.LessOrEqual)) {
|
||||
return null;
|
||||
}
|
||||
ExpressionNode n1 = expr.getGroup();
|
||||
ExpressionNode n2 = expr.getOpNext();
|
||||
if (n2.getKind() != Kind.Constant || n2.getInner() != null || n2.getOpNext() != null || !"1".equals(n2.getConstant().primitiveValue())) {
|
||||
return null;
|
||||
}
|
||||
ElementChoiceGroup grp = new ElementChoiceGroup(c.getKey(), expr.getOperation() == Operation.Equals);
|
||||
while (n1 != null) {
|
||||
if (n1.getKind() != Kind.Name || n1.getInner() != null) {
|
||||
return null;
|
||||
}
|
||||
grp.elements.add(n1.getName());
|
||||
if (n1.getOperation() == null || n1.getOperation() == Operation.Union) {
|
||||
n1 = n1.getOpNext();
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
int total = 0;
|
||||
for (String n : grp.elements) {
|
||||
boolean found = false;
|
||||
for (ElementDefinition child : children) {
|
||||
String name = tail(child.getPath());
|
||||
if (n.equals(name)) {
|
||||
found = true;
|
||||
if (!"0".equals(child.getMax())) {
|
||||
total++;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
if (total <= 1) {
|
||||
return null;
|
||||
}
|
||||
return grp;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
|
@ -171,8 +171,9 @@ private Map<String, Object> userData;
|
|||
List<Property> children = new ArrayList<Property>();
|
||||
listChildren(children);
|
||||
for (Property c : children)
|
||||
if (c.getName().equals(name))
|
||||
if (c.getName().equals(name) || c.getName().equals(name+"[x]")) {
|
||||
return c;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
|
|
@ -295,8 +295,7 @@ public class BundleRenderer extends ResourceRenderer {
|
|||
try {
|
||||
xn = rr.build(be.getResource());
|
||||
} catch (Exception e) {
|
||||
xn = new XhtmlNode();
|
||||
xn.para().b().tx("Exception generating narrative: "+e.getMessage());
|
||||
xn = makeExceptionXhtml(e, "generating narrative");
|
||||
}
|
||||
}
|
||||
root.blockquote().getChildNodes().addAll(checkInternalLinks(b, xn.getChildNodes()));
|
||||
|
|
|
@ -14,10 +14,16 @@ import org.hl7.fhir.r5.model.Annotation;
|
|||
import org.hl7.fhir.r5.model.Base;
|
||||
import org.hl7.fhir.r5.model.BaseDateTimeType;
|
||||
import org.hl7.fhir.r5.model.CanonicalResource;
|
||||
import org.hl7.fhir.r5.model.CanonicalType;
|
||||
import org.hl7.fhir.r5.model.CodeSystem;
|
||||
import org.hl7.fhir.r5.model.CodeableConcept;
|
||||
import org.hl7.fhir.r5.model.Coding;
|
||||
import org.hl7.fhir.r5.model.ContactPoint;
|
||||
import org.hl7.fhir.r5.model.DataRequirement;
|
||||
import org.hl7.fhir.r5.model.DataRequirement.DataRequirementCodeFilterComponent;
|
||||
import org.hl7.fhir.r5.model.DataRequirement.DataRequirementDateFilterComponent;
|
||||
import org.hl7.fhir.r5.model.DataRequirement.DataRequirementSortComponent;
|
||||
import org.hl7.fhir.r5.model.DataRequirement.SortDirection;
|
||||
import org.hl7.fhir.r5.model.ContactPoint.ContactPointSystem;
|
||||
import org.hl7.fhir.r5.model.DataType;
|
||||
import org.hl7.fhir.r5.model.DateTimeType;
|
||||
|
@ -52,6 +58,7 @@ import org.hl7.fhir.utilities.MarkDownProcessor;
|
|||
import org.hl7.fhir.utilities.MarkDownProcessor.Dialect;
|
||||
import org.hl7.fhir.utilities.Utilities;
|
||||
import org.hl7.fhir.utilities.validation.ValidationOptions;
|
||||
import org.hl7.fhir.utilities.xhtml.NodeType;
|
||||
import org.hl7.fhir.utilities.xhtml.XhtmlNode;
|
||||
import org.hl7.fhir.utilities.xhtml.XhtmlParser;
|
||||
|
||||
|
@ -778,6 +785,102 @@ public class DataRenderer extends Renderer {
|
|||
x.addText(!p.hasEnd() ? "(ongoing)" : p.getEndElement().toHumanDisplay());
|
||||
}
|
||||
|
||||
public void renderDataRequirement(XhtmlNode x, DataRequirement dr) {
|
||||
XhtmlNode tbl = x.table("grid");
|
||||
XhtmlNode tr = tbl.tr();
|
||||
XhtmlNode td = tr.td().colspan("2");
|
||||
td.b().tx("Type");
|
||||
td.tx(": ");
|
||||
StructureDefinition sd = context.getWorker().fetchTypeDefinition(dr.getType().toCode());
|
||||
if (sd != null && sd.hasUserData("path")) {
|
||||
td.ah(sd.getUserString("path")).tx(dr.getType().toCode());
|
||||
} else {
|
||||
td.tx(dr.getType().toCode());
|
||||
}
|
||||
if (dr.hasProfile()) {
|
||||
td.tx(" (");
|
||||
boolean first = true;
|
||||
for (CanonicalType p : dr.getProfile()) {
|
||||
if (first) first = false; else td.tx(" | ");
|
||||
sd = context.getWorker().fetchResource(StructureDefinition.class, p.getValue());
|
||||
if (sd != null && sd.hasUserData("path")) {
|
||||
td.ah(sd.getUserString("path")).tx(sd.present());
|
||||
} else {
|
||||
td.tx(p.asStringValue());
|
||||
}
|
||||
}
|
||||
td.tx(")");
|
||||
}
|
||||
if (dr.hasSubject()) {
|
||||
tr = tbl.tr();
|
||||
td = tr.td().colspan("2");
|
||||
td.b().tx("Subject");
|
||||
if (dr.hasSubjectReference()) {
|
||||
renderReference(td, dr.getSubjectReference());
|
||||
} else {
|
||||
renderCodeableConcept(td, dr.getSubjectCodeableConcept());
|
||||
}
|
||||
}
|
||||
if (dr.hasCodeFilter() || dr.hasDateFilter()) {
|
||||
tr = tbl.tr().backgroundColor("#efefef");
|
||||
tr.td().tx("Filter");
|
||||
tr.td().tx("Value");
|
||||
}
|
||||
for (DataRequirementCodeFilterComponent cf : dr.getCodeFilter()) {
|
||||
tr = tbl.tr();
|
||||
if (cf.hasPath()) {
|
||||
tr.td().tx(cf.getPath());
|
||||
} else {
|
||||
tr.td().tx("Search on " +cf.getSearchParam());
|
||||
}
|
||||
if (cf.hasValueSet()) {
|
||||
td = tr.td();
|
||||
td.tx("In ValueSet ");
|
||||
render(td, cf.getValueSetElement());
|
||||
} else {
|
||||
boolean first = true;
|
||||
td = tr.td();
|
||||
td.tx("One of these codes: ");
|
||||
for (Coding c : cf.getCode()) {
|
||||
if (first) first = false; else td.tx(", ");
|
||||
render(td, c);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (DataRequirementDateFilterComponent cf : dr.getDateFilter()) {
|
||||
tr = tbl.tr();
|
||||
if (cf.hasPath()) {
|
||||
tr.td().tx(cf.getPath());
|
||||
} else {
|
||||
tr.td().tx("Search on " +cf.getSearchParam());
|
||||
}
|
||||
render(tr.td(), cf.getValue());
|
||||
}
|
||||
if (dr.hasSort() || dr.hasLimit()) {
|
||||
tr = tbl.tr();
|
||||
td = tr.td().colspan("2");
|
||||
if (dr.hasLimit()) {
|
||||
td.b().tx("Limit");
|
||||
td.tx(": ");
|
||||
td.tx(dr.getLimit());
|
||||
if (dr.hasSort()) {
|
||||
td.tx(", ");
|
||||
}
|
||||
}
|
||||
if (dr.hasSort()) {
|
||||
td.b().tx("Sort");
|
||||
td.tx(": ");
|
||||
boolean first = true;
|
||||
for (DataRequirementSortComponent p : dr.getSort()) {
|
||||
if (first) first = false; else td.tx(" | ");
|
||||
td.tx(p.getDirection() == SortDirection.ASCENDING ? "+" : "-");
|
||||
td.tx(p.getPath());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private String displayTiming(Timing s) throws FHIRException {
|
||||
CommaSeparatedStringBuilder b = new CommaSeparatedStringBuilder();
|
||||
if (s.hasCode())
|
||||
|
@ -919,6 +1022,21 @@ public class DataRenderer extends Renderer {
|
|||
}
|
||||
|
||||
|
||||
public XhtmlNode makeExceptionXhtml(Exception e, String function) {
|
||||
XhtmlNode xn;
|
||||
xn = new XhtmlNode(NodeType.Element, "div");
|
||||
xn.para().b().tx("Exception "+function+": "+e.getMessage()).addComment(getStackTrace(e));
|
||||
return xn;
|
||||
}
|
||||
|
||||
private String getStackTrace(Exception e) {
|
||||
StringBuilder b = new StringBuilder();
|
||||
b.append("\r\n");
|
||||
for (StackTraceElement t : e.getStackTrace()) {
|
||||
b.append(t.getClassName()+"."+t.getMethodName()+" ("+t.getFileName()+":"+t.getLineNumber());
|
||||
b.append("\r\n");
|
||||
}
|
||||
return b.toString();
|
||||
}
|
||||
|
||||
}
|
|
@ -31,13 +31,19 @@ import org.hl7.fhir.utilities.xhtml.XhtmlNode;
|
|||
|
||||
public class ParametersRenderer extends ResourceRenderer {
|
||||
|
||||
public ParametersRenderer(RenderingContext context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
public ParametersRenderer(RenderingContext context, ResourceContext rcontext) {
|
||||
super(context, rcontext);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean render(XhtmlNode x, Resource r) throws FHIRFormatError, DefinitionException, IOException, FHIRException, EOperationOutcome {
|
||||
x.h2().tx("Parameters");
|
||||
XhtmlNode tbl = x.table("grid");
|
||||
params(tbl, ((Parameters) r).getParameter(), 0);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -27,6 +27,7 @@ import org.hl7.fhir.r5.model.CodeableReference;
|
|||
import org.hl7.fhir.r5.model.Coding;
|
||||
import org.hl7.fhir.r5.model.ContactDetail;
|
||||
import org.hl7.fhir.r5.model.ContactPoint;
|
||||
import org.hl7.fhir.r5.model.DataRequirement;
|
||||
import org.hl7.fhir.r5.model.DateTimeType;
|
||||
import org.hl7.fhir.r5.model.DomainResource;
|
||||
import org.hl7.fhir.r5.model.Dosage;
|
||||
|
@ -40,6 +41,7 @@ import org.hl7.fhir.r5.model.InstantType;
|
|||
import org.hl7.fhir.r5.model.Meta;
|
||||
import org.hl7.fhir.r5.model.Narrative;
|
||||
import org.hl7.fhir.r5.model.Narrative.NarrativeStatus;
|
||||
import org.hl7.fhir.r5.model.StructureDefinition.StructureDefinitionKind;
|
||||
import org.hl7.fhir.r5.model.Period;
|
||||
import org.hl7.fhir.r5.model.PrimitiveType;
|
||||
import org.hl7.fhir.r5.model.Property;
|
||||
|
@ -82,6 +84,7 @@ import org.w3c.dom.Element;
|
|||
public class ProfileDrivenRenderer extends ResourceRenderer {
|
||||
|
||||
private Set<String> containedIds = new HashSet<>();
|
||||
private boolean hasExtensions;
|
||||
|
||||
public ProfileDrivenRenderer(RenderingContext context, ResourceContext rcontext) {
|
||||
super(context, rcontext);
|
||||
|
@ -108,12 +111,13 @@ public class ProfileDrivenRenderer extends ResourceRenderer {
|
|||
System.out.println("hah!");
|
||||
}
|
||||
containedIds.clear();
|
||||
hasExtensions = false;
|
||||
generateByProfile(r, sd, r.root(), sd.getSnapshot().getElement(), ed, context.getProfileUtilities().getChildList(sd, ed), x, r.fhirType(), false, 0);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
x.para().b().style("color: maroon").tx("Exception generating Narrative: "+e.getMessage());
|
||||
}
|
||||
return false;
|
||||
return hasExtensions;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -317,6 +321,12 @@ public class ProfileDrivenRenderer extends ResourceRenderer {
|
|||
} else {
|
||||
x.addText("??");
|
||||
}
|
||||
} else if (e instanceof org.hl7.fhir.r5.model.Integer64Type) {
|
||||
if (((org.hl7.fhir.r5.model.Integer64Type) e).hasValue()) {
|
||||
x.addText(Long.toString(((org.hl7.fhir.r5.model.Integer64Type) e).getValue()));
|
||||
} else {
|
||||
x.addText("??");
|
||||
}
|
||||
} else if (e instanceof org.hl7.fhir.r5.model.DecimalType) {
|
||||
x.addText(((org.hl7.fhir.r5.model.DecimalType) e).getValue().toString());
|
||||
} else if (e instanceof HumanName) {
|
||||
|
@ -378,15 +388,13 @@ public class ProfileDrivenRenderer extends ResourceRenderer {
|
|||
}
|
||||
} else if (e instanceof Resource) {
|
||||
return;
|
||||
} else if (e instanceof DataRequirement) {
|
||||
DataRequirement p = (DataRequirement) e;
|
||||
renderDataRequirement(x, p);
|
||||
} else if (e instanceof ElementDefinition) {
|
||||
x.tx("todo-bundle");
|
||||
} else if (e != null && !(e instanceof Attachment) && !(e instanceof Narrative) && !(e instanceof Meta)) {
|
||||
StructureDefinition sd = getContext().getWorker().fetchTypeDefinition(e.fhirType());
|
||||
if (sd == null)
|
||||
throw new NotImplementedException("type "+e.getClass().getName()+" not handled yet, and no structure found");
|
||||
else
|
||||
generateByProfile(res, sd, ew, sd.getSnapshot().getElement(), sd.getSnapshot().getElementFirstRep(),
|
||||
getChildrenForPath(sd.getSnapshot().getElement(), sd.getSnapshot().getElementFirstRep().getPath()), x, path, showCodeDetails, indent + 1);
|
||||
throw new NotImplementedException("type "+e.getClass().getName()+" not handled - should not be here");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -547,12 +555,28 @@ public class ProfileDrivenRenderer extends ResourceRenderer {
|
|||
|
||||
private boolean isPrimitive(ElementDefinition e) {
|
||||
//we can tell if e is a primitive because it has types
|
||||
if (e.getType().isEmpty())
|
||||
if (e.getType().isEmpty()) {
|
||||
return false;
|
||||
if (e.getType().size() == 1 && isBase(e.getType().get(0).getWorkingCode()))
|
||||
}
|
||||
if (e.getType().size() == 1 && isBase(e.getType().get(0).getWorkingCode())) {
|
||||
return false;
|
||||
return true;
|
||||
// return !e.getType().isEmpty()
|
||||
}
|
||||
if (e.getType().size() > 1) {
|
||||
return true;
|
||||
}
|
||||
StructureDefinition sd = context.getWorker().fetchTypeDefinition(e.getTypeFirstRep().getCode());
|
||||
if (sd != null) {
|
||||
if (sd.getKind() == StructureDefinitionKind.PRIMITIVETYPE) {
|
||||
return true;
|
||||
}
|
||||
if (sd.getKind() == StructureDefinitionKind.COMPLEXTYPE) {
|
||||
if (Utilities.existsInList(e.getTypeFirstRep().getCode(), "Extension", "CodeableConcept", "Coding", "Annotation", "Identifier", "HumanName", "SampledData",
|
||||
"Address", "ContactPoint", "ContactDetail", "Timing", "Range", "Quantity", "Ratio", "Period", "Reference")) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean isBase(String code) {
|
||||
|
@ -612,65 +636,99 @@ public class ProfileDrivenRenderer extends ResourceRenderer {
|
|||
for (PropertyWrapper p : splitExtensions(profile, e.children())) {
|
||||
if (p.hasValues()) {
|
||||
ElementDefinition child = getElementDefinition(children, path+"."+p.getName(), p);
|
||||
if (child == null) {
|
||||
child = p.getElementDefinition();
|
||||
}
|
||||
if (child != null) {
|
||||
Map<String, String> displayHints = readDisplayHints(child);
|
||||
if ("DomainResource.contained".equals(child.getBase().getPath())) {
|
||||
generateElementByProfile(res, profile, allElements, x, path, showCodeDetails, indent, p, child);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void generateElementByProfile(ResourceWrapper res, StructureDefinition profile, List<ElementDefinition> allElements, XhtmlNode x, String path,
|
||||
boolean showCodeDetails, int indent, PropertyWrapper p, ElementDefinition child) throws UnsupportedEncodingException, IOException, EOperationOutcome {
|
||||
Map<String, String> displayHints = readDisplayHints(child);
|
||||
if ("DomainResource.contained".equals(child.getBase().getPath())) {
|
||||
// if (p.getValues().size() > 0 && child != null) {
|
||||
// for (BaseWrapper v : p.getValues()) {
|
||||
// x.an(v.get("id").primitiveValue());
|
||||
// }
|
||||
// }
|
||||
} else if (!exemptFromRendering(child)) {
|
||||
List<ElementDefinition> grandChildren = getChildrenForPath(allElements, path+"."+p.getName());
|
||||
filterGrandChildren(grandChildren, path+"."+p.getName(), p);
|
||||
if (p.getValues().size() > 0) {
|
||||
if (isPrimitive(child)) {
|
||||
XhtmlNode para = x.para();
|
||||
String name = p.getName();
|
||||
if (name.endsWith("[x]"))
|
||||
name = name.substring(0, name.length() - 3);
|
||||
if (showCodeDetails || !isDefaultValue(displayHints, p.getValues())) {
|
||||
para.b().addText(name);
|
||||
para.tx(": ");
|
||||
if (renderAsList(child) && p.getValues().size() > 1) {
|
||||
XhtmlNode list = x.ul();
|
||||
for (BaseWrapper v : p.getValues())
|
||||
renderLeaf(res, v, child, x, list.li(), false, showCodeDetails, displayHints, path, indent);
|
||||
} else {
|
||||
boolean first = true;
|
||||
for (BaseWrapper v : p.getValues()) {
|
||||
if (first)
|
||||
first = false;
|
||||
else
|
||||
para.tx(", ");
|
||||
renderLeaf(res, v, child, x, para, false, showCodeDetails, displayHints, path, indent);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (canDoTable(path, p, grandChildren)) {
|
||||
x.addTag(getHeader()).addText(Utilities.capitalize(Utilities.camelCase(Utilities.pluralizeMe(p.getName()))));
|
||||
XhtmlNode tbl = x.table( "grid");
|
||||
XhtmlNode tr = tbl.tr();
|
||||
tr.td().tx("-"); // work around problem with empty table rows
|
||||
addColumnHeadings(tr, grandChildren);
|
||||
for (BaseWrapper v : p.getValues()) {
|
||||
if (v != null) {
|
||||
tr = tbl.tr();
|
||||
tr.td().tx("*"); // work around problem with empty table rows
|
||||
addColumnValues(res, tr, grandChildren, v, showCodeDetails, displayHints, path, indent);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (BaseWrapper v : p.getValues()) {
|
||||
if (v != null) {
|
||||
XhtmlNode bq = x.addTag("blockquote");
|
||||
bq.para().b().addText(p.getName());
|
||||
generateByProfile(res, profile, v, allElements, child, grandChildren, bq, path+"."+p.getName(), showCodeDetails, indent+1);
|
||||
}
|
||||
}
|
||||
} else if (!exemptFromRendering(child)) {
|
||||
if (isExtension(p)) {
|
||||
hasExtensions = true;
|
||||
}
|
||||
List<ElementDefinition> grandChildren = getChildrenForPath(allElements, path+"."+p.getName());
|
||||
filterGrandChildren(grandChildren, path+"."+p.getName(), p);
|
||||
if (p.getValues().size() > 0) {
|
||||
if (isPrimitive(child)) {
|
||||
XhtmlNode para = x.isPara() ? para = x : x.para();
|
||||
String name = p.getName();
|
||||
if (name.endsWith("[x]"))
|
||||
name = name.substring(0, name.length() - 3);
|
||||
if (showCodeDetails || !isDefaultValue(displayHints, p.getValues())) {
|
||||
para.b().addText(name);
|
||||
para.tx(": ");
|
||||
if (renderAsList(child) && p.getValues().size() > 1) {
|
||||
XhtmlNode list = x.ul();
|
||||
for (BaseWrapper v : p.getValues())
|
||||
renderLeaf(res, v, child, x, list.li(), false, showCodeDetails, displayHints, path, indent);
|
||||
} else {
|
||||
boolean first = true;
|
||||
for (BaseWrapper v : p.getValues()) {
|
||||
if (first) {
|
||||
first = false;
|
||||
} else {
|
||||
para.tx(", ");
|
||||
}
|
||||
renderLeaf(res, v, child, x, para, false, showCodeDetails, displayHints, path, indent);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (canDoTable(path, p, grandChildren, x)) {
|
||||
x.addTag(getHeader()).addText(Utilities.capitalize(Utilities.camelCase(Utilities.pluralizeMe(p.getName()))));
|
||||
XhtmlNode tbl = x.table("grid");
|
||||
XhtmlNode tr = tbl.tr();
|
||||
tr.td().tx("-"); // work around problem with empty table rows
|
||||
addColumnHeadings(tr, grandChildren);
|
||||
for (BaseWrapper v : p.getValues()) {
|
||||
if (v != null) {
|
||||
tr = tbl.tr();
|
||||
tr.td().tx("*"); // work around problem with empty table rows
|
||||
addColumnValues(res, tr, grandChildren, v, showCodeDetails, displayHints, path, indent);
|
||||
}
|
||||
}
|
||||
} else if (isExtension(p)) {
|
||||
for (BaseWrapper v : p.getValues()) {
|
||||
if (v != null) {
|
||||
PropertyWrapper vp = v.getChildByName("value");
|
||||
PropertyWrapper ev = v.getChildByName("extension");
|
||||
if (vp.hasValues()) {
|
||||
BaseWrapper vv = vp.value();
|
||||
XhtmlNode para = x.para();
|
||||
para.b().addText(p.getStructure().present());
|
||||
para.tx(": ");
|
||||
renderLeaf(res, v, child, x, para, false, showCodeDetails, displayHints, path, indent);
|
||||
} else if (ev.hasValues()) {
|
||||
XhtmlNode bq = x.addTag("blockquote");
|
||||
bq.para().b().addText(isExtension(p) ? p.getStructure().present() : p.getName());
|
||||
for (BaseWrapper vv : ev.getValues()) {
|
||||
StructureDefinition ex = context.getWorker().fetchTypeDefinition("Extension");
|
||||
List<ElementDefinition> children = getChildrenForPath(ex.getSnapshot().getElement(), "Extension");
|
||||
generateByProfile(res, ex, vv, allElements, child, children, bq, "Extension", showCodeDetails, indent+1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (BaseWrapper v : p.getValues()) {
|
||||
if (v != null) {
|
||||
XhtmlNode bq = x.addTag("blockquote");
|
||||
bq.para().b().addText(isExtension(p) ? p.getStructure().present() : p.getName());
|
||||
generateByProfile(res, profile, v, allElements, child, grandChildren, bq, path+"."+p.getName(), showCodeDetails, indent+1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -698,7 +756,13 @@ public class ProfileDrivenRenderer extends ResourceRenderer {
|
|||
return res;
|
||||
}
|
||||
|
||||
private boolean canDoTable(String path, PropertyWrapper p, List<ElementDefinition> grandChildren) {
|
||||
private boolean canDoTable(String path, PropertyWrapper p, List<ElementDefinition> grandChildren, XhtmlNode x) {
|
||||
if (isExtension(p)) {
|
||||
return false;
|
||||
}
|
||||
if (x.getName().equals("p")) {
|
||||
return false;
|
||||
}
|
||||
for (ElementDefinition e : grandChildren) {
|
||||
List<PropertyWrapper> values = getValues(path, p, e);
|
||||
if (values.size() > 1 || !isPrimitive(e) || !canCollapse(e))
|
||||
|
@ -707,6 +771,10 @@ public class ProfileDrivenRenderer extends ResourceRenderer {
|
|||
return true;
|
||||
}
|
||||
|
||||
public boolean isExtension(PropertyWrapper p) {
|
||||
return p.getName().contains("extension[");
|
||||
}
|
||||
|
||||
|
||||
private boolean canCollapse(ElementDefinition e) {
|
||||
// we can collapse any data type
|
||||
|
@ -784,18 +852,20 @@ public class ProfileDrivenRenderer extends ResourceRenderer {
|
|||
getContext().getWorker().cacheResource(ed);
|
||||
}
|
||||
}
|
||||
if (p.getName().equals("modifierExtension") && ed == null)
|
||||
if (p.getName().equals("modifierExtension") && ed == null) {
|
||||
throw new DefinitionException("Unknown modifier extension "+url);
|
||||
}
|
||||
PropertyWrapper pe = map.get(p.getName()+"["+url+"]");
|
||||
if (pe == null) {
|
||||
if (ed == null) {
|
||||
if (url.startsWith("http://hl7.org/fhir") && !url.startsWith("http://hl7.org/fhir/us"))
|
||||
if (url.startsWith("http://hl7.org/fhir") && !url.startsWith("http://hl7.org/fhir/us")) {
|
||||
throw new DefinitionException("unknown extension "+url);
|
||||
}
|
||||
// System.out.println("unknown extension "+url);
|
||||
pe = new PropertyWrapperDirect(this.context, new Property(p.getName()+"["+url+"]", p.getTypeCode(), p.getDefinition(), p.getMinCardinality(), p.getMaxCardinality(), ex));
|
||||
pe = new PropertyWrapperDirect(this.context, new Property(p.getName()+"["+url+"]", p.getTypeCode(), p.getDefinition(), p.getMinCardinality(), p.getMaxCardinality(), ex), null);
|
||||
} else {
|
||||
ElementDefinition def = ed.getSnapshot().getElement().get(0);
|
||||
pe = new PropertyWrapperDirect(this.context, new Property(p.getName()+"["+url+"]", "Extension", def.getDefinition(), def.getMin(), def.getMax().equals("*") ? Integer.MAX_VALUE : Integer.parseInt(def.getMax()), ex));
|
||||
pe = new PropertyWrapperDirect(this.context, new Property(p.getName()+"["+url+"]", "Extension", def.getDefinition(), def.getMin(), def.getMax().equals("*") ? Integer.MAX_VALUE : Integer.parseInt(def.getMax()), ex), ed.getSnapshot().getElementFirstRep());
|
||||
((PropertyWrapperDirect) pe).getWrapped().setStructure(ed);
|
||||
}
|
||||
results.add(pe);
|
||||
|
|
|
@ -72,6 +72,9 @@ public class RendererFactory {
|
|||
if ("OperationOutcome".equals(resourceName)) {
|
||||
return new OperationOutcomeRenderer(context);
|
||||
}
|
||||
if ("Parameters".equals(resourceName)) {
|
||||
return new ParametersRenderer(context);
|
||||
}
|
||||
return new ProfileDrivenRenderer(context);
|
||||
}
|
||||
|
||||
|
|
|
@ -8,6 +8,7 @@ import java.util.List;
|
|||
import org.hl7.fhir.exceptions.FHIRException;
|
||||
import org.hl7.fhir.exceptions.FHIRFormatError;
|
||||
import org.hl7.fhir.r5.model.Base;
|
||||
import org.hl7.fhir.r5.model.ElementDefinition;
|
||||
import org.hl7.fhir.r5.model.StructureDefinition;
|
||||
import org.hl7.fhir.r5.model.Narrative.NarrativeStatus;
|
||||
import org.hl7.fhir.r5.renderers.ResourceRenderer;
|
||||
|
@ -28,6 +29,7 @@ public class BaseWrappers {
|
|||
public int getMinCardinality();
|
||||
public int getMaxCardinality();
|
||||
public StructureDefinition getStructure();
|
||||
public ElementDefinition getElementDefinition();
|
||||
public BaseWrapper value();
|
||||
public ResourceWrapper getAsResource();
|
||||
public String fhirType();
|
||||
|
@ -87,7 +89,7 @@ public class BaseWrappers {
|
|||
@Override
|
||||
public boolean has(String name) {
|
||||
for (PropertyWrapper p : children()) {
|
||||
if (p.getName().equals(name)) {
|
||||
if (p.getName().equals(name) || p.getName().equals(name+"[x]") ) {
|
||||
return p.hasValues();
|
||||
}
|
||||
}
|
||||
|
@ -97,7 +99,7 @@ public class BaseWrappers {
|
|||
@Override
|
||||
public Base get(String name) throws UnsupportedEncodingException, FHIRException, IOException {
|
||||
for (PropertyWrapper p : children()) {
|
||||
if (p.getName().equals(name)) {
|
||||
if (p.getName().equals(name) || p.getName().equals(name+"[x]")) {
|
||||
if (p.hasValues()) {
|
||||
return p.getValues().get(0).getBase();
|
||||
} else {
|
||||
|
@ -111,7 +113,7 @@ public class BaseWrappers {
|
|||
@Override
|
||||
public List<BaseWrapper> children(String name) throws UnsupportedEncodingException, FHIRException, IOException {
|
||||
for (PropertyWrapper p : children()) {
|
||||
if (p.getName().equals(name)) {
|
||||
if (p.getName().equals(name) || p.getName().equals(name+"[x]")) {
|
||||
List<BaseWrapper> res = new ArrayList<>();
|
||||
for (BaseWrapper b : p.getValues()) {
|
||||
res.add(b);
|
||||
|
|
|
@ -206,6 +206,11 @@ public class DOMWrappers {
|
|||
return getTypeCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ElementDefinition getElementDefinition() {
|
||||
return definition;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class ResourceWrapperElement extends WrapperBaseImpl implements ResourceWrapper {
|
||||
|
|
|
@ -8,6 +8,7 @@ import java.util.List;
|
|||
import org.hl7.fhir.r5.model.Base;
|
||||
import org.hl7.fhir.r5.model.CanonicalResource;
|
||||
import org.hl7.fhir.r5.model.DomainResource;
|
||||
import org.hl7.fhir.r5.model.ElementDefinition;
|
||||
import org.hl7.fhir.r5.model.Encounter;
|
||||
import org.hl7.fhir.r5.model.Narrative.NarrativeStatus;
|
||||
import org.hl7.fhir.r5.model.Patient;
|
||||
|
@ -29,6 +30,7 @@ public class DirectWrappers {
|
|||
public static class PropertyWrapperDirect extends RendererWrapperImpl implements PropertyWrapper {
|
||||
private Property wrapped;
|
||||
private List<BaseWrapper> list;
|
||||
private ElementDefinition ed;
|
||||
|
||||
public PropertyWrapperDirect(RenderingContext context, Property wrapped) {
|
||||
super(context);
|
||||
|
@ -37,6 +39,14 @@ public class DirectWrappers {
|
|||
this.wrapped = wrapped;
|
||||
}
|
||||
|
||||
public PropertyWrapperDirect(RenderingContext context, Property wrapped, ElementDefinition ed) {
|
||||
super(context);
|
||||
if (wrapped == null)
|
||||
throw new Error("wrapped == null");
|
||||
this.wrapped = wrapped;
|
||||
this.ed = ed;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return wrapped.getName();
|
||||
|
@ -106,6 +116,11 @@ public class DirectWrappers {
|
|||
public String fhirType() {
|
||||
return wrapped.getTypeCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ElementDefinition getElementDefinition() {
|
||||
return ed;
|
||||
}
|
||||
}
|
||||
|
||||
public static class BaseWrapperDirect extends WrapperBaseImpl implements BaseWrapper {
|
||||
|
|
|
@ -58,7 +58,7 @@ public class ElementWrappers {
|
|||
throw new FHIRException(e.getMessage(), e);
|
||||
}
|
||||
if (context.getParser() == null) {
|
||||
System.out.println("Noe version specific parser provided");
|
||||
System.out.println("No version specific parser provided");
|
||||
}
|
||||
if (context.getParser() == null) {
|
||||
throw new Error("No type parser provided to renderer context");
|
||||
|
@ -324,6 +324,11 @@ public class ElementWrappers {
|
|||
return getTypeCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ElementDefinition getElementDefinition() {
|
||||
return definition;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -319,6 +319,20 @@ public class FHIRPathEngine {
|
|||
}
|
||||
}
|
||||
|
||||
public FHIRPathEngine(IWorkerContext worker, ProfileUtilities utilities) {
|
||||
super();
|
||||
this.worker = worker;
|
||||
profileUtilities = utilities;
|
||||
for (StructureDefinition sd : worker.getStructures()) {
|
||||
if (sd.getDerivation() == TypeDerivationRule.SPECIALIZATION && sd.getKind() != StructureDefinitionKind.LOGICAL) {
|
||||
allTypes.put(sd.getName(), sd);
|
||||
}
|
||||
if (sd.getDerivation() == TypeDerivationRule.SPECIALIZATION && sd.getKind() == StructureDefinitionKind.PRIMITIVETYPE) {
|
||||
primitiveTypes.add(sd.getName());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// --- 3 methods to override in children -------------------------------------------------------
|
||||
// if you don't override, it falls through to the using the base reference implementation
|
||||
|
@ -781,6 +795,10 @@ public class FHIRPathEngine {
|
|||
return item.primitiveValue();
|
||||
} else if (item instanceof Quantity) {
|
||||
Quantity q = (Quantity) item;
|
||||
if (q.hasUnit() && Utilities.existsInList(q.getUnit(), "year", "years", "month", "months", "week", "weeks", "day", "days", "hour", "hours", "minute", "minutes", "second", "seconds", "millisecond", "milliseconds")
|
||||
&& (!q.hasSystem() || q.getSystem().equals("http://unitsofmeasure.org"))) {
|
||||
return q.getValue().toPlainString()+" "+q.getUnit();
|
||||
}
|
||||
if (q.getSystem().equals("http://unitsofmeasure.org")) {
|
||||
String u = "'"+q.getCode()+"'";
|
||||
return q.getValue().toPlainString()+" "+u;
|
||||
|
@ -940,12 +958,14 @@ public class FHIRPathEngine {
|
|||
if (!isString && !lexer.done() && (result.getConstant() instanceof IntegerType || result.getConstant() instanceof DecimalType) && (lexer.isStringConstant() || lexer.hasToken("year", "years", "month", "months", "week", "weeks", "day", "days", "hour", "hours", "minute", "minutes", "second", "seconds", "millisecond", "milliseconds"))) {
|
||||
// it's a quantity
|
||||
String ucum = null;
|
||||
String unit = null;
|
||||
if (lexer.hasToken("year", "years", "month", "months", "week", "weeks", "day", "days", "hour", "hours", "minute", "minutes", "second", "seconds", "millisecond", "milliseconds")) {
|
||||
String s = lexer.take();
|
||||
unit = s;
|
||||
if (s.equals("year") || s.equals("years")) {
|
||||
ucum = "a";
|
||||
// this is not the UCUM year
|
||||
} else if (s.equals("month") || s.equals("months")) {
|
||||
ucum = "mo";
|
||||
// this is not the UCUM month
|
||||
} else if (s.equals("week") || s.equals("weeks")) {
|
||||
ucum = "wk";
|
||||
} else if (s.equals("day") || s.equals("days")) {
|
||||
|
@ -962,7 +982,7 @@ public class FHIRPathEngine {
|
|||
} else {
|
||||
ucum = lexer.readConstant("units");
|
||||
}
|
||||
result.setConstant(new Quantity().setValue(new BigDecimal(result.getConstant().primitiveValue())).setSystem("http://unitsofmeasure.org").setCode(ucum));
|
||||
result.setConstant(new Quantity().setValue(new BigDecimal(result.getConstant().primitiveValue())).setUnit(unit).setSystem(ucum == null ? null : "http://unitsofmeasure.org").setCode(ucum));
|
||||
}
|
||||
result.setEnd(lexer.getCurrentLocation());
|
||||
} else if ("(".equals(lexer.getCurrent())) {
|
||||
|
@ -1920,18 +1940,50 @@ public class FHIRPathEngine {
|
|||
}
|
||||
}
|
||||
|
||||
private boolean qtyEqual(Quantity left, Quantity right) {
|
||||
private Boolean qtyEqual(Quantity left, Quantity right) {
|
||||
if (!left.hasValue() && !right.hasValue()) {
|
||||
return true;
|
||||
}
|
||||
if (!left.hasValue() || !right.hasValue()) {
|
||||
return null;
|
||||
}
|
||||
if (worker.getUcumService() != null) {
|
||||
DecimalType dl = qtyToCanonical(left);
|
||||
DecimalType dr = qtyToCanonical(right);
|
||||
Pair dl = qtyToCanonicalPair(left);
|
||||
Pair dr = qtyToCanonicalPair(right);
|
||||
if (dl != null && dr != null) {
|
||||
return doEquals(dl, dr);
|
||||
if (dl.getCode().equals(dr.getCode())) {
|
||||
return doEquals(new DecimalType(dl.getValue().asDecimal()), new DecimalType(dr.getValue().asDecimal()));
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return left.equals(right);
|
||||
if (left.hasCode() || right.hasCode()) {
|
||||
if (!(left.hasCode() && right.hasCode()) || !left.getCode().equals(right.getCode())) {
|
||||
return null;
|
||||
}
|
||||
} else if (!left.hasUnit() || right.hasUnit()) {
|
||||
if (!(left.hasUnit() && right.hasUnit()) || !left.getUnit().equals(right.getUnit())) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return doEquals(new DecimalType(left.getValue()), new DecimalType(right.getValue()));
|
||||
}
|
||||
|
||||
private DecimalType qtyToCanonical(Quantity q) {
|
||||
private Pair qtyToCanonicalPair(Quantity q) {
|
||||
if (!"http://unitsofmeasure.org".equals(q.getSystem())) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
Pair p = new Pair(new Decimal(q.getValue().toPlainString()), q.getCode() == null ? "1" : q.getCode());
|
||||
Pair c = worker.getUcumService().getCanonicalForm(p);
|
||||
return c;
|
||||
} catch (UcumException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private DecimalType qtyToCanonicalDecimal(Quantity q) {
|
||||
if (!"http://unitsofmeasure.org".equals(q.getSystem())) {
|
||||
return null;
|
||||
}
|
||||
|
@ -1961,15 +2013,34 @@ public class FHIRPathEngine {
|
|||
}
|
||||
|
||||
|
||||
private boolean qtyEquivalent(Quantity left, Quantity right) throws PathEngineException {
|
||||
private Boolean qtyEquivalent(Quantity left, Quantity right) throws PathEngineException {
|
||||
if (!left.hasValue() && !right.hasValue()) {
|
||||
return true;
|
||||
}
|
||||
if (!left.hasValue() || !right.hasValue()) {
|
||||
return null;
|
||||
}
|
||||
if (worker.getUcumService() != null) {
|
||||
DecimalType dl = qtyToCanonical(left);
|
||||
DecimalType dr = qtyToCanonical(right);
|
||||
Pair dl = qtyToCanonicalPair(left);
|
||||
Pair dr = qtyToCanonicalPair(right);
|
||||
if (dl != null && dr != null) {
|
||||
return doEquivalent(dl, dr);
|
||||
if (dl.getCode().equals(dr.getCode())) {
|
||||
return doEquivalent(new DecimalType(dl.getValue().asDecimal()), new DecimalType(dr.getValue().asDecimal()));
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return left.equals(right);
|
||||
if (left.hasCode() || right.hasCode()) {
|
||||
if (!(left.hasCode() && right.hasCode()) || !left.getCode().equals(right.getCode())) {
|
||||
return null;
|
||||
}
|
||||
} else if (!left.hasUnit() || right.hasUnit()) {
|
||||
if (!(left.hasUnit() && right.hasUnit()) || !left.getUnit().equals(right.getUnit())) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return doEquivalent(new DecimalType(left.getValue()), new DecimalType(right.getValue()));
|
||||
}
|
||||
|
||||
|
||||
|
@ -2058,9 +2129,9 @@ public class FHIRPathEngine {
|
|||
return makeBoolean(false);
|
||||
} else {
|
||||
List<Base> dl = new ArrayList<Base>();
|
||||
dl.add(qtyToCanonical((Quantity) left.get(0)));
|
||||
dl.add(qtyToCanonicalDecimal((Quantity) left.get(0)));
|
||||
List<Base> dr = new ArrayList<Base>();
|
||||
dr.add(qtyToCanonical((Quantity) right.get(0)));
|
||||
dr.add(qtyToCanonicalDecimal((Quantity) right.get(0)));
|
||||
return opLessThan(dl, dr);
|
||||
}
|
||||
}
|
||||
|
@ -2105,9 +2176,9 @@ public class FHIRPathEngine {
|
|||
return makeBoolean(false);
|
||||
} else {
|
||||
List<Base> dl = new ArrayList<Base>();
|
||||
dl.add(qtyToCanonical((Quantity) left.get(0)));
|
||||
dl.add(qtyToCanonicalDecimal((Quantity) left.get(0)));
|
||||
List<Base> dr = new ArrayList<Base>();
|
||||
dr.add(qtyToCanonical((Quantity) right.get(0)));
|
||||
dr.add(qtyToCanonicalDecimal((Quantity) right.get(0)));
|
||||
return opGreater(dl, dr);
|
||||
}
|
||||
}
|
||||
|
@ -2155,9 +2226,9 @@ public class FHIRPathEngine {
|
|||
return makeBoolean(false);
|
||||
} else {
|
||||
List<Base> dl = new ArrayList<Base>();
|
||||
dl.add(qtyToCanonical((Quantity) left.get(0)));
|
||||
dl.add(qtyToCanonicalDecimal((Quantity) left.get(0)));
|
||||
List<Base> dr = new ArrayList<Base>();
|
||||
dr.add(qtyToCanonical((Quantity) right.get(0)));
|
||||
dr.add(qtyToCanonicalDecimal((Quantity) right.get(0)));
|
||||
return opLessOrEqual(dl, dr);
|
||||
}
|
||||
}
|
||||
|
@ -2203,9 +2274,9 @@ public class FHIRPathEngine {
|
|||
return makeBoolean(false);
|
||||
} else {
|
||||
List<Base> dl = new ArrayList<Base>();
|
||||
dl.add(qtyToCanonical((Quantity) left.get(0)));
|
||||
dl.add(qtyToCanonicalDecimal((Quantity) left.get(0)));
|
||||
List<Base> dr = new ArrayList<Base>();
|
||||
dr.add(qtyToCanonical((Quantity) right.get(0)));
|
||||
dr.add(qtyToCanonicalDecimal((Quantity) right.get(0)));
|
||||
return opGreaterOrEqual(dl, dr);
|
||||
}
|
||||
}
|
||||
|
@ -4843,7 +4914,7 @@ public class FHIRPathEngine {
|
|||
if (s.equals("year") || s.equals("years")) {
|
||||
return Quantity.fromUcum(v, "a");
|
||||
} else if (s.equals("month") || s.equals("months")) {
|
||||
return Quantity.fromUcum(v, "mo");
|
||||
return Quantity.fromUcum(v, "mo_s");
|
||||
} else if (s.equals("week") || s.equals("weeks")) {
|
||||
return Quantity.fromUcum(v, "wk");
|
||||
} else if (s.equals("day") || s.equals("days")) {
|
||||
|
|
|
@ -104,17 +104,17 @@ public class NPMPackageGenerator {
|
|||
private NpmPackageIndexBuilder indexer;
|
||||
|
||||
|
||||
public NPMPackageGenerator(String destFile, String canonical, String url, PackageType kind, ImplementationGuide ig, Date date) throws FHIRException, IOException {
|
||||
public NPMPackageGenerator(String destFile, String canonical, String url, PackageType kind, ImplementationGuide ig, Date date, boolean notForPublication) throws FHIRException, IOException {
|
||||
super();
|
||||
this.destFile = destFile;
|
||||
start();
|
||||
List<String> fhirVersion = new ArrayList<>();
|
||||
for (Enumeration<FHIRVersion> v : ig.getFhirVersion())
|
||||
fhirVersion.add(v.asStringValue());
|
||||
buildPackageJson(canonical, kind, url, date, ig, fhirVersion);
|
||||
buildPackageJson(canonical, kind, url, date, ig, fhirVersion, notForPublication);
|
||||
}
|
||||
|
||||
public static NPMPackageGenerator subset(NPMPackageGenerator master, String destFile, String id, String name, Date date) throws FHIRException, IOException {
|
||||
public static NPMPackageGenerator subset(NPMPackageGenerator master, String destFile, String id, String name, Date date, boolean notForPublication) throws FHIRException, IOException {
|
||||
JsonObject p = master.packageJ.deepCopy();
|
||||
p.remove("name");
|
||||
p.addProperty("name", id);
|
||||
|
@ -122,24 +122,30 @@ public class NPMPackageGenerator {
|
|||
p.addProperty("type", PackageType.SUBSET.getCode());
|
||||
p.remove("title");
|
||||
p.addProperty("title", name);
|
||||
if (notForPublication) {
|
||||
p.addProperty("notForPublication", true);
|
||||
}
|
||||
|
||||
return new NPMPackageGenerator(destFile, p, date);
|
||||
return new NPMPackageGenerator(destFile, p, date, notForPublication);
|
||||
}
|
||||
|
||||
public NPMPackageGenerator(String destFile, String canonical, String url, PackageType kind, ImplementationGuide ig, Date date, List<String> fhirVersion) throws FHIRException, IOException {
|
||||
public NPMPackageGenerator(String destFile, String canonical, String url, PackageType kind, ImplementationGuide ig, Date date, List<String> fhirVersion, boolean notForPublication) throws FHIRException, IOException {
|
||||
super();
|
||||
this.destFile = destFile;
|
||||
start();
|
||||
buildPackageJson(canonical, kind, url, date, ig, fhirVersion);
|
||||
buildPackageJson(canonical, kind, url, date, ig, fhirVersion, notForPublication);
|
||||
}
|
||||
|
||||
public NPMPackageGenerator(String destFile, JsonObject npm, Date date) throws FHIRException, IOException {
|
||||
public NPMPackageGenerator(String destFile, JsonObject npm, Date date, boolean notForPublication) throws FHIRException, IOException {
|
||||
super();
|
||||
String dt = new SimpleDateFormat("yyyyMMddHHmmss").format(date);
|
||||
packageJ = npm;
|
||||
packageManifest = new JsonObject();
|
||||
packageManifest.addProperty("version", npm.get("version").getAsString());
|
||||
packageManifest.addProperty("date", dt);
|
||||
if (notForPublication) {
|
||||
packageManifest.addProperty("notForPublication", true);
|
||||
}
|
||||
npm.addProperty("date", dt);
|
||||
packageManifest.addProperty("name", npm.get("name").getAsString());
|
||||
this.destFile = destFile;
|
||||
|
@ -152,19 +158,23 @@ public class NPMPackageGenerator {
|
|||
}
|
||||
}
|
||||
|
||||
private void buildPackageJson(String canonical, PackageType kind, String web, Date date, ImplementationGuide ig, List<String> fhirVersion) throws FHIRException, IOException {
|
||||
private void buildPackageJson(String canonical, PackageType kind, String web, Date date, ImplementationGuide ig, List<String> fhirVersion, boolean notForPublication) throws FHIRException, IOException {
|
||||
String dtHuman = new SimpleDateFormat("EEE, MMM d, yyyy HH:mmZ", new Locale("en", "US")).format(date);
|
||||
String dt = new SimpleDateFormat("yyyyMMddHHmmss").format(date);
|
||||
|
||||
CommaSeparatedStringBuilder b = new CommaSeparatedStringBuilder();
|
||||
if (!ig.hasPackageId())
|
||||
if (!ig.hasPackageId()) {
|
||||
b.append("packageId");
|
||||
if (!ig.hasVersion())
|
||||
}
|
||||
if (!ig.hasVersion()) {
|
||||
b.append("version");
|
||||
if (!ig.hasFhirVersion())
|
||||
}
|
||||
if (!ig.hasFhirVersion()) {
|
||||
b.append("fhirVersion");
|
||||
if (!ig.hasLicense())
|
||||
}
|
||||
if (!ig.hasLicense()) {
|
||||
b.append("license");
|
||||
}
|
||||
for (ImplementationGuideDependsOnComponent d : ig.getDependsOn()) {
|
||||
if (!d.hasVersion()) {
|
||||
b.append("dependsOn.version("+d.getUri()+")");
|
||||
|
@ -177,14 +187,20 @@ public class NPMPackageGenerator {
|
|||
npm.addProperty("tools-version", ToolsVersion.TOOLS_VERSION);
|
||||
npm.addProperty("type", kind.getCode());
|
||||
npm.addProperty("date", dt);
|
||||
if (ig.hasLicense())
|
||||
if (ig.hasLicense()) {
|
||||
npm.addProperty("license", ig.getLicense().toCode());
|
||||
}
|
||||
npm.addProperty("canonical", canonical);
|
||||
if (notForPublication) {
|
||||
npm.addProperty("notForPublication", true);
|
||||
}
|
||||
npm.addProperty("url", web);
|
||||
if (ig.hasTitle())
|
||||
if (ig.hasTitle()) {
|
||||
npm.addProperty("title", ig.getTitle());
|
||||
if (ig.hasDescription())
|
||||
}
|
||||
if (ig.hasDescription()) {
|
||||
npm.addProperty("description", ig.getDescription()+ " (built "+dtHuman+timezone()+")");
|
||||
}
|
||||
JsonArray vl = new JsonArray();
|
||||
|
||||
npm.add("fhirVersions", vl);
|
||||
|
@ -205,8 +221,9 @@ public class NPMPackageGenerator {
|
|||
dep.addProperty(d.getPackageId(), d.getVersion());
|
||||
}
|
||||
}
|
||||
if (ig.hasPublisher())
|
||||
if (ig.hasPublisher()) {
|
||||
npm.addProperty("author", ig.getPublisher());
|
||||
}
|
||||
JsonArray m = new JsonArray();
|
||||
for (ContactDetail t : ig.getContact()) {
|
||||
String email = email(t.getTelecom());
|
||||
|
|
|
@ -145,7 +145,7 @@ public class FHIRPathTests {
|
|||
public void test(String name, Element test) throws FileNotFoundException, IOException, FHIRException, org.hl7.fhir.exceptions.FHIRException, UcumException {
|
||||
// Setting timezone for this test. Grahame is in UTC+11, Travis is in GMT, and I'm here in Toronto, Canada with
|
||||
// all my time based tests failing locally...
|
||||
TimeZone.setDefault(TimeZone.getTimeZone("UTC+1100"));
|
||||
TimeZone.setDefault(TimeZone.getTimeZone("UTC+1100"));
|
||||
|
||||
fp.setHostServices(new FHIRPathTestEvaluationServices());
|
||||
String input = test.getAttribute("inputfile");
|
||||
|
@ -178,6 +178,7 @@ public class FHIRPathTests {
|
|||
outcome.clear();
|
||||
outcome.add(new BooleanType(ok));
|
||||
}
|
||||
System.out.println(name);
|
||||
if (fp.hasLog()) {
|
||||
System.out.println(name);
|
||||
System.out.println(fp.takeLog());
|
||||
|
|
|
@ -11,22 +11,32 @@ import javax.xml.parsers.ParserConfigurationException;
|
|||
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.commons.lang3.SystemUtils;
|
||||
import org.hl7.fhir.exceptions.FHIRException;
|
||||
import org.hl7.fhir.exceptions.FHIRFormatError;
|
||||
import org.hl7.fhir.r5.context.IWorkerContext;
|
||||
import org.hl7.fhir.r5.elementmodel.Manager;
|
||||
import org.hl7.fhir.r5.elementmodel.Manager.FhirFormat;
|
||||
import org.hl7.fhir.r5.formats.IParser.OutputStyle;
|
||||
import org.hl7.fhir.r5.formats.JsonParser;
|
||||
import org.hl7.fhir.r5.formats.XmlParser;
|
||||
import org.hl7.fhir.r5.model.Base;
|
||||
import org.hl7.fhir.r5.model.DomainResource;
|
||||
import org.hl7.fhir.r5.model.Questionnaire;
|
||||
import org.hl7.fhir.r5.model.Resource;
|
||||
import org.hl7.fhir.r5.renderers.RendererFactory;
|
||||
import org.hl7.fhir.r5.renderers.ResourceRenderer;
|
||||
import org.hl7.fhir.r5.renderers.utils.ElementWrappers;
|
||||
import org.hl7.fhir.r5.renderers.utils.RenderingContext;
|
||||
import org.hl7.fhir.r5.renderers.utils.RenderingContext.ITypeParser;
|
||||
import org.hl7.fhir.r5.renderers.utils.RenderingContext.QuestionnaireRendererMode;
|
||||
import org.hl7.fhir.r5.renderers.utils.RenderingContext.ResourceRendererMode;
|
||||
import org.hl7.fhir.r5.test.NarrativeGenerationTests.TestTypeParser;
|
||||
import org.hl7.fhir.r5.test.utils.TestingUtilities;
|
||||
import org.hl7.fhir.utilities.TerminologyServiceOptions;
|
||||
import org.hl7.fhir.utilities.TextFile;
|
||||
import org.hl7.fhir.utilities.xhtml.XhtmlComposer;
|
||||
import org.hl7.fhir.utilities.xhtml.XhtmlNode;
|
||||
import org.hl7.fhir.utilities.xhtml.XhtmlParser;
|
||||
import org.hl7.fhir.utilities.xml.XMLUtil;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
|
@ -40,6 +50,15 @@ import org.xml.sax.SAXException;
|
|||
|
||||
public class NarrativeGenerationTests {
|
||||
|
||||
public class TestTypeParser implements ITypeParser {
|
||||
|
||||
@Override
|
||||
public Base parseType(String xml, String type) throws FHIRFormatError, IOException, FHIRException {
|
||||
return new org.hl7.fhir.r5.formats.XmlParser().parseType(xml, type);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static final String WINDOWS = "WINDOWS";
|
||||
|
||||
private static final String HEADER = "<html><head>"+
|
||||
|
@ -57,11 +76,13 @@ public class NarrativeGenerationTests {
|
|||
public static class TestDetails {
|
||||
private String id;
|
||||
private boolean header;
|
||||
private boolean meta;
|
||||
|
||||
public TestDetails(Element test) {
|
||||
super();
|
||||
id = test.getAttribute("id");
|
||||
header = "true".equals(test.getAttribute("header"));
|
||||
meta = "true".equals(test.getAttribute("meta"));
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
|
@ -70,6 +91,10 @@ public class NarrativeGenerationTests {
|
|||
|
||||
public boolean isHeader() {
|
||||
return header;
|
||||
}
|
||||
|
||||
public boolean isMeta() {
|
||||
return meta;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -107,19 +132,30 @@ public class NarrativeGenerationTests {
|
|||
rc.setHeader(test.isHeader());
|
||||
rc.setDefinitionsTarget("test.html");
|
||||
rc.setTerminologyServiceOptions(TerminologyServiceOptions.defaults());
|
||||
IOUtils.copy(TestingUtilities.loadTestResourceStream("r5", "narrative", test.getId() + "-expected.xml"), new FileOutputStream(TestingUtilities.tempFile("narrative", test.getId() + "-expected.xml")));
|
||||
DomainResource source;
|
||||
if (TestingUtilities.findTestResource("r5", "narrative", test.getId() + "-input.json")) {
|
||||
source = (DomainResource) new JsonParser().parse(TestingUtilities.loadTestResourceStream("r5", "narrative", test.getId() + "-input.json"));
|
||||
rc.setParser(new TestTypeParser());
|
||||
Resource source;
|
||||
if (TestingUtilities.findTestResource("r5", "narrative", test.getId() + ".json")) {
|
||||
source = (Resource) new JsonParser().parse(TestingUtilities.loadTestResourceStream("r5", "narrative", test.getId() + ".json"));
|
||||
} else {
|
||||
source = (DomainResource) new XmlParser().parse(TestingUtilities.loadTestResourceStream("r5", "narrative", test.getId() + "-input.xml"));
|
||||
source = (Resource) new XmlParser().parse(TestingUtilities.loadTestResourceStream("r5", "narrative", test.getId() + ".xml"));
|
||||
}
|
||||
|
||||
XhtmlNode x = RendererFactory.factory(source, rc).build(source);
|
||||
String target = TextFile.streamToString(TestingUtilities.loadTestResourceStream("r5", "narrative", test.getId() + ".html"));
|
||||
String output = HEADER+new XhtmlComposer(true, true).compose(x)+FOOTER;
|
||||
TextFile.stringToFile(target, TestingUtilities.tempFile("narrative", test.getId() + ".target.html"));
|
||||
TextFile.stringToFile(output, TestingUtilities.tempFile("narrative", test.getId() + ".output.html"));
|
||||
Assertions.assertTrue(output.equals(target), "Output does not match expected");
|
||||
|
||||
if (test.isMeta()) {
|
||||
org.hl7.fhir.r5.elementmodel.Element e = Manager.parse(context, TestingUtilities.loadTestResourceStream("r5", "narrative", test.getId() + ".xml"), FhirFormat.XML);
|
||||
x = RendererFactory.factory(source, rc).render(new ElementWrappers.ResourceWrapperMetaElement(rc, e));
|
||||
|
||||
target = TextFile.streamToString(TestingUtilities.loadTestResourceStream("r5", "narrative", test.getId() + "-meta.html"));
|
||||
output = HEADER+new XhtmlComposer(true, true).compose(x)+FOOTER;
|
||||
TextFile.stringToFile(output, TestingUtilities.tempFile("narrative", test.getId() + "-meta.output.html"));
|
||||
Assertions.assertTrue(output.equals(target), "Output does not match expected (meta)");
|
||||
}
|
||||
DomainResource target = (DomainResource) new XmlParser().parse(TestingUtilities.loadTestResourceStream("r5", "narrative", test.getId() + "-expected.xml"));
|
||||
RendererFactory.factory(source, rc).render(source);
|
||||
new XmlParser().setOutputStyle(OutputStyle.PRETTY).compose(new FileOutputStream(TestingUtilities.tempFile("narrative", test.getId() + "-actual.xml")), source);
|
||||
source = (DomainResource) new XmlParser().parse(new FileInputStream(TestingUtilities.tempFile("narrative", test.getId() + "-actual.xml")));
|
||||
String html = HEADER+new XhtmlComposer(true).compose(source.getText().getDiv())+FOOTER;
|
||||
TextFile.stringToFile(html, TestingUtilities.tempFile("narrative", test.getId() + ".html"));
|
||||
Assertions.assertTrue(source.equalsDeep(target), "Output does not match expected");
|
||||
}
|
||||
|
||||
}
|
|
@ -13,6 +13,7 @@ import org.hl7.fhir.r5.formats.IParser.OutputStyle;
|
|||
import org.hl7.fhir.r5.formats.JsonParser;
|
||||
import org.hl7.fhir.r5.formats.XmlParser;
|
||||
import org.hl7.fhir.r5.model.Bundle;
|
||||
import org.hl7.fhir.r5.model.DateTimeType;
|
||||
import org.hl7.fhir.r5.model.DomainResource;
|
||||
import org.hl7.fhir.r5.model.Resource;
|
||||
import org.hl7.fhir.r5.renderers.RendererFactory;
|
||||
|
@ -20,6 +21,7 @@ import org.hl7.fhir.r5.renderers.utils.RenderingContext;
|
|||
import org.hl7.fhir.r5.renderers.utils.RenderingContext.ResourceRendererMode;
|
||||
import org.hl7.fhir.r5.test.utils.TestingUtilities;
|
||||
import org.hl7.fhir.r5.utils.EOperationOutcome;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
public class ResourceRoundTripTests {
|
||||
|
@ -52,4 +54,5 @@ public class ResourceRoundTripTests {
|
|||
if (result == null)
|
||||
throw new FHIRException("Bundle was null");
|
||||
}
|
||||
|
||||
}
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>org.hl7.fhir.core</artifactId>
|
||||
<version>5.1.8-SNAPSHOT</version>
|
||||
<version>5.1.9-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>org.hl7.fhir.core</artifactId>
|
||||
<version>5.1.8-SNAPSHOT</version>
|
||||
<version>5.1.9-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -56,6 +56,12 @@ public class MarkDownProcessor {
|
|||
|
||||
|
||||
public String process(String source, String context) {
|
||||
if (source == null) {
|
||||
return null;
|
||||
}
|
||||
if ("".equals(source)) {
|
||||
return "";
|
||||
}
|
||||
switch (dialect) {
|
||||
case DARING_FIREBALL : return Processor.process(source);
|
||||
case COMMON_MARK : return processCommonMark(source);
|
||||
|
|
|
@ -1091,6 +1091,10 @@ public class NpmPackage {
|
|||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean isNotForPublication() {
|
||||
return JSONUtil.bool(npm, "notForPublication");
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -161,7 +161,7 @@ public class I18nConstants {
|
|||
public static final String FHIRPATH_UNKNOWN_CONSTANT = "FHIRPATH_UNKNOWN_CONSTANT";
|
||||
public static final String FHIRPATH_UNKNOWN_CONTEXT = "FHIRPATH_UNKNOWN_CONTEXT";
|
||||
public static final String FHIRPATH_UNKNOWN_CONTEXT_ELEMENT = "FHIRPATH_UNKNOWN_CONTEXT_ELEMENT";
|
||||
public static final String FHIRPATH_UNKNOWN_NAME = "FHIRPATH_UNWKNOWN_NAME";
|
||||
public static final String FHIRPATH_UNKNOWN_NAME = "FHIRPATH_UNKNOWN_NAME";
|
||||
public static final String FHIRPATH_WRONG_PARAM_TYPE = "FHIRPATH_WRONG_PARAM_TYPE";
|
||||
public static final String FIXED_TYPE_CHECKS_DT_ADDRESS_LINE = "Fixed_Type_Checks_DT_Address_Line";
|
||||
public static final String FIXED_TYPE_CHECKS_DT_NAME_FAMILY = "Fixed_Type_Checks_DT_Name_Family";
|
||||
|
@ -466,6 +466,7 @@ public class I18nConstants {
|
|||
public static final String TYPE_SPECIFIC_CHECKS_DT_PRIMITIVE_REGEX = "Type_Specific_Checks_DT_Primitive_Regex";
|
||||
public static final String TYPE_SPECIFIC_CHECKS_DT_PRIMITIVE_VALUEEXT = "Type_Specific_Checks_DT_Primitive_ValueExt";
|
||||
public static final String TYPE_SPECIFIC_CHECKS_DT_PRIMITIVE_WS = "Type_Specific_Checks_DT_Primitive_WS";
|
||||
public static final String TYPE_SPECIFIC_CHECKS_DT_QTY_NO_ANNOTATIONS = "TYPE_SPECIFIC_CHECKS_DT_QTY_NO_ANNOTATIONS";
|
||||
public static final String TYPE_SPECIFIC_CHECKS_DT_STRING_LENGTH = "Type_Specific_Checks_DT_String_Length";
|
||||
public static final String TYPE_SPECIFIC_CHECKS_DT_STRING_WS = "Type_Specific_Checks_DT_String_WS";
|
||||
public static final String TYPE_SPECIFIC_CHECKS_DT_TIME_VALID = "Type_Specific_Checks_DT_Time_Valid";
|
||||
|
@ -578,6 +579,7 @@ public class I18nConstants {
|
|||
public static final String XHTML_XHTML_ATTRIBUTE_ILLEGAL = "XHTML_XHTML_Attribute_Illegal";
|
||||
public static final String XHTML_XHTML_DOCTYPE_ILLEGAL = "XHTML_XHTML_DOCTYPE_ILLEGAL";
|
||||
public static final String XHTML_XHTML_ELEMENT_ILLEGAL = "XHTML_XHTML_Element_Illegal";
|
||||
public static final String XHTML_XHTML_ELEMENT_ILLEGAL_IN_PARA = "XHTML_XHTML_ELEMENT_ILLEGAL_IN_PARA";
|
||||
public static final String XHTML_XHTML_NAME_INVALID = "XHTML_XHTML_Name_Invalid";
|
||||
public static final String XHTML_XHTML_NS_INVALID = "XHTML_XHTML_NS_InValid";
|
||||
public static final String XML_ATTR_VALUE_INVALID = "xml_attr_value_invalid";
|
||||
|
|
|
@ -41,7 +41,10 @@ import java.util.Map;
|
|||
import org.hl7.fhir.exceptions.FHIRException;
|
||||
import org.hl7.fhir.instance.model.api.IBaseXhtml;
|
||||
import org.hl7.fhir.utilities.Utilities;
|
||||
import org.hl7.fhir.utilities.i18n.I18nConstants;
|
||||
import org.hl7.fhir.utilities.validation.ValidationMessage.IssueType;
|
||||
|
||||
import ca.uhn.fhir.model.api.annotation.ChildOrder;
|
||||
import ca.uhn.fhir.model.primitive.XhtmlDt;
|
||||
|
||||
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||
|
@ -137,11 +140,53 @@ public class XhtmlNode implements IBaseXhtml {
|
|||
return this;
|
||||
}
|
||||
|
||||
public void validate(List<String> errors, String path, boolean inResource, boolean inPara) {
|
||||
if (nodeType == NodeType.Element || nodeType == NodeType.Document) {
|
||||
path = Utilities.noString(path) ? name : path+"/"+name;
|
||||
if (inResource) {
|
||||
if (!Utilities.existsInList(name, "p", "br", "div", "h1", "h2", "h3", "h4", "h5", "h6", "a", "span", "b", "em", "i", "strong",
|
||||
"small", "big", "tt", "small", "dfn", "q", "var", "abbr", "acronym", "cite", "blockquote", "hr", "address", "bdo", "kbd", "q", "sub", "sup",
|
||||
"ul", "ol", "li", "dl", "dt", "dd", "pre", "table", "caption", "colgroup", "col", "thead", "tr", "tfoot", "tbody", "th", "td",
|
||||
"code", "samp", "img", "map", "area")) {
|
||||
errors.add("Error at "+path+": Found "+name+" in a resource");
|
||||
}
|
||||
for (String an : attributes.keySet()) {
|
||||
boolean ok = an.startsWith("xmlns") || Utilities.existsInList(an,
|
||||
"title", "style", "class", "ID", "lang", "xml:lang", "dir", "accesskey", "tabindex",
|
||||
// tables
|
||||
"span", "width", "align", "valign", "char", "charoff", "abbr", "axis", "headers", "scope", "rowspan", "colspan") ||
|
||||
Utilities.existsInList(name + "." + an, "a.href", "a.name", "img.src", "img.border", "div.xmlns", "blockquote.cite", "q.cite",
|
||||
"a.charset", "a.type", "a.name", "a.href", "a.hreflang", "a.rel", "a.rev", "a.shape", "a.coords", "img.src",
|
||||
"img.alt", "img.longdesc", "img.height", "img.width", "img.usemap", "img.ismap", "map.name", "area.shape",
|
||||
"area.coords", "area.href", "area.nohref", "area.alt", "table.summary", "table.width", "table.border",
|
||||
"table.frame", "table.rules", "table.cellspacing", "table.cellpadding", "pre.space", "td.nowrap"
|
||||
);
|
||||
if (!ok)
|
||||
errors.add("Error at "+path+": Found attribute "+name+"."+an+" in a resource");
|
||||
}
|
||||
}
|
||||
if (inPara && Utilities.existsInList(name, "div", "blockquote", "table", "ol", "ul", "p")) {
|
||||
errors.add("Error at "+path+": Found "+name+" inside an html paragraph");
|
||||
}
|
||||
|
||||
if (childNodes != null) {
|
||||
if ("p".equals(name)) {
|
||||
inPara = true;
|
||||
}
|
||||
for (XhtmlNode child : childNodes) {
|
||||
child.validate(errors, path, inResource, inPara);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public XhtmlNode addTag(String name)
|
||||
{
|
||||
|
||||
if (!(nodeType == NodeType.Element || nodeType == NodeType.Document))
|
||||
if (!(nodeType == NodeType.Element || nodeType == NodeType.Document)) {
|
||||
throw new Error("Wrong node type - node is "+nodeType.toString()+" ('"+getName()+"/"+getContent()+"')");
|
||||
}
|
||||
|
||||
XhtmlNode node = new XhtmlNode(NodeType.Element);
|
||||
node.setName(name);
|
||||
childNodes.add(node);
|
||||
|
@ -188,7 +233,6 @@ public class XhtmlNode implements IBaseXhtml {
|
|||
childNodes.add(node);
|
||||
return node;
|
||||
}
|
||||
|
||||
public XhtmlNode addText(String content)
|
||||
{
|
||||
if (!(nodeType == NodeType.Element || nodeType == NodeType.Document))
|
||||
|
@ -741,6 +785,17 @@ public class XhtmlNode implements IBaseXhtml {
|
|||
}
|
||||
|
||||
|
||||
public XhtmlNode backgroundColor(String color) {
|
||||
style("background-color: "+color);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
public boolean isPara() {
|
||||
return "p".equals(name);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -536,8 +536,9 @@ private boolean elementIsOk(String name) throws FHIRFormatError {
|
|||
return;
|
||||
else
|
||||
{
|
||||
if (mustBeWellFormed)
|
||||
if (mustBeWellFormed) {
|
||||
throw new FHIRFormatError("Malformed XHTML: Found \"</"+n.getName()+">\" expecting \"</"+node.getName()+">\""+descLoc());
|
||||
}
|
||||
for (int i = parents.size() - 1; i >= 0; i--)
|
||||
{
|
||||
if (parents.get(i).getName().equals(n))
|
||||
|
|
|
@ -605,3 +605,5 @@ FHIRPATH_NUMERICAL_ONLY = Error evaluating FHIRPath expression: The function {0}
|
|||
FHIRPATH_DECIMAL_ONLY = Error evaluating FHIRPath expression: The function {0} can only be used on a decimal but found {1}
|
||||
FHIRPATH_FOCUS_PLURAL = Error evaluating FHIRPath expression: focus for {0} has more than one value
|
||||
REFERENCE_REF_SUSPICIOUS = The syntax of the reference ''{0}'' looks incorrect, and it should be checked
|
||||
TYPE_SPECIFIC_CHECKS_DT_QTY_NO_ANNOTATIONS = UCUM Codes that contain human readable annotations like {0} can be misleading. Best Practice is not to use annotations in the UCUM code, and rather to make sure that Quantity.unit is correctly human readable
|
||||
XHTML_XHTML_ELEMENT_ILLEGAL_IN_PARA = Illegal element name inside in a paragraph in the XHTML (''{0}'')
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>org.hl7.fhir.core</artifactId>
|
||||
<version>5.1.8-SNAPSHOT</version>
|
||||
<version>5.1.9-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>org.hl7.fhir.core</artifactId>
|
||||
<version>5.1.8-SNAPSHOT</version>
|
||||
<version>5.1.9-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -2,7 +2,12 @@ package org.hl7.fhir.validation;
|
|||
|
||||
import static org.apache.commons.lang3.StringUtils.isBlank;
|
||||
|
||||
import java.text.DateFormat;
|
||||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
|
||||
/*
|
||||
Copyright (c) 2011+, HL7, Inc.
|
||||
|
@ -64,6 +69,8 @@ POSSIBILITY OF SUCH DAMAGE.
|
|||
|
||||
*/
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.hl7.fhir.r5.context.IWorkerContext;
|
||||
import org.hl7.fhir.r5.elementmodel.Element;
|
||||
|
@ -78,10 +85,28 @@ 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;
|
||||
import org.hl7.fhir.validation.BaseValidator.ValidationControl;
|
||||
import org.hl7.fhir.validation.instance.utils.IndexedElement;
|
||||
|
||||
public class BaseValidator {
|
||||
|
||||
public class ValidationControl {
|
||||
private boolean allowed;
|
||||
private IssueSeverity level;
|
||||
|
||||
public ValidationControl(boolean allowed, IssueSeverity level) {
|
||||
super();
|
||||
this.allowed = allowed;
|
||||
this.level = level;
|
||||
}
|
||||
public boolean isAllowed() {
|
||||
return allowed;
|
||||
}
|
||||
public IssueSeverity getLevel() {
|
||||
return level;
|
||||
}
|
||||
}
|
||||
|
||||
protected final String META = "meta";
|
||||
protected final String ENTRY = "entry";
|
||||
protected final String DOCUMENT = "document";
|
||||
|
@ -98,7 +123,16 @@ public class BaseValidator {
|
|||
protected Source source;
|
||||
protected IWorkerContext context;
|
||||
protected TimeTracker timeTracker = new TimeTracker();
|
||||
|
||||
|
||||
/**
|
||||
* Use to control what validation the validator performs.
|
||||
* Using this, you can turn particular kinds of validation on and off
|
||||
* In addition, you can override the error | warning | hint level and make it a different level
|
||||
*
|
||||
* There is no way to do this using the command line validator; it's a service that is only
|
||||
* offered when the validator is hosted in some other process
|
||||
*/
|
||||
private Map<String, ValidationControl> validationControl = new HashMap<>();
|
||||
|
||||
public BaseValidator(IWorkerContext context){
|
||||
this.context = context;
|
||||
|
@ -287,7 +321,10 @@ public class BaseValidator {
|
|||
protected boolean txRule(List<ValidationMessage> errors, String txLink, IssueType type, int line, int col, String path, boolean thePass, String theMessage, Object... theMessageArguments) {
|
||||
if (!thePass) {
|
||||
String message = context.formatMessage(theMessage, theMessageArguments);
|
||||
errors.add(new ValidationMessage(Source.TerminologyEngine, type, line, col, path, message, IssueSeverity.ERROR).setTxLink(txLink));
|
||||
ValidationMessage vm = new ValidationMessage(Source.TerminologyEngine, type, line, col, path, message, IssueSeverity.ERROR).setMessageId(theMessage);
|
||||
if (checkMsgId(theMessage, vm)) {
|
||||
errors.add(vm.setTxLink(txLink));
|
||||
}
|
||||
}
|
||||
return thePass;
|
||||
}
|
||||
|
@ -415,10 +452,23 @@ public class BaseValidator {
|
|||
|
||||
protected ValidationMessage addValidationMessage(List<ValidationMessage> errors, IssueType type, int line, int col, String path, String msg, IssueSeverity theSeverity, Source theSource, String id) {
|
||||
ValidationMessage validationMessage = new ValidationMessage(theSource, type, line, col, path, msg, theSeverity).setMessageId(id);
|
||||
errors.add(validationMessage);
|
||||
if (checkMsgId(id, validationMessage)) {
|
||||
errors.add(validationMessage);
|
||||
}
|
||||
return validationMessage;
|
||||
}
|
||||
|
||||
public boolean checkMsgId(String id, ValidationMessage vm) {
|
||||
if (id != null && validationControl.containsKey(id)) {
|
||||
ValidationControl control = validationControl.get(id);
|
||||
if (control.level != null) {
|
||||
vm.setLevel(control.level);
|
||||
}
|
||||
return control.isAllowed();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Test a rule and add a {@link IssueSeverity#WARNING} validation message if the validation fails
|
||||
*
|
||||
|
@ -429,7 +479,10 @@ public class BaseValidator {
|
|||
protected boolean txWarning(List<ValidationMessage> errors, String txLink, IssueType type, int line, int col, String path, boolean thePass, String msg, Object... theMessageArguments) {
|
||||
if (!thePass) {
|
||||
String nmsg = context.formatMessage(msg, theMessageArguments);
|
||||
errors.add(new ValidationMessage(Source.TerminologyEngine, type, line, col, path, nmsg, IssueSeverity.WARNING).setTxLink(txLink).setMessageId(msg));
|
||||
ValidationMessage vmsg = new ValidationMessage(Source.TerminologyEngine, type, line, col, path, nmsg, IssueSeverity.WARNING).setTxLink(txLink).setMessageId(msg);
|
||||
if (checkMsgId(msg, vmsg)) {
|
||||
errors.add(vmsg);
|
||||
}
|
||||
}
|
||||
return thePass;
|
||||
|
||||
|
@ -567,7 +620,10 @@ public class BaseValidator {
|
|||
}
|
||||
|
||||
protected void addValidationMessage(List<ValidationMessage> errors, IssueType type, String path, String msg, String html, IssueSeverity theSeverity, String id) {
|
||||
errors.add(new ValidationMessage(source, type, -1, -1, path, msg, html, theSeverity).setMessageId(id));
|
||||
ValidationMessage vm = new ValidationMessage(source, type, -1, -1, path, msg, html, theSeverity);
|
||||
if (checkMsgId(id, vm)) {
|
||||
errors.add(vm.setMessageId(id));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -802,5 +858,9 @@ public class BaseValidator {
|
|||
}
|
||||
}
|
||||
|
||||
public Map<String, ValidationControl> getValidationControl() {
|
||||
return validationControl;
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -34,6 +34,7 @@ import org.hl7.fhir.r5.utils.*;
|
|||
import org.hl7.fhir.r5.utils.IResourceValidator.*;
|
||||
import org.hl7.fhir.r5.utils.StructureMapUtilities.ITransformerServices;
|
||||
import org.hl7.fhir.utilities.i18n.I18nConstants;
|
||||
import org.hl7.fhir.validation.BaseValidator.ValidationControl;
|
||||
import org.hl7.fhir.validation.cli.services.StandAloneValidatorFetcher.IPackageInstaller;
|
||||
import org.hl7.fhir.validation.instance.InstanceValidator;
|
||||
import org.hl7.fhir.utilities.IniFile;
|
||||
|
@ -309,6 +310,7 @@ public class ValidationEngine implements IValidatorResourceFetcher, IPackageInst
|
|||
private List<ImplementationGuide> igs = new ArrayList<>();
|
||||
private boolean showTimes;
|
||||
private List<BundleValidationRule> bundleValidationRules = new ArrayList<>();
|
||||
private Map<String, ValidationControl> validationControl = new HashMap<>();
|
||||
|
||||
private class AsteriskFilter implements FilenameFilter {
|
||||
String dir;
|
||||
|
@ -1580,6 +1582,7 @@ public class ValidationEngine implements IValidatorResourceFetcher, IPackageInst
|
|||
validator.setFetcher(this);
|
||||
validator.getImplementationGuides().addAll(igs);
|
||||
validator.getBundleValidationRules().addAll(bundleValidationRules);
|
||||
validator.getValidationControl().putAll(validationControl );
|
||||
return validator;
|
||||
}
|
||||
|
||||
|
@ -2391,9 +2394,27 @@ public class ValidationEngine implements IValidatorResourceFetcher, IPackageInst
|
|||
return pcm.packageExists(id, ver);
|
||||
}
|
||||
|
||||
|
||||
public void loadPackage(String id, String ver) throws IOException, FHIRException {
|
||||
loadIg(id+(ver == null ? "" : "#"+ver), true);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Systems that host the ValidationEngine can use this to control what validation the validator performs.
|
||||
*
|
||||
* Using this, you can turn particular kinds of validation on and off. In addition, you can override
|
||||
* the error | warning | hint level and make it a different level.
|
||||
*
|
||||
* Each entry has
|
||||
* * 'allowed': a boolean flag. if this is false, the Validator will not report the error.
|
||||
* * 'level' : set to error, warning, information
|
||||
*
|
||||
* Entries are registered by ID, using the IDs in /org.hl7.fhir.utilities/src/main/resources/Messages.properties
|
||||
*
|
||||
* This feature is not supported by the validator CLI - and won't be. It's for systems hosting
|
||||
* the validation framework in their own implementation context
|
||||
*/
|
||||
public Map<String, ValidationControl> getValidationControl() {
|
||||
return validationControl;
|
||||
}
|
||||
|
||||
}
|
|
@ -491,17 +491,17 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
|||
return false;
|
||||
}
|
||||
|
||||
private void bpCheck(List<ValidationMessage> errors, IssueType invalid, int line, int col, String literalPath, boolean test, String message) {
|
||||
private void bpCheck(List<ValidationMessage> errors, IssueType invalid, int line, int col, String literalPath, boolean test, String message, Object... theMessageArguments) {
|
||||
if (bpWarnings != null) {
|
||||
switch (bpWarnings) {
|
||||
case Error:
|
||||
rule(errors, invalid, line, col, literalPath, test, message);
|
||||
rule(errors, invalid, line, col, literalPath, test, message, theMessageArguments);
|
||||
break;
|
||||
case Warning:
|
||||
warning(errors, invalid, line, col, literalPath, test, message);
|
||||
warning(errors, invalid, line, col, literalPath, test, message, theMessageArguments);
|
||||
break;
|
||||
case Hint:
|
||||
hint(errors, invalid, line, col, literalPath, test, message);
|
||||
hint(errors, invalid, line, col, literalPath, test, message, theMessageArguments);
|
||||
break;
|
||||
default: // do nothing
|
||||
break;
|
||||
|
@ -2070,7 +2070,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
|||
checkInnerNS(errors, e, path, xhtml.getChildNodes());
|
||||
rule(errors, IssueType.INVALID, e.line(), e.col(), path, "div".equals(xhtml.getName()), I18nConstants.XHTML_XHTML_NAME_INVALID, ns);
|
||||
// check that no illegal elements and attributes have been used
|
||||
checkInnerNames(errors, e, path, xhtml.getChildNodes());
|
||||
checkInnerNames(errors, e, path, xhtml.getChildNodes(), false);
|
||||
checkUrls(errors, e, path, xhtml.getChildNodes());
|
||||
}
|
||||
}
|
||||
|
@ -2171,7 +2171,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
|||
"http://hl7.org/fhirpath/System.Decimal", "http://hl7.org/fhirpath/System.Date", "http://hl7.org/fhirpath/System.Time", "http://hl7.org/fhirpath/System.DateTime", "http://hl7.org/fhirpath/System.Quantity");
|
||||
}
|
||||
|
||||
private void checkInnerNames(List<ValidationMessage> errors, Element e, String path, List<XhtmlNode> list) {
|
||||
private void checkInnerNames(List<ValidationMessage> errors, Element e, String path, List<XhtmlNode> list, boolean inPara) {
|
||||
for (XhtmlNode node : list) {
|
||||
if (node.getNodeType() == NodeType.Comment) {
|
||||
rule(errors, IssueType.INVALID, e.line(), e.col(), path, !node.getContent().startsWith("DOCTYPE"), I18nConstants.XHTML_XHTML_DOCTYPE_ILLEGAL);
|
||||
|
@ -2181,9 +2181,8 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
|||
"p", "br", "div", "h1", "h2", "h3", "h4", "h5", "h6", "a", "span", "b", "em", "i", "strong",
|
||||
"small", "big", "tt", "small", "dfn", "q", "var", "abbr", "acronym", "cite", "blockquote", "hr", "address", "bdo", "kbd", "q", "sub", "sup",
|
||||
"ul", "ol", "li", "dl", "dt", "dd", "pre", "table", "caption", "colgroup", "col", "thead", "tr", "tfoot", "tbody", "th", "td",
|
||||
"code", "samp", "img", "map", "area"
|
||||
|
||||
), I18nConstants.XHTML_XHTML_ELEMENT_ILLEGAL, node.getName());
|
||||
"code", "samp", "img", "map", "area"), I18nConstants.XHTML_XHTML_ELEMENT_ILLEGAL, node.getName());
|
||||
|
||||
for (String an : node.getAttributes().keySet()) {
|
||||
boolean ok = an.startsWith("xmlns") || Utilities.existsInList(an,
|
||||
"title", "style", "class", ID, "lang", "xml:lang", "dir", "accesskey", "tabindex",
|
||||
|
@ -2195,11 +2194,15 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
|||
"img.alt", "img.longdesc", "img.height", "img.width", "img.usemap", "img.ismap", "map.name", "area.shape",
|
||||
"area.coords", "area.href", "area.nohref", "area.alt", "table.summary", "table.width", "table.border",
|
||||
"table.frame", "table.rules", "table.cellspacing", "table.cellpadding", "pre.space", "td.nowrap"
|
||||
);
|
||||
if (!ok)
|
||||
);
|
||||
if (!ok) {
|
||||
rule(errors, IssueType.INVALID, e.line(), e.col(), path, false, I18nConstants.XHTML_XHTML_ATTRIBUTE_ILLEGAL, an, node.getName());
|
||||
}
|
||||
}
|
||||
checkInnerNames(errors, e, path, node.getChildNodes());
|
||||
|
||||
rule(errors, IssueType.INVALID, e.line(), e.col(), path, !(inPara && Utilities.existsInList(node.getName(), "div", "blockquote", "table", "ol", "ul", "p")) , I18nConstants.XHTML_XHTML_ELEMENT_ILLEGAL_IN_PARA, node.getName());
|
||||
|
||||
checkInnerNames(errors, e, path, node.getChildNodes(), inPara || "p".equals(node.getName()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2320,6 +2323,14 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
|||
if (system != null || code != null ) {
|
||||
checkCodedElement(theErrors, thePath, element, theProfile, definition, false, false, theStack, code, system, unit);
|
||||
}
|
||||
|
||||
if (code != null && "http://unitsofmeasure.org".equals(system)) {
|
||||
int b = code.indexOf("{");
|
||||
int e = code.indexOf("}");
|
||||
if (b >= 0 && e > 0 && b < e) {
|
||||
bpCheck(theErrors, IssueType.BUSINESSRULE, element.line(), element.col(), thePath, !code.contains("{"), I18nConstants.TYPE_SPECIFIC_CHECKS_DT_QTY_NO_ANNOTATIONS, code.substring(b, e+1));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void checkAttachment(List<ValidationMessage> errors, String path, Element element, StructureDefinition theProfile, ElementDefinition definition, boolean theInCodeableConcept, boolean theCheckDisplayInContext, NodeStack theStack) {
|
||||
|
|
|
@ -36,6 +36,7 @@ import org.hl7.fhir.r5.test.utils.TestingUtilities;
|
|||
import org.hl7.fhir.r5.utils.FHIRPathEngine;
|
||||
import org.hl7.fhir.r5.utils.FHIRPathEngine.IEvaluationContext;
|
||||
import org.hl7.fhir.r5.utils.IResourceValidator;
|
||||
import org.hl7.fhir.r5.utils.IResourceValidator.BestPracticeWarningLevel;
|
||||
import org.hl7.fhir.r5.utils.IResourceValidator.BundleValidationRule;
|
||||
import org.hl7.fhir.r5.utils.IResourceValidator.IValidatorResourceFetcher;
|
||||
import org.hl7.fhir.r5.utils.IResourceValidator.ReferenceValidationPolicy;
|
||||
|
@ -204,6 +205,9 @@ public class ValidationTestSuite implements IEvaluationContext, IValidatorResour
|
|||
} else {
|
||||
val.setDebug(false);
|
||||
}
|
||||
if (content.has("best-practice")) {
|
||||
val.setBestPracticeWarningLevel(BestPracticeWarningLevel.valueOf(content.get("best-practice").getAsString()));
|
||||
}
|
||||
if (content.has("examples")) {
|
||||
val.setAllowExamples(content.get("examples").getAsBoolean());
|
||||
} else {
|
||||
|
@ -367,10 +371,12 @@ public class ValidationTestSuite implements IEvaluationContext, IValidatorResour
|
|||
}
|
||||
if (!TestingUtilities.context(version).isNoTerminologyServer() || !focus.has("tx-dependent")) {
|
||||
Assert.assertEquals("Test " + name + (profile == null ? "" : " profile: "+ profile) + ": Expected " + Integer.toString(java.get("errorCount").getAsInt()) + " errors, but found " + Integer.toString(ec) + ".", java.get("errorCount").getAsInt(), ec);
|
||||
if (java.has("warningCount"))
|
||||
if (java.has("warningCount")) {
|
||||
Assert.assertEquals( "Test " + name + (profile == null ? "" : " profile: "+ profile) + ": Expected " + Integer.toString(java.get("warningCount").getAsInt()) + " warnings, but found " + Integer.toString(wc) + ".", java.get("warningCount").getAsInt(), wc);
|
||||
if (java.has("infoCount"))
|
||||
}
|
||||
if (java.has("infoCount")) {
|
||||
Assert.assertEquals( "Test " + name + (profile == null ? "" : " profile: "+ profile) + ": Expected " + Integer.toString(java.get("infoCount").getAsInt()) + " hints, but found " + Integer.toString(hc) + ".", java.get("infoCount").getAsInt(), hc);
|
||||
}
|
||||
}
|
||||
if (java.has("error-locations")) {
|
||||
JsonArray el = java.getAsJsonArray("error-locations");
|
||||
|
|
4
pom.xml
4
pom.xml
|
@ -13,11 +13,11 @@
|
|||
each other. It is fine to bump the point version of this POM without affecting
|
||||
HAPI FHIR.
|
||||
-->
|
||||
<version>5.1.8-SNAPSHOT</version>
|
||||
<version>5.1.9-SNAPSHOT</version>
|
||||
|
||||
<properties>
|
||||
<hapi_fhir_version>5.1.0</hapi_fhir_version>
|
||||
<validator_test_case_version>1.1.35</validator_test_case_version>
|
||||
<validator_test_case_version>1.1.36</validator_test_case_version>
|
||||
<junit_jupiter_version>5.6.2</junit_jupiter_version>
|
||||
<maven_surefire_version>3.0.0-M4</maven_surefire_version>
|
||||
<jacoco_version>0.8.5</jacoco_version>
|
||||
|
|
Loading…
Reference in New Issue